Commit 5d4a1199b5

5d4a1199b51602f64985982c82de9b7c537583c6

parent: e86c55dbfa

Unsigned

cmc <hello@cleberg.net> · 2024-07-29 00:46 UTC

conversion from Zola to Weblorg

Layout: unified · split

.gitignore +1
@@ -1,3 +1,4 @@
1.build
12.DS_Store
23.vscode
34public
README.md deleted −32
@@ -1,32 +0,0 @@
1# cleberg.net
2
3[cleberg.net](https://cleberg.net) is my personal webpage and blog.
4
5## Overview
6
7This website & blog uses Markdown for content, HTML for templates, built and
8published with [Zola](https://getzola.org).
9
10## Configuration
11
12All configuration options are available within the =config.toml= file. Refer to
13the Zola documentation for further configuration options.
14
15## Building
16
17To get running:
18
19```sh
20git clone https://git.sr.ht/~cyborg/cleberg.net && \
21cd cleberg.net && \
22zola build
23```
24
25## Publishing
26
27Use `zola build` and deploy to whichever deployment target you prefer.
28
29## Tasks
30
31Refer to the [todo page](https://todo.sr.ht/~cyborg/cleberg.net) for open
32tickets or to submit a new ticket.
README.org added +44
@@ -0,0 +1,44 @@
1* cleberg.net
2
3[[https://cleberg.net][cleberg.net]] is my personal webpage and blog.
4
5** Overview
6
7This website & blog uses [[https://orgmode.org/][Org-Mode]], published with [[https://github.com/emacs-love/weblorg][weblorg]].
8
9** Configuration
10
11All configuration options are available within the =publish.el= file. Refer to
12the weblorg documentation for further configuration options.
13
14** Building & Publishing
15
16Local testing is available via [[https://www.gnu.org/software/emacs/][Emacs]] or through the command line.
17
18#+begin_src sh
19git clone https://git.cleberg.net/cleberg.net.git && \
20cd cleberg.net && \
21emacs -nw
22#+end_src
23
24Within Emacs, open any of the repository files. In Doom, I do this with =Spc f
25f= and selecting =README.org=. Make any changes necessary to customize the
26project.
27
28To publish, you can use the =build.sh= script (change the deployment target!) or
29you can run the following commands.
30
31Use the =ENV= environment variable to determine which base URL weblorg will use.
32If ENV is ommitted, it will default to =localhost:8000=. If =ENV=prod=, weblorg
33will look in the =publish.el= file for the production base URL.
34
35#+begin_src sh
36ENV=prod emacs --script publish.el
37#+end_src
38
39The files will be published to the =.build= directory. You can deploy these
40files to the target through any number of methods, such as =scp= or SFTP.
41
42** Tasks
43
44Refer to the [[https://todo.sr.ht/~cyborg/cleberg.net][todo page]] for open tickets or to submit a new ticket.
config.toml deleted −21
@@ -1,21 +0,0 @@
1# Site settings
2base_url = "https://cleberg.net"
3title = "cleberg.net"
4description = "The personal website of Christian Cleberg."
5author = "Christian Cleberg"
6
7# RSS settings
8generate_feeds = true
9feed_filenames = ["atom.xml"]
10
11[markdown]
12highlight_code = true
13highlight_theme = "css"
14highlight_themes_css = [
15 { theme = "ayu-dark", filename = "syntax-theme-dark.css" },
16 { theme = "inspired-github", filename = "syntax-theme-light.css" },
17]
18external_links_target_blank = true
19lazy_async_image = true
20
21[extra]
content/blog/#2022-03-24-server-hardening.org# added +293
@@ -0,0 +1,293 @@
1#+date: <2022-03-24>
2#+title: Hardening a Public-Facing Home Server
3#+description:
4
5
6* Post Updates
7
8#+begin_quote
9After reviewing this post today (2022-10-04), I noticed quite a few gaps in my
10write-up and wanted to add a few things, even though this blog is really just a
11retrospective and knowledge dump for myself. I left things intact and simply
12crossed them out (+like this+) for posterity.
13#+end_quote
14
15* Planning Data Flows & Security
16
17** My Personal Data Flow
18
19#+begin_src
20 ┌───────┐ ┌─────────────────┐
21 ┌──► VLAN1 ├───► Private Devices │
22 │ └───────┘ └─────────────────┘
23┌──────────┐ ┌────────┐ ┌──────────┐ ┌────────┐ │
24│ Internet ├───► Router ├───► Firewall ├───► Switch ├──┤
25└──────────┘ └────────┘ └──────────┘ └────────┘ │
26 │ ┌───────┐ ┌───────────────┐
27 └──► VLAN2 ├───► Public Server │
28 └───────┘ └───────────────┘
29#+end_src
30
31** Thought Process
32
33To serve content from your home server and harden your security posture, you
34have to think about the transport of data from =server= to =client=.
35
36Let's start with the actual server itself. Think about the following:
37
38- Do I have a firewall enabled? Do I need to update this to allow new ports or
39 IPs?
40- Do I have an IPS/IDS that may prevent outside traffic?
41- Do I have any other security software installed?
42- Are the services hosted inside Docker containers, behind a reverse proxy, or
43 virtualized? If so, are they configured to allow outside traffic?
44
45Once the data leaves the server, where does it go? In my case, it goes to a
46managed switch. In this case, I asked the following:
47
48- What configurations is the switch using?
49- Am I using VLANs?
50 - Yes, I am using 802.1Q VLANs.
51- Are the VLANs configured properly?
52 - Yes, as shown in the Switch section below, I have a separate VLAN to allow
53 outside traffic to and from the server alone. No other devices, except for a
54 service port, and in that VLAN.
55
56At this point, the data has been processed through the switch. Where does it go
57next? In my case, it's pretty simple: it goes to the router/modem device.
58
59- Does my ISP block any ports that I need?
60 - This is an important step that a lot of people run into when self-hosting at
61 home. Use an online port-checker tool for your IP or call your ISP if you
62 think ports are blocked.
63- Is there a router firewall?
64 - Yes, I checked that it's configured to allow the ports I need to run my
65 services publicly. Common web servers and reverse proxies require ports 80
66 and 443, but other services like media servers or games can require unique
67 ports, so be sure to check the documentation for your service(s).
68- Are there any other settings affecting inbound/outbound traffic?
69 - Schedules or access blocks
70 - Static Routing
71 - QoS
72 - Port Forwarding
73 - DMZ Hosting
74 - Remote Management (this can sometimes mess with services that also require
75 the use of ports 80 and 443)
76
77Once the data leaves my router, it goes to the upstream ISP and can be accessed
78publicly.
79
80*** Server
81
82The services I run on my server are installed straight into the OS, without any
83use of Docker or VMs, so I don't need any extra application configuration to
84make them accessible to the outside world.+
85
86As of 2022-10-04, the paragraph above is no longer true as I now run a reverse
87proxy with Nginx and host many services inside Docker. However, it doesn't
88change anything regarding this post as I still just need to open ports 80 & 443
89and create the necessary website configuration files.
90
91When creating new services - either installed directly on bare metal or within
92something like Docker - I ensure that I read through the documentation
93thoroughly to understand a few key things: - What network activities should this
94app perform (if any)? Using which ports and protocols? - Does this app require
95any commands/services to be run as =root=? - Does this app log errors,
96authentication failures/successes, or anything else that would be useful for an
97investigation?
98
99For extra security, I use limit all incoming connections to SSH connections
100through my server firewall (=ufw=) and disable common SSH settings. After all of
101that, I use =fail2ban= as a preventative measure against brute-force login
102attempts.
103
104As another piece of security, you can randomize your SSH port to ensure that
105random scanners or attackers can't easily try to force their way into your
106network. For example, you can edit the port rules in your server to block all
107connection requests to port =22= but forward all remote connections from port
108=12345= to your server's port =22=. Then you just need to SSH to your network
109via your randomized port.
110
111** =ufw=
112
113To see how to configure =ufw=, see my other post: [[/blog/ufw.html][Secure Your
114Network with the Uncomplicated Firewall]].
115
116The general notion with an on-device firewall is that you want to deny all
117incoming connections by default and then selectively open certain ports for
118services or users that you know need access.
119
120If you know that you will only be logging into this server from a certain set or
121list of IPs, you can always set the firewall to only allow connections to port
12222 from those IPs.
123
124For a quick start to only allow SSH connections to the server, use this:
125
126#+begin_src sh
127sudo ufw default deny incoming
128sudo ufw default allow outgoing
129sudo ufw allow 22
130sudo ufw enable
131#+end_src
132
133** =ssh=
134
1351. Using SSH Keys
136
137 First, make sure you have an SSH keypair generated on the device(s) that
138 you'll be using to log in to the server. If you don't have an SSH key, run
139 this command:
140
141 #+begin_src sh
142 ssh-keygen
143 #+end_src
144
145 Now that we have an SSH key, copy it to the server with the following
146 command, which will ask for the user's password before accepting the key:
147
148 #+begin_src sh
149 ssh-copy-id my_user@my_server
150 #+end_src
151
152 If you have multiple keys, you'll need to specify which to use. After it's
153 complete, =ssh= back into the server as that user and make sure it doesn't
154 ask for a password.
155
1562. Disable Password & Root Authentication
157
158 Now that we can access the server without a password, we will disable
159 password authentication and disable anyone from using =ssh= to login as
160 =root=.
161
162 To do this, open the =sshd_config= file:
163
164 #+begin_src sh
165 sudo nano /etc/ssh/sshd_config
166 #+end_src
167
168 You'll need to update the parameters to the values below. If one of these
169 rules is commented-out or doesn't exist, create the rule at the bottom of the
170 file.
171
172 #+begin_src config
173 PermitRootLogin no
174 PasswordAuthentication no
175 PubkeyAuthentication yes
176 #+end_src
177
178 Finally, restart the =ssh= service:
179
180 #+begin_src sh
181 sudo systemctl restart sshd.service
182 #+end_src
183
184 To test that everything's working so far, open ANOTHER terminal and try
185 logging in as =root= over SSH. It is very important that you keep your
186 current SSH session open and test with an additional session, or you will
187 lock yourself out at some point and will need to use a recovery method (e.g.,
188 hooking monitor up to home server) to get yourself back in.
189
1903. Enable MFA for =ssh=
191
192 This part is optional, but I highly recommend it. So far, we've ensured that
193 no one can log into our user on the server without using our secret key, and
194 we've ensured that no one can log in remotely as =root=. Next, you can enable
195 MFA authentication for =ssh= connections.
196
197 This process involves editing a couple files and installing an MFA package,
198 so I will not include all the details in this post. To see how to configure
199 MFA for =ssh=, see my other post: [[/blog/ssh-mfa.html][Enabling MFA for
200 SSH]].
201
202** =fail2ban=
203
204I haven't written a post on how I use =fail2ban=, but it's quite simple. I use
205the default =sshd= jail, but you can always create new jails for respective
206applications or ports. For example, if you use Nginx as your web server, you can
207use the =nginx-http-auth= jail.
208
209In order to get it up and running, use the following commands:
210
211#+begin_src sh
212sudo apt install fail2ban
213sudo fail2ban-client start sshd
214sudo fail2ban-client status sshd
215#+end_src
216
217This should be used as a last-resort defense and shouldn't be a replacement for
218the security measures mentioned above.
219
220* Switch
221
222Between the router and any local devices is my managed switch, which is used to
223create VLANs. The example below shows how I would isolate the VLANs if I were
224starting to host a single service at home.
225
226** 802.1Q VLAN Configuration
227
228In this configuration, port 8 is the public server that needs to be accessed
229from the outside. Port 23 is my 'dedicated service port' for this server. In
230order to SSH to this server, I need to plug my laptop into port 23 or else I
231cannot SSH. Otherwise, I'd need to hook up a monitor and keyboard directly to
232the server to manage it.
233
234#+begin_verse
235#+end_verse
236
237| VLAN ID | VLAN Name | Member Ports | Tagged Ports | Untagged Ports |
238|---------+-----------+--------------+--------------+----------------|
239| 1 | Default | 1-24 | 1-24 | |
240| 2 | Server | 1,8,23 | 1,8,23 | |
241
242** 802.1Q VLAN PVID Setting
243
244Once the VLAN is created, I simply add the =VLAN ID= of =2= as the =PVID= for
245any related ports (in this case, see that ports =8= and =23= have a PVID of
246=2=).
247
248| Port | PVID |
249|------+------|
250| 1 | 1 |
251| 2 | 1 |
252| 3 | 1 |
253| 4 | 1 |
254| 5 | 1 |
255| 6 | 1 |
256| 7 | 1 |
257| 8 | 2 |
258| 9 | 1 |
259| 10 | 1 |
260| 11 | 1 |
261| 12 | 1 |
262| 13 | 1 |
263| 14 | 1 |
264| 15 | 1 |
265| 16 | 1 |
266| 17 | 1 |
267| 18 | 1 |
268| 19 | 1 |
269| 20 | 1 |
270| 21 | 1 |
271| 22 | 1 |
272| 23 | 2 |
273| 24 | 1 |
274
275* Router
276
277On my router, the configuration was as easy as opening the firewall settings and
278unblocking the ports I needed for my services (e.g., HTTP/S, Plex, SSH, MySQL,
279etc.).
280
281Since I'm relying on an ISP-provided modem/router combo for now (not by
282choice), I do not use any other advanced settings on my router that would
283inhibit any valid traffic to these services.
284
285The paragraph above regarding the ISP-owned router is no longer accurate as I
286now use the Ubiquiti Unifi Dream Machine Pro as my router. Within this router, I
287enabled port forwarding/firewall rules, segregate the network based on the
288device, and enable traffic restrictions (e.g., silently drop traffic from
289certain countries and threat categories).
290
291If you have the option with your ISP, I recommend using a personal router with
292software that you are familiar with so that you can explore all the options
293available to you.
content/blog/2018-11-28-aes-encryption.md deleted −118
@@ -1,118 +0,0 @@
1+++
2date = 2018-11-28
3title = "AES Encryption"
4description = ""
5draft = false
6+++
7
8# Basic AES
9
10If you're not familiar with encryption techniques,
11[AES](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) is the
12**Advanced Encryption Standard**. This specification was established by the
13National Institute of Standards and Technology, sub-selected from the Rijndael
14family of ciphers (128, 192, and 256 bits) in 2001. Furthering its popularity
15and status, the US government chose AES as their default encryption method for
16top-secret data, removing the previous standard which had been in place since 1977.
17
18AES has proven to be an extremely safe encryption method, with 7-round and
198-round attacks making no material improvements since the release of this
20encryption standard almost two decades ago.
21
22> Though many papers have been published on the cryptanalysis of AES, the
23> fastest single-key attacks on round-reduced AES variants [20, 33] so far are
24> only slightly more powerful than those proposed 10 years ago [23,24].
25>
26> - [Bogdonav, et
27> al.](http://research.microsoft.com/en-us/projects/cryptanalysis/aesbc.pdf)
28
29# How Secure is AES?
30
31In theory, AES-256 is non-crackable due to the massive number of combinations
32that can be produced. However, AES-128 is no longer recommended as a viable
33implementation to protect important data.
34
35A semi-short [comic
36strip](http://www.moserware.com/2009/09/stick-figure-guide-to-advanced.html)
37from Moserware quickly explains AES for the public to understand. Basically AES
38encrypts the data by obscuring the relationship between the data and the
39encrypted data. Additionally, this method spreads the message out. Lastly, the
40key produced by AES is the secret to decrypting it. Someone may know the method
41of AES, but without the key, they are powerless.
42
43To obscure and spread the data out, AES creates a substitution-permutation
44network. Wikipedia has a wonderful [example of an SP
45network](https://upload.wikimedia.org/wikipedia/commons/thumb/c/cd/SubstitutionPermutationNetwork2.png/468px-SubstitutionPermutationNetwork2.png)
46available. This network sends the data through a set of S boxes (using the
47unique key) to substitute the bits with another block of bits. Then, a P box
48will permutate, or rearrange, the bits. This is done over and over, with the key
49being derived from the last round. For AES, the key size specifies the number of
50transformation rounds: 10, 12, and 14 rounds for 128-bit, 192-bit, and 256-bit
51keys, respectively.
52
53# The Process
54
551. **KeyExpansion**: Using [Rijndael's key
56 schedule](https://en.m.wikipedia.org/wiki/Advanced_Encryption_Standard), the
57 keys are dynamically generated.
582. **AddRoundKey**: Each byte of the data is combined with this key using
59 bitwise xor.
603. **SubBytes**: This is followed by the substitution of each byte of data.
614. **ShiftRows**: Then, the final three rows are shifted a certain number of
62 steps, dictated by the cipher.
635. **MixColumns**: After the rows have been shifted, the columns are mixed and
64 combined.
65
66This process does not necessarily stop after one full round. Steps 2 through 5
67will repeat for the number of rounds specified by the key. However, the final
68round excludes the MixColumns step. As you can see, this is a fairly complex
69process. One must have a solid understanding of general mathematic principles to
70fully understand how the sequence works (and to even attempt to find a
71weakness).
72
73According to research done by Bogdanov et al., it would take billions of years
74to brute force a 126-bit key with current hardware. Additionally, this brute
75force attack would require storing 2^88^ bits of data! However, there are a few
76different attacks that have been used to show vulnerabilities with the use of
77this technology. Side-channel attacks use inadvertent leaks of data from the
78hardware or software, which can allow attackers to obtain the key or run
79programs on a user's hardware.
80
81Please note that this is not something you should run out and try to implement
82in your `Hello, World!` app after only a few hours of research. While AES
83(basically all encryption methods) is extremely efficient in what it does, it
84takes a lot of time and patience to understand. If you're looking for something
85which currently implements AES, check out the [Legion of the Bouncy
86Castle](https://www.bouncycastle.org/documentation.html) for Java
87implementations of cryptographic algorithms.
88
89# Why Does Encryption Matter?
90
91There are limitless reasons to enable encryption at-rest or in-transit for
92various aspects of your digital life. You can research specific examples, such
93as [Australia passes new law to thwart strong
94encryption](https://arstechnica.com/tech-policy/2018/12/australia-passes-new-law-to-thwart-strong-encryption/).
95However, I will simply list a few basic reasons to always enable encryption,
96where feasible:
97
981. Privacy is a human right and is recognized as a national right in some
99 countries (e.g., [US Fourth
100 Amendment](https://www.law.cornell.edu/wex/fourth_amendment)).
1012. "Why not?" Encryption rarely affects performance or speed, so there's usually
102 not a reason to avoid it in the first place.
1033. Your digital identity and activity (texts, emails, phone calls, online
104 accounts, etc.) are extremely valuable and can result in terrible
105 consequences, such as identity theft, if leaked to other parties. Encrypting
106 this data prevents such leaks from ruining lives.
1074. Wiping or factory-resetting does not actually wipe all data from the storage
108 device. There are methods to read data from the physical disks/boards inside
109 devices.
1105. Corporations, governments, and other nefarious groups/individuals are
111 actively looking for ways to collect personal information about anyone they
112 can. If someone's data is unencrypted, that person may become a target due to
113 the ease of data collection.
114
115​**Read More:**
116
117- [Federal Information Processing Standards Publication
118 197](http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf)
content/blog/2018-11-28-aes-encryption.org added +124
@@ -0,0 +1,124 @@
1#+title: AES Encryption
2#+date: <2018-11-28>
3#+description: A beginner's guide to AES encryption.
4
5* Basic AES
6
7If you're not familiar with encryption techniques,
8[[https://en.wikipedia.org/wiki/Advanced_Encryption_Standard][AES]] is
9the *Advanced Encryption Standard*. This specification was established
10by the National Institute of Standards and Technology, sub-selected from
11the Rijndael family of ciphers (128, 192, and 256 bits) in 2001.
12Furthering its popularity and status, the US government chose AES as
13their default encryption method for top-secret data, removing the
14previous standard which had been in place since 1977.
15
16AES has proven to be an extremely safe encryption method, with 7-round
17and 8-round attacks making no material improvements since the release of
18this encryption standard almost two decades ago.
19
20#+begin_quote
21Though many papers have been published on the cryptanalysis of AES, the
22fastest single-key attacks on round-reduced AES variants [20, 33] so far
23are only slightly more powerful than those proposed 10 years ago
24[23,24].
25
26- [[http://research.microsoft.com/en-us/projects/cryptanalysis/aesbc.pdf][Bogdonav,
27 et al.]]
28#+end_quote
29
30* How Secure is AES?
31
32In theory, AES-256 is non-crackable due to the massive number of
33combinations that can be produced. However, AES-128 is no longer
34recommended as a viable implementation to protect important data.
35
36A semi-short
37[[http://www.moserware.com/2009/09/stick-figure-guide-to-advanced.html][comic
38strip]] from Moserware quickly explains AES for the public to
39understand. Basically AES encrypts the data by obscuring the
40relationship between the data and the encrypted data. Additionally, this
41method spreads the message out. Lastly, the key produced by AES is the
42secret to decrypting it. Someone may know the method of AES, but without
43the key, they are powerless.
44
45To obscure and spread the data out, AES creates a
46substitution-permutation network. Wikipedia has a wonderful
47[[https://upload.wikimedia.org/wikipedia/commons/thumb/c/cd/SubstitutionPermutationNetwork2.png/468px-SubstitutionPermutationNetwork2.png][example
48of an SP network]] available. This network sends the data through a set
49of S boxes (using the unique key) to substitute the bits with another
50block of bits. Then, a P box will permutate, or rearrange, the bits.
51This is done over and over, with the key being derived from the last
52round. For AES, the key size specifies the number of transformation
53rounds: 10, 12, and 14 rounds for 128-bit, 192-bit, and 256-bit keys,
54respectively.
55
56* The Process
57
581. *KeyExpansion*: Using
59 [[https://en.m.wikipedia.org/wiki/Advanced_Encryption_Standard][Rijndael's
60 key schedule]], the keys are dynamically generated.
612. *AddRoundKey*: Each byte of the data is combined with this key using
62 bitwise xor.
633. *SubBytes*: This is followed by the substitution of each byte of
64 data.
654. *ShiftRows*: Then, the final three rows are shifted a certain number
66 of steps, dictated by the cipher.
675. *MixColumns*: After the rows have been shifted, the columns are mixed
68 and combined.
69
70This process does not necessarily stop after one full round. Steps 2
71through 5 will repeat for the number of rounds specified by the key.
72However, the final round excludes the MixColumns step. As you can see,
73this is a fairly complex process. One must have a solid understanding of
74general mathematic principles to fully understand how the sequence works
75(and to even attempt to find a weakness).
76
77According to research done by Bogdanov et al., it would take billions of
78years to brute force a 126-bit key with current hardware. Additionally,
79this brute force attack would require storing 2^{88} bits of data!
80However, there are a few different attacks that have been used to show
81vulnerabilities with the use of this technology. Side-channel attacks
82use inadvertent leaks of data from the hardware or software, which can
83allow attackers to obtain the key or run programs on a user's hardware.
84
85Please note that this is not something you should run out and try to
86implement in your =Hello, World!= app after only a few hours of
87research. While AES (basically all encryption methods) is extremely
88efficient in what it does, it takes a lot of time and patience to
89understand. If you're looking for something which currently implements
90AES, check out the
91[[https://www.bouncycastle.org/documentation.html][Legion of the Bouncy
92Castle]] for Java implementations of cryptographic algorithms.
93
94* Why Does Encryption Matter?
95
96There are limitless reasons to enable encryption at-rest or in-transit
97for various aspects of your digital life. You can research specific
98examples, such as
99[[https://arstechnica.com/tech-policy/2018/12/australia-passes-new-law-to-thwart-strong-encryption/][Australia
100passes new law to thwart strong encryption]]. However, I will simply
101list a few basic reasons to always enable encryption, where feasible:
102
1031. Privacy is a human right and is recognized as a national right in
104 some countries (e.g.,
105 [[https://www.law.cornell.edu/wex/fourth_amendment][US Fourth
106 Amendment]]).
1072. "Why not?" Encryption rarely affects performance or speed, so there's
108 usually not a reason to avoid it in the first place.
1093. Your digital identity and activity (texts, emails, phone calls,
110 online accounts, etc.) are extremely valuable and can result in
111 terrible consequences, such as identity theft, if leaked to other
112 parties. Encrypting this data prevents such leaks from ruining lives.
1134. Wiping or factory-resetting does not actually wipe all data from the
114 storage device. There are methods to read data from the physical
115 disks/boards inside devices.
1165. Corporations, governments, and other nefarious groups/individuals are
117 actively looking for ways to collect personal information about
118 anyone they can. If someone's data is unencrypted, that person may
119 become a target due to the ease of data collection.
120
121​*Read More:*
122
123- [[http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf][Federal
124 Information Processing Standards Publication 197]]
content/blog/2018-11-28-cpp-compiler.md deleted −136
@@ -1,136 +0,0 @@
1+++
2date = 2018-11-28
3title = "The C++ Compiler"
4description = ""
5draft = false
6+++
7
8# A Brief Introduction
9
10[C++](https://en.wikipedia.org/wiki/C%2B%2B) is a general-purpose programming
11language with object-oriented, generic, and functional features in addition to
12facilities for low-level memory manipulation.
13
14The source code, shown in the snippet below, must be compiled before it can be
15executed. There are many steps and intricacies to the compilation process, and
16this post was a personal exercise to learn and remember as much information as I
17can.
18
19```cpp
20#include <iostream>
21
22int main()
23{
24 std::cout << "Hello, world!\n";
25}
26```
27
28## Compilation Process
29
30### An Overview
31
32Compiling C++ projects is a frustrating task most days. Seemingly nonexistent
33errors keeping your program from successfully compiling can be annoying
34(especially since you know you wrote it perfectly the first time, right?).
35
36I'm learning more and more about C++ these days and decided to write this
37concept down so that I can cement it even further in my own head. However, C++
38is not the only compiled language. Check out [the Wikipedia entry for compiled
39languages](https://en.wikipedia.org/wiki/Compiled_language) for more examples of
40compiled languages.
41
42I'll start with a wonderful, graphical way to conceptualize the C++ compiler.
43View [The C++ Compilation
44Process](https://web.archive.org/web/20190419035048/http://faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html)
45by Kurt MacMahon, an NIU professor, to see the graphic and an explanation. The
46goal of the compilation process is to take the C++ code and produce a shared
47library, dynamic library, or an executable file.
48
49## Compilation Phases
50
51Let's break down the compilation process. There are four major steps to
52compiling C++ code.
53
54### Step 1
55
56The first step is to expand the source code file to meet all dependencies. The
57C++ preprocessor includes the code from all the header files, such as `#include
58<iostream>`. Now, what does that mean? The previous example includes the
59`iostream` header. This tells the computer that you want to use the `iostream`
60standard library, which contains classes and functions written in the core
61language. This specific header allows you to manipulate input/output streams.
62After all this, you'll end up which a temporary file that contains the expanded
63source code.
64
65In the example of the C++ code above, the `iostream` class would be included in
66the expanded code.
67
68### Step 2
69
70After the code is expanded, the compiler comes into play. The compiler takes the
71C++ code and converts this code into the assembly language, understood by the
72platform. You can see this in action if you head over to the [GodBolt Compiler
73Explorer](https://godbolt.org), which shows C++ being converted into assembly
74dynamically.
75
76For example, the `Hello, world!` code snippet above compiles into the following
77assembly code:
78
79```asm
80.LC0:
81 .string "Hello, world!\n"
82main:
83 push rbp
84 mov rbp, rsp
85 mov esi, OFFSET FLAT:.LC0
86 mov edi, OFFSET FLAT:_ZSt4cout
87 call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
88 mov eax, 0
89 pop rbp
90 ret
91__static_initialization_and_destruction_0(int, int):
92 push rbp
93 mov rbp, rsp
94 sub rsp, 16
95 mov DWORD PTR [rbp-4], edi
96 mov DWORD PTR [rbp-8], esi
97 cmp DWORD PTR [rbp-4], 1
98 jne .L5
99 cmp DWORD PTR [rbp-8], 65535
100 jne .L5
101 mov edi, OFFSET FLAT:_ZStL8__ioinit
102 call std::ios_base::Init::Init() [complete object constructor]
103 mov edx, OFFSET FLAT:__dso_handle
104 mov esi, OFFSET FLAT:_ZStL8__ioinit
105 mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
106 call __cxa_atexit
107.L5:
108 nop
109 leave
110 ret
111_GLOBAL__sub_I_main:
112 push rbp
113 mov rbp, rsp
114 mov esi, 65535
115 mov edi, 1
116 call __static_initialization_and_destruction_0(int, int)
117 pop rbp
118 ret
119```
120
121### Step 3
122
123Third, the assembly code generated by the compiler is assembled into the object
124code for the platform. Essentially, this is when the compiler takes the assembly
125code and assembles it into machine code in a binary format. After researching
126this online, I figured out that a lot of compilers will allow you to stop
127compilation at this step. This would be useful for compiling each source code
128file separately. This saves time later if a single file changes; only that file
129needs to be recompiled.
130
131### Step 4
132
133Finally, the object code file generated by the assembler is linked together with
134the object code files for any library functions used to produce a shared
135library, dynamic library, or an executable file. It replaces all references to
136undefined symbols with the correct addresses.
content/blog/2018-11-28-cpp-compiler.org added +136
@@ -0,0 +1,136 @@
1#+date: <2018-11-28>
2#+title: The C++ Compiler
3#+description:
4
5
6* A Brief Introduction
7
8[[https://en.wikipedia.org/wiki/C%2B%2B][C++]] is a general-purpose
9programming language with object-oriented, generic, and functional
10features in addition to facilities for low-level memory manipulation.
11
12The source code, shown in the snippet below, must be compiled before it
13can be executed. There are many steps and intricacies to the compilation
14process, and this post was a personal exercise to learn and remember as
15much information as I can.
16
17#+begin_src cpp
18#include <iostream>
19
20int main()
21{
22 std::cout << "Hello, world!\n";
23}
24#+end_src
25
26** Compilation Process
27
28*** An Overview
29
30Compiling C++ projects is a frustrating task most days. Seemingly
31nonexistent errors keeping your program from successfully compiling can
32be annoying (especially since you know you wrote it perfectly the first
33time, right?).
34
35I'm learning more and more about C++ these days and decided to write
36this concept down so that I can cement it even further in my own head.
37However, C++ is not the only compiled language. Check out
38[[https://en.wikipedia.org/wiki/Compiled_language][the Wikipedia entry
39for compiled languages]] for more examples of compiled languages.
40
41I'll start with a wonderful, graphical way to conceptualize the C++
42compiler. View
43[[https://web.archive.org/web/20190419035048/http://faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html][The
44C++ Compilation Process]] by Kurt MacMahon, an NIU professor, to see the
45graphic and an explanation. The goal of the compilation process is to
46take the C++ code and produce a shared library, dynamic library, or an
47executable file.
48
49** Compilation Phases
50
51Let's break down the compilation process. There are four major steps to
52compiling C++ code.
53
54*** Step 1
55
56The first step is to expand the source code file to meet all
57dependencies. The C++ preprocessor includes the code from all the header
58files, such as =#include <iostream>=. Now, what does that mean? The
59previous example includes the =iostream= header. This tells the computer
60that you want to use the =iostream= standard library, which contains
61classes and functions written in the core language. This specific header
62allows you to manipulate input/output streams. After all this, you'll
63end up which a temporary file that contains the expanded source code.
64
65In the example of the C++ code above, the =iostream= class would be
66included in the expanded code.
67
68*** Step 2
69
70After the code is expanded, the compiler comes into play. The compiler
71takes the C++ code and converts this code into the assembly language,
72understood by the platform. You can see this in action if you head over
73to the [[https://godbolt.org][GodBolt Compiler Explorer]], which shows
74C++ being converted into assembly dynamically.
75
76For example, the =Hello, world!= code snippet above compiles into the
77following assembly code:
78
79#+begin_src asm
80.LC0:
81 .string "Hello, world!\n"
82main:
83 push rbp
84 mov rbp, rsp
85 mov esi, OFFSET FLAT:.LC0
86 mov edi, OFFSET FLAT:_ZSt4cout
87 call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
88 mov eax, 0
89 pop rbp
90 ret
91__static_initialization_and_destruction_0(int, int):
92 push rbp
93 mov rbp, rsp
94 sub rsp, 16
95 mov DWORD PTR [rbp-4], edi
96 mov DWORD PTR [rbp-8], esi
97 cmp DWORD PTR [rbp-4], 1
98 jne .L5
99 cmp DWORD PTR [rbp-8], 65535
100 jne .L5
101 mov edi, OFFSET FLAT:_ZStL8__ioinit
102 call std::ios_base::Init::Init() [complete object constructor]
103 mov edx, OFFSET FLAT:__dso_handle
104 mov esi, OFFSET FLAT:_ZStL8__ioinit
105 mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
106 call __cxa_atexit
107.L5:
108 nop
109 leave
110 ret
111_GLOBAL__sub_I_main:
112 push rbp
113 mov rbp, rsp
114 mov esi, 65535
115 mov edi, 1
116 call __static_initialization_and_destruction_0(int, int)
117 pop rbp
118 ret
119#+end_src
120
121*** Step 3
122
123Third, the assembly code generated by the compiler is assembled into the
124object code for the platform. Essentially, this is when the compiler
125takes the assembly code and assembles it into machine code in a binary
126format. After researching this online, I figured out that a lot of
127compilers will allow you to stop compilation at this step. This would be
128useful for compiling each source code file separately. This saves time
129later if a single file changes; only that file needs to be recompiled.
130
131*** Step 4
132
133Finally, the object code file generated by the assembler is linked
134together with the object code files for any library functions used to
135produce a shared library, dynamic library, or an executable file. It
136replaces all references to undefined symbols with the correct addresses.
content/blog/2019-01-07-useful-css.md deleted −190
@@ -1,190 +0,0 @@
1+++
2date = 2019-01-07
3title = "Useful CSS Snippets"
4description = ""
5draft = false
6+++
7
8# Introduction to CSS
9
10[CSS](https://en.wikipedia.org/wiki/CSS), the language used to markup HTML code
11and make it "pretty", is one of the most effective ways to increase the
12attractiveness of a website. It can also lead to increased user engagement,
13retention, and satisfaction. In fact, there are whole career fields are
14dedicated to the improvement of user experiences, known as UI design and UX
15design.
16
17Some web developers are used to the common CSS properties, such as element
18sizing, fonts, colors, etc., but are not as well versed in less-used properties
19and values such as `flexbox`, `clip-path`, and `transform`. This article will
20provide some insight into the less-used and unique CSS properties.
21
22# CSS Variables
23
24The first topic today is CSS variables. Variables are not often used by smaller
25developers. CSS variables allow you to give your website a well-defined
26structure, where you can easily reuse CSS properties throughout the project.
27
28You can use variables to define things, such as color palettes. Then, you can
29use these colors for backgrounds anywhere else in the HTML. This could be
30extended, where extra variables could be defined for `primary-text`,
31`quoted-text`, etc. Variables can also be used to define spacing (e.g. `32px` or
32`2rem`), which can then be applied to margins, padding, font sizes, and more.
33
34For example, here are some variables defined at the root of the website, which
35allows for any subsequent CSS rules to use those variables:
36
37```css
38:root {
39 --primary-color: black;
40 --secondary-color: white;
41}
42
43body {
44 background-color: var(--primary-color);
45 color: var(--secondary-color);
46}
47```
48
49# CSS Box Shadows
50
51Box shadows were once my mortal enemy. No matter how hard I tried, I just
52couldn't get them to work how I wanted. Because of this, my favorite discovery
53has been CSSMatic's [box shadow generator](https://www.cssmatic.com/box-shadow).
54It provides an excellent tool to generate box shadows using their simple
55sliders. Surprisingly, this is the reason I learned how box shadows work! You
56can use the sliders and watch how the CSS code changes in the image that is
57displayed. Through this, you should understand that the basic structure for box
58shadows is:
59
60```css
61box-shadow: inset horizontal vertical blur spread color;
62```
63
64Now, let's look at some basic examples! You can copy and paste the following
65code into a site like CodePen or your own HTML files. Feel free to play around
66with the code, experiment, and learn.
67
68**Box Shadow #1**
69
70```html
71<div class="shadow-examples">
72 <div class="box effect1">
73 <h3>Effect 1</h3>
74 </div>
75</div>
76```
77
78```css
79.box h3 {
80 text-align: center;
81 position: relative;
82 top: 80px;
83}
84.box {
85 width: 70%;
86 height: 200px;
87 background: #fff;
88 margin: 40px auto;
89}
90.effect1 {
91 box-shadow: 0 10px 6px -6px #777;
92}
93```
94
95**Box Shadow #2**
96
97```html
98<div class="shadow-examples">
99 <div class="box effect2">
100 <h3>Effect 2</h3>
101 </div>
102</div>
103```
104
105```css
106.box h3 {
107 text-align: center;
108 position: relative;
109 top: 80px;
110}
111.box {
112 width: 70%;
113 height: 200px;
114 background: #fff;
115 margin: 40px auto;
116}
117.effect2 {
118 box-shadow: 10px 10px 5px -5px rgba(0, 0, 0, 0.75);
119}
120```
121
122Try these box shadows out on your own and see how changing each shadow value
123works.
124
125# CSS Flexbox
126
127Now, let's move on to the best part of this article: flexbox. The flexbox is by
128far my favorite new toy. I originally stumbled across this solution after
129looking for more efficient ways of centering content horizontally AND
130vertically. I had used a few hack-ish methods before, but flexbox throws those
131out the window. The best part of it all is that flexbox is _dead simple_.
132
133Flexbox pertains to the parent div of any element. You want the parent to be the
134flexbox in which items are arranged to use the flex methods. It's easier to see
135this in action that explained, so let's see an example.
136
137**Flexbox**
138
139```html
140<div class="flex-examples">
141 <div class="sm-box">
142 <h3>1</h3>
143 </div>
144 <div class="sm-box">
145 <h3>2</h3>
146 </div>
147</div>
148```
149
150```css
151.flex-examples {
152 display: flex;
153 flex-wrap: wrap;
154 justify-content: flex-start;
155 align-items: center;
156 padding: 10px;
157 background-color: #f2f2f2;
158}
159.sm-box {
160 display: flex;
161 justify-content: center;
162 align-items: center;
163 width: 20%;
164 height: 100px;
165 background: #fff;
166 margin: 40px 10px;
167}
168```
169
170You may notice that we no longer need to use the `top` property for the `h3`
171elements in our code. This is because we set the display box to be a flex
172container for the small boxes, AND we made the small boxes flex containers for
173their elements (the h3 tags). Flex boxes can be nested like this to center
174content that is inside centered content.
175
176For the example above, we designated the `justify-content` property to be
177`flex-start` so that the boxes stack from the left side of the screen. This
178property can be changed to `center` to make the boxes appear in the center of
179the screen.
180
181For an interactive example, [check out this
182CodePen](https://codepen.io/LandonSchropp/pen/KpzzGo) from
183[LandonScropp](https://codepen.io/LandonSchropp/). Resize the window with dice
184to see how they collapse and re-align.
185
186# Even More CSS
187
188For more inspiration, you can visit [CodePen](https://www.codepen.io),
189[Dribbble](https://dribbble.com), or [UI Movement](https://uimovement.com) to
190browse the collections of many amazing web designers.
content/blog/2019-01-07-useful-css.org added +194
@@ -0,0 +1,194 @@
1#+date: <2019-01-07>
2#+title: Useful CSS Snippets
3#+description:
4
5
6* Introduction to CSS
7
8[[https://en.wikipedia.org/wiki/CSS][CSS]], the language used to markup
9HTML code and make it "pretty", is one of the most effective ways to
10increase the attractiveness of a website. It can also lead to increased
11user engagement, retention, and satisfaction. In fact, there are whole
12career fields are dedicated to the improvement of user experiences,
13known as UI design and UX design.
14
15Some web developers are used to the common CSS properties, such as
16element sizing, fonts, colors, etc., but are not as well versed in
17less-used properties and values such as =flexbox=, =clip-path=, and
18=transform=. This article will provide some insight into the less-used
19and unique CSS properties.
20
21* CSS Variables
22
23The first topic today is CSS variables. Variables are not often used by
24smaller developers. CSS variables allow you to give your website a
25well-defined structure, where you can easily reuse CSS properties
26throughout the project.
27
28You can use variables to define things, such as color palettes. Then,
29you can use these colors for backgrounds anywhere else in the HTML. This
30could be extended, where extra variables could be defined for
31=primary-text=, =quoted-text=, etc. Variables can also be used to define
32spacing (e.g. =32px= or =2rem=), which can then be applied to margins,
33padding, font sizes, and more.
34
35For example, here are some variables defined at the root of the website,
36which allows for any subsequent CSS rules to use those variables:
37
38#+begin_src css
39:root {
40 --primary-color: black;
41 --secondary-color: white;
42}
43
44body {
45 background-color: var(--primary-color);
46 color: var(--secondary-color);
47}
48#+end_src
49
50* CSS Box Shadows
51
52Box shadows were once my mortal enemy. No matter how hard I tried, I
53just couldn't get them to work how I wanted. Because of this, my
54favorite discovery has been CSSMatic's
55[[https://www.cssmatic.com/box-shadow][box shadow generator]]. It
56provides an excellent tool to generate box shadows using their simple
57sliders. Surprisingly, this is the reason I learned how box shadows
58work! You can use the sliders and watch how the CSS code changes in the
59image that is displayed. Through this, you should understand that the
60basic structure for box shadows is:
61
62#+begin_src css
63box-shadow: inset horizontal vertical blur spread color;
64#+end_src
65
66Now, let's look at some basic examples! You can copy and paste the
67following code into a site like CodePen or your own HTML files. Feel
68free to play around with the code, experiment, and learn.
69
70*Box Shadow #1*
71
72#+begin_src html
73<div class="shadow-examples">
74 <div class="box effect1">
75 <h3>Effect 1</h3>
76 </div>
77</div>
78#+end_src
79
80#+begin_src css
81.box h3 {
82 text-align: center;
83 position: relative;
84 top: 80px;
85}
86.box {
87 width: 70%;
88 height: 200px;
89 background: #fff;
90 margin: 40px auto;
91}
92.effect1 {
93 box-shadow: 0 10px 6px -6px #777;
94}
95#+end_src
96
97*Box Shadow #2*
98
99#+begin_src html
100<div class="shadow-examples">
101 <div class="box effect2">
102 <h3>Effect 2</h3>
103 </div>
104</div>
105#+end_src
106
107#+begin_src css
108.box h3 {
109 text-align: center;
110 position: relative;
111 top: 80px;
112}
113.box {
114 width: 70%;
115 height: 200px;
116 background: #fff;
117 margin: 40px auto;
118}
119.effect2 {
120 box-shadow: 10px 10px 5px -5px rgba(0, 0, 0, 0.75);
121}
122#+end_src
123
124Try these box shadows out on your own and see how changing each shadow
125value works.
126
127* CSS Flexbox
128
129Now, let's move on to the best part of this article: flexbox. The
130flexbox is by far my favorite new toy. I originally stumbled across this
131solution after looking for more efficient ways of centering content
132horizontally AND vertically. I had used a few hack-ish methods before,
133but flexbox throws those out the window. The best part of it all is that
134flexbox is /dead simple/.
135
136Flexbox pertains to the parent div of any element. You want the parent
137to be the flexbox in which items are arranged to use the flex methods.
138It's easier to see this in action that explained, so let's see an
139example.
140
141*Flexbox*
142
143#+begin_src html
144<div class="flex-examples">
145 <div class="sm-box">
146 <h3>1</h3>
147 </div>
148 <div class="sm-box">
149 <h3>2</h3>
150 </div>
151</div>
152#+end_src
153
154#+begin_src css
155.flex-examples {
156 display: flex;
157 flex-wrap: wrap;
158 justify-content: flex-start;
159 align-items: center;
160 padding: 10px;
161 background-color: #f2f2f2;
162}
163.sm-box {
164 display: flex;
165 justify-content: center;
166 align-items: center;
167 width: 20%;
168 height: 100px;
169 background: #fff;
170 margin: 40px 10px;
171}
172#+end_src
173
174You may notice that we no longer need to use the =top= property for the
175=h3= elements in our code. This is because we set the display box to be
176a flex container for the small boxes, AND we made the small boxes flex
177containers for their elements (the h3 tags). Flex boxes can be nested
178like this to center content that is inside centered content.
179
180For the example above, we designated the =justify-content= property to
181be =flex-start= so that the boxes stack from the left side of the
182screen. This property can be changed to =center= to make the boxes
183appear in the center of the screen.
184
185For an interactive example,
186[[https://codepen.io/LandonSchropp/pen/KpzzGo][check out this CodePen]]
187from [[https://codepen.io/LandonSchropp/][LandonScropp]]. Resize the
188window with dice to see how they collapse and re-align.
189
190* Even More CSS
191
192For more inspiration, you can visit [[https://www.codepen.io][CodePen]],
193[[https://dribbble.com][Dribbble]], or [[https://uimovement.com][UI
194Movement]] to browse the collections of many amazing web designers.
content/blog/2019-09-09-audit-analytics.md deleted −201
@@ -1,201 +0,0 @@
1+++
2date = 2019-09-09
3title = "Audit Analytics"
4description = ""
5draft = false
6+++
7
8# What Are Data Analytics?
9
10A quick aside before I dive into this post: `data analytics` is a vague term
11that has become popular in recent years. Think of a `data analytic` as the
12output of any data analysis you perform. For example, a pivot table or a pie
13chart could be a data analytic.
14
15[Data analysis](https://en.wikipedia.org/wiki/Data_analysis) is a process that
16utilizes statistics and other mathematical methods to discover useful
17information within datasets. This involves examining, cleaning, transforming,
18and modeling data so that you can use the data to support an opinion, create
19more useful viewpoints, and gain knowledge to implement into audit planning or
20risk assessments.
21
22One of the common mistakes that managers (and anyone new to the process) make is
23assuming that everything involved with this process is "data analytics". In
24fact, data analytics are only a small part of the process.
25
26See **Figure 1** for a more accurate representation of where data analysis sits
27within the full process. This means that data analysis does not include querying
28or extracting data, selecting samples, or performing audit tests. These steps
29can be necessary for an audit (and may even be performed by the same
30associates), but they are not data analytics.
31
32# Current Use of Analytics in Auditing
33
34While data analysis has been an integral part of most businesses and departments
35for the better part of the last century, only recently have internal audit
36functions been adopting this practice. The internal audit function works
37exclusively to provide assurance and consulting services to the business areas
38within the firm (except for internal auditing firms who are hired by different
39companies to perform their roles).
40
41> Internal Auditing helps an organization accomplish its objectives by bringing
42> a systematic, disciplined approach to evaluate and improve the effectiveness
43> of risk management, control and governance processes.
44>
45> - The IIA's Definition of Internal Audit
46
47Part of the blame for the slow adoption of data analysis can be attributed to
48the fact that internal auditing is strongly based on tradition and following the
49precedents set by previous auditors. However, there can be no progress without
50auditors who are willing to break the mold and test new audit techniques. In
51fact, as of 2018, [only 63% of internal audit departments currently utilize data
52analytics](https://www.cpapracticeadvisor.com/accounting-audit/news/12404086/internal-audit-groups-are-lagging-in-data-analytics)
53in North America. This number should be as close as possible to 100%. I have
54never been part of an audit that would not have benefited from data analytics.
55
56So, how do internal audit functions remedy this situation? It's definitely not
57as easy as walking into work on Monday and telling your Chief Audit Executive
58that you're going to start implementing analytics in the next audit. You need a
59plan and a system to make the analysis process as effective as possible.
60
61# The DELTA Model
62
63One of the easiest ways to experiment with data analytics and gain an
64understanding of the processes is to implement them within your own department.
65But how do we do this if we've never worked with analysis before? One of the
66most common places to start is to research some data analysis models currently
67available. For this post, we'll take a look at the DELTA model. You can take a
68look at **Figure 2** for a quick overview of the model.
69
70The DELTA model sets a few guidelines for areas wanting to implement data
71analytics so that the results can be as comprehensive as possible:
72
73- **Data**: Must be clean, accessible, and (usually) unique.
74- **Enterprise-Wide Focus**: Key data systems and analytical resources must be
75 available for use (by the Internal Audit Function).
76- **Leaders**: Must promote a data analytics approach and show the value of
77 analytical results.
78- **Targets**: Must be set for key areas and risks that the analytics can be
79 compared against (KPIs).
80- **Analysts**: There must be auditors willing and able to perform data
81 analytics or else the system cannot be sustained.
82
83# Finding the Proper KPIs
84
85Once the Internal Audit Function has decided that they want to start using data
86analytics internally and have ensured they're properly set up to do so, they
87need to figure out what they will be testing against. Key Performance Indicators
88(KPIs) are qualitative or quantitative factors that can be evaluated and
89assessed to determine if the department is performing well, usually compared to
90historical or industry benchmarks. Once KPIs have been agreed upon and set,
91auditors can use data analytics to assess and report on these KPIs. This allows
92the person performing the analytics the freedom to express opinions on the
93results, whereas the results are ambiguous if no KPIs exist.
94
95It should be noted that tracking KPIs in the department can help ensure you have
96a rigorous Quality Assurance and Improvement Program (QAIP) in accordance with
97some applicable standards, such as IPPF Standard 1300.
98
99> The chief audit executive must develop and maintain a quality assurance and
100> improvement program that covers all aspects of the internal audit activity.
101>
102> - IPPF Standard 1300
103
104Additionally, IPPF Standard 2060 discusses reporting:
105
106> The chief audit executive must report periodically to senior management and
107> the board on the internal audit activity's purpose, authority, responsibility,
108> and performance relative to its plan and on its conformance with the Code of
109> Ethics and the Standards. Reporting must also include significant risk and
110> control issues, including fraud risks, governance issues, and other matters
111> that require the attention of senior management and/or the board.
112>
113> - IPPF Standard 2060
114
115The hardest part of finding KPIs is to determine which KPIs are appropriate for
116your department. Since every department is different and has different goals,
117KPIs will vary drastically between companies. To give you an idea of where to
118look, here are some ideas I came up with when discussing the topic with a few
119colleagues.
120
121- Efficiency/Budgeting:
122 - Audit hours to staff utilization ratio (annual hours divided by total
123 annual work hours).
124 - Audit hours compared to the number of audits completed.
125 - Time between audit steps or to complete the whole audit. E.g., time from
126 fieldwork completion to audit report issuance.
127- Reputation:
128 - The frequency that management has requested the services of the IAF.
129 - Management, audit committee, or external audit satisfaction survey
130 results.
131 - Education, experience, certifications, tenure, and training of the
132 auditors on staff.
133- Quality:
134 - Number and frequency of audit findings. Assign monetary or numerical
135 values, if possible.
136 - Percentage of recommendations issued and implemented.
137- Planning:
138 - Percentage or number of key risks audited per year or per audit.
139 - Proportion of audit universe audited per year.
140
141# Data Analysis Tools
142
143Finally, to be able to analyze and report on the data analysis, auditors need to
144evaluate the tools at their disposal. There are many options available, but a
145few of the most common ones can easily get the job done. For example, almost
146every auditor already has access to Microsoft Excel. Excel is more powerful than
147most people give it credit for and can accomplish a lot of basic statistics
148without much work. If you don't know a lot about statistics but still want to
149see some of the more basic results, Excel is a great option.
150
151To perform more in-depth statistical analysis or to explore large datasets that
152Excel cannot handle, auditors will need to explore other options. The big three
153that have had a lot of success in recent years are Python, R, and ACL. ACL can
154be used as either a graphical tool (point and click) or as a scripting tool,
155where the auditor must write the scripts manually. Python and the R-language are
156solely scripting languages.
157
158The general trend in the data analytics environment is that if the tool allows
159you to do everything by clicking buttons or dragging elements, you won't be able
160to fully utilize the analytics you need. The most robust solutions are created
161by those who understand how to write the scripts manually. It should be noted
162that as the utility of a tool increases, it usually means that the learning
163curve for that tool will also be higher. It will take auditors longer to learn
164how to utilize Python, R, or ACL versus learning how to utilize Excel.
165
166# Visualization
167
168Once an auditor has finally found the right data, KPIs, and tools, they must
169report these results so that actions can be taken. Performing in-depth data
170analysis is only useful if the results are understood by the audiences of the
171data. The best way to create this understanding is to visualize the results of
172the data. Let's take a look at some of the best options to visualize and report
173the results you've found.
174
175Some of the most popular commercial tools for visualization are Microsoft
176PowerBI and Tableau Desktop. However, other tools exist such as JMP, Plotly,
177Qlikview, Alteryx, or D3. Some require commercial licenses while others are
178simply free to use. For corporate data, you may want to make sure that the tool
179does not communicate any of the data outside the company (such as cloud
180storage). I won't be going into depth on any of these tools since visualization
181is largely a subjective and creative experience, but remember to constantly
182explore new options as you repeat the process.
183
184Lastly, let's take a look at an example of data visualization. This example
185comes from a [blog post written by Kushal
186Chakrabarti](https://talent.works/2018/03/28/the-science-of-the-job-search-part-iii-61-of-entry-level-jobs-require-3-years-of-experience/)
187in 2018 about the percent of entry-level US jobs that require experience.
188**Figure 3** shows us an easy-to-digest picture of the data. We can quickly tell
189that only about 12.5% of entry-level jobs don't require experience.
190
191This is the kind of result that easily describes the data for you. However, make
192sure to include an explanation of what the results mean. Don't let the reader
193assume what the data means, especially if it relates to a complex subject. _Tell
194a story_ about the data and why the results matter.
195
196# Wrap-Up
197
198While this is not an all-encompassing program that you can just adopt into your
199department, it should be enough to get anyone started on the process of
200understanding and implementing data analytics. Always remember to continue
201learning and exploring new options as your processes grow and evolve.
content/blog/2019-09-09-audit-analytics.org added +220
@@ -0,0 +1,220 @@
1#+date: <2019-09-09>
2#+title: Audit Analytics
3#+description:
4
5
6* What Are Data Analytics?
7
8A quick aside before I dive into this post: =data analytics= is a vague
9term that has become popular in recent years. Think of a =data analytic=
10as the output of any data analysis you perform. For example, a pivot
11table or a pie chart could be a data analytic.
12
13[[https://en.wikipedia.org/wiki/Data_analysis][Data analysis]] is a
14process that utilizes statistics and other mathematical methods to
15discover useful information within datasets. This involves examining,
16cleaning, transforming, and modeling data so that you can use the data
17to support an opinion, create more useful viewpoints, and gain knowledge
18to implement into audit planning or risk assessments.
19
20One of the common mistakes that managers (and anyone new to the process)
21make is assuming that everything involved with this process is "data
22analytics". In fact, data analytics are only a small part of the
23process.
24
25See *Figure 1* for a more accurate representation of where data analysis
26sits within the full process. This means that data analysis does not
27include querying or extracting data, selecting samples, or performing
28audit tests. These steps can be necessary for an audit (and may even be
29performed by the same associates), but they are not data analytics.
30
31* Current Use of Analytics in Auditing
32
33While data analysis has been an integral part of most businesses and
34departments for the better part of the last century, only recently have
35internal audit functions been adopting this practice. The internal audit
36function works exclusively to provide assurance and consulting services
37to the business areas within the firm (except for internal auditing
38firms who are hired by different companies to perform their roles).
39
40#+begin_quote
41Internal Auditing helps an organization accomplish its objectives by
42bringing a systematic, disciplined approach to evaluate and improve the
43effectiveness of risk management, control and governance processes.
44
45- The IIA's Definition of Internal Audit
46#+end_quote
47
48Part of the blame for the slow adoption of data analysis can be
49attributed to the fact that internal auditing is strongly based on
50tradition and following the precedents set by previous auditors.
51However, there can be no progress without auditors who are willing to
52break the mold and test new audit techniques. In fact, as of 2018,
53[[https://www.cpapracticeadvisor.com/accounting-audit/news/12404086/internal-audit-groups-are-lagging-in-data-analytics][only
5463% of internal audit departments currently utilize data analytics]] in
55North America. This number should be as close as possible to 100%. I
56have never been part of an audit that would not have benefited from data
57analytics.
58
59So, how do internal audit functions remedy this situation? It's
60definitely not as easy as walking into work on Monday and telling your
61Chief Audit Executive that you're going to start implementing analytics
62in the next audit. You need a plan and a system to make the analysis
63process as effective as possible.
64
65* The DELTA Model
66
67One of the easiest ways to experiment with data analytics and gain an
68understanding of the processes is to implement them within your own
69department. But how do we do this if we've never worked with analysis
70before? One of the most common places to start is to research some data
71analysis models currently available. For this post, we'll take a look at
72the DELTA model. You can take a look at *Figure 2* for a quick overview
73of the model.
74
75The DELTA model sets a few guidelines for areas wanting to implement
76data analytics so that the results can be as comprehensive as possible:
77
78- *Data*: Must be clean, accessible, and (usually) unique.
79- *Enterprise-Wide Focus*: Key data systems and analytical resources
80 must be available for use (by the Internal Audit Function).
81- *Leaders*: Must promote a data analytics approach and show the value
82 of analytical results.
83- *Targets*: Must be set for key areas and risks that the analytics can
84 be compared against (KPIs).
85- *Analysts*: There must be auditors willing and able to perform data
86 analytics or else the system cannot be sustained.
87
88* Finding the Proper KPIs
89
90Once the Internal Audit Function has decided that they want to start
91using data analytics internally and have ensured they're properly set up
92to do so, they need to figure out what they will be testing against. Key
93Performance Indicators (KPIs) are qualitative or quantitative factors
94that can be evaluated and assessed to determine if the department is
95performing well, usually compared to historical or industry benchmarks.
96Once KPIs have been agreed upon and set, auditors can use data analytics
97to assess and report on these KPIs. This allows the person performing
98the analytics the freedom to express opinions on the results, whereas
99the results are ambiguous if no KPIs exist.
100
101It should be noted that tracking KPIs in the department can help ensure
102you have a rigorous Quality Assurance and Improvement Program (QAIP) in
103accordance with some applicable standards, such as IPPF Standard 1300.
104
105#+begin_quote
106The chief audit executive must develop and maintain a quality assurance
107and improvement program that covers all aspects of the internal audit
108activity.
109
110- IPPF Standard 1300
111#+end_quote
112
113Additionally, IPPF Standard 2060 discusses reporting:
114
115#+begin_quote
116The chief audit executive must report periodically to senior management
117and the board on the internal audit activity's purpose, authority,
118responsibility, and performance relative to its plan and on its
119conformance with the Code of Ethics and the Standards. Reporting must
120also include significant risk and control issues, including fraud risks,
121governance issues, and other matters that require the attention of
122senior management and/or the board.
123
124- IPPF Standard 2060
125#+end_quote
126
127The hardest part of finding KPIs is to determine which KPIs are
128appropriate for your department. Since every department is different and
129has different goals, KPIs will vary drastically between companies. To
130give you an idea of where to look, here are some ideas I came up with
131when discussing the topic with a few colleagues.
132
133- Efficiency/Budgeting:
134 - Audit hours to staff utilization ratio (annual hours divided by
135 total annual work hours).
136 - Audit hours compared to the number of audits completed.
137 - Time between audit steps or to complete the whole audit. E.g., time
138 from fieldwork completion to audit report issuance.
139- Reputation:
140 - The frequency that management has requested the services of the IAF.
141 - Management, audit committee, or external audit satisfaction survey
142 results.
143 - Education, experience, certifications, tenure, and training of the
144 auditors on staff.
145- Quality:
146 - Number and frequency of audit findings. Assign monetary or numerical
147 values, if possible.
148 - Percentage of recommendations issued and implemented.
149- Planning:
150 - Percentage or number of key risks audited per year or per audit.
151 - Proportion of audit universe audited per year.
152
153* Data Analysis Tools
154
155Finally, to be able to analyze and report on the data analysis, auditors
156need to evaluate the tools at their disposal. There are many options
157available, but a few of the most common ones can easily get the job
158done. For example, almost every auditor already has access to Microsoft
159Excel. Excel is more powerful than most people give it credit for and
160can accomplish a lot of basic statistics without much work. If you don't
161know a lot about statistics but still want to see some of the more basic
162results, Excel is a great option.
163
164To perform more in-depth statistical analysis or to explore large
165datasets that Excel cannot handle, auditors will need to explore other
166options. The big three that have had a lot of success in recent years
167are Python, R, and ACL. ACL can be used as either a graphical tool
168(point and click) or as a scripting tool, where the auditor must write
169the scripts manually. Python and the R-language are solely scripting
170languages.
171
172The general trend in the data analytics environment is that if the tool
173allows you to do everything by clicking buttons or dragging elements,
174you won't be able to fully utilize the analytics you need. The most
175robust solutions are created by those who understand how to write the
176scripts manually. It should be noted that as the utility of a tool
177increases, it usually means that the learning curve for that tool will
178also be higher. It will take auditors longer to learn how to utilize
179Python, R, or ACL versus learning how to utilize Excel.
180
181* Visualization
182
183Once an auditor has finally found the right data, KPIs, and tools, they
184must report these results so that actions can be taken. Performing
185in-depth data analysis is only useful if the results are understood by
186the audiences of the data. The best way to create this understanding is
187to visualize the results of the data. Let's take a look at some of the
188best options to visualize and report the results you've found.
189
190Some of the most popular commercial tools for visualization are
191Microsoft PowerBI and Tableau Desktop. However, other tools exist such
192as JMP, Plotly, Qlikview, Alteryx, or D3. Some require commercial
193licenses while others are simply free to use. For corporate data, you
194may want to make sure that the tool does not communicate any of the data
195outside the company (such as cloud storage). I won't be going into depth
196on any of these tools since visualization is largely a subjective and
197creative experience, but remember to constantly explore new options as
198you repeat the process.
199
200Lastly, let's take a look at an example of data visualization. This
201example comes from a
202[[https://talent.works/2018/03/28/the-science-of-the-job-search-part-iii-61-of-entry-level-jobs-require-3-years-of-experience/][blog
203post written by Kushal Chakrabarti]] in 2018 about the percent of
204entry-level US jobs that require experience. *Figure 3* shows us an
205easy-to-digest picture of the data. We can quickly tell that only about
20612.5% of entry-level jobs don't require experience.
207
208This is the kind of result that easily describes the data for you.
209However, make sure to include an explanation of what the results mean.
210Don't let the reader assume what the data means, especially if it
211relates to a complex subject. /Tell a story/ about the data and why the
212results matter.
213
214* Wrap-Up
215
216While this is not an all-encompassing program that you can just adopt
217into your department, it should be enough to get anyone started on the
218process of understanding and implementing data analytics. Always
219remember to continue learning and exploring new options as your
220processes grow and evolve.
content/blog/2019-12-03-the-ansoff-matrix.md deleted −116
@@ -1,116 +0,0 @@
1+++
2date = 2019-12-03
3title = "The Ansoff Matrix"
4description = ""
5draft = false
6+++
7
8# Overview
9
10As the world of business evolves, managers must approach business planning and
11strategy with a contemporary mindset. According to Dess, McNamara, Eisner, and
12Lee, managers must be willing to adapt to the modern business environment by
13going beyond "'incremental management', whereby they view their job as making a
14series of small, minor changes to improve the efficiency of the firm's
15operations"(2019).
16
17One reason that strategic management is crucial is because most businesses that
18fail in the United States each year fail due to a lack of strategic focus or
19direction(2019). The rate of failure for businesses with poor strategies shows
20that strategic planning and management are crucial to a business's strength and
21longevity, injecting the critical factors of growth and direction into a
22company's business plan.
23
24One of the most significant strategic planning and management frameworks that
25companies can use is the [Ansoff
26Matrix](https://en.wikipedia.org/wiki/Ansoff_matrix). While this framework has
27unique purposes and use-cases, it can effectively help an organization grow and
28compete. Specifically, the Ansoff matrix is one of the most effective frameworks
29for companies who want to focus on increasing sales revenue or
30profitability(2019).
31
32This framework uses a two-by-two figure to show the four strategic options for
33companies to use in this framework: market penetration, market development,
34product development, and diversification (see **Figure 1**). The x-axis of the
35matrix focuses on the firm's markets and also determines if the firm is looking
36to enter new markets or innovate in its current markets. The y-axis of the
37matrix focuses on the firm's products and determines if the firm wants to pursue
38strategies around their existing products or explore new products.
39
40# Strategic Options
41
42## Market Penetration
43
44The most straightforward strategy in the Ansoff matrix is to focus on existing
45products in existing markets, also known as market penetration(2019). Companies
46such as Coca-Cola have used market penetration successfully by investing a lot
47of money to get further value out of their current markets. Coca-Cola does this
48by introducing new features such as Christmas-themed bottles, personal names on
49the bottles, and other marketing schemes.
50
51## Market Development
52
53Market development extends existing products into new markets in an attempt to
54increase the number of buyers. One interesting way that Coca-Cola used this
55strategy comes from the stigma that Diet Coke is a woman's drink(2019).
56Coca-Cola introduced Coca-Cola Zero, which contained the same nutritional
57content as Diet Coke, but was packaged in a dark black can to appear more
58"manly"(2019).
59
60## Product Development
61
62Product development uses existing markets to introduce new products so that the
63firm can better meet customer needs(2019). The extreme end of diversification is
64home to companies such as Johnson & Johnson, a healthcare company that has
65developed a business portfolio of more than 60,000 different products(2019).
66Johnson & Johnson's dedication to continuous diversification has led them to a
67balance sheet rating of "AAA", industry recognition for diversification, and
68increases in their investor dividends for 57 consecutive years(2019).
69
70## Related Diversification
71
72Diversification, the final strategy of the Ansoff Matrix, is more difficult than
73the others since it involves exploring both new markets and new products.
74Related diversification is a diversification strategy that closely relates to
75the firm's core business. Coca-Cola's best example of related diversification is
76its acquisition of Glaceau and Vitamin Water, which expanded their drinking
77lines of business(2019).
78
79## Unrelated Diversification
80
81Unrelated diversification is a diversification strategy that does not really
82relate to the firm's core business but still diversifies their business
83portfolio. A good example of this would be a coffee company who has decided to
84enter the market for bicycle sales. The main purpose of this strategy is to an
85extremely diverse company that will not go bankrupt if one market goes through
86difficult times. However, this requires a lot of independent skills and heavy
87investments since the company most likely cannot easily transfer knowledge
88between the markets they compete in.
89
90# Requirements for Success
91
92To use the Ansoff Matrix framework, managers need to formulate corporate goals
93and objectives. Without goals and direction, management frameworks do not
94present much practical utility. Further, the Ansoff Matrix requires the managers
95involved to make tactical decisions and create a path for the company to take
96toward their goals. Lastly, both the Ansoff Matrix needs to consider both
97internal and external perspectives throughout the strategy formulation process.
98
99One interesting probability is that companies will be using multiple strategic
100planning and management frameworks at the same time. While this may sound like
101it could crowd the management process, there are numerous reasons to do so. For
102example, the Ansoff Matrix and the Balanced Scorecard are relatively popular,
103and they cover entirely different parts of a company's strategy. Using the
104results from the Balanced Scorecard could inform a company of the potential
105product and market demands, such as from customer or supplier survey results, to
106help the company determine which Ansoff Matrix strategy to pursue. However, a
107combined approach at this level would require mature frameworks and focused
108managers who are able to strategize at a high level.
109
110Lastly, it should be noted that the author of the Ansoff matrix, Igor Ansoff,
111often used the term [paralysis by
112analysis](https://en.wikipedia.org/wiki/Analysis_paralysis) to explain the
113mistake of companies who overuse analysis and spend too much time planning.
114Companies need to understand the utility of a strategic management framework
115while ensuring that the company is poised to execute as efficiently as they have
116planned.
content/blog/2019-12-03-the-ansoff-matrix.org added +122
@@ -0,0 +1,122 @@
1#+date: <2019-12-03>
2#+title: The Ansoff Matrix
3#+description:
4
5
6* Overview
7
8As the world of business evolves, managers must approach business
9planning and strategy with a contemporary mindset. According to Dess,
10McNamara, Eisner, and Lee, managers must be willing to adapt to the
11modern business environment by going beyond "'incremental management',
12whereby they view their job as making a series of small, minor changes
13to improve the efficiency of the firm's operations"(2019).
14
15One reason that strategic management is crucial is because most
16businesses that fail in the United States each year fail due to a lack
17of strategic focus or direction(2019). The rate of failure for
18businesses with poor strategies shows that strategic planning and
19management are crucial to a business's strength and longevity, injecting
20the critical factors of growth and direction into a company's business
21plan.
22
23One of the most significant strategic planning and management frameworks
24that companies can use is the
25[[https://en.wikipedia.org/wiki/Ansoff_matrix][Ansoff Matrix]]. While
26this framework has unique purposes and use-cases, it can effectively
27help an organization grow and compete. Specifically, the Ansoff matrix
28is one of the most effective frameworks for companies who want to focus
29on increasing sales revenue or profitability(2019).
30
31This framework uses a two-by-two figure to show the four strategic
32options for companies to use in this framework: market penetration,
33market development, product development, and diversification (see
34*Figure 1*). The x-axis of the matrix focuses on the firm's markets and
35also determines if the firm is looking to enter new markets or innovate
36in its current markets. The y-axis of the matrix focuses on the firm's
37products and determines if the firm wants to pursue strategies around
38their existing products or explore new products.
39
40* Strategic Options
41
42** Market Penetration
43
44The most straightforward strategy in the Ansoff matrix is to focus on
45existing products in existing markets, also known as market
46penetration(2019). Companies such as Coca-Cola have used market
47penetration successfully by investing a lot of money to get further
48value out of their current markets. Coca-Cola does this by introducing
49new features such as Christmas-themed bottles, personal names on the
50bottles, and other marketing schemes.
51
52** Market Development
53
54Market development extends existing products into new markets in an
55attempt to increase the number of buyers. One interesting way that
56Coca-Cola used this strategy comes from the stigma that Diet Coke is a
57woman's drink(2019). Coca-Cola introduced Coca-Cola Zero, which
58contained the same nutritional content as Diet Coke, but was packaged in
59a dark black can to appear more "manly"(2019).
60
61** Product Development
62
63Product development uses existing markets to introduce new products so
64that the firm can better meet customer needs(2019). The extreme end of
65diversification is home to companies such as Johnson & Johnson, a
66healthcare company that has developed a business portfolio of more than
6760,000 different products(2019). Johnson & Johnson's dedication to
68continuous diversification has led them to a balance sheet rating of
69"AAA", industry recognition for diversification, and increases in their
70investor dividends for 57 consecutive years(2019).
71
72** Related Diversification
73
74Diversification, the final strategy of the Ansoff Matrix, is more
75difficult than the others since it involves exploring both new markets
76and new products. Related diversification is a diversification strategy
77that closely relates to the firm's core business. Coca-Cola's best
78example of related diversification is its acquisition of Glaceau and
79Vitamin Water, which expanded their drinking lines of business(2019).
80
81** Unrelated Diversification
82
83Unrelated diversification is a diversification strategy that does not
84really relate to the firm's core business but still diversifies their
85business portfolio. A good example of this would be a coffee company who
86has decided to enter the market for bicycle sales. The main purpose of
87this strategy is to an extremely diverse company that will not go
88bankrupt if one market goes through difficult times. However, this
89requires a lot of independent skills and heavy investments since the
90company most likely cannot easily transfer knowledge between the markets
91they compete in.
92
93* Requirements for Success
94
95To use the Ansoff Matrix framework, managers need to formulate corporate
96goals and objectives. Without goals and direction, management frameworks
97do not present much practical utility. Further, the Ansoff Matrix
98requires the managers involved to make tactical decisions and create a
99path for the company to take toward their goals. Lastly, both the Ansoff
100Matrix needs to consider both internal and external perspectives
101throughout the strategy formulation process.
102
103One interesting probability is that companies will be using multiple
104strategic planning and management frameworks at the same time. While
105this may sound like it could crowd the management process, there are
106numerous reasons to do so. For example, the Ansoff Matrix and the
107Balanced Scorecard are relatively popular, and they cover entirely
108different parts of a company's strategy. Using the results from the
109Balanced Scorecard could inform a company of the potential product and
110market demands, such as from customer or supplier survey results, to
111help the company determine which Ansoff Matrix strategy to pursue.
112However, a combined approach at this level would require mature
113frameworks and focused managers who are able to strategize at a high
114level.
115
116Lastly, it should be noted that the author of the Ansoff matrix, Igor
117Ansoff, often used the term
118[[https://en.wikipedia.org/wiki/Analysis_paralysis][paralysis by
119analysis]] to explain the mistake of companies who overuse analysis and
120spend too much time planning. Companies need to understand the utility
121of a strategic management framework while ensuring that the company is
122poised to execute as efficiently as they have planned.
content/blog/2019-12-16-password-security.md deleted −117
@@ -1,117 +0,0 @@
1+++
2date = 2019-12-16
3title = "Password Security"
4description = ""
5draft = false
6+++
7
8# Users
9
10## Why Does It Matter?
11
12Information security, including passwords and identities, has become one of the
13most important digital highlights of the last decade. With [billions of people
14affected by data breaches each
15year](https://www.usatoday.com/story/money/2018/12/28/data-breaches-2018-billions-hit-growing-number-cyberattacks/2413411002/),
16there's a greater need to introduce strong information security systems. If you
17think you've been part of a breach, or you want to check and see, you can use
18[Have I Been Pwned](https://haveibeenpwned.com/) to see if your email has been
19involved in any public breaches. Remember that there's a possibility that a
20company experienced a breach and did not report it to anyone.
21
22## How Do I Protect Myself?
23
24The first place to start with any personal security check-up is to gather a list
25of all the different websites, apps, or programs that require you to have login
26credentials. Optionally, once you know where your information is being stored,
27you can sort the list from the most-important items such as banks or government
28logins to less important items such as your favorite meme site. You will want to
29ensure that your critical logins are secure before getting to the others.
30
31Once you think you have a good idea of all your different authentication
32methods, I recommend using a password manager such as
33[Bitwarden](https://bitwarden.com/). Using a password manager allows you to
34automatically save your logins, create randomized passwords, and transfer
35passwords across devices. However, you'll need to memorize your "vault password"
36that allows you to open the password manager. It's important to make this
37something hard to guess since it would allow anyone who has it to access every
38password you've stored in there.
39
40Personally, I recommend using a
41[passphrase](https://en.wikipedia.org/wiki/Passphrase) instead of a
42[password](https://en.wikipedia.org/wiki/Password) for your vault password.
43Instead of using a string of characters (whether random or simple), use a phrase
44and add in symbols and a number. For example, your vault password could be
45`Racing-Alphabet-Gourd-Parrot3`. Swap the symbols out for whichever symbol you
46want, move the number around, and fine-tune the passphrase until you are
47confident that you can remember it whenever necessary.
48
49Once you've stored your passwords, make sure you continually check up on your
50account and make sure you aren't following bad password practices. Krebs on
51Security has a great [blog post on password
52recommendations](https://krebsonsecurity.com/password-dos-and-donts/). Any time
53that a data breach happens, make sure you check to see if you were included, and
54if you need to reset any account passwords.
55
56# Developers
57
58## What Are the Basic Requirements?
59
60When developing any password-protected application, there are a few basic rules
61that anyone should follow even if they do not follow any official guidelines
62such as NIST. The foremost practice is to require users to use passwords that
63are at least 8 characters and cannot easily be guessed. This sounds extremely
64simple, but it requires quite a few different strategies. First, the application
65should check the potential passwords against a dictionary of insecure passwords
66such `password`, `1234abc`, or `application_name`.
67
68Next, the application should offer guidance on the strength of passwords being
69entered during enrollment. Further, NIST officially recommends **not**
70implementing any composition rules that make passwords hard to remember (e.g.
71passwords with letters, numbers, and special characters) and instead encouraging
72the use of long pass phrases which can include spaces. It should be noted that
73to be able to keep spaces within passwords, all unicode characters should be
74supported, and passwords should not be truncated.
75
76## What Does NIST Recommend?
77
78The National Institute of Standards and Technology
79([NIST](https://www.nist.gov)) in the US Department of Commerce regularly
80publishes information around information security and digital identity
81guidelines. Recently, NIST published [Special Publication
82800-63b](https://pages.nist.gov/800-63-3/sp800-63b.html): Digital Identity
83Guidelines and Authentication and Lifecycle Management.
84
85> A Memorized Secret authenticator - commonly referred to as a password or, if
86> numeric, a PIN - is a secret value intended to be chosen and memorized by the
87> user. Memorized secrets need to be of sufficient complexity and secrecy that
88> it would be impractical for an attacker to guess or otherwise discover the
89> correct secret value. A memorized secret is something you know.
90>
91> - NIST Special Publication 800-63B
92
93NIST offers a lot of guidance on passwords, but I'm going to highlight just a
94few of the important factors:
95
96- Require passwords to be a minimum of 8 characters (6 characters if randomly
97 generated and be generated using an approved random bit generator).
98- Compare potential passwords against a list that contains values known to be
99 commonly-used, expected, or compromised.
100- Offer guidance on password strength, such as a strength meter.
101- Implement a rate-limiting mechanism to limit the number of failed
102 authentication attempts for each user account.
103- Do not require composition rules for passwords and do not require passwords
104 to be changed periodically (unless compromised).
105- Allow pasting of user identification and passwords to facilitate the use of
106 password managers.
107- Allow users to view the password as it is being entered.
108- Use secure forms of communication and storage, including salting and hashing
109 passwords using a one-way key derivation function.
110
111NIST offers further guidance on other devices that require specific security
112policies, querying for passwords, and more. All the information discussed so far
113comes from [NIST SP800-63b](https://pages.nist.gov/800-63-3/sp800-63b.html) but
114NIST offers a lot of information on digital identities, enrollment, identity
115proofing, authentication, lifecycle management, federation, and assertions in
116the total [NIST SP800-63 Digital Identity
117Guidelines](https://pages.nist.gov/800-63-3/).
content/blog/2019-12-16-password-security.org added +127
@@ -0,0 +1,127 @@
1#+date: <2019-12-16>
2#+title: Password Security
3#+description:
4
5
6* Users
7
8** Why Does It Matter?
9
10Information security, including passwords and identities, has become one
11of the most important digital highlights of the last decade. With
12[[https://www.usatoday.com/story/money/2018/12/28/data-breaches-2018-billions-hit-growing-number-cyberattacks/2413411002/][billions
13of people affected by data breaches each year]], there's a greater need
14to introduce strong information security systems. If you think you've
15been part of a breach, or you want to check and see, you can use
16[[https://haveibeenpwned.com/][Have I Been Pwned]] to see if your email
17has been involved in any public breaches. Remember that there's a
18possibility that a company experienced a breach and did not report it to
19anyone.
20
21** How Do I Protect Myself?
22
23The first place to start with any personal security check-up is to
24gather a list of all the different websites, apps, or programs that
25require you to have login credentials. Optionally, once you know where
26your information is being stored, you can sort the list from the
27most-important items such as banks or government logins to less
28important items such as your favorite meme site. You will want to ensure
29that your critical logins are secure before getting to the others.
30
31Once you think you have a good idea of all your different authentication
32methods, I recommend using a password manager such as
33[[https://bitwarden.com/][Bitwarden]]. Using a password manager allows
34you to automatically save your logins, create randomized passwords, and
35transfer passwords across devices. However, you'll need to memorize your
36"vault password" that allows you to open the password manager. It's
37important to make this something hard to guess since it would allow
38anyone who has it to access every password you've stored in there.
39
40Personally, I recommend using a
41[[https://en.wikipedia.org/wiki/Passphrase][passphrase]] instead of a
42[[https://en.wikipedia.org/wiki/Password][password]] for your vault
43password. Instead of using a string of characters (whether random or
44simple), use a phrase and add in symbols and a number. For example, your
45vault password could be =Racing-Alphabet-Gourd-Parrot3=. Swap the
46symbols out for whichever symbol you want, move the number around, and
47fine-tune the passphrase until you are confident that you can remember
48it whenever necessary.
49
50Once you've stored your passwords, make sure you continually check up on
51your account and make sure you aren't following bad password practices.
52Krebs on Security has a great
53[[https://krebsonsecurity.com/password-dos-and-donts/][blog post on
54password recommendations]]. Any time that a data breach happens, make
55sure you check to see if you were included, and if you need to reset any
56account passwords.
57
58* Developers
59
60** What Are the Basic Requirements?
61
62When developing any password-protected application, there are a few
63basic rules that anyone should follow even if they do not follow any
64official guidelines such as NIST. The foremost practice is to require
65users to use passwords that are at least 8 characters and cannot easily
66be guessed. This sounds extremely simple, but it requires quite a few
67different strategies. First, the application should check the potential
68passwords against a dictionary of insecure passwords such =password=,
69=1234abc=, or =application_name=.
70
71Next, the application should offer guidance on the strength of passwords
72being entered during enrollment. Further, NIST officially recommends
73*not* implementing any composition rules that make passwords hard to
74remember (e.g. passwords with letters, numbers, and special characters)
75and instead encouraging the use of long pass phrases which can include
76spaces. It should be noted that to be able to keep spaces within
77passwords, all unicode characters should be supported, and passwords
78should not be truncated.
79
80** What Does NIST Recommend?
81
82The National Institute of Standards and Technology
83([[https://www.nist.gov][NIST]]) in the US Department of Commerce
84regularly publishes information around information security and digital
85identity guidelines. Recently, NIST published
86[[https://pages.nist.gov/800-63-3/sp800-63b.html][Special Publication
87800-63b]]: Digital Identity Guidelines and Authentication and Lifecycle
88Management.
89
90#+begin_quote
91A Memorized Secret authenticator - commonly referred to as a password
92or, if numeric, a PIN - is a secret value intended to be chosen and
93memorized by the user. Memorized secrets need to be of sufficient
94complexity and secrecy that it would be impractical for an attacker to
95guess or otherwise discover the correct secret value. A memorized secret
96is something you know.
97
98- NIST Special Publication 800-63B
99#+end_quote
100
101NIST offers a lot of guidance on passwords, but I'm going to highlight
102just a few of the important factors:
103
104- Require passwords to be a minimum of 8 characters (6 characters if
105 randomly generated and be generated using an approved random bit
106 generator).
107- Compare potential passwords against a list that contains values known
108 to be commonly-used, expected, or compromised.
109- Offer guidance on password strength, such as a strength meter.
110- Implement a rate-limiting mechanism to limit the number of failed
111 authentication attempts for each user account.
112- Do not require composition rules for passwords and do not require
113 passwords to be changed periodically (unless compromised).
114- Allow pasting of user identification and passwords to facilitate the
115 use of password managers.
116- Allow users to view the password as it is being entered.
117- Use secure forms of communication and storage, including salting and
118 hashing passwords using a one-way key derivation function.
119
120NIST offers further guidance on other devices that require specific
121security policies, querying for passwords, and more. All the information
122discussed so far comes from
123[[https://pages.nist.gov/800-63-3/sp800-63b.html][NIST SP800-63b]] but
124NIST offers a lot of information on digital identities, enrollment,
125identity proofing, authentication, lifecycle management, federation, and
126assertions in the total [[https://pages.nist.gov/800-63-3/][NIST
127SP800-63 Digital Identity Guidelines]].
content/blog/2020-01-25-linux-software.md deleted −246
@@ -1,246 +0,0 @@
1+++
2date = 2020-01-25
3title = "My Favorite Linux Software"
4description = ""
5draft = false
6+++
7
8# GUI Applications
9
10## Etcher
11
12[Etcher](https://www.balena.io/etcher/) is a quick and easy way to burn ISO
13images to CDs and USB devices. There are two different ways you can install this
14program. First, you can navigate to the [official
15website](https://www.balena.io/etcher/) and download the AppImage file, which
16can run without installation.
17
18However, AppImage files are not executable by default, so you'll either need to
19right-click to open the properties of the file and click the "Allow executing
20file as program" box in the Permissions tab or use the following command:
21
22```sh
23chmod u+x FILE_NAME
24```
25
26If you don't like AppImage files or just prefer repositories, you can use the
27following commands to add the author's repository and install it through the
28command-line only.
29
30First, you'll have to echo the repo and write it to a list file:
31
32```sh
33echo "deb https://deb.etcher.io stable etcher" | sudo tee /etc/apt/sources.list.d/balena-etcher.list
34```
35
36Next, add the application keys to Ubuntu's keyring:
37
38```sh
39sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 379CE192D401AB61
40```
41
42Finally, update the repositories and install the app.
43
44```sh
45sudo apt update && sudo apt install balena-etcher-electron
46```
47
48Using Arch, Manjaro, or another distro using the AUR? Use this command instead:
49
50```sh
51sudo pacman -S etcher
52```
53
54## Atom
55
56[Atom](https://atom.io) is the self-proclaimed "hackable text editor for the
5721st century". This text editor is made by GitHub, [now owned by
58Microsoft](https://news.microsoft.com/2018/06/04/microsoft-to-acquire-github-for-7-5-billion/),
59and has some of the best add-ons available to customize the layout and abilities
60of the app.
61
62First, add the Atom repository to your sources.
63
64```sh
65sudo add-apt-repository ppa:webupd8team/atom
66```
67
68Next, update your package listings and install atom.
69
70```sh
71sudo apt update && sudo apt install atom
72```
73
74If you have issues updating your packages with the Atom repository, you'll need
75to use the snap package described below instead of the repository. To remove the
76repository we just added, use this command:
77
78```sh
79sudo add-apt-repository -r ppa:webupd8team/atom
80```
81
82You can also install Atom as a snap package, but it must be installed with the
83`--classic` flag. A [full explanation is
84available](https://language-bash.com/blog/how-to-snap-introducing-classic-confinement)
85if you'd like to read more about why you need the classic flag.
86
87```sh
88snap install atom --classic
89```
90
91Using Arch, Manjaro, or another distro using the AUR? Use this command instead:
92
93```sh
94sudo pacman -S atom
95```
96
97## Visual Studio Code
98
99[Visual Studio Code](https://code.visualstudio.com) is yet another fantastic
100choice for programming on Linux, especially if you need those extra add-ons to
101spice up your late-night coding sessions. The theme used in the screenshot is
102[Mars](https://marketplace.visualstudio.com/items?itemName=EliverLara.mars) by
103theme creator [Eliver Lara](https://github.com/EliverLara), who makes a ton of
104great themes for VS Code, Atom, and various Linux desktop environments.
105
106To install VS Code, you'll need to download the `.deb` file from the official
107website. Once you've downloaded the file, either double-click it to install
108through the Software Center or run the following command:
109
110```sh
111sudo dpkg -i FILE_NAME.deb
112```
113
114You can also install VS Code as a snap package, but it must be installed with
115the `--classic` flag. A [full explanation is
116available](https://language-bash.com/blog/how-to-snap-introducing-classic-confinement)
117if you'd like to read more about why you need the classic flag.
118
119```sh
120snap install code --classic
121```
122
123Using Arch, Manjaro, or another distro using the AUR? Use these commands
124instead:
125
126```sh
127sudo pacman -S yay binutils make gcc pkg-config fakeroot yay -S visual-studio-code-bin
128```
129
130## GNOME Tweaks
131
132[Gnome Tweaks](https://gitlab.gnome.org/GNOME/gnome-tweaks) is the ultimate tool
133to use if you want to customize your GNOME desktop environment. This is how you
134can switch application themes (GTK), shell themes, icons, fonts, and more. To
135install GNOME Tweaks on Ubuntu, you just need to install the official package.
136
137```sh
138sudo apt install gnome-tweaks
139```
140
141If you've installed Manjaro or Arch with Gnome, you should have the tweak tool
142pre-installed. If you're on Fedora, this tool is available as an official
143package:
144
145```sh
146sudo dnf install gnome-tweaks
147```
148
149## Steam
150
151[Steam](https://steampowered.com) is one of the most popular gaming libraries
152for computers and is one of the main reasons that many people have been able to
153switch to Linux in recent years, thanks to Steam Proton, which makes it easier
154to play games not officially created for Linux platforms.
155
156To install Steam on Ubuntu, you just need to install the official package.
157
158```sh
159sudo apt install steam-installer
160```
161
162For Arch-based systems, you'll simply need to install the `steam` package.
163However, this requires that you enable the `multilib` source. To do so, use the
164following command:
165
166```sh
167sudo nano /etc/pacman.conf
168```
169
170Now, scroll down and uncomment the `multilib` section.
171
172```config
173# Before:
174#[multilib]
175#Include = /etc/pacman.d/mirrorlist
176
177# After:
178[multilib]
179Include = /etc/pacman.d/mirrorlist
180```
181
182Finally, install the program:
183
184```sh
185sudo pacman -S steam
186```
187
188[Problem Launching Steam Games? Click
189Here.](./2020-01-26-steam-on-ntfs-drives.html)
190
191# Command-Line Packages
192
193## neofetch
194
195[Neofetch](https://github.com/dylanaraps/neofetch) is a customizable tool used
196in the command-line to show system information. This is exceptionally useful if
197you want to see your system's information quickly without the clutter of some
198resource-heavy GUI apps.
199
200This is an official package if you're running Ubuntu 17.04 or later, so simply
201use the following command:
202
203```sh
204sudo apt install neofetch
205```
206
207If you're running Ubuntu 16.10 or earlier, you'll have to use a series of
208commands:
209
210```sh
211sudo add-apt-repository ppa:dawidd0811/neofetch; sudo apt update; sudo apt install neofetch
212```
213
214Using Arch, Manjaro, or another distro using the AUR? Use this command instead:
215
216```sh
217sudo pacman -S neofetch
218```
219
220## yt-dlp
221
222[yt-dlp](https://github.com/yt-dlp/yt-dlp) is an extremely handy command-line
223tool that allows you to download video or audio files from various websites,
224such as YouTube. There are a ton of different options when running this package,
225so be sure to run `yt-dlp --help` first to look through everything you can do
226(or give up and search for the best config online).
227
228While this shouldn't be a problem for most users, yt-dlp requires Python 2.6,
2292.7, or 3.2+ to work correctly, so install Python if you don't have it already.
230You can check to see if you have Python installed by running:
231
232```sh
233python -V
234```
235
236To get the youtube-dl package, simply curl the URL and output the results.
237
238```sh
239sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp
240```
241
242Finally, make the file executable so that it can be run from the command-line.
243
244```sh
245sudo chmod a+rx /usr/local/bin/yt-dlp
246```
content/blog/2020-01-25-linux-software.org added +259
@@ -0,0 +1,259 @@
1#+date: <2020-01-25>
2#+title: My Favorite Linux Software
3#+description:
4
5
6* GUI Applications
7
8** Etcher
9
10[[https://www.balena.io/etcher/][Etcher]] is a quick and easy way to
11burn ISO images to CDs and USB devices. There are two different ways you
12can install this program. First, you can navigate to the
13[[https://www.balena.io/etcher/][official website]] and download the
14AppImage file, which can run without installation.
15
16However, AppImage files are not executable by default, so you'll either
17need to right-click to open the properties of the file and click the
18"Allow executing file as program" box in the Permissions tab or use the
19following command:
20
21#+begin_src sh
22chmod u+x FILE_NAME
23#+end_src
24
25If you don't like AppImage files or just prefer repositories, you can
26use the following commands to add the author's repository and install it
27through the command-line only.
28
29First, you'll have to echo the repo and write it to a list file:
30
31#+begin_src sh
32echo "deb https://deb.etcher.io stable etcher" | sudo tee /etc/apt/sources.list.d/balena-etcher.list
33#+end_src
34
35Next, add the application keys to Ubuntu's keyring:
36
37#+begin_src sh
38sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 379CE192D401AB61
39#+end_src
40
41Finally, update the repositories and install the app.
42
43#+begin_src sh
44sudo apt update && sudo apt install balena-etcher-electron
45#+end_src
46
47Using Arch, Manjaro, or another distro using the AUR? Use this command
48instead:
49
50#+begin_src sh
51sudo pacman -S etcher
52#+end_src
53
54** Atom
55
56[[https://atom.io][Atom]] is the self-proclaimed "hackable text editor
57for the 21st century". This text editor is made by GitHub,
58[[https://news.microsoft.com/2018/06/04/microsoft-to-acquire-github-for-7-5-billion/][now
59owned by Microsoft]], and has some of the best add-ons available to
60customize the layout and abilities of the app.
61
62First, add the Atom repository to your sources.
63
64#+begin_src sh
65sudo add-apt-repository ppa:webupd8team/atom
66#+end_src
67
68Next, update your package listings and install atom.
69
70#+begin_src sh
71sudo apt update && sudo apt install atom
72#+end_src
73
74If you have issues updating your packages with the Atom repository,
75you'll need to use the snap package described below instead of the
76repository. To remove the repository we just added, use this command:
77
78#+begin_src sh
79sudo add-apt-repository -r ppa:webupd8team/atom
80#+end_src
81
82You can also install Atom as a snap package, but it must be installed
83with the =--classic= flag. A
84[[https://language-bash.com/blog/how-to-snap-introducing-classic-confinement][full
85explanation is available]] if you'd like to read more about why you need
86the classic flag.
87
88#+begin_src sh
89snap install atom --classic
90#+end_src
91
92Using Arch, Manjaro, or another distro using the AUR? Use this command
93instead:
94
95#+begin_src sh
96sudo pacman -S atom
97#+end_src
98
99** Visual Studio Code
100
101[[https://code.visualstudio.com][Visual Studio Code]] is yet another
102fantastic choice for programming on Linux, especially if you need those
103extra add-ons to spice up your late-night coding sessions. The theme
104used in the screenshot is
105[[https://marketplace.visualstudio.com/items?itemName=EliverLara.mars][Mars]]
106by theme creator [[https://github.com/EliverLara][Eliver Lara]], who
107makes a ton of great themes for VS Code, Atom, and various Linux desktop
108environments.
109
110To install VS Code, you'll need to download the =.deb= file from the
111official website. Once you've downloaded the file, either double-click
112it to install through the Software Center or run the following command:
113
114#+begin_src sh
115sudo dpkg -i FILE_NAME.deb
116#+end_src
117
118You can also install VS Code as a snap package, but it must be installed
119with the =--classic= flag. A
120[[https://language-bash.com/blog/how-to-snap-introducing-classic-confinement][full
121explanation is available]] if you'd like to read more about why you need
122the classic flag.
123
124#+begin_src sh
125snap install code --classic
126#+end_src
127
128Using Arch, Manjaro, or another distro using the AUR? Use these commands
129instead:
130
131#+begin_src sh
132sudo pacman -S yay binutils make gcc pkg-config fakeroot yay -S visual-studio-code-bin
133#+end_src
134
135** GNOME Tweaks
136
137[[https://gitlab.gnome.org/GNOME/gnome-tweaks][Gnome Tweaks]] is the
138ultimate tool to use if you want to customize your GNOME desktop
139environment. This is how you can switch application themes (GTK), shell
140themes, icons, fonts, and more. To install GNOME Tweaks on Ubuntu, you
141just need to install the official package.
142
143#+begin_src sh
144sudo apt install gnome-tweaks
145#+end_src
146
147If you've installed Manjaro or Arch with Gnome, you should have the
148tweak tool pre-installed. If you're on Fedora, this tool is available as
149an official package:
150
151#+begin_src sh
152sudo dnf install gnome-tweaks
153#+end_src
154
155** Steam
156
157[[https://steampowered.com][Steam]] is one of the most popular gaming
158libraries for computers and is one of the main reasons that many people
159have been able to switch to Linux in recent years, thanks to Steam
160Proton, which makes it easier to play games not officially created for
161Linux platforms.
162
163To install Steam on Ubuntu, you just need to install the official
164package.
165
166#+begin_src sh
167sudo apt install steam-installer
168#+end_src
169
170For Arch-based systems, you'll simply need to install the =steam=
171package. However, this requires that you enable the =multilib= source.
172To do so, use the following command:
173
174#+begin_src sh
175sudo nano /etc/pacman.conf
176#+end_src
177
178Now, scroll down and uncomment the =multilib= section.
179
180#+begin_src config
181# Before:
182#[multilib]
183#Include = /etc/pacman.d/mirrorlist
184
185# After:
186[multilib]
187Include = /etc/pacman.d/mirrorlist
188#+end_src
189
190Finally, install the program:
191
192#+begin_src sh
193sudo pacman -S steam
194#+end_src
195
196[[./2020-01-26-steam-on-ntfs-drives.html][Problem Launching Steam Games?
197Click Here.]]
198
199* Command-Line Packages
200
201** neofetch
202
203[[https://github.com/dylanaraps/neofetch][Neofetch]] is a customizable
204tool used in the command-line to show system information. This is
205exceptionally useful if you want to see your system's information
206quickly without the clutter of some resource-heavy GUI apps.
207
208This is an official package if you're running Ubuntu 17.04 or later, so
209simply use the following command:
210
211#+begin_src sh
212sudo apt install neofetch
213#+end_src
214
215If you're running Ubuntu 16.10 or earlier, you'll have to use a series
216of commands:
217
218#+begin_src sh
219sudo add-apt-repository ppa:dawidd0811/neofetch; sudo apt update; sudo apt install neofetch
220#+end_src
221
222Using Arch, Manjaro, or another distro using the AUR? Use this command
223instead:
224
225#+begin_src sh
226sudo pacman -S neofetch
227#+end_src
228
229** yt-dlp
230
231[[https://github.com/yt-dlp/yt-dlp][yt-dlp]] is an extremely handy
232command-line tool that allows you to download video or audio files from
233various websites, such as YouTube. There are a ton of different options
234when running this package, so be sure to run =yt-dlp --help= first to
235look through everything you can do (or give up and search for the best
236config online).
237
238While this shouldn't be a problem for most users, yt-dlp requires Python
2392.6, 2.7, or 3.2+ to work correctly, so install Python if you don't have
240it already. You can check to see if you have Python installed by
241running:
242
243#+begin_src sh
244python -V
245#+end_src
246
247To get the youtube-dl package, simply curl the URL and output the
248results.
249
250#+begin_src sh
251sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp
252#+end_src
253
254Finally, make the file executable so that it can be run from the
255command-line.
256
257#+begin_src sh
258sudo chmod a+rx /usr/local/bin/yt-dlp
259#+end_src
content/blog/2020-01-26-steam-on-ntfs.md deleted −91
@@ -1,91 +0,0 @@
1+++
2date = 2020-01-26
3title = "Linux Gaming Tweak: Steam on NTFS Drives"
4description = ""
5draft = false
6+++
7
8# Auto-Mount Steam Drives
9
10If you want to see how to install Steam on Linux, see my other post: [Linux
11Software](../linux-software/).
12
13Are you having trouble launching games, even though they've installed correctly?
14This may happen if you're storing your games on an NTFS-formatted drive. This
15shouldn't be an issue if you're storing your games on the same drive that Steam
16is on, but some gamers prefer to put Steam on their main drive and game files on
17another SSD or HDD.
18
19To fix this problem, you'll need to try a few things. First, you'll need to
20install the `ntfs-3g` package, which is meant for better interoperability with
21Linux.
22
23```sh
24sudo apt install ntfs-3g
25```
26
27Next, you should set up the `/etc/fstab` file to automatically mount your drives
28on boot. To automatically mount your drives when the computer boots up, you'll
29have to create the folders you want to mount your drive to first. I store mine
30in the `/mnt` folder using names that I'll recognize, but you can create your
31folders wherever you want.
32
33```sh
34mkdir /path/to/folder
35```
36
37For example:
38
39```sh
40mkdir /mnt/steam_library
41```
42
43To automatically mount drives upon system boot, you will need to collect a few
44items. The UUID is the identification number connected to whichever drive you're
45using to store Steam games.
46
47Drives are usually labeled similar to `/dev/nvme0n1p1` or `/dev/sda1`, so you'll
48need to find the line in the output of the command below that correlates to your
49drive and copy the UUID over to the `/etc/fstab` file.
50
51```sh
52sudo blkid | grep UUID=
53```
54
55Next, you'll need your `uid` and `gid`. To find these, run the following
56command:
57
58```sh
59id -u && id -g
60```
61
62Now that you have collected the necessary information, open the `/etc/fstab`
63file:
64
65```sh
66sudo nano /etc/fstab
67```
68
69Each drive you want to mount on boot should have its own line in the
70`/etc/fstab` file that looks similar to this:
71
72```config
73UUID=B64E53824E5339F7 /mnt/steam_library ntfs-3g uid=1000,gid=1000 0 0
74```
75
76Now all you need to do is unmount your drive and re-mount it. You can unmount
77the drive by doing this (be sure to use the correct drive name here):
78
79```sh
80sudo umount /dev/sdxX
81```
82
83You can re-mount all your drives by executing the following:
84
85```sh
86sudo mount -a
87```
88
89If you don't know what your drive name is, or you're nervous about unmounting
90and re-mounting, simply reboot your computer, and it will be done for you
91automatically.
content/blog/2020-01-26-steam-on-ntfs.org added +91
@@ -0,0 +1,91 @@
1#+date: <2020-01-26>
2#+title: Linux Gaming Tweak: Steam on NTFS Drives
3#+description:
4
5
6* Auto-Mount Steam Drives
7
8If you want to see how to install Steam on Linux, see my other post:
9[[../linux-software/][Linux Software]].
10
11Are you having trouble launching games, even though they've installed
12correctly? This may happen if you're storing your games on an
13NTFS-formatted drive. This shouldn't be an issue if you're storing your
14games on the same drive that Steam is on, but some gamers prefer to put
15Steam on their main drive and game files on another SSD or HDD.
16
17To fix this problem, you'll need to try a few things. First, you'll need
18to install the =ntfs-3g= package, which is meant for better
19interoperability with Linux.
20
21#+begin_src sh
22sudo apt install ntfs-3g
23#+end_src
24
25Next, you should set up the =/etc/fstab= file to automatically mount
26your drives on boot. To automatically mount your drives when the
27computer boots up, you'll have to create the folders you want to mount
28your drive to first. I store mine in the =/mnt= folder using names that
29I'll recognize, but you can create your folders wherever you want.
30
31#+begin_src sh
32mkdir /path/to/folder
33#+end_src
34
35For example:
36
37#+begin_src sh
38mkdir /mnt/steam_library
39#+end_src
40
41To automatically mount drives upon system boot, you will need to collect
42a few items. The UUID is the identification number connected to
43whichever drive you're using to store Steam games.
44
45Drives are usually labeled similar to =/dev/nvme0n1p1= or =/dev/sda1=,
46so you'll need to find the line in the output of the command below that
47correlates to your drive and copy the UUID over to the =/etc/fstab=
48file.
49
50#+begin_src sh
51sudo blkid | grep UUID=
52#+end_src
53
54Next, you'll need your =uid= and =gid=. To find these, run the following
55command:
56
57#+begin_src sh
58id -u && id -g
59#+end_src
60
61Now that you have collected the necessary information, open the
62=/etc/fstab= file:
63
64#+begin_src sh
65sudo nano /etc/fstab
66#+end_src
67
68Each drive you want to mount on boot should have its own line in the
69=/etc/fstab= file that looks similar to this:
70
71#+begin_src config
72UUID=B64E53824E5339F7 /mnt/steam_library ntfs-3g uid=1000,gid=1000 0 0
73#+end_src
74
75Now all you need to do is unmount your drive and re-mount it. You can
76unmount the drive by doing this (be sure to use the correct drive name
77here):
78
79#+begin_src sh
80sudo umount /dev/sdxX
81#+end_src
82
83You can re-mount all your drives by executing the following:
84
85#+begin_src sh
86sudo mount -a
87#+end_src
88
89If you don't know what your drive name is, or you're nervous about
90unmounting and re-mounting, simply reboot your computer, and it will be
91done for you automatically.
content/blog/2020-02-09-cryptography-basics.md deleted −167
@@ -1,167 +0,0 @@
1+++
2date = 2020-02-09
3title = "Cryptography Basics"
4description = ""
5draft = false
6+++
7
8# Similar Article Available
9
10If you haven't already, feel free to read my post on [AES
11Encryption](../aes-encryption/).
12
13# What is Cryptography?
14
15In layman's terms, cryptography is a process that can change data from a
16readable format into an unreadable format (and vice-versa) through a series of
17processes and secrets. More technically, this is the Internet Security
18Glossary's definition:
19
20> [Cryptography is] the mathematical science that deals with transforming data
21> to render its meaning unintelligible (i.e., to hide its semantic content),
22> prevent its undetected alteration, or prevent its unauthorized use. If the
23> transformation is reversible, cryptography also deals with restoring encrypted
24> data to an intelligible form.
25>
26> - [Internet Security Glossary (2000)](https://tools.ietf.org/html/rfc2828)
27
28Cryptography cannot offer protection against the loss of data; it simply offers
29encryption methods to protect data at-rest and data in-traffic. At a high-level,
30encrypted is when plaintext data is encrypted to ciphertext (a secure form of
31text that cannot be understood unless decrypted back to plaintext). The
32encryption process is completed through the use of a mathematical function that
33utilizes one or more values called keys to encrypt or decrypt the data.
34
35# Key Elements of Cryptographic Systems
36
37To create or evaluate a cryptographic system, you need to know the essential
38pieces to the system:
39
40- **Encryption Algorithm (Primitive):** A mathematical process that encrypts
41 and decrypts data.
42- **Encryption Key:** A string of bits used within the encryption algorithm as
43 the secret that allows successful encryption or decryption of data.
44- **Key Length (Size):** The maximum number of bits within the encryption key.
45 It's important to remember that key size is regulated in many countries.
46- **Message Digest:** A smaller, fixed-size bit string version of the original
47 message. This is practically infeasible to reverse, which is why it's
48 commonly used to verify integrity.
49
50# Symmetric Systems (Secret Key Cryptography)
51
52Symmetric cryptography utilizes a secret, bidirectional key to perform both
53encryption and decryption of the data. The most common implementation of
54symmetric cryptography is the Advanced Encryption Standard, which uses keys that
55are 128 bits to 256 bits in size. This standard came after the National
56Institute of Standards and Technology (NIST) decided to retire the Data
57Encryption Standard (DES) in 2001.
58
59Since brute force attacks strongly correlate with key length, the 56-bit key
60length of DES was considered insecure after it was publicly broken in under 24
61hours. However, there is a modern implementation of DES called Triple DES where
62the DES method is applied three times to each data block.
63
64The main advantages to symmetric systems are the ease of use, since only one key
65is required for both encryption and decryption, and the simplicity of the
66algorithms. This helps with bulk data encryption that may unnecessarily waste
67time and power using asymmetric systems.
68
69However, symmetric systems have disadvantages to keep in mind. Since the key is
70private, it can be difficult to safely distribute keys to communication
71partners. Additionally, the key cannot be used to sign messages since it's
72necessary to keep the key private.
73
74# Asymmetric Systems (Public Key Cryptography)
75
76Asymmetric cryptography utilizes two keys within the system: a secret key that
77is privately-held and a public key that can be distributed freely. The
78interesting aspect of asymmetric cryptography is that either key can be used to
79encrypt the data, there's no rule that dictates which key must be used for
80encryption. Once one key is used to encrypt the data, only the other key can be
81used to decrypt the data. This means that if the private key encrypts the data,
82only the public key can decrypt the data.
83
84An advantage of this system is that if you successfully decrypt data using one
85of the keys, you can be sure of the sender since only the other key could have
86encrypted the data.
87
88One of the major implementations of an asymmetric system is a digital signature.
89A digital signature can be generated using the sender's private key, or a
90one-way hash function and is used to provide assurance for the integrity and
91authenticity of the message. A couple common message digest algorithms are
92SHA-256 and SHA-512, which securely compress data and produce a 128-bit message
93digest.
94
95It should be noted that man-in-the-middle attacks are one of the risks with
96digital signatures and public keys. To combat this, applications often use a
97public key infrastructure (PKI) to independently authenticate the validity of
98signatures and keys.
99
100Due to the large key size and [inefficient mathematical
101functions](https://crypto.stackexchange.com/a/591) of asymmetric encryption,
102elliptical curve cryptography (ECC) is often used to increase security while
103using fewer resources.
104
105# Applications of Cryptographic Systems
106
107There are quite a few implementations of cryptographic systems around the world.
108Here are a few popular examples:
109
110**Transport Layer Security (TLS):** One of the most famous cryptographic
111solutions created is TLS, a session-layered or connection-layered internet
112protocol that allows for secure communications between browsers and servers.
113Using handshakes, peer negotiation, and authentication allows TLS to prevent
114eavesdropping and malicious transformation of data. The major reason for TLS
115popularity is that a major vulnerability was found in the SSL protocol in 2014.
116Instead of SSL, TLS can be used with HTTP to form HTTPS and is the preferred
117method for modern web development due to its increased security.
118
119**Secure Hypertext Transfer Protocol (HTTPS):** An application layer protocol
120that allows for secure transport of data between servers and web clients. One of
121the unique parts of HTTPS is that it uses a secured port number instead of the
122default web port address.
123
124**Virtual Private Network (VPN):** VPNs are made to securely extend a private
125network across public networks by utilizing an encrypted layered tunneling
126protocol paired with an authentication method, such as usernames and passwords.
127This technology originally allowed remote employees to access their company's
128data but have evolved into one of the top choices for anyone who wishes to mask
129their sensitive personal data.
130
131**Internet Protocol Security (IPSec):** This protocol suite facilitates
132communication between two or more hosts or subnets by authenticating and
133encrypting the data packets. IPSec is used in a lot of VPNs to establish the VPN
134connection through the transport and tunnel mode encryption methods. IPSec
135encrypts just the data portion of packets in the transport methods, but it
136encrypts both the data and headers in the tunnel method (introducing an
137additional header for authentication).
138
139**Secure Shell (SSH):** SSH is another network protocol used to protect network
140services by authenticating users through a secure channel. This protocol is
141often used for command-line (shell) functions such as remote shell commands,
142logins, and file transfers.
143
144**Kerberos:** Developed by MIT, Kerberos is a computer-network authentication
145protocol that works on the basis of tickets to allow nodes communicating over a
146non-secure network to prove their identity to one another securely. This is most
147commonly used in business environments when used as the authentication and
148encryption method for Windows Active Directory (AD).
149
150# Cybersecurity Controls
151
152If you're someone who needs solutions on how to control risks associated with
153utilizing a crytograhpic system, start with a few basic controls:
154
155- **Policies:** A policy on the use of cryptographic controls for protection
156 of information is implemented and is in accordance with organizational
157 objectives.
158- **Key management:** A policy on the use, protection and lifetime of
159 cryptographic keys is implemented through the entire application lifecycle.
160- **Key size:** The organization has researched the optimal key size for their
161 purposes, considering national laws, required processing power, and
162 longevity of the solution.
163- **Algorithm selection:** Implemented algorithms are sufficiently appropriate
164 for the business of the organization, robust, and align with recommended
165 guidelines.
166- **Protocol configuration:** Protocols have been reviewed and configured
167 suitable to the purpose of the business.
content/blog/2020-02-09-cryptography-basics.org added +177
@@ -0,0 +1,177 @@
1#+date: <2020-02-09>
2#+title: Cryptography Basics
3#+description:
4
5
6* Similar Article Available
7
8If you haven't already, feel free to read my post on
9[[../aes-encryption/][AES Encryption]].
10
11* What is Cryptography?
12
13In layman's terms, cryptography is a process that can change data from a
14readable format into an unreadable format (and vice-versa) through a
15series of processes and secrets. More technically, this is the Internet
16Security Glossary's definition:
17
18#+begin_quote
19[Cryptography is] the mathematical science that deals with transforming
20data to render its meaning unintelligible (i.e., to hide its semantic
21content), prevent its undetected alteration, or prevent its unauthorized
22use. If the transformation is reversible, cryptography also deals with
23restoring encrypted data to an intelligible form.
24
25- [[https://tools.ietf.org/html/rfc2828][Internet Security Glossary
26 (2000)]]
27#+end_quote
28
29Cryptography cannot offer protection against the loss of data; it simply
30offers encryption methods to protect data at-rest and data in-traffic.
31At a high-level, encrypted is when plaintext data is encrypted to
32ciphertext (a secure form of text that cannot be understood unless
33decrypted back to plaintext). The encryption process is completed
34through the use of a mathematical function that utilizes one or more
35values called keys to encrypt or decrypt the data.
36
37* Key Elements of Cryptographic Systems
38
39To create or evaluate a cryptographic system, you need to know the
40essential pieces to the system:
41
42- *Encryption Algorithm (Primitive):* A mathematical process that
43 encrypts and decrypts data.
44- *Encryption Key:* A string of bits used within the encryption
45 algorithm as the secret that allows successful encryption or
46 decryption of data.
47- *Key Length (Size):* The maximum number of bits within the encryption
48 key. It's important to remember that key size is regulated in many
49 countries.
50- *Message Digest:* A smaller, fixed-size bit string version of the
51 original message. This is practically infeasible to reverse, which is
52 why it's commonly used to verify integrity.
53
54* Symmetric Systems (Secret Key Cryptography)
55
56Symmetric cryptography utilizes a secret, bidirectional key to perform
57both encryption and decryption of the data. The most common
58implementation of symmetric cryptography is the Advanced Encryption
59Standard, which uses keys that are 128 bits to 256 bits in size. This
60standard came after the National Institute of Standards and Technology
61(NIST) decided to retire the Data Encryption Standard (DES) in 2001.
62
63Since brute force attacks strongly correlate with key length, the 56-bit
64key length of DES was considered insecure after it was publicly broken
65in under 24 hours. However, there is a modern implementation of DES
66called Triple DES where the DES method is applied three times to each
67data block.
68
69The main advantages to symmetric systems are the ease of use, since only
70one key is required for both encryption and decryption, and the
71simplicity of the algorithms. This helps with bulk data encryption that
72may unnecessarily waste time and power using asymmetric systems.
73
74However, symmetric systems have disadvantages to keep in mind. Since the
75key is private, it can be difficult to safely distribute keys to
76communication partners. Additionally, the key cannot be used to sign
77messages since it's necessary to keep the key private.
78
79* Asymmetric Systems (Public Key Cryptography)
80
81Asymmetric cryptography utilizes two keys within the system: a secret
82key that is privately-held and a public key that can be distributed
83freely. The interesting aspect of asymmetric cryptography is that either
84key can be used to encrypt the data, there's no rule that dictates which
85key must be used for encryption. Once one key is used to encrypt the
86data, only the other key can be used to decrypt the data. This means
87that if the private key encrypts the data, only the public key can
88decrypt the data.
89
90An advantage of this system is that if you successfully decrypt data
91using one of the keys, you can be sure of the sender since only the
92other key could have encrypted the data.
93
94One of the major implementations of an asymmetric system is a digital
95signature. A digital signature can be generated using the sender's
96private key, or a one-way hash function and is used to provide assurance
97for the integrity and authenticity of the message. A couple common
98message digest algorithms are SHA-256 and SHA-512, which securely
99compress data and produce a 128-bit message digest.
100
101It should be noted that man-in-the-middle attacks are one of the risks
102with digital signatures and public keys. To combat this, applications
103often use a public key infrastructure (PKI) to independently
104authenticate the validity of signatures and keys.
105
106Due to the large key size and
107[[https://crypto.stackexchange.com/a/591][inefficient mathematical
108functions]] of asymmetric encryption, elliptical curve cryptography
109(ECC) is often used to increase security while using fewer resources.
110
111* Applications of Cryptographic Systems
112
113There are quite a few implementations of cryptographic systems around
114the world. Here are a few popular examples:
115
116*Transport Layer Security (TLS):* One of the most famous cryptographic
117solutions created is TLS, a session-layered or connection-layered
118internet protocol that allows for secure communications between browsers
119and servers. Using handshakes, peer negotiation, and authentication
120allows TLS to prevent eavesdropping and malicious transformation of
121data. The major reason for TLS popularity is that a major vulnerability
122was found in the SSL protocol in 2014. Instead of SSL, TLS can be used
123with HTTP to form HTTPS and is the preferred method for modern web
124development due to its increased security.
125
126*Secure Hypertext Transfer Protocol (HTTPS):* An application layer
127protocol that allows for secure transport of data between servers and
128web clients. One of the unique parts of HTTPS is that it uses a secured
129port number instead of the default web port address.
130
131*Virtual Private Network (VPN):* VPNs are made to securely extend a
132private network across public networks by utilizing an encrypted layered
133tunneling protocol paired with an authentication method, such as
134usernames and passwords. This technology originally allowed remote
135employees to access their company's data but have evolved into one of
136the top choices for anyone who wishes to mask their sensitive personal
137data.
138
139*Internet Protocol Security (IPSec):* This protocol suite facilitates
140communication between two or more hosts or subnets by authenticating and
141encrypting the data packets. IPSec is used in a lot of VPNs to establish
142the VPN connection through the transport and tunnel mode encryption
143methods. IPSec encrypts just the data portion of packets in the
144transport methods, but it encrypts both the data and headers in the
145tunnel method (introducing an additional header for authentication).
146
147*Secure Shell (SSH):* SSH is another network protocol used to protect
148network services by authenticating users through a secure channel. This
149protocol is often used for command-line (shell) functions such as remote
150shell commands, logins, and file transfers.
151
152*Kerberos:* Developed by MIT, Kerberos is a computer-network
153authentication protocol that works on the basis of tickets to allow
154nodes communicating over a non-secure network to prove their identity to
155one another securely. This is most commonly used in business
156environments when used as the authentication and encryption method for
157Windows Active Directory (AD).
158
159* Cybersecurity Controls
160
161If you're someone who needs solutions on how to control risks associated
162with utilizing a crytograhpic system, start with a few basic controls:
163
164- *Policies:* A policy on the use of cryptographic controls for
165 protection of information is implemented and is in accordance with
166 organizational objectives.
167- *Key management:* A policy on the use, protection and lifetime of
168 cryptographic keys is implemented through the entire application
169 lifecycle.
170- *Key size:* The organization has researched the optimal key size for
171 their purposes, considering national laws, required processing power,
172 and longevity of the solution.
173- *Algorithm selection:* Implemented algorithms are sufficiently
174 appropriate for the business of the organization, robust, and align
175 with recommended guidelines.
176- *Protocol configuration:* Protocols have been reviewed and configured
177 suitable to the purpose of the business.
content/blog/2020-03-25-session-messenger.md deleted −111
@@ -1,111 +0,0 @@
1+++
2date = 2020-03-25
3title = "Session Private Messenger"
4description = ""
5draft = false
6+++
7
8# Privacy Warning
9
10The company behind Session (Loki Foundation) is from Australia. If you didn't
11know, Australia has introduced
12[legislation](https://parlinfo.aph.gov.au/parlInfo/download/legislation/bills/r6195_aspassed/toc_pdf/18204b01.pdf)
13mandating companies comply with government requests to build backdoor access
14into applications. For more information, read my article on [AES
15Encryption](./2020-01-25-aes-encryption.html).
16
17# About Session
18
19[Session](https://getsession.org) is a private, cross-platform messaging app
20from the [Loki Foundation](https://loki.foundation). As someone who has spent
21years looking for quality alternatives to major messaging apps, I was excited
22when I first heard about Session. Reading through [Session's white
23paper](https://arxiv.org/pdf/2002.04609.pdf), you can learn the technologies
24behind the Session app. Part of the security of Session comes from the Signal
25protocol, which was forked as the origin of Session.
26
27> Session is an end-to-end encrypted messenger that removes sensitive metadata
28> collection, and is designed for people who want privacy and freedom from any
29> forms of surveillance.
30
31In general, this app promises security through end-to-end encryption,
32decentralized onion routing, and private identities. The biggest change that the
33Loki Foundation has made to the Signal protocol is removing the need for a phone
34number. Instead, a random identification string is generated for any session you
35create. This means you can create a new session for each device if you want to,
36or link new devices with your ID.
37
38Since Session's website and white paper describe the details of Session's
39security, I'm going to focus on using the app in this post.
40
41# Features
42
43Since most people are looking for an alternative to a popular chat app, I am
44going to list out the features that Session has so that you are able to
45determine if the app would suit your needs:
46
47- Multiple device linking (via QR code or ID)
48- App locking via device screen lock, password, or fingerprint
49- Screenshot blocking
50- Incognito keyboard
51- Read receipts and typing indicators
52- Mobile notification customization
53- Old message deletion and conversation limit
54- Backups
55- Recovery phrase
56- Account deletion, including ID, messages, sessions, and contacts
57
58# Downloads
59
60I have tested this app on Ubuntu 19.10, Android 10, macOS Monterey, and iOS 15.
61All apps have worked well without many issues.
62
63Below is a brief overview of the Session app on Linux. To get this app, you'll
64need to go to the [Downloads](https://getsession.org/download/) page and click
65to link to the operating system you're using.
66
67For Linux, it will download an AppImage that you'll need to enable with the
68following command:
69
70```sh
71sudo chmod u+x session-messenger-desktop-linux-x86_64-1.0.5.AppImage
72```
73
74# Creating an Account
75
76Once you've installed the app, simply run the app and create your unique Session
77ID. It will look something like this:
78`05af1835afdd63c947b47705867501d6373f486aa1ae05b1f2f3fcd24570eba608`.
79
80You'll need to set a display name and, optionally, a password. If you set a
81password, you will need to enter it every time you open the app.
82
83# Start Messaging
84
85Once you've created your account and set up your profile details, the next step
86is to start messaging other people. To do so, you'll need to share your Session
87ID with other people. From this point, it's fairly straightforward and acts like
88any other messaging app, so I won't dive into much detail here.
89
90## macOS
91
92One key feature to note is that the desktop application now provides a helpful
93pop-up box explaining the process that Session uses to hide your IP address:
94
95## iOS
96
97The mobile app is quite simple and effective, giving you all the standard mobile
98messaging options you'd expect.
99
100# Potential Issues
101
102I've discovered one annoying issue that would prevent from using this app
103regularly. On a mobile device, there have been issues with receiving messages on
104time. Even with battery optimization disabled and no network restrictions,
105Session notifications sometimes do not display until I open the app or the
106conversation itself and wait a few moments. This is actually one of the reasons
107I stopped using Signal (this seems fixed as of my updates in 2021/2022, so I
108wouldn't worry about this issue anymore).
109
110Looking for another messenger instead of Session? I recommend Signal, Matrix,
111and IRC.
content/blog/2020-03-25-session-messenger.org added +116
@@ -0,0 +1,116 @@
1#+date: <2020-03-25>
2#+title: Session Private Messenger
3#+description:
4
5
6* Privacy Warning
7
8The company behind Session (Loki Foundation) is from Australia. If you
9didn't know, Australia has introduced
10[[https://parlinfo.aph.gov.au/parlInfo/download/legislation/bills/r6195_aspassed/toc_pdf/18204b01.pdf][legislation]]
11mandating companies comply with government requests to build backdoor
12access into applications. For more information, read my article on
13[[./2020-01-25-aes-encryption.html][AES Encryption]].
14
15* About Session
16
17[[https://getsession.org][Session]] is a private, cross-platform
18messaging app from the [[https://loki.foundation][Loki Foundation]]. As
19someone who has spent years looking for quality alternatives to major
20messaging apps, I was excited when I first heard about Session. Reading
21through [[https://arxiv.org/pdf/2002.04609.pdf][Session's white paper]],
22you can learn the technologies behind the Session app. Part of the
23security of Session comes from the Signal protocol, which was forked as
24the origin of Session.
25
26#+begin_quote
27Session is an end-to-end encrypted messenger that removes sensitive
28metadata collection, and is designed for people who want privacy and
29freedom from any forms of surveillance.
30#+end_quote
31
32In general, this app promises security through end-to-end encryption,
33decentralized onion routing, and private identities. The biggest change
34that the Loki Foundation has made to the Signal protocol is removing the
35need for a phone number. Instead, a random identification string is
36generated for any session you create. This means you can create a new
37session for each device if you want to, or link new devices with your
38ID.
39
40Since Session's website and white paper describe the details of
41Session's security, I'm going to focus on using the app in this post.
42
43* Features
44
45Since most people are looking for an alternative to a popular chat app,
46I am going to list out the features that Session has so that you are
47able to determine if the app would suit your needs:
48
49- Multiple device linking (via QR code or ID)
50- App locking via device screen lock, password, or fingerprint
51- Screenshot blocking
52- Incognito keyboard
53- Read receipts and typing indicators
54- Mobile notification customization
55- Old message deletion and conversation limit
56- Backups
57- Recovery phrase
58- Account deletion, including ID, messages, sessions, and contacts
59
60* Downloads
61
62I have tested this app on Ubuntu 19.10, Android 10, macOS Monterey, and
63iOS 15. All apps have worked well without many issues.
64
65Below is a brief overview of the Session app on Linux. To get this app,
66you'll need to go to the [[https://getsession.org/download/][Downloads]]
67page and click to link to the operating system you're using.
68
69For Linux, it will download an AppImage that you'll need to enable with
70the following command:
71
72#+begin_src sh
73sudo chmod u+x session-messenger-desktop-linux-x86_64-1.0.5.AppImage
74#+end_src
75
76* Creating an Account
77
78Once you've installed the app, simply run the app and create your unique
79Session ID. It will look something like this:
80=05af1835afdd63c947b47705867501d6373f486aa1ae05b1f2f3fcd24570eba608=.
81
82You'll need to set a display name and, optionally, a password. If you
83set a password, you will need to enter it every time you open the app.
84
85* Start Messaging
86
87Once you've created your account and set up your profile details, the
88next step is to start messaging other people. To do so, you'll need to
89share your Session ID with other people. From this point, it's fairly
90straightforward and acts like any other messaging app, so I won't dive
91into much detail here.
92
93** macOS
94
95One key feature to note is that the desktop application now provides a
96helpful pop-up box explaining the process that Session uses to hide your
97IP address:
98
99** iOS
100
101The mobile app is quite simple and effective, giving you all the
102standard mobile messaging options you'd expect.
103
104* Potential Issues
105
106I've discovered one annoying issue that would prevent from using this
107app regularly. On a mobile device, there have been issues with receiving
108messages on time. Even with battery optimization disabled and no network
109restrictions, Session notifications sometimes do not display until I
110open the app or the conversation itself and wait a few moments. This is
111actually one of the reasons I stopped using Signal (this seems fixed as
112of my updates in 2021/2022, so I wouldn't worry about this issue
113anymore).
114
115Looking for another messenger instead of Session? I recommend Signal,
116Matrix, and IRC.
content/blog/2020-05-03-homelab.md deleted −152
@@ -1,152 +0,0 @@
1+++
2date = 2020-05-03
3title = "An Inside Look at My Home Lab"
4description = ""
5draft = false
6+++
7
8# What is a Homelab?
9
10Starting as a developer, I have largely stayed away from hardware-based hobbies
11(other than building a gaming desktop). However, as the quarantine for COVID-19
12stretches out further and further, I found myself bored and in search of new
13hobbies. After spending the last few months browsing the
14[r/homelab](https://www.reddit.com/r/homelab/) subreddit, I decided it was time
15to jump in and try things out for myself.
16
17Since I am a beginner and just recently graduated from college, everything I've
18done so far in my homelab is fairly low-budget.
19
20# Hardware
21
22## Raspberry Pi 4
23
24Luckily, I had actually purchased a [Raspberry Pi
254](https://www.raspberrypi.org/products/raspberry-pi-4-model-b/) before the
26quarantine started so that I could try to keep Plex Media Center running, even
27while my desktop computer was turned off. I started here, using the Pi to hold
28Plex and Pi-hole until I grew tired with the slow performance.
29
30Here are the specifications for the Pi 4:
31
32- Broadcom BCM2711, Quad core Cortex-A72 (ARM v8) 64-bit SoC @ 1.5GHz
33- 4GB LPDDR4-3200 SDRAM
34- Gigabit Ethernet
35- H.265 (4kp60 decode), H264 (1080p60 decode, 1080p30 encode)
36- 64 GB MicroSD Card
37
38## Dell Optiplex 5040
39
40Since I wasn't happy with the Pi as my main server, I turned to Craigslist. I
41know a lot of other homelabbers use Ebay, but I can't seem to ever trust it
42enough to purchase items on there. So I used Craigslist and found a Dell
43Optiplex 5040 desktop computer on sale for $90. While this computer might be
44underpowered, it was one of the few computers under $100 that was available
45during quarantine.
46
47Here are the specifications for the Dell Optiplex 5040:
48
49- Intel Core i3 6100
50- 8GB RAM DDR3
51- Intel HD Graphics
52- Gigabit Ethernet
53- 500GB Hard Drive
54
55While this hardware would be awful for a work computer or a gaming rig, it
56turned out to be wonderful for my server purposes. The only limitation I have
57found so far is the CPU. The i3-6100 only has enough power for a single 4k video
58transcode at a time. I haven't tested more than three 1080p streams at a time,
59but the maximum amount of streams I've ever actually used is two.
60
61## WD easystore 10TB & 8TB
62
63Application storage and temporary files are stored on the internal hard drive of
64the server, but all media files (movies, tv, games, books, etc) are stored
65externally on my WD easystore hard drive. Creating auto-boot configurations in
66the `/etc/fstab` file on my server allows the hard drives to automatically mount
67whenever I need to restart my server.
68
69> Update: In March 2022, I shucked the hard drives out of their external cases,
70> put some Kapton tape on the third power pin to prevent power shutdowns, and
71> stuck them inside my server tower using internal SATA cables.
72
73## Netgear Unmanaged Switch
74
75To manage all the ethernet cords used by my homelab, my desktop, and my living
76room media center, I purchased an 8-port gigabit ethernet switch for $50 at my
77local computer store. This is probably much more than I should have spent on an
78unmanaged switch, but I am comfortable with the choice.
79
80## TP-Link Managed Switch
81
82Since I use the unmanaged switch to group all living room devices together, I
83use the managed switch to configure VLANs and secure my network.
84
85## Arris TM1602A Modem & Sagecom Fast 5280 Router
86
87My default modem and router, provided by my ISP, are fairly standard. The Arris
88modem supports DOCSIS 3.0, which is something that I definitely wanted as a
89minimum. The Sagecom router is also standard, no fancy bells or whistles.
90However, it does support DHCP and DHCPv6, which is something you can use to
91route all household traffic through a pi-hole or firewall.
92
93## TP-Link EAP
94
95In order to gain better control over the network, I use my own wireless access
96point instead of the one included in the Sagecom router above. Now I can control
97and organize all of my ethernet connections through the VLANs on the managed
98switch and wireless connections through the VLANS on the EAP.
99
100## Generic Printer
101
102The last piece to my homelab is a standard wireless printer. Nothing special
103here.
104
105# Software
106
107## Ubuntu Server 20.04
108
109While the 20.04 version of Ubuntu was just released, I always like to experiment
110with new features (and I don't mind breaking my system - it just gives me more
111experience learning how to fix things). So, I have Ubuntu Server 20.04 installed
112on the Dell Optiplex server and Ubuntu Server 19.10 installed on the Raspberry
113Pi. Once I find an acceptable use for the Pi, I will most likely switch the
114operating system.
115
116## Docker
117
118I am _very_ new to Docker, but I have had a lot of fun playing with it so far.
119Docker is used to create containers that can hold all the contents of a system
120without interfering with other software on the same system. So far, I have
121successfully installed pi-hole, GitLab, Gogs, and Nextcloud in containers.
122However, I opted to delete all of those so that I can reconfigure them more
123professionally at a later time.
124
125## Plex Media Server
126
127Plex is a media center software that allows you to organize your movies, TV
128shows, music, photos, and videos automatically. It will even download metadata
129for you so that you can easily browse these collections.
130
131## Pi-hole
132
133Pi-hole is an alternative ad-blocker that runs at the DNS level, allowing you to
134block traffic when it hits your network, so that you can reject any traffic you
135deem to be bad. Pi-hole uses blacklists and whitelists to decide which traffic
136block and, luckily, there are a lot of pre-made lists out there on Reddit,
137GitHub, etc.
138
139## Nextcloud
140
141While I had trouble with the Docker version of Nextcloud, I was very successful
142when setting up the snap version. Using this, I was able to map Nextcloud to a
143subdomain of a domain I own in Namecheap. Additionally, Nextcloud has an
144integration with Let's Encrypt that allows me to issue certificates
145automatically to any new domain I authorize.
146
147## Webmin
148
149To monitor my servers, and the processes running on them, I use the Webmin
150dashboard. This was fairly painless to set up, and I currently access it
151straight through the server's IP address. In the future, I will be looking to
152configure Webmin to use a custom domain just like Nextcloud.
content/blog/2020-05-03-homelab.org added +162
@@ -0,0 +1,162 @@
1#+date: <2020-05-03>
2#+title: An Inside Look at My Home Lab
3#+description:
4
5
6* What is a Homelab?
7
8Starting as a developer, I have largely stayed away from hardware-based
9hobbies (other than building a gaming desktop). However, as the
10quarantine for COVID-19 stretches out further and further, I found
11myself bored and in search of new hobbies. After spending the last few
12months browsing the [[https://www.reddit.com/r/homelab/][r/homelab]]
13subreddit, I decided it was time to jump in and try things out for
14myself.
15
16Since I am a beginner and just recently graduated from college,
17everything I've done so far in my homelab is fairly low-budget.
18
19* Hardware
20
21** Raspberry Pi 4
22
23Luckily, I had actually purchased a
24[[https://www.raspberrypi.org/products/raspberry-pi-4-model-b/][Raspberry
25Pi 4]] before the quarantine started so that I could try to keep Plex
26Media Center running, even while my desktop computer was turned off. I
27started here, using the Pi to hold Plex and Pi-hole until I grew tired
28with the slow performance.
29
30Here are the specifications for the Pi 4:
31
32- Broadcom BCM2711, Quad core Cortex-A72 (ARM v8) 64-bit SoC @ 1.5GHz
33- 4GB LPDDR4-3200 SDRAM
34- Gigabit Ethernet
35- H.265 (4kp60 decode), H264 (1080p60 decode, 1080p30 encode)
36- 64 GB MicroSD Card
37
38** Dell Optiplex 5040
39
40Since I wasn't happy with the Pi as my main server, I turned to
41Craigslist. I know a lot of other homelabbers use Ebay, but I can't seem
42to ever trust it enough to purchase items on there. So I used Craigslist
43and found a Dell Optiplex 5040 desktop computer on sale for $90. While
44this computer might be underpowered, it was one of the few computers
45under $100 that was available during quarantine.
46
47Here are the specifications for the Dell Optiplex 5040:
48
49- Intel Core i3 6100
50- 8GB RAM DDR3
51- Intel HD Graphics
52- Gigabit Ethernet
53- 500GB Hard Drive
54
55While this hardware would be awful for a work computer or a gaming rig,
56it turned out to be wonderful for my server purposes. The only
57limitation I have found so far is the CPU. The i3-6100 only has enough
58power for a single 4k video transcode at a time. I haven't tested more
59than three 1080p streams at a time, but the maximum amount of streams
60I've ever actually used is two.
61
62** WD easystore 10TB & 8TB
63
64Application storage and temporary files are stored on the internal hard
65drive of the server, but all media files (movies, tv, games, books, etc)
66are stored externally on my WD easystore hard drive. Creating auto-boot
67configurations in the =/etc/fstab= file on my server allows the hard
68drives to automatically mount whenever I need to restart my server.
69
70#+begin_quote
71Update: In March 2022, I shucked the hard drives out of their external
72cases, put some Kapton tape on the third power pin to prevent power
73shutdowns, and stuck them inside my server tower using internal SATA
74cables.
75#+end_quote
76
77** Netgear Unmanaged Switch
78
79To manage all the ethernet cords used by my homelab, my desktop, and my
80living room media center, I purchased an 8-port gigabit ethernet switch
81for $50 at my local computer store. This is probably much more than I
82should have spent on an unmanaged switch, but I am comfortable with the
83choice.
84
85** TP-Link Managed Switch
86
87Since I use the unmanaged switch to group all living room devices
88together, I use the managed switch to configure VLANs and secure my
89network.
90
91** Arris TM1602A Modem & Sagecom Fast 5280 Router
92
93My default modem and router, provided by my ISP, are fairly standard.
94The Arris modem supports DOCSIS 3.0, which is something that I
95definitely wanted as a minimum. The Sagecom router is also standard, no
96fancy bells or whistles. However, it does support DHCP and DHCPv6, which
97is something you can use to route all household traffic through a
98pi-hole or firewall.
99
100** TP-Link EAP
101
102In order to gain better control over the network, I use my own wireless
103access point instead of the one included in the Sagecom router above.
104Now I can control and organize all of my ethernet connections through
105the VLANs on the managed switch and wireless connections through the
106VLANS on the EAP.
107
108** Generic Printer
109
110The last piece to my homelab is a standard wireless printer. Nothing
111special here.
112
113* Software
114
115** Ubuntu Server 20.04
116
117While the 20.04 version of Ubuntu was just released, I always like to
118experiment with new features (and I don't mind breaking my system - it
119just gives me more experience learning how to fix things). So, I have
120Ubuntu Server 20.04 installed on the Dell Optiplex server and Ubuntu
121Server 19.10 installed on the Raspberry Pi. Once I find an acceptable
122use for the Pi, I will most likely switch the operating system.
123
124** Docker
125
126I am /very/ new to Docker, but I have had a lot of fun playing with it
127so far. Docker is used to create containers that can hold all the
128contents of a system without interfering with other software on the same
129system. So far, I have successfully installed pi-hole, GitLab, Gogs, and
130Nextcloud in containers. However, I opted to delete all of those so that
131I can reconfigure them more professionally at a later time.
132
133** Plex Media Server
134
135Plex is a media center software that allows you to organize your movies,
136TV shows, music, photos, and videos automatically. It will even download
137metadata for you so that you can easily browse these collections.
138
139** Pi-hole
140
141Pi-hole is an alternative ad-blocker that runs at the DNS level,
142allowing you to block traffic when it hits your network, so that you can
143reject any traffic you deem to be bad. Pi-hole uses blacklists and
144whitelists to decide which traffic block and, luckily, there are a lot
145of pre-made lists out there on Reddit, GitHub, etc.
146
147** Nextcloud
148
149While I had trouble with the Docker version of Nextcloud, I was very
150successful when setting up the snap version. Using this, I was able to
151map Nextcloud to a subdomain of a domain I own in Namecheap.
152Additionally, Nextcloud has an integration with Let's Encrypt that
153allows me to issue certificates automatically to any new domain I
154authorize.
155
156** Webmin
157
158To monitor my servers, and the processes running on them, I use the
159Webmin dashboard. This was fairly painless to set up, and I currently
160access it straight through the server's IP address. In the future, I
161will be looking to configure Webmin to use a custom domain just like
162Nextcloud.
content/blog/2020-05-19-customizing-ubuntu.md deleted −193
@@ -1,193 +0,0 @@
1+++
2date = 2020-05-19
3title = "A Beginner's Guide: Customizing Ubuntu"
4description = ""
5draft = false
6+++
7
8# More Information
9
10For inspiration on designing your \*nix computer, check out the
11[r/unixporn](https://libredd.it/r/unixporn) subreddit!
12
13# Customizing Ubuntu
14
15New to Linux and want to add a personal touch to your machine? One of the best
16perks of Linux is that it is **extremely** customizable. You can change the
17styles of the windows, shell (status bars/docks), icons, fonts, terminals, and
18more.
19
20In this post, I'm going to go through customization on Ubuntu 20.04 (GNOME)
21since most new users tend to choose Ubuntu-based distros. If you've found a way
22to install Arch with i3-gaps, I'm assuming you know how to find more advanced
23tutorials out there on customizations.
24
25## Required Tools
26
27Ubuntu 20.04 ships with the default desktop environment
28[Gnome](https://www.gnome.org/), which includes the handy `gnome-tweaks` tool to
29quickly change designs. To install this, just open your terminal and enter the
30following command:
31
32```sh
33sudo apt install gnome-tweaks
34```
35
36After you've finished installing the tool, simply launch the Tweaks application,
37and you'll be able to access the various customization options available by
38default on Ubuntu. You might even like some of the pre-installed options.
39
40## GNOME Application Themes
41
42To change the themes applied to applications in GNOME, you will need to change
43the Applications dropdown in the Appearance section of Tweaks. To add more
44themes, you will have to find your preferred theme online and follow the steps
45below to have it show up in the Tweaks tool. While you may find themes anywhere,
46one of the most popular sites for GNOME themes is
47[gnome-look.org](https://www.gnome-look.org/). This website contains themes for
48applications, shells, icons, and cursors.
49
50Steps to import themes into Tweaks:
51
521. Download the theme.
532. These files are usually compressed (.zip, .tar.gz, .tar.xz), so you will need
54 to extract the contents. This is easiest when opening the file explorer,
55 right-clicking the compressed file, and choosing "Extract here."
563. Move the theme folder to `/usr/share/themes/`. You can do so with the
57 following command: `sudo mv theme-folder/ /usr/share/themes/`.
58 - Icons and cursors will be moved to the `/usr/share/icons/` folder.
59 - Fonts will be moved to the `/usr/share/fonts/` folder Alternatively, you
60 can move them to the `/usr/share/fonts/opentype/` or
61 `/usr/share/fonts/opentype/` folders, if you have a specific font type.
624. Close tweaks if it is open. Re-open Tweaks and your new theme will be
63 available in the Applications dropdown in the Appearance section of Tweaks.
64
65If the theme is not showing up after you've moved it into the themes folder, you
66may have uncompressed the folder into a sub-folder. You can check this by
67entering the theme folder and listing the contents:
68
69```sh
70cd /usr/share/themes/Mojave-Dark && ls -la
71```
72
73This is an example of what the contents of your theme folder should look like.
74If you just see another folder there, you should move that folder up into the
75`/usr/share/themes/` folder.
76
77```sh
78cinnamon COPYING gnome-shell gtk-2.0 gtk-3.0 index.theme metacity-1 plank xfwm4
79```
80
81## GNOME Shell Themes
82
83To change the appearance of the title bar, default dock, app menu, and other
84parts of the GNOME shell, you'll need to install the [user
85themes](https://extensions.gnome.org/extension/19/user-themes/) extension on
86[Gnome Extensions](https://extensions.gnome.org/). To be able to install
87extensions, you will first need to install the browser extension that the
88website instructs you to. See this screenshot for the blue box with a link to
89the extension.
90
91After the browser extension is installed, you will need to install the native
92host connector:
93
94```sh
95sudo apt install chrome-gnome-shell
96```
97
98Finally, you can go the [user
99themes](https://extensions.gnome.org/extension/19/user-themes/) extension page
100and click the installation button. This will enable the Shell option in Tweaks.
101Now you can move shell themes to the `/usr/share/themes` directory, using the
102same steps mentioned in the previous section, and enable the new theme in
103Tweaks.
104
105## Icons & Cursors
106
107Icons and cursors are installed exactly the same way, so I'm grouping these
108together in this post. Both of these items will need to follow the same process
109as installing themes, except you will want to move your font folders to the
110`/usr/share/icons/` directory instead.
111
112## Fonts
113
114Fonts are one of the overlooked parts of customization, but a good font can make
115the whole screen look different. For example, I have installed the [IBM
116Plex](https://github.com/IBM/plex/releases) fonts on my system. This follows the
117same process as installing themes, except you will want to move your font
118folders to the `/usr/share/fonts/` directory instead.
119
120## Terminal
121
122If you spend a lot of time typing commands, you know how important the style and
123functionality of the terminal is. After spending a lot of time using the default
124GNOME terminal with [unix
125shell](<https://en.wikipedia.org/wiki/Bash_(Unix_shell)>), I decided to try some
126different options. I ended up choosing
127[Terminator](https://terminator-gtk3.readthedocs.io/en/latest/) with
128[zsh](https://en.wikipedia.org/wiki/Z_shell).
129
130Terminator is great if you need to open multiple terminals at one time by simply
131right-clicking and splitting the screen into as many terminals as you want.
132While this project hasn't been updated in a while, [it is coming under new
133development](https://github.com/gnome-terminator/terminator/issues/1). However,
134this terminal is great and I haven't experienced any errors yet.
135
136For the shell choice, I decided to choose zsh after trying it out on a fresh
137Manjaro installation. Zsh is great if you like to change the themes of your
138terminal, include icons, or add plugins.
139
140The desktop uses the
141[zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions) to
142suggest past commands as you type. In addition, it suggests corrections if you
143misspell a command. Lastly, it uses the `af-magic` theme, which adds dashed
144lines between commands, moving the user@host tag to the right side of the
145terminal, and changes the colors. There are plenty of plugins and themes to
146choose from. Just figure out what you like and add it to your `~/.zshrc` file!
147
148### Steps to Replicate My Terminal
149
150To install zsh on Ubuntu, enter the following command into a terminal:
151
152```sh
153sudo apt install zsh
154```
155
156Then, enter the next command to activate zsh:
157
158```sh
159sudo chsh -s $(which zsh) $(whoami)
160```
161
162To install Terminator on Ubuntu:
163
164```sh
165sudo apt install terminator
166```
167
168To install Oh My Zsh on Ubuntu:
169
170```sh
171sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
172```
173
174To install zsh-autosuggestions via Oh My Zsh:
175
176```sh
177git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
178```
179
180Then, add the following plugin wording to your `~/.zshrc` file (the default
181config usually has the `git` plugin activated, so just add any other plugins to
182the parentheses separated by a space):
183
184```sh
185nano ~/.zshrc
186```
187
188```sh
189plugins=(git zsh-autosuggestions)
190```
191
192Finally, you need to log out of your computer and log back in so your user shell
193can refresh.
content/blog/2020-05-19-customizing-ubuntu.org added +198
@@ -0,0 +1,198 @@
1#+date: <2020-05-19>
2#+title: A Beginner's Guide: Customizing Ubuntu
3#+description:
4
5
6* More Information
7
8For inspiration on designing your *nix computer, check out the
9[[https://libredd.it/r/unixporn][r/unixporn]] subreddit!
10
11* Customizing Ubuntu
12
13New to Linux and want to add a personal touch to your machine? One of
14the best perks of Linux is that it is *extremely* customizable. You can
15change the styles of the windows, shell (status bars/docks), icons,
16fonts, terminals, and more.
17
18In this post, I'm going to go through customization on Ubuntu 20.04
19(GNOME) since most new users tend to choose Ubuntu-based distros. If
20you've found a way to install Arch with i3-gaps, I'm assuming you know
21how to find more advanced tutorials out there on customizations.
22
23** Required Tools
24
25Ubuntu 20.04 ships with the default desktop environment
26[[https://www.gnome.org/][Gnome]], which includes the handy
27=gnome-tweaks= tool to quickly change designs. To install this, just
28open your terminal and enter the following command:
29
30#+begin_src sh
31sudo apt install gnome-tweaks
32#+end_src
33
34After you've finished installing the tool, simply launch the Tweaks
35application, and you'll be able to access the various customization
36options available by default on Ubuntu. You might even like some of the
37pre-installed options.
38
39** GNOME Application Themes
40
41To change the themes applied to applications in GNOME, you will need to
42change the Applications dropdown in the Appearance section of Tweaks. To
43add more themes, you will have to find your preferred theme online and
44follow the steps below to have it show up in the Tweaks tool. While you
45may find themes anywhere, one of the most popular sites for GNOME themes
46is [[https://www.gnome-look.org/][gnome-look.org]]. This website
47contains themes for applications, shells, icons, and cursors.
48
49Steps to import themes into Tweaks:
50
511. Download the theme.
522. These files are usually compressed (.zip, .tar.gz, .tar.xz), so you
53 will need to extract the contents. This is easiest when opening the
54 file explorer, right-clicking the compressed file, and choosing
55 "Extract here."
563. Move the theme folder to =/usr/share/themes/=. You can do so with the
57 following command: =sudo mv theme-folder/ /usr/share/themes/=.
58 - Icons and cursors will be moved to the =/usr/share/icons/= folder.
59 - Fonts will be moved to the =/usr/share/fonts/= folder
60 Alternatively, you can move them to the
61 =/usr/share/fonts/opentype/= or =/usr/share/fonts/opentype/=
62 folders, if you have a specific font type.
634. Close tweaks if it is open. Re-open Tweaks and your new theme will be
64 available in the Applications dropdown in the Appearance section of
65 Tweaks.
66
67If the theme is not showing up after you've moved it into the themes
68folder, you may have uncompressed the folder into a sub-folder. You can
69check this by entering the theme folder and listing the contents:
70
71#+begin_src sh
72cd /usr/share/themes/Mojave-Dark && ls -la
73#+end_src
74
75This is an example of what the contents of your theme folder should look
76like. If you just see another folder there, you should move that folder
77up into the =/usr/share/themes/= folder.
78
79#+begin_src sh
80cinnamon COPYING gnome-shell gtk-2.0 gtk-3.0 index.theme metacity-1 plank xfwm4
81#+end_src
82
83** GNOME Shell Themes
84
85To change the appearance of the title bar, default dock, app menu, and
86other parts of the GNOME shell, you'll need to install the
87[[https://extensions.gnome.org/extension/19/user-themes/][user themes]]
88extension on [[https://extensions.gnome.org/][Gnome Extensions]]. To be
89able to install extensions, you will first need to install the browser
90extension that the website instructs you to. See this screenshot for the
91blue box with a link to the extension.
92
93After the browser extension is installed, you will need to install the
94native host connector:
95
96#+begin_src sh
97sudo apt install chrome-gnome-shell
98#+end_src
99
100Finally, you can go the
101[[https://extensions.gnome.org/extension/19/user-themes/][user themes]]
102extension page and click the installation button. This will enable the
103Shell option in Tweaks. Now you can move shell themes to the
104=/usr/share/themes= directory, using the same steps mentioned in the
105previous section, and enable the new theme in Tweaks.
106
107** Icons & Cursors
108
109Icons and cursors are installed exactly the same way, so I'm grouping
110these together in this post. Both of these items will need to follow the
111same process as installing themes, except you will want to move your
112font folders to the =/usr/share/icons/= directory instead.
113
114** Fonts
115
116Fonts are one of the overlooked parts of customization, but a good font
117can make the whole screen look different. For example, I have installed
118the [[https://github.com/IBM/plex/releases][IBM Plex]] fonts on my
119system. This follows the same process as installing themes, except you
120will want to move your font folders to the =/usr/share/fonts/= directory
121instead.
122
123** Terminal
124
125If you spend a lot of time typing commands, you know how important the
126style and functionality of the terminal is. After spending a lot of time
127using the default GNOME terminal with
128[[https://en.wikipedia.org/wiki/Bash_(Unix_shell)][unix shell]], I
129decided to try some different options. I ended up choosing
130[[https://terminator-gtk3.readthedocs.io/en/latest/][Terminator]] with
131[[https://en.wikipedia.org/wiki/Z_shell][zsh]].
132
133Terminator is great if you need to open multiple terminals at one time
134by simply right-clicking and splitting the screen into as many terminals
135as you want. While this project hasn't been updated in a while,
136[[https://github.com/gnome-terminator/terminator/issues/1][it is coming
137under new development]]. However, this terminal is great and I haven't
138experienced any errors yet.
139
140For the shell choice, I decided to choose zsh after trying it out on a
141fresh Manjaro installation. Zsh is great if you like to change the
142themes of your terminal, include icons, or add plugins.
143
144The desktop uses the
145[[https://github.com/zsh-users/zsh-autosuggestions][zsh-autosuggestions]]
146to suggest past commands as you type. In addition, it suggests
147corrections if you misspell a command. Lastly, it uses the =af-magic=
148theme, which adds dashed lines between commands, moving the user@host
149tag to the right side of the terminal, and changes the colors. There are
150plenty of plugins and themes to choose from. Just figure out what you
151like and add it to your =~/.zshrc= file!
152
153*** Steps to Replicate My Terminal
154
155To install zsh on Ubuntu, enter the following command into a terminal:
156
157#+begin_src sh
158sudo apt install zsh
159#+end_src
160
161Then, enter the next command to activate zsh:
162
163#+begin_src sh
164sudo chsh -s $(which zsh) $(whoami)
165#+end_src
166
167To install Terminator on Ubuntu:
168
169#+begin_src sh
170sudo apt install terminator
171#+end_src
172
173To install Oh My Zsh on Ubuntu:
174
175#+begin_src sh
176sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
177#+end_src
178
179To install zsh-autosuggestions via Oh My Zsh:
180
181#+begin_src sh
182git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
183#+end_src
184
185Then, add the following plugin wording to your =~/.zshrc= file (the
186default config usually has the =git= plugin activated, so just add any
187other plugins to the parentheses separated by a space):
188
189#+begin_src sh
190nano ~/.zshrc
191#+end_src
192
193#+begin_src sh
194plugins=(git zsh-autosuggestions)
195#+end_src
196
197Finally, you need to log out of your computer and log back in so your
198user shell can refresh.
content/blog/2020-07-20-video-game-sales.md → content/blog/2020-07-20-video-game-sales.org renamed +59 −60
@@ -1,36 +1,35 @@
1+++
2date = 2020-07-20
3title = "Data Exploration: Video Games Sales"
4description = ""
5draft = false
6+++
1#+date: <2020-07-20>
2#+title: Data Exploration: Video Games Sales
3#+description:
74
8# Background Information
5
6* Background Information
97
108This dataset (obtained from
11[Kaggle](https://www.kaggle.com/gregorut/videogamesales/data)) contains a list
12of video games with sales greater than 100,000 copies. It was generated by a
13scrape of vgchartz.com.
9[[https://www.kaggle.com/gregorut/videogamesales/data][Kaggle]])
10contains a list of video games with sales greater than 100,000 copies.
11It was generated by a scrape of vgchartz.com.
1412
1513Fields include:
1614
17- Rank: Ranking of overall sales
18- Name: The game name
19- Platform: Platform of the game release (i.e. PC,PS4, etc.)
20- Year: Year of the game's release
21- Genre: Genre of the game
22- Publisher: Publisher of the game
23- NA~Sales~: Sales in North America (in millions)
24- EU~Sales~: Sales in Europe (in millions)
25- JP~Sales~: Sales in Japan (in millions)
26- Other~Sales~: Sales in the rest of the world (in millions)
27- Global~Sales~: Total worldwide sales.
15- Rank: Ranking of overall sales
16- Name: The game name
17- Platform: Platform of the game release (i.e. PC,PS4, etc.)
18- Year: Year of the game's release
19- Genre: Genre of the game
20- Publisher: Publisher of the game
21- NA_{Sales}: Sales in North America (in millions)
22- EU_{Sales}: Sales in Europe (in millions)
23- JP_{Sales}: Sales in Japan (in millions)
24- Other_{Sales}: Sales in the rest of the world (in millions)
25- Global_{Sales}: Total worldwide sales.
2826
29There are 16,598 records. 2 records were dropped due to incomplete information.
27There are 16,598 records. 2 records were dropped due to incomplete
28information.
3029
31# Import the Data
30* Import the Data
3231
33```python
32#+begin_src python
3433# Import the Python libraries we will be using
3534import pandas as pd
3635import numpy as np
@@ -41,42 +40,42 @@ import matplotlib.pyplot as plt
4140file = r'video_game_sales.csv'
4241df = pd.read_csv(file)
4342df
44```
43#+end_src
4544
46# Explore the Data
45* Explore the Data
4746
48```python
47#+begin_src python
4948# With the description function, we can see the basic stats. For example, we can
5049# also see that the 'Year' column has some incomplete values.
5150df.describe()
52```
51#+end_src
5352
54```python
53#+begin_src python
5554# This function shows the rows and columns of NaN values. For example, df[179,3] = nan
5655np.where(pd.isnull(df))
5756
5857(array([179, ..., 16553], dtype=int64),
5958 array([3, ..., 5], dtype=int64))
60```
59#+end_src
6160
62# Visualize the Data
61* Visualize the Data
6362
64```python
63#+begin_src python
6564# This function plots the global sales by platform
6665sns.catplot(x='Platform', y='Global_Sales', data=df, jitter=False).set_xticklabels(rotation=90)
67```
66#+end_src
6867
69```python
68#+begin_src python
7069# This function plots the global sales by genre
7170sns.catplot(x='Genre', y='Global_Sales', data=df, jitter=False).set_xticklabels(rotation=45)
72```
71#+end_src
7372
74```python
73#+begin_src python
7574# This function plots the global sales by year
7675sns.lmplot(x='Year', y='Global_Sales', data=df).set_xticklabels(rotation=45)
77```
76#+end_src
7877
79```python
78#+begin_src python
8079# This function plots four different lines to show sales from different regions.
8180# The global sales plot line is commented-out, but can be included for comparison
8281df2 = df.groupby('Year').sum()
@@ -100,11 +99,11 @@ ax.plot(years, d, label='Other_Sales')
10099
101100ax.legend()
102101plt.show()
103```
102#+end_src
104103
105## Investigate Outliers
104** Investigate Outliers
106105
107```python
106#+begin_src python
108107# Find the game with the highest sales in North America
109108df.loc[df['NA_Sales'].idxmax()]
110109
@@ -124,33 +123,33 @@ Name: 0, dtype: object
124123# Explore statistics in the year 2006 (highest selling year)
125124df3 = df[(df['Year'] == 2006)]
126125df3.describe()
127```
126#+end_src
128127
129```python
128#+begin_src python
130129# Plot the results of the previous dataframe (games from 2006) - we can see the year's results were largely carried by Wii Sports
131130sns.catplot(x="Genre", y="Global_Sales", data=df3, jitter=False).set_xticklabels(rotation=45)
132```
131#+end_src
133132
134```python
133#+begin_src python
135134# We can see 4 outliers in the graph above, so let's get the top 5 games from that dataframe
136135# The results below show that Nintendo had all top 5 games (3 on the Wii and 2 on the DS)
137136df3.sort_values(by=['Global_Sales'], ascending=False).head(5)
138```
137#+end_src
139138
140# Discussion
139* Discussion
141140
142The purpose of exploring datasets is to ask questions, answer questions, and
143discover intelligence that can be used to inform decision-making. So, what have
144we found in this dataset?
141The purpose of exploring datasets is to ask questions, answer questions,
142and discover intelligence that can be used to inform decision-making.
143So, what have we found in this dataset?
145144
146Today we simply explored a publicly-available dataset to see what kind of
147information it contained. During that exploration, we found that video game
148sales peaked in 2006. That peak was largely due to Nintendo, who sold the top 5
149games in 2006 and has a number of games in the top-10 list for the years
1501980-2020. Additionally, the top four platforms by global sales (Wii, NES, GB,
151DS) are owned by Nintendo.
145Today we simply explored a publicly-available dataset to see what kind
146of information it contained. During that exploration, we found that
147video game sales peaked in 2006. That peak was largely due to Nintendo,
148who sold the top 5 games in 2006 and has a number of games in the top-10
149list for the years 1980-2020. Additionally, the top four platforms by
150global sales (Wii, NES, GB, DS) are owned by Nintendo.
152151
153We didn't explore everything this dataset has to offer, but we can tell from a
154brief analysis that Nintendo seems to rule sales in the video gaming world.
155Further analysis could provide insight into which genres, regions, publishers,
156or world events are correlated with sales.
152We didn't explore everything this dataset has to offer, but we can tell
153from a brief analysis that Nintendo seems to rule sales in the video
154gaming world. Further analysis could provide insight into which genres,
155regions, publishers, or world events are correlated with sales.
content/blog/2020-07-26-business-analysis.md → content/blog/2020-07-26-business-analysis.org renamed +102 −98
@@ -1,32 +1,30 @@
1+++
2date = 2020-07-26
3title = "Algorithmically Analyzing Local Businesses "
4description = ""
5draft = false
6+++
1#+date: <2020-07-26>
2#+title: Algorithmically Analyzing Local Businesses
3#+description:
74
8# Background Information
95
10This project aims to help investors learn more about a random city in order to
11determine optimal locations for business investments. The data used in this
12project was obtained using Foursquare's developer API.
6* Background Information
7
8This project aims to help investors learn more about a random city in
9order to determine optimal locations for business investments. The data
10used in this project was obtained using Foursquare's developer API.
1311
1412Fields include:
1513
16- Venue Name
17- Venue Category
18- Venue Latitude
19- Venue Longitude
14- Venue Name
15- Venue Category
16- Venue Latitude
17- Venue Longitude
2018
21There are 232 records found using the center of Lincoln as the area of interest
22with a radius of 10,000.
19There are 232 records found using the center of Lincoln as the area of
20interest with a radius of 10,000.
2321
24# Import the Data
22* Import the Data
2523
26The first step is the simplest: import the applicable libraries. We will be
27using the libraries below for this project.
24The first step is the simplest: import the applicable libraries. We will
25be using the libraries below for this project.
2826
29```python
27#+begin_src python
3028# Import the Python libraries we will be using
3129import pandas as pd
3230import requests
@@ -35,29 +33,29 @@ import math
3533import json
3634from pandas.io.json import json_normalize
3735from sklearn.cluster import KMeans
38```
36#+end_src
3937
40To begin our analysis, we need to import the data for this project. The data we
41are using in this project comes directly from the Foursquare API. The first step
42is to get the latitude and longitude of the city being studied (Lincoln, NE) and
43setting up the folium map.
38To begin our analysis, we need to import the data for this project. The
39data we are using in this project comes directly from the Foursquare
40API. The first step is to get the latitude and longitude of the city
41being studied (Lincoln, NE) and setting up the folium map.
4442
45```python
43#+begin_src python
4644# Define the latitude and longitude, then map the results
4745latitude = 40.806862
4846longitude = -96.681679
4947map_LNK = folium.Map(location=[latitude, longitude], zoom_start=12)
5048
5149map_LNK
52```
50#+end_src
5351
54Now that we have defined our city and created the map, we need to go get the
55business data. The Foursquare API will limit the results to 100 per API call, so
56we use our first API call below to determine the total results that Foursquare
57has found. Since the total results are 232, we perform the API fetching process
58three times (100 + 100 + 32 = 232).
52Now that we have defined our city and created the map, we need to go get
53the business data. The Foursquare API will limit the results to 100 per
54API call, so we use our first API call below to determine the total
55results that Foursquare has found. Since the total results are 232, we
56perform the API fetching process three times (100 + 100 + 32 = 232).
5957
60```python
58#+begin_src python
6159# Foursquare API credentials
6260CLIENT_ID = 'your-client-id'
6361CLIENT_SECRET = 'your-client-secret'
@@ -115,18 +113,19 @@ url3 = 'https://api.foursquare.com/v2/venues/explore?&client_id={}&client_secret
115113
116114# Fetch the final results (201 - 232)
117115results3 = requests.get(url3).json()
118```
116#+end_src
119117
120# Clean the Data
118* Clean the Data
121119
122Now that we have our data in three separate dataframes, we need to combine them
123into a single dataframe and make sure to reset the index so that we have a
124unique ID for each business. The `get~categorytype~` function below will pull
125the categories and name from each business's entry in the Foursquare data
126automatically. Once all the data has been labeled and combined, the results are
127stored in the `nearby_venues` dataframe.
120Now that we have our data in three separate dataframes, we need to
121combine them into a single dataframe and make sure to reset the index so
122that we have a unique ID for each business. The =get~categorytype~=
123function below will pull the categories and name from each business's
124entry in the Foursquare data automatically. Once all the data has been
125labeled and combined, the results are stored in the =nearby_venues=
126dataframe.
128127
129```python
128#+begin_src python
130129# This function will extract the category of the venue from the API dictionary
131130def get_category_type(row):
132131 try:
@@ -189,15 +188,15 @@ nearby_venues3.columns = [col.split(".")[-1] for col in nearby_venues3.columns]
189188nearby_venues = nearby_venues.append(nearby_venues3)
190189nearby_venues = nearby_venues.reset_index(drop=True)
191190nearby_venues
192```
191#+end_src
193192
194# Visualize the Data
193* Visualize the Data
195194
196We now have a complete, clean data set. The next step is to visualize this data
197onto the map we created earlier. We will be using folium's `CircleMarker()`
198function to do this.
195We now have a complete, clean data set. The next step is to visualize
196this data onto the map we created earlier. We will be using folium's
197=CircleMarker()= function to do this.
199198
200```python
199#+begin_src python
201200# add markers to map
202201for lat, lng, name, categories in zip(nearby_venues['lat'], nearby_venues['lng'], nearby_venues['name'], nearby_venues['categories']):
203202 label = '{} ({})'.format(name, categories)
@@ -213,20 +212,21 @@ for lat, lng, name, categories in zip(nearby_venues['lat'], nearby_venues['lng']
213212 ).add_to(map_LNK)
214213
215214map_LNK
216```
215#+end_src
217216
218# Clustering: _k-means_
217* Clustering: /k-means/
219218
220To cluster the data, we will be using the _k-means_ algorithm. This algorithm is
221iterative and will automatically make sure that data points in each cluster are
222as close as possible to each other, while being as far as possible away from
223other clusters.
219To cluster the data, we will be using the /k-means/ algorithm. This
220algorithm is iterative and will automatically make sure that data points
221in each cluster are as close as possible to each other, while being as
222far as possible away from other clusters.
224223
225However, we first have to figure out how many clusters to use (defined as the
226variable _'k'_). To do so, we will use the next two functions to calculate the
227sum of squares within clusters and then return the optimal number of clusters.
224However, we first have to figure out how many clusters to use (defined
225as the variable /'k'/). To do so, we will use the next two functions to
226calculate the sum of squares within clusters and then return the optimal
227number of clusters.
228228
229```python
229#+begin_src python
230230# This function will return the sum of squares found in the data
231231def calculate_wcss(data):
232232 wcss = []
@@ -260,13 +260,13 @@ def optimal_number_of_clusters(wcss):
260260
261261# calculating the optimal number of clusters
262262n = optimal_number_of_clusters(sum_of_squares)
263```
263#+end_src
264264
265Now that we have found that our optimal number of clusters is six, we need to
266perform k-means clustering. When this clustering occurs, each business is
267assigned a cluster number from 0 to 5 in the dataframe.
265Now that we have found that our optimal number of clusters is six, we
266need to perform k-means clustering. When this clustering occurs, each
267business is assigned a cluster number from 0 to 5 in the dataframe.
268268
269```python
269#+begin_src python
270270# set number of clusters equal to the optimal number
271271kclusters = n
272272
@@ -275,12 +275,13 @@ kmeans = KMeans(n_clusters=kclusters, random_state=0).fit(cluster_df)
275275
276276# add clustering labels to dataframe
277277nearby_venues.insert(0, 'Cluster Labels', kmeans.labels_)
278```
278#+end_src
279279
280Success! We now have a dataframe with clean business data, along with a cluster
281number for each business. Now let's map the data using six different colors.
280Success! We now have a dataframe with clean business data, along with a
281cluster number for each business. Now let's map the data using six
282different colors.
282283
283```python
284#+begin_src python
284285# create map with clusters
285286map_clusters = folium.Map(location=[latitude, longitude], zoom_start=12)
286287colors = ['#0F9D58', '#DB4437', '#4285F4', '#800080', '#ce12c0', '#171717']
@@ -299,30 +300,31 @@ for lat, lng, name, categories, cluster in zip(nearby_venues['lat'], nearby_venu
299300 fill_opacity=0.7).add_to(map_clusters)
300301
301302map_clusters
302```
303#+end_src
303304
304# Investigate Clusters
305* Investigate Clusters
305306
306Now that we have figured out our clusters, let's do a little more analysis to
307provide more insight into the clusters. With the information below, we can see
308which clusters are more popular for businesses and which are less popular. The
309results below show us that clusters 0 through 3 are popular, while clusters 4
310and 5 are not very popular at all.
307Now that we have figured out our clusters, let's do a little more
308analysis to provide more insight into the clusters. With the information
309below, we can see which clusters are more popular for businesses and
310which are less popular. The results below show us that clusters 0
311through 3 are popular, while clusters 4 and 5 are not very popular at
312all.
311313
312```python
314#+begin_src python
313315# Show how many venues are in each cluster
314316color_names = ['Dark Green', 'Red', 'Blue', 'Purple', 'Pink', 'Black']
315317for x in range(0,6):
316318 print("Color of Cluster", x, ":", color_names[x])
317319 print("Venues found in Cluster", x, ":", nearby_venues.loc[nearby_venues['Cluster Labels'] == x, nearby_venues.columns[:]].shape[0])
318320 print("---")
319```
321#+end_src
320322
321Our last piece of analysis is to summarize the categories of businesses within
322each cluster. With these results, we can clearly see that restaurants, coffee
323shops, and grocery stores are the most popular.
323Our last piece of analysis is to summarize the categories of businesses
324within each cluster. With these results, we can clearly see that
325restaurants, coffee shops, and grocery stores are the most popular.
324326
325```python
327#+begin_src python
326328# Calculate how many venues there are in each category
327329# Sort from largest to smallest
328330temp_df = nearby_venues.drop(columns=['name', 'lat', 'lng'])
@@ -342,21 +344,23 @@ with pd.option_context('display.max_rows', None, 'display.max_columns', None):
342344 print("\n\n", "Cluster 3:", "\n", cluster3_grouped.loc[cluster3_grouped['Cluster Labels'] > 1])
343345 print("\n\n", "Cluster 4:", "\n", cluster4_grouped.loc[cluster4_grouped['Cluster Labels'] > 1])
344346 print("\n\n", "Cluster 5:", "\n", cluster5_grouped.loc[cluster5_grouped['Cluster Labels'] > 1])
345```
346
347# Discussion
348
349In this project, we gathered location data for Lincoln, Nebraska, USA and
350clustered the data using the k-means algorithm in order to identify the unique
351clusters of businesses in Lincoln. Through these actions, we found that there
352are six unique business clusters in Lincoln and that two of the clusters are
353likely unsuitable for investors. The remaining four clusters have a variety of
354businesses, but are largely dominated by restaurants and grocery stores.
355
356Using this project, investors can now make more informed decisions when deciding
357the location and category of business in which to invest.
358
359Further studies may involve other attributes for business locations, such as
360population density, average wealth across the city, or crime rates. In addition,
361further studies may include additional location data and businesses by utilizing
362multiple sources, such as Google Maps and OpenStreetMap.
347#+end_src
348
349* Discussion
350
351In this project, we gathered location data for Lincoln, Nebraska, USA
352and clustered the data using the k-means algorithm in order to identify
353the unique clusters of businesses in Lincoln. Through these actions, we
354found that there are six unique business clusters in Lincoln and that
355two of the clusters are likely unsuitable for investors. The remaining
356four clusters have a variety of businesses, but are largely dominated by
357restaurants and grocery stores.
358
359Using this project, investors can now make more informed decisions when
360deciding the location and category of business in which to invest.
361
362Further studies may involve other attributes for business locations,
363such as population density, average wealth across the city, or crime
364rates. In addition, further studies may include additional location data
365and businesses by utilizing multiple sources, such as Google Maps and
366OpenStreetMap.
content/blog/2020-08-22-redirect-github-pages.md deleted −124
@@ -1,124 +0,0 @@
1+++
2date = 2020-08-22
3title = "Redirect GitHub Pages from Subdomain to the Top-Level Domain"
4description = ""
5draft = false
6+++
7
8# Short answer
9
10## Step 1
11
12Add a new file CNAME to your GitHub Pages repository containing only one line:
13your top-level domain name. E.g.: `example.com`
14
15## Step 2
16
17[Optional] but highly recommended
18
192.1: Remove all other top-level records (prefixed with @) of type A from your
20DNS configuration.
21
222.2: Remove a CNAME record for the second-level domain www if it is present.
23
24## Step 3
25
26Add these 5 entries to the very top of your DNS configuration:
27
28```txt
29@ A 185.199.108.153
30@ A 185.199.109.153
31@ A 185.199.110.153
32@ A 185.199.111.153
33www CNAME your_github_username.github.io.
34```
35
36Replace `your_github_username` with your actual GitHub username.
37
38## Step 4
39
40Wait for your DNS changes to propagate. DNS changes aren't effective
41immediately. They can take up to a full day to propagate.
42
43# Long answer
44
45This issue has two sides. One is the DNS configuration itself. Another one is
46the way GitHub Pages will forward HTTP requests.
47
48We need to know a few things to understand what GitHub is trying to say in their
49documentation.
50
51## DNS Entry Types
52
53There are two types of DNS records which interest us: CNAME and A.
54
55`A` is also known as `Apex` or sometimes as `root entry`. It forwards requests
56to a specified fixed IP address. `CNAME` entry forwards requests to a specified
57URL (actual valid plain text URL, not an IP address).
58
59## DNS Load balancing
60
61GitHub has one central URL address which accepts all DNS requests for GitHub
62Pages: `http://username.github.io`. That URL is resolved to different IP
63addresses based on your geographical location. Website hosted on GitHub Pages is
64a simple collection of `HTML`, `CSS` and `JS` files. GitHub distributes these
65files to different servers across the globe. So that when your browser sends a
66request from Europe, it receives data from a server in Europe. The same is valid
67for the requests from Asia and the USA.
68
69## What GitHub is trying to say
70
71Since `A` records in DNS must contain IP addresses, and they must be either
72`185.199.108.153` or `185.199.109.153` or `185.199.110.153` or
73`185.199.111.153`, there is no way to forward requests to a server located
74somewhere in Europe or Asia. Your website hosted at GitHub Pages will be
75downloaded from a central GitHub Pages server. There is a minor risk that if
76GitHub Pages DNS servers (`x.x.x.153`) are down for some reason, all custom
77domains which use fixed GitHub Pages IP addresses will not be accessible (their
78DNS requests will not be resolvable).
79
80That is why GitHub strongly suggests to either use a second-level domain for
81your GitHub Pages (e.g. `blog.example.com`) or use a DNS service provider that
82supports a record type `ALIAS` that acts as `A` record but forwards request to a
83URL address (e.g. `username.github.io`) instead of a fixed IP address.
84
85## How GitHub Pages treats HTTP requests
86
87After a DNS request for `your_github_username.github.io` is resolved into an IP
88address, e.g. `185.199.108.153` your browser sends an HTTP request to that
89server with an HTTP header `Host`. Below are `curl` examples that load the same
90website (these examples might not work if you are behind a proxy server):
91
92```sh
93curl --header "Host: your_github_username.github.io" http://185.199.108.153/
94curl --header "Host: www.example.com" http://185.199.108.153/
95curl --header "Host: example.com" http://185.199.108.153/
96```
97
98This way GitHub Pages servers know which user website to serve.
99
100> GitHub Pages server will automatically redirect HTTP requests to the top-level
101> domain if your `CNAME` file contains `example.com` but `www.example.com` is
102> requested.
103>
104> The same is valid if your `CNAME` file contains `www.example.com` but the
105> header `Host` in the `HTTP` request contains `example.com`.
106
107## Why can't I add a `CNAME` record entry that accepts a top-level request (`@`) to my DNS configuration?
108
109Quote from the GitHub Pages documentation:
110
111> Warning: Do not create a CNAME record for your custom apex domain! Doing so
112> may cause issues with other services, such as email, on that domain.
113
114## References:
115
1161. [Setting up a custom domain with GitHub
117 Pages](https://docs.github.com/en/github/working-with-github-pages/configuring-a-custom-domain-for-your-github-pages-site)
1182. [My custom domain isn't
119 working](https://docs.github.com/en/github/working-with-github-pages/troubleshooting-custom-domains-and-github-pages)
1203. [Cannot access my GitHub Pages website by IP
121 Address](https://serverfault.com/questions/589370/cannot-access-my-github-pages-website-by-ip-address)
1224. [How do I set up GitHub Pages to redirect DNS requests from a subdomain (e.g.
123 www) to the top-level domain (TLD, Apex
124 record)?](https://stackoverflow.com/questions/23375422/how-do-i-set-up-github-pages-to-redirect-dns-requests-from-a-subdomain-e-g-www)
content/blog/2020-08-22-redirect-github-pages.org added +131
@@ -0,0 +1,131 @@
1#+date: <2020-08-22>
2#+title: Redirect GitHub Pages from Subdomain to the Top-Level Domain
3#+description:
4
5
6* Short answer
7
8** Step 1
9
10Add a new file CNAME to your GitHub Pages repository containing only one
11line: your top-level domain name. E.g.: =example.com=
12
13** Step 2
14
15[Optional] but highly recommended
16
172.1: Remove all other top-level records (prefixed with @) of type A from
18your DNS configuration.
19
202.2: Remove a CNAME record for the second-level domain www if it is
21present.
22
23** Step 3
24
25Add these 5 entries to the very top of your DNS configuration:
26
27#+begin_src txt
28@ A 185.199.108.153
29@ A 185.199.109.153
30@ A 185.199.110.153
31@ A 185.199.111.153
32www CNAME your_github_username.github.io.
33#+end_src
34
35Replace =your_github_username= with your actual GitHub username.
36
37** Step 4
38
39Wait for your DNS changes to propagate. DNS changes aren't effective
40immediately. They can take up to a full day to propagate.
41
42* Long answer
43
44This issue has two sides. One is the DNS configuration itself. Another
45one is the way GitHub Pages will forward HTTP requests.
46
47We need to know a few things to understand what GitHub is trying to say
48in their documentation.
49
50** DNS Entry Types
51
52There are two types of DNS records which interest us: CNAME and A.
53
54=A= is also known as =Apex= or sometimes as =root entry=. It forwards
55requests to a specified fixed IP address. =CNAME= entry forwards
56requests to a specified URL (actual valid plain text URL, not an IP
57address).
58
59** DNS Load balancing
60
61GitHub has one central URL address which accepts all DNS requests for
62GitHub Pages: =http://username.github.io=. That URL is resolved to
63different IP addresses based on your geographical location. Website
64hosted on GitHub Pages is a simple collection of =HTML=, =CSS= and =JS=
65files. GitHub distributes these files to different servers across the
66globe. So that when your browser sends a request from Europe, it
67receives data from a server in Europe. The same is valid for the
68requests from Asia and the USA.
69
70** What GitHub is trying to say
71
72Since =A= records in DNS must contain IP addresses, and they must be
73either =185.199.108.153= or =185.199.109.153= or =185.199.110.153= or
74=185.199.111.153=, there is no way to forward requests to a server
75located somewhere in Europe or Asia. Your website hosted at GitHub Pages
76will be downloaded from a central GitHub Pages server. There is a minor
77risk that if GitHub Pages DNS servers (=x.x.x.153=) are down for some
78reason, all custom domains which use fixed GitHub Pages IP addresses
79will not be accessible (their DNS requests will not be resolvable).
80
81That is why GitHub strongly suggests to either use a second-level domain
82for your GitHub Pages (e.g. =blog.example.com=) or use a DNS service
83provider that supports a record type =ALIAS= that acts as =A= record but
84forwards request to a URL address (e.g. =username.github.io=) instead of
85a fixed IP address.
86
87** How GitHub Pages treats HTTP requests
88
89After a DNS request for =your_github_username.github.io= is resolved
90into an IP address, e.g. =185.199.108.153= your browser sends an HTTP
91request to that server with an HTTP header =Host=. Below are =curl=
92examples that load the same website (these examples might not work if
93you are behind a proxy server):
94
95#+begin_src sh
96curl --header "Host: your_github_username.github.io" http://185.199.108.153/
97curl --header "Host: www.example.com" http://185.199.108.153/
98curl --header "Host: example.com" http://185.199.108.153/
99#+end_src
100
101This way GitHub Pages servers know which user website to serve.
102
103#+begin_quote
104GitHub Pages server will automatically redirect HTTP requests to the
105top-level domain if your =CNAME= file contains =example.com= but
106=www.example.com= is requested.
107
108The same is valid if your =CNAME= file contains =www.example.com= but
109the header =Host= in the =HTTP= request contains =example.com=.
110#+end_quote
111
112** Why can't I add a =CNAME= record entry that accepts a top-level request (=@=) to my DNS configuration?
113
114Quote from the GitHub Pages documentation:
115
116#+begin_quote
117Warning: Do not create a CNAME record for your custom apex domain! Doing
118so may cause issues with other services, such as email, on that domain.
119#+end_quote
120
121** References:
122
1231. [[https://docs.github.com/en/github/working-with-github-pages/configuring-a-custom-domain-for-your-github-pages-site][Setting
124 up a custom domain with GitHub Pages]]
1252. [[https://docs.github.com/en/github/working-with-github-pages/troubleshooting-custom-domains-and-github-pages][My
126 custom domain isn't working]]
1273. [[https://serverfault.com/questions/589370/cannot-access-my-github-pages-website-by-ip-address][Cannot
128 access my GitHub Pages website by IP Address]]
1294. [[https://stackoverflow.com/questions/23375422/how-do-i-set-up-github-pages-to-redirect-dns-requests-from-a-subdomain-e-g-www][How
130 do I set up GitHub Pages to redirect DNS requests from a subdomain
131 (e.g. www) to the top-level domain (TLD, Apex record)?]]
content/blog/2020-08-29-php-auth-flow.md → content/blog/2020-08-29-php-auth-flow.org renamed +74 −72
@@ -1,23 +1,22 @@
1+++
2date = 2020-08-29
3title = "PHP Authentication Flow"
4description = ""
5draft = false
6+++
7
8# Introduction
9
10When creating websites that will allow users to create accounts, the developer
11always needs to consider the proper authentication flow for their app. For
12example, some developers will utilize an API for authentication, some will use
13OAuth, and some may just use their own simple database.
14
15For those using pre-built libraries, authentication may simply be a problem of
16copying and pasting the code from their library's documentation. For example,
17here's the code I use to authenticate users with the Tumblr OAuth API for my
18Tumblr client, Vox Populi:
19
20```php
1#+date: <2020-08-29>
2#+title: PHP Authentication Flow
3#+description:
4
5
6* Introduction
7
8When creating websites that will allow users to create accounts, the
9developer always needs to consider the proper authentication flow for
10their app. For example, some developers will utilize an API for
11authentication, some will use OAuth, and some may just use their own
12simple database.
13
14For those using pre-built libraries, authentication may simply be a
15problem of copying and pasting the code from their library's
16documentation. For example, here's the code I use to authenticate users
17with the Tumblr OAuth API for my Tumblr client, Vox Populi:
18
19#+begin_src php
2120// Start the session
2221session_start();
2322
@@ -38,27 +37,28 @@ $client = new Tumblr\API\Client(
3837 $token,
3938 $token_secret
4039);
41```
40#+end_src
4241
43However, developers creating authentication flows from scratch will need to
44think carefully about when to make sure a web page will check the user's
45authenticity.
42However, developers creating authentication flows from scratch will need
43to think carefully about when to make sure a web page will check the
44user's authenticity.
4645
47In this article, we're going to look at a simple authentication flow using a
48MySQL database and PHP.
46In this article, we're going to look at a simple authentication flow
47using a MySQL database and PHP.
4948
50# Creating User Accounts
49* Creating User Accounts
5150
52The beginning to any type of user authentication is to create a user account.
53This process can take many formats, but the simplest is to accept user input
54from a form (e.g., username and password) and send it over to your database. For
55example, here's a snippet that shows how to get username and password parameters
56that would come when a user submits a form to your PHP script.
51The beginning to any type of user authentication is to create a user
52account. This process can take many formats, but the simplest is to
53accept user input from a form (e.g., username and password) and send it
54over to your database. For example, here's a snippet that shows how to
55get username and password parameters that would come when a user submits
56a form to your PHP script.
5757
58**Note**: Ensure that your password column is large enough to hold the hashed
59value (at least 60 characters or longer).
58*Note*: Ensure that your password column is large enough to hold the
59hashed value (at least 60 characters or longer).
6060
61```php
61#+begin_src php
6262// Get the values from the URL
6363$username = $_POST['username'];
6464$raw_password = $_POST['password'];
@@ -91,15 +91,15 @@ if ($conn->query($sql) === TRUE) {
9191}
9292
9393$conn->close();
94```
94#+end_src
9595
96## Validate Returning Users
96** Validate Returning Users
9797
98To be able to verify that a returning user has a valid username and password in
99your database is as simple as having users fill out a form and comparing their
100inputs to your database.
98To be able to verify that a returning user has a valid username and
99password in your database is as simple as having users fill out a form
100and comparing their inputs to your database.
101101
102```php
102#+begin_src php
103103// Query the database for username and password
104104// ...
105105
@@ -110,41 +110,42 @@ if(password_verify($password_input, $hashed_password)) {
110110
111111// Else, Redirect them back to the login page.
112112...
113```
113#+end_src
114114
115# Storing Authentication State
115* Storing Authentication State
116116
117Once you've created the user's account, now you're ready to initialize the
118user's session. **You will need to do this on every page you load while the user
119is logged in.** To do so, simply enter the following code snippet:
117Once you've created the user's account, now you're ready to initialize
118the user's session. *You will need to do this on every page you load
119while the user is logged in.* To do so, simply enter the following code
120snippet:
120121
121```php
122#+begin_src php
122123session_start();
123```
124#+end_src
124125
125Once you've initialized the session, the next step is to store the session in a
126cookie so that you can access it later.
126Once you've initialized the session, the next step is to store the
127session in a cookie so that you can access it later.
127128
128```php
129#+begin_src php
129130setcookie(session_name());
130```
131#+end_src
131132
132Now that the session name has been stored, you'll be able to check if there's an
133active session whenever you load a page.
133Now that the session name has been stored, you'll be able to check if
134there's an active session whenever you load a page.
134135
135```php
136#+begin_src php
136137if(isset(session_name())) {
137138 // The session is active
138139}
139```
140#+end_src
140141
141## Removing User Authentication
142** Removing User Authentication
142143
143The next logical step is to give your users the option to log out once they are
144done using your application. This can be tricky in PHP since a few of the
145standard ways do not always work.
144The next logical step is to give your users the option to log out once
145they are done using your application. This can be tricky in PHP since a
146few of the standard ways do not always work.
146147
147```php
148#+begin_src php
148149// Initialize the session.
149150// If you are using session_name("something"), don't forget it now!
150151session_start();
@@ -176,17 +177,18 @@ session_write_close();
176177// Go back to sign-in page
177178header('Location: https://example.com/logged-out/');
178179die();
179```
180#+end_src
180181
181# Wrapping Up
182* Wrapping Up
182183
183Now you should be ready to begin your authentication programming with PHP. You
184can create user accounts, create sessions for users across different pages of
185your site, and then destroy the user data when they're ready to leave.
184Now you should be ready to begin your authentication programming with
185PHP. You can create user accounts, create sessions for users across
186different pages of your site, and then destroy the user data when
187they're ready to leave.
186188
187For more information on this subject, I recommend reading the [PHP
188Documentation](https://www.php.net/). Specifically, you may want to look at
189[HTTP Authentication with
190PHP](https://www.php.net/manual/en/features.http-auth.php), [session
191handling](https://www.php.net/manual/en/book.session.php), and
192[hash](https://www.php.net/manual/en/function.hash.php).
189For more information on this subject, I recommend reading the
190[[https://www.php.net/][PHP Documentation]]. Specifically, you may want
191to look at [[https://www.php.net/manual/en/features.http-auth.php][HTTP
192Authentication with PHP]],
193[[https://www.php.net/manual/en/book.session.php][session handling]],
194and [[https://www.php.net/manual/en/function.hash.php][hash]].
content/blog/2020-09-01-visual-recognition.md deleted −199
@@ -1,199 +0,0 @@
1+++
2date = 2020-09-01
3title = "IBM Watson Visual Recognition"
4description = ""
5draft = false
6+++
7
8# What is IBM Watson?
9
10If you've never heard of [Watson](https://www.ibm.com/watson), this service is a
11suite of enterprise-ready AI services, applications, and tooling provided by
12IBM. Watson contains quite a few useful tools for data scientists and students,
13including the subject of this post today: visual recognition.
14
15If you'd like to view the official documentation for the Visual Recognition API,
16visit the [API
17Docs](https://cloud.ibm.com/apidocs/visual-recognition/visual-recognition-v3?code=python).
18
19# Prerequisites
20
21To be able to use Watson Visual Recognition, you'll need the following:
22
231. Create a free account on [IBM Watson
24 Studio](https://www.ibm.com/cloud/watson-studio).
252. Add the [Watson Visual
26 Recognition](https://www.ibm.com/cloud/watson-visual-recognition) service to
27 your IBM Watson account.
283. Get your API key and URL. To do this, first go to the [profile
29 dashboard](https://dataplatform.cloud.ibm.com/home2?context=cpdaas) for your
30 IBM account and click on the Watson Visual Recognition service you created.
31 This will be listed in the section titled **Your services**. Then click the
32 **Credentials** tab and open the **Auto-generated credentials** dropdown.
33 Copy your API key and URL so that you can use them in the Python script
34 later.
354. **[Optional]** While not required, you can also create the Jupyter Notebook
36 for this project right inside [Watson
37 Studio](https://www.ibm.com/cloud/watson-studio). Watson Studio will save
38 your notebooks inside an organized project and allow you to use their other
39 integrated products, such as storage containers, AI models, documentation,
40 external sharing, etc.
41
42# Calling the IBM Watson Visual Recognition API
43
44Okay, now let's get started.
45
46To begin, we need to install the proper Python package for IBM Watson.
47
48```sh
49pip install --upgrade --user "ibm-watson>=4.5.0"
50```
51
52Next, we need to specify the API key, version, and URL given to us when we
53created the Watson Visual Recognition service.
54
55```python
56apikey = "<your-apikey>"
57version = "2018-03-19"
58url = "<your-url>"
59```
60
61Now, let's import the necessary libraries and authenticate our service.
62
63```python
64import json
65from ibm_watson import VisualRecognitionV3
66from ibm_cloud_sdk_core.authenticators import IAMAuthenticator
67
68authenticator = IAMAuthenticator(apikey)
69visual_recognition = VisualRecognitionV3(
70 version=version,
71 authenticator=authenticator
72)
73
74visual_recognition.set_service_url(url)
75```
76
77**[Optional]** If you'd like to tell the API not to use any data to improve
78their products, set the following header.
79
80```python
81visual_recognition.set_default_headers({'x-watson-learning-opt-out': "true"})
82```
83
84Now we have our API all set and ready to go. For this example, I'm going to
85include a `dict` of photos to load as we test out the API.
86
87```python
88data = [
89 {
90 "title": "Grizzly Bear",
91 "url": "https://example.com/photos/image1.jpg"
92 },
93 {
94 "title": "Nature Lake",
95 "url": "https://example.com/photos/image2.jpg"
96 },
97 {
98 "title": "Welcome Sign",
99 "url": "https://example.com/photos/image3.jpg"
100 },
101 {
102 "title": "Honey Badger",
103 "url": "https://example.com/photos/image4.jpg"
104 },
105 {
106 "title": "Grand Canyon Lizard",
107 "url": "https://example.com/photos/image5.jpg"
108 },
109 {
110 "title": "Castle",
111 "url": "https://example.com/photos/image6.jpg"
112 }
113]
114```
115
116Now that we've set up our libraries and have the photos ready, let's create a
117loop to call the API for each image. The code below shows a loop that calls the
118URL of each image and sends it to the API, requesting results with at least 60%
119confidence. The results are output to the console with dotted lines separating
120each section.
121
122In the case of an API error, the codes and explanations are output to the
123console.
124
125```python
126from ibm_watson import ApiException
127
128for x in range(len(data)):
129try:
130 url = data[x]["url"]
131 images_filename = data[x]["title"]
132 classes = visual_recognition.classify(
133 url=url,
134 images_filename=images_filename,
135 threshold='0.6',
136 owners=["IBM"]).get_result()
137 print("-----------------------------------------------")
138 print("Image Title: ", data[x]["title"], "\n")
139 print("Image URL: ", data[x]["url"], "\n")
140 classification_results = classes["images"][0]["classifiers"][0]["classes"]
141 for result in classification_results:
142 print(result["class"], "(", result["score"], ")")
143 print("-----------------------------------------------")
144except ApiException as ex:
145 print("Method failed with status code " + str(ex.code) + ": " + ex.message)
146```
147
148# The Results
149
150Here we can see the full result set of our function above. If you view each of
151the URLs that we sent to the API, you'll be able to see that it was remarkably
152accurate. To be fair, these are clear high-resolution, clear photos shot with a
153professional camera. In reality, you will most likely be processing images that
154are lower quality and may have a lot of noise in the photo.
155
156However, we can clearly see the benefit of being able to call this API instead
157of attempting to write our own image recognition function. Each of the
158classifications returned was a fair description of the image.
159
160If you wanted to restrict the results to those that are at least 90% confident
161or greater, you would simply adjust the `threshold` in the
162`visual_recognition.classify()` function.
163
164When your program runs, it should show the output below for each photo you
165provide.
166
167```txt
168----------------------------------------------------------------
169Image Title: Grizzly Bear
170Image URL: https://example.com/photos/image1.jpg
171
172brown bear ( 0.944 )
173bear ( 1 )
174carnivore ( 1 )
175mammal ( 1 )
176animal ( 1 )
177Alaskan brown bear ( 0.759 )
178greenishness color ( 0.975 )
179----------------------------------------------------------------
180```
181
182# Discussion
183
184Now, this was a very minimal implementation of the API. We simply supplied some
185images and looked to see how accurate the results were. However, you could
186implement this type of API into many machine learning (ML) models.
187
188For example, you could be working for a company that scans their warehouses or
189inventory using drones. Would you want to pay employees to sit there and watch
190drone footage all day in order to identify or count things in the video?
191Probably not. Instead, you could use a classification system similar to this one
192in order to train your machine learning model to correctly identify items that
193the drones show through video. More specifically, you could have your machine
194learning model watch a drone fly over a field of sheep in order to count how
195many sheep are living in that field.
196
197There are many ways to implement machine learning functionality, but hopefully
198this post helped inspire some deeper thought about the tools that can help
199propel us further into the future of machine learning and AI.
content/blog/2020-09-01-visual-recognition.org added +202
@@ -0,0 +1,202 @@
1#+date: <2020-09-01>
2#+title: IBM Watson Visual Recognition
3#+description:
4
5
6* What is IBM Watson?
7
8If you've never heard of [[https://www.ibm.com/watson][Watson]], this
9service is a suite of enterprise-ready AI services, applications, and
10tooling provided by IBM. Watson contains quite a few useful tools for
11data scientists and students, including the subject of this post today:
12visual recognition.
13
14If you'd like to view the official documentation for the Visual
15Recognition API, visit the
16[[https://cloud.ibm.com/apidocs/visual-recognition/visual-recognition-v3?code=python][API
17Docs]].
18
19* Prerequisites
20
21To be able to use Watson Visual Recognition, you'll need the following:
22
231. Create a free account on
24 [[https://www.ibm.com/cloud/watson-studio][IBM Watson Studio]].
252. Add the [[https://www.ibm.com/cloud/watson-visual-recognition][Watson
26 Visual Recognition]] service to your IBM Watson account.
273. Get your API key and URL. To do this, first go to the
28 [[https://dataplatform.cloud.ibm.com/home2?context=cpdaas][profile
29 dashboard]] for your IBM account and click on the Watson Visual
30 Recognition service you created. This will be listed in the section
31 titled *Your services*. Then click the *Credentials* tab and open the
32 *Auto-generated credentials* dropdown. Copy your API key and URL so
33 that you can use them in the Python script later.
344. *[Optional]* While not required, you can also create the Jupyter
35 Notebook for this project right inside
36 [[https://www.ibm.com/cloud/watson-studio][Watson Studio]]. Watson
37 Studio will save your notebooks inside an organized project and allow
38 you to use their other integrated products, such as storage
39 containers, AI models, documentation, external sharing, etc.
40
41* Calling the IBM Watson Visual Recognition API
42
43Okay, now let's get started.
44
45To begin, we need to install the proper Python package for IBM Watson.
46
47#+begin_src sh
48pip install --upgrade --user "ibm-watson>=4.5.0"
49#+end_src
50
51Next, we need to specify the API key, version, and URL given to us when
52we created the Watson Visual Recognition service.
53
54#+begin_src python
55apikey = "<your-apikey>"
56version = "2018-03-19"
57url = "<your-url>"
58#+end_src
59
60Now, let's import the necessary libraries and authenticate our service.
61
62#+begin_src python
63import json
64from ibm_watson import VisualRecognitionV3
65from ibm_cloud_sdk_core.authenticators import IAMAuthenticator
66
67authenticator = IAMAuthenticator(apikey)
68visual_recognition = VisualRecognitionV3(
69 version=version,
70 authenticator=authenticator
71)
72
73visual_recognition.set_service_url(url)
74#+end_src
75
76*[Optional]* If you'd like to tell the API not to use any data to
77improve their products, set the following header.
78
79#+begin_src python
80visual_recognition.set_default_headers({'x-watson-learning-opt-out': "true"})
81#+end_src
82
83Now we have our API all set and ready to go. For this example, I'm going
84to include a =dict= of photos to load as we test out the API.
85
86#+begin_src python
87data = [
88 {
89 "title": "Grizzly Bear",
90 "url": "https://example.com/photos/image1.jpg"
91 },
92 {
93 "title": "Nature Lake",
94 "url": "https://example.com/photos/image2.jpg"
95 },
96 {
97 "title": "Welcome Sign",
98 "url": "https://example.com/photos/image3.jpg"
99 },
100 {
101 "title": "Honey Badger",
102 "url": "https://example.com/photos/image4.jpg"
103 },
104 {
105 "title": "Grand Canyon Lizard",
106 "url": "https://example.com/photos/image5.jpg"
107 },
108 {
109 "title": "Castle",
110 "url": "https://example.com/photos/image6.jpg"
111 }
112]
113#+end_src
114
115Now that we've set up our libraries and have the photos ready, let's
116create a loop to call the API for each image. The code below shows a
117loop that calls the URL of each image and sends it to the API,
118requesting results with at least 60% confidence. The results are output
119to the console with dotted lines separating each section.
120
121In the case of an API error, the codes and explanations are output to
122the console.
123
124#+begin_src python
125from ibm_watson import ApiException
126
127for x in range(len(data)):
128try:
129 url = data[x]["url"]
130 images_filename = data[x]["title"]
131 classes = visual_recognition.classify(
132 url=url,
133 images_filename=images_filename,
134 threshold='0.6',
135 owners=["IBM"]).get_result()
136 print("-----------------------------------------------")
137 print("Image Title: ", data[x]["title"], "\n")
138 print("Image URL: ", data[x]["url"], "\n")
139 classification_results = classes["images"][0]["classifiers"][0]["classes"]
140 for result in classification_results:
141 print(result["class"], "(", result["score"], ")")
142 print("-----------------------------------------------")
143except ApiException as ex:
144 print("Method failed with status code " + str(ex.code) + ": " + ex.message)
145#+end_src
146
147* The Results
148
149Here we can see the full result set of our function above. If you view
150each of the URLs that we sent to the API, you'll be able to see that it
151was remarkably accurate. To be fair, these are clear high-resolution,
152clear photos shot with a professional camera. In reality, you will most
153likely be processing images that are lower quality and may have a lot of
154noise in the photo.
155
156However, we can clearly see the benefit of being able to call this API
157instead of attempting to write our own image recognition function. Each
158of the classifications returned was a fair description of the image.
159
160If you wanted to restrict the results to those that are at least 90%
161confident or greater, you would simply adjust the =threshold= in the
162=visual_recognition.classify()= function.
163
164When your program runs, it should show the output below for each photo
165you provide.
166
167#+begin_src txt
168----------------------------------------------------------------
169Image Title: Grizzly Bear
170Image URL: https://example.com/photos/image1.jpg
171
172brown bear ( 0.944 )
173bear ( 1 )
174carnivore ( 1 )
175mammal ( 1 )
176animal ( 1 )
177Alaskan brown bear ( 0.759 )
178greenishness color ( 0.975 )
179----------------------------------------------------------------
180#+end_src
181
182* Discussion
183
184Now, this was a very minimal implementation of the API. We simply
185supplied some images and looked to see how accurate the results were.
186However, you could implement this type of API into many machine learning
187(ML) models.
188
189For example, you could be working for a company that scans their
190warehouses or inventory using drones. Would you want to pay employees to
191sit there and watch drone footage all day in order to identify or count
192things in the video? Probably not. Instead, you could use a
193classification system similar to this one in order to train your machine
194learning model to correctly identify items that the drones show through
195video. More specifically, you could have your machine learning model
196watch a drone fly over a field of sheep in order to count how many sheep
197are living in that field.
198
199There are many ways to implement machine learning functionality, but
200hopefully this post helped inspire some deeper thought about the tools
201that can help propel us further into the future of machine learning and
202AI.
content/blog/2020-09-22-internal-audit.md deleted −233
@@ -1,233 +0,0 @@
1+++
2date = 2020-09-22
3title = "Who is Internal Audit?"
4description = ""
5draft = false
6+++
7
8# Definitions
9
10One of the many reasons that Internal Audit needs such thorough explaining to
11non-auditors is that Internal Audit can serve many purposes, depending on the
12organization's size and needs. However, the Institute of Internal Auditors (IIA)
13defines Internal Auditing as:
14
15> Internal auditing is an independent, objective assurance and consulting
16> activity designed to add value and improve an organization's operations. It
17> helps an organization accomplish its objectives by bringing a systematic,
18> disciplined approach to evaluate and improve the effectiveness of risk
19> management, control, and governance processes.
20
21However, this definition uses quite a few terms that aren't clear unless the
22reader already has a solid understanding of the auditing profession. To further
23explain, the following is a list of definitions that can help supplement
24understanding of internal auditing.
25
26## Independent
27
28Independence is the freedom from conditions that threaten the ability of the
29internal audit activity to carry out internal audit responsibilities in an
30unbiased manner. To achieve the degree of independence necessary to effectively
31carry out the responsibilities of the internal audit activity, the chief audit
32executive has direct and unrestricted access to senior management and the board.
33This can be achieved through a dual-reporting relationship. Threats to
34independence must be managed at the individual auditor, engagement, functional,
35and organizational levels.
36
37## Objective
38
39Objectivity is an unbiased mental attitude that allows internal auditors to
40perform engagements in such a manner that they believe in their work product and
41that no quality compromises are made. Objectivity requires that internal
42auditors do not subordinate their judgment on audit matters to others. Threats
43to objectivity must be managed at the individual auditor, engagement,
44functional, and organizational levels.
45
46## Assurance
47
48Assurance services involve the internal auditor's objective assessment of
49evidence to provide opinions or conclusions regarding an entity, operation,
50function, process, system, or other subject matters. The internal auditor
51determines the nature and scope of an assurance engagement. Generally, three
52parties are participants in assurance services: (1) the person or group directly
53involved with the entity, operation, function, process, system, or other subject
54
55- (the process owner), (2) the person or group making the assessment - (the
56 internal auditor), and (3) the person or group using the assessment - (the
57 user).
58
59## Consulting
60
61Consulting services are advisory in nature and are generally performed at the
62specific request of an engagement client. The nature and scope of the consulting
63engagement are subject to agreement with the engagement client. Consulting
64services generally involve two parties: (1) the person or group offering the
65advice (the internal auditor), and (2) the person or group seeking and receiving
66the advice (the engagement client). When performing consulting services, the
67internal auditor should maintain objectivity and not assume management
68responsibility.
69
70## Governance, Risk Management, & Compliance (GRC)
71
72The integrated collection of capabilities that enable an organization to
73reliably achieve objectives, address uncertainty and act with integrity.
74
75# Audit Charter & Standards
76
77First, it's important to note that not every organization needs internal
78auditors. In fact, it's unwise for an organization to hire internal auditors
79unless they have regulatory requirements for auditing and have the capital to
80support the department. Internal audit is a cost center that can only affect
81revenue indirectly.
82
83Once an organization determines the need for internal assurance services, they
84will hire a Chief Audit Executive and create the audit charter. This charter is
85a document, approved by the company's governing body, that will define internal
86audit's purpose, authority, responsibility, and position within the
87organization. Fortunately, the IIA has model charters available to IIA members
88for those developing or improving their charter.
89
90Beyond the charter and organizational documents, internal auditors follow a few
91different standards in order to perform their job. First is the International
92Professional Practices Framework (IPPF) by the IIA, which is the model of
93standards for internal auditing. In addition, ISACA's Information Technology
94Assurance Framework (ITAF) helps guide auditors in reference to information
95technology (IT) compliance and assurance. Finally, additional standards such as
96FASB, GAAP, and industry-specific standards are used when performing internal
97audit work.
98
99# Three Lines of Defense
100
101[The IIA](https://theiia.org) released the original Three Lines of Defense model
102in 2013, but have released an updated version in 2020.
103
104I won't go into depth about the changes made to the model in this article.
105Instead, let's take a look at the most current model.
106
107The updated model forgets the strict idea of areas performing their own
108functions or line of defense. Instead of talking about management, risk, and
109internal audit as 1-2-3, the new model creates a more fluid and cooperative
110model.
111
112Looking at this model from an auditing perspective shows us that auditors will
113need to align, communicate, and collaborate with management, including business
114area managers and chief officers, as well as reporting to the governing body.
115The governing body will instruct internal audit _functionally_ on their goals
116and track their progress periodically.
117
118However, the internal audit department will report _administratively_ to a chief
119officer in the company for the purposes of collaboration, direction, and
120assistance with the business. Note that in most situations, the governing body
121is the audit committee on the company's board of directors.
122
123The result of this structure is that internal audit is an independent and
124objective function that can provide assurance over the topics they audit.
125
126# Audit Process
127
128A normal audit will generally follow the same process, regardless of the topic.
129However, certain special projects or abnormal business areas may call for
130changes to the audit process. The audit process is not set in stone, it's simply
131a set of best practices so that audits can be performed consistently.
132
133While different organizations may tweak the process, it will generally follow
134this flow:
135
136## 1. Risk Assessment
137
138The risk assessment part of the process has historically been performed
139annually, but many organizations have moved to performing this process much more
140frequently. In fact, some organizations are moving to an agile approach that can
141take new risks into the risk assessment and re-prioritize risk areas on-the-go.
142To perform a risk assessment, leaders in internal audit will research industry
143risks, consult with business leaders around the company, and perform analyses on
144company data.
145
146Once a risk assessment has been documented, the audit department has a
147prioritized list of risks that can be audited. This is usually in the form of
148auditable entities, such as business areas or departments.
149
150## 2. Planning
151
152During the planning phase of an audit, auditors will meet with the business area
153to discuss the various processes, controls, and risks applicable to the
154business. This helps the auditors determine the scope limits for the audit, as
155well as timing and subject-matter experts. Certain documents will be created in
156this phase that will be used to keep the audit on-track an in-scope as it goes
157forward.
158
159## 3. Testing
160
161The testing phase, also known as fieldwork or execution, is where internal
162auditors will take the information they've discovered and test it against
163regulations, industry standards, company rules, best practices, as well as
164validating that any processes are complete and accurate. For example, an audit
165of HR would most likely examine processes such as employee on-boarding, employee
166termination, security of personally identifiable information (PII), or the IT
167systems involved in these processes. Company standards would be examined and
168compared against how the processes are actually being performed day-to-day, as
169well as compared against regulations such as the Equal Employment Opportunity
170(EEO), American with Disabilities Act, and National Labor Relations Act.
171
172## 4. Reporting
173
174Once all the tests have been completed, the audit will enter the reporting
175phase. This is when the audit team will conclude on the evidence they've
176collected, interviews they've held, and any opinions they've formed on the
177controls in place. A summary of the audit findings, conclusions, and specific
178recommendations are officially communicated to the client through a draft
179report. Clients have the opportunity to respond to the report and submit an
180action plan and time frame. These responses become part of the final report
181which is distributed to the appropriate level of administration.
182
183## 5. Follow-Up
184
185After audits have been completed and management has formed action plans and time
186frames for audit issues, internal audit will follow up once that due date has
187arrived. In most cases, the follow-up will simply consist of a meeting to
188discuss how the action plan has been completed and to request documentation to
189prove it.
190
191# Audit Department Structure
192
193While an internal audit department is most often thought of as a team of
194full-time employees, there are actually many different ways in which a
195department can be structured. As the world becomes more digital and fast-paced,
196outsourcing has become a more attractive option for some organizations. Internal
197audit can be fully outsourced or partially outsourced, allowing for flexibility
198in cases where turnover is high.
199
200In addition, departments can implement a rotational model. This allows for
201interested employees around the organization to rotate into the internal audit
202department for a period of time, allowing them to obtain knowledge of risks and
203controls and allowing the internal audit team to obtain more business area
204knowledge. This program is popular in very large organizations, but
205organizations tend to rotate lower-level audit staff instead of managers. This
206helps prevent any significant knowledge loss as auditors rotate out to business
207areas.
208
209# Consulting
210
211Consulting is not an easy task at any organization, especially for a department
212that can have negative perceptions within the organization as the "compliance
213police." However, once an internal audit department has delivered value to
214organization, adding consulting to their suite of services is a smart move. In
215most cases, Internal Audit can insert themselves into a consulting role without
216affecting the process of project management at the company. This means that
217internal audit can add objective assurance and opinions to business areas as
218they develop new processes, instead of coming in periodically to audit an area
219and file issues that could have been fixed at the beginning.
220
221# Data Science & Data Analytics
222
223One major piece of the internal audit function in the modern world is data
224science. While the process is data science, most auditors will refer to anything
225in this realm as data analytics. Hot topics such as robotic process automation
226(RPA), machine learning (ML), and data mining have taken over the auditing world
227in recent years. These technologies have been immensely helpful with increasing
228the effectiveness and efficiency of auditors.
229
230For example, mundane and repetitive tasks can be automated in order for auditors
231to make more room in their schedules for labor-intensive work. Further, auditors
232will need to adapt technologies like machine learning in order to extract more
233value from the data they're using to form conclusions.
content/blog/2020-09-22-internal-audit.org added +248
@@ -0,0 +1,248 @@
1#+date: <2020-09-22>
2#+title: Who is Internal Audit?
3#+description:
4
5
6* Definitions
7
8One of the many reasons that Internal Audit needs such thorough
9explaining to non-auditors is that Internal Audit can serve many
10purposes, depending on the organization's size and needs. However, the
11Institute of Internal Auditors (IIA) defines Internal Auditing as:
12
13#+begin_quote
14Internal auditing is an independent, objective assurance and consulting
15activity designed to add value and improve an organization's operations.
16It helps an organization accomplish its objectives by bringing a
17systematic, disciplined approach to evaluate and improve the
18effectiveness of risk management, control, and governance processes.
19#+end_quote
20
21However, this definition uses quite a few terms that aren't clear unless
22the reader already has a solid understanding of the auditing profession.
23To further explain, the following is a list of definitions that can help
24supplement understanding of internal auditing.
25
26** Independent
27
28Independence is the freedom from conditions that threaten the ability of
29the internal audit activity to carry out internal audit responsibilities
30in an unbiased manner. To achieve the degree of independence necessary
31to effectively carry out the responsibilities of the internal audit
32activity, the chief audit executive has direct and unrestricted access
33to senior management and the board. This can be achieved through a
34dual-reporting relationship. Threats to independence must be managed at
35the individual auditor, engagement, functional, and organizational
36levels.
37
38** Objective
39
40Objectivity is an unbiased mental attitude that allows internal auditors
41to perform engagements in such a manner that they believe in their work
42product and that no quality compromises are made. Objectivity requires
43that internal auditors do not subordinate their judgment on audit
44matters to others. Threats to objectivity must be managed at the
45individual auditor, engagement, functional, and organizational levels.
46
47** Assurance
48
49Assurance services involve the internal auditor's objective assessment
50of evidence to provide opinions or conclusions regarding an entity,
51operation, function, process, system, or other subject matters. The
52internal auditor determines the nature and scope of an assurance
53engagement. Generally, three parties are participants in assurance
54services: (1) the person or group directly involved with the entity,
55operation, function, process, system, or other subject
56
57- (the process owner), (2) the person or group making the assessment -
58 (the internal auditor), and (3) the person or group using the
59 assessment - (the user).
60
61** Consulting
62
63Consulting services are advisory in nature and are generally performed
64at the specific request of an engagement client. The nature and scope of
65the consulting engagement are subject to agreement with the engagement
66client. Consulting services generally involve two parties: (1) the
67person or group offering the advice (the internal auditor), and (2) the
68person or group seeking and receiving the advice (the engagement
69client). When performing consulting services, the internal auditor
70should maintain objectivity and not assume management responsibility.
71
72** Governance, Risk Management, & Compliance (GRC)
73
74The integrated collection of capabilities that enable an organization to
75reliably achieve objectives, address uncertainty and act with integrity.
76
77* Audit Charter & Standards
78
79First, it's important to note that not every organization needs internal
80auditors. In fact, it's unwise for an organization to hire internal
81auditors unless they have regulatory requirements for auditing and have
82the capital to support the department. Internal audit is a cost center
83that can only affect revenue indirectly.
84
85Once an organization determines the need for internal assurance
86services, they will hire a Chief Audit Executive and create the audit
87charter. This charter is a document, approved by the company's governing
88body, that will define internal audit's purpose, authority,
89responsibility, and position within the organization. Fortunately, the
90IIA has model charters available to IIA members for those developing or
91improving their charter.
92
93Beyond the charter and organizational documents, internal auditors
94follow a few different standards in order to perform their job. First is
95the International Professional Practices Framework (IPPF) by the IIA,
96which is the model of standards for internal auditing. In addition,
97ISACA's Information Technology Assurance Framework (ITAF) helps guide
98auditors in reference to information technology (IT) compliance and
99assurance. Finally, additional standards such as FASB, GAAP, and
100industry-specific standards are used when performing internal audit
101work.
102
103* Three Lines of Defense
104
105[[https://theiia.org][The IIA]] released the original Three Lines of
106Defense model in 2013, but have released an updated version in 2020.
107
108I won't go into depth about the changes made to the model in this
109article. Instead, let's take a look at the most current model.
110
111The updated model forgets the strict idea of areas performing their own
112functions or line of defense. Instead of talking about management, risk,
113and internal audit as 1-2-3, the new model creates a more fluid and
114cooperative model.
115
116Looking at this model from an auditing perspective shows us that
117auditors will need to align, communicate, and collaborate with
118management, including business area managers and chief officers, as well
119as reporting to the governing body. The governing body will instruct
120internal audit /functionally/ on their goals and track their progress
121periodically.
122
123However, the internal audit department will report /administratively/ to
124a chief officer in the company for the purposes of collaboration,
125direction, and assistance with the business. Note that in most
126situations, the governing body is the audit committee on the company's
127board of directors.
128
129The result of this structure is that internal audit is an independent
130and objective function that can provide assurance over the topics they
131audit.
132
133* Audit Process
134
135A normal audit will generally follow the same process, regardless of the
136topic. However, certain special projects or abnormal business areas may
137call for changes to the audit process. The audit process is not set in
138stone, it's simply a set of best practices so that audits can be
139performed consistently.
140
141While different organizations may tweak the process, it will generally
142follow this flow:
143
144** 1. Risk Assessment
145
146The risk assessment part of the process has historically been performed
147annually, but many organizations have moved to performing this process
148much more frequently. In fact, some organizations are moving to an agile
149approach that can take new risks into the risk assessment and
150re-prioritize risk areas on-the-go. To perform a risk assessment,
151leaders in internal audit will research industry risks, consult with
152business leaders around the company, and perform analyses on company
153data.
154
155Once a risk assessment has been documented, the audit department has a
156prioritized list of risks that can be audited. This is usually in the
157form of auditable entities, such as business areas or departments.
158
159** 2. Planning
160
161During the planning phase of an audit, auditors will meet with the
162business area to discuss the various processes, controls, and risks
163applicable to the business. This helps the auditors determine the scope
164limits for the audit, as well as timing and subject-matter experts.
165Certain documents will be created in this phase that will be used to
166keep the audit on-track an in-scope as it goes forward.
167
168** 3. Testing
169
170The testing phase, also known as fieldwork or execution, is where
171internal auditors will take the information they've discovered and test
172it against regulations, industry standards, company rules, best
173practices, as well as validating that any processes are complete and
174accurate. For example, an audit of HR would most likely examine
175processes such as employee on-boarding, employee termination, security
176of personally identifiable information (PII), or the IT systems involved
177in these processes. Company standards would be examined and compared
178against how the processes are actually being performed day-to-day, as
179well as compared against regulations such as the Equal Employment
180Opportunity (EEO), American with Disabilities Act, and National Labor
181Relations Act.
182
183** 4. Reporting
184
185Once all the tests have been completed, the audit will enter the
186reporting phase. This is when the audit team will conclude on the
187evidence they've collected, interviews they've held, and any opinions
188they've formed on the controls in place. A summary of the audit
189findings, conclusions, and specific recommendations are officially
190communicated to the client through a draft report. Clients have the
191opportunity to respond to the report and submit an action plan and time
192frame. These responses become part of the final report which is
193distributed to the appropriate level of administration.
194
195** 5. Follow-Up
196
197After audits have been completed and management has formed action plans
198and time frames for audit issues, internal audit will follow up once
199that due date has arrived. In most cases, the follow-up will simply
200consist of a meeting to discuss how the action plan has been completed
201and to request documentation to prove it.
202
203* Audit Department Structure
204
205While an internal audit department is most often thought of as a team of
206full-time employees, there are actually many different ways in which a
207department can be structured. As the world becomes more digital and
208fast-paced, outsourcing has become a more attractive option for some
209organizations. Internal audit can be fully outsourced or partially
210outsourced, allowing for flexibility in cases where turnover is high.
211
212In addition, departments can implement a rotational model. This allows
213for interested employees around the organization to rotate into the
214internal audit department for a period of time, allowing them to obtain
215knowledge of risks and controls and allowing the internal audit team to
216obtain more business area knowledge. This program is popular in very
217large organizations, but organizations tend to rotate lower-level audit
218staff instead of managers. This helps prevent any significant knowledge
219loss as auditors rotate out to business areas.
220
221* Consulting
222
223Consulting is not an easy task at any organization, especially for a
224department that can have negative perceptions within the organization as
225the "compliance police." However, once an internal audit department has
226delivered value to organization, adding consulting to their suite of
227services is a smart move. In most cases, Internal Audit can insert
228themselves into a consulting role without affecting the process of
229project management at the company. This means that internal audit can
230add objective assurance and opinions to business areas as they develop
231new processes, instead of coming in periodically to audit an area and
232file issues that could have been fixed at the beginning.
233
234* Data Science & Data Analytics
235
236One major piece of the internal audit function in the modern world is
237data science. While the process is data science, most auditors will
238refer to anything in this realm as data analytics. Hot topics such as
239robotic process automation (RPA), machine learning (ML), and data mining
240have taken over the auditing world in recent years. These technologies
241have been immensely helpful with increasing the effectiveness and
242efficiency of auditors.
243
244For example, mundane and repetitive tasks can be automated in order for
245auditors to make more room in their schedules for labor-intensive work.
246Further, auditors will need to adapt technologies like machine learning
247in order to extract more value from the data they're using to form
248conclusions.
content/blog/2020-09-25-happiness-map.md → content/blog/2020-09-25-happiness-map.org renamed +72 −71
@@ -1,88 +1,87 @@
1+++
2date = 2020-09-25
3title = "Data Visualization: World Choropleth Map of Happiness"
4description = ""
5draft = false
6+++
1#+date: <2020-09-25>
2#+title: Data Visualization: World Choropleth Map of Happiness
3#+description:
74
8# Background Information
5
6* Background Information
97
108The dataset (obtained from
11[Kaggle](https://www.kaggle.com/unsdsn/world-happiness)) used in this article
12contains a list of countries around the world, their happiness rankings and
13scores, as well as other national scoring measures.
9[[https://www.kaggle.com/unsdsn/world-happiness][Kaggle]]) used in this
10article contains a list of countries around the world, their happiness
11rankings and scores, as well as other national scoring measures.
1412
1513Fields include:
1614
17- Overall rank
18- Country or region
19- GDP per capita
20- Social support
21- Healthy life expectancy
22- Freedom to make life choices
23- Generosity
24- Perceptions of corruption
15- Overall rank
16- Country or region
17- GDP per capita
18- Social support
19- Healthy life expectancy
20- Freedom to make life choices
21- Generosity
22- Perceptions of corruption
2523
26There are 156 records. Since there are ~195 countries in the world, we can see
27that around 40 countries will be missing from this dataset.
24There are 156 records. Since there are ~195 countries in the world, we
25can see that around 40 countries will be missing from this dataset.
2826
29# Install Packages
27* Install Packages
3028
31As always, run the `install` command for all packages needed to perform
29As always, run the =install= command for all packages needed to perform
3230analysis.
3331
34```python
32#+begin_src python
3533!pip install folium geopandas matplotlib numpy pandas
36```
34#+end_src
3735
38# Import the Data
36* Import the Data
3937
4038We only need a couple packages to create a choropleth map. We will use
41[Folium](https://python-visualization.github.io/folium/), which provides map
42visualizations in Python. We will also use geopandas and pandas to wrangle our
43data before we put it on a map.
39[[https://python-visualization.github.io/folium/][Folium]], which
40provides map visualizations in Python. We will also use geopandas and
41pandas to wrangle our data before we put it on a map.
4442
45```python
43#+begin_src python
4644# Import the necessary Python packages
4745import folium
4846import geopandas as gpd
4947import pandas as pd
50```
48#+end_src
5149
52To get anything to show up on a map, we need a file that will specify the
53boundaries of each country. Luckily, GeoJSON files exist (for free!) on the
54internet. To get the boundaries of every country in the world, we will use the
55GeoJSON link shown below.
50To get anything to show up on a map, we need a file that will specify
51the boundaries of each country. Luckily, GeoJSON files exist (for free!)
52on the internet. To get the boundaries of every country in the world, we
53will use the GeoJSON link shown below.
5654
57GeoPandas will take this data and load it into a dataframe so that we can easily
58match it to the data we're trying to analyze. Let's look at the GeoJSON
59dataframe:
55GeoPandas will take this data and load it into a dataframe so that we
56can easily match it to the data we're trying to analyze. Let's look at
57the GeoJSON dataframe:
6058
61```python
59#+begin_src python
6260# Load the GeoJSON data with geopandas
6361geo_data = gpd.read_file('https://raw.githubusercontent.com/datasets/geo-countries/master/data/countries.geojson')
6462geo_data.head()
65```
63#+end_src
6664
67Next, let's load the data from the Kaggle dataset. I've downloaded this file, so
68update the file path if you have it somewhere else. After loading, let's take a
69look at this dataframe:
65Next, let's load the data from the Kaggle dataset. I've downloaded this
66file, so update the file path if you have it somewhere else. After
67loading, let's take a look at this dataframe:
7068
71```python
69#+begin_src python
7270# Load the world happiness data with pandas
7371happy_data = pd.read_csv(r'~/Downloads/world_happiness_data_2019.csv')
7472happy_data.head()
75```
73#+end_src
7674
77# Clean the Data
75* Clean the Data
7876
79Some countries need to be renamed, or they will be lost when you merge the
80happiness and GeoJSON dataframes. This is something I discovered when the map
81below showed empty countries. I searched both data frames for the missing
82countries to see the naming differences. Any countries that do not have records
83in the `happy_data` df will not show up on the map.
77Some countries need to be renamed, or they will be lost when you merge
78the happiness and GeoJSON dataframes. This is something I discovered
79when the map below showed empty countries. I searched both data frames
80for the missing countries to see the naming differences. Any countries
81that do not have records in the =happy_data= df will not show up on the
82map.
8483
85```python
84#+begin_src python
8685# Rename some countries to match our GeoJSON data
8786
8887# Rename USA
@@ -100,34 +99,35 @@ happy_data.at[republic_congo_index, 'Country or region'] = 'Republic of Congo'
10099# Rename the DRC
101100democratic_congo_index = happy_data.index[happy_data['Country or region'] == 'Congo (Kinshasa)']
102101happy_data.at[democratic_congo_index, 'Country or region'] = 'Democratic Republic of the Congo'
103```
102#+end_src
104103
105# Merge the Data
104* Merge the Data
106105
107106Now that we have clean data, we need to merge the GeoJSON data with the
108happiness data. Since we've stored them both in dataframes, we just need to call
109the `.merge()` function.
107happiness data. Since we've stored them both in dataframes, we just need
108to call the =.merge()= function.
110109
111We will also rename a couple columns, just so that they're a little easier to
112use when we create the map.
110We will also rename a couple columns, just so that they're a little
111easier to use when we create the map.
113112
114```python
113#+begin_src python
115114# Merge the two previous dataframes into a single geopandas dataframe
116115merged_df = geo_data.merge(happy_data,left_on='ADMIN', right_on='Country or region')
117116
118117# Rename columns for ease of use
119118merged_df = merged_df.rename(columns = {'ADMIN':'GeoJSON_Country'})
120119merged_df = merged_df.rename(columns = {'Country or region':'Country'})
121```
120#+end_src
122121
123# Create the Map
122* Create the Map
124123
125124The data is finally ready to be added to a map. The code below shows the
126simplest way to find the center of the map and create a Folium map object. The
127important part is to remember to reference the merged dataframe for our GeoJSON
128data and value data. The columns specify which geo data and value data to use.
125simplest way to find the center of the map and create a Folium map
126object. The important part is to remember to reference the merged
127dataframe for our GeoJSON data and value data. The columns specify which
128geo data and value data to use.
129129
130```python
130#+begin_src python
131131# Assign centroids to map
132132x_map = merged_df.centroid.x.mean()
133133y_map = merged_df.centroid.y.mean()
@@ -151,17 +151,18 @@ folium.Choropleth(
151151 smooth_factor=0,
152152 highlight=True
153153).add_to(world_map)
154```
154#+end_src
155155
156156Let's look at the resulting map.
157157
158# Create a Tooltip on Hover
158* Create a Tooltip on Hover
159159
160Now that we have a map set up, we could stop. However, I want to add a tooltip
161so that I can see more information about each country. The `tooltip_data` code
162below will show a popup on hover with all the data fields shown.
160Now that we have a map set up, we could stop. However, I want to add a
161tooltip so that I can see more information about each country. The
162=tooltip_data= code below will show a popup on hover with all the data
163fields shown.
163164
164```python
165#+begin_src python
165166 # Adding labels to map
166167 style_function = lambda x: {'fillColor': '#ffffff',
167168 'color':'#000000',
@@ -202,6 +203,6 @@ folium.LayerControl().add_to(world_map)
202203
203204# Display the map
204205world_map
205```
206#+end_src
206207
207208The tooltip will now appear whenever you hover over a country.
content/blog/2020-10-12-mediocrity.md deleted −116
@@ -1,116 +0,0 @@
1+++
2date = 2020-10-12
3title = "On the Pursuit of Mediocrity"
4description = ""
5draft = false
6+++
7
8# Perfect is the Enemy of Good
9
10As the saying goes, "the best is the enemy of the good." As we strive for
11perfection, we often fail to realize the implications of such an undertaking.
12Attempting to reach perfection is often unrealistic. Even worse, it can get in
13the way of achieving a good outcome. In certain situations, we try so hard to
14achieve the ideal solution that we have burned the bridges that would have
15allowed us to reach a lesser yet still superb solution.
16
17Philosophers throughout history have inspected this plight from many viewpoints.
18Greek mythology speaks of the [golden
19mean](<https://en.wikipedia.org/wiki/Golden_mean_(philosophy)>), which uses the
20story of Icarus to illustrate that sometimes "the middle course" is the best
21solution. In this story, Daedalus, a famous artist of his time, built feathered
22wings for himself and his son so that they might escape the clutches of King
23Minos. Daedalus warns his beloved son whom he loved so much to "fly the middle
24course", between the sea spray and the sun's heat. Icarus did not heed his
25father; he flew up and up until the sun melted the wax off his wings. For not
26heeding the middle course, he fell into the sea and drowned.
27
28More recently, management scholars have explored the [Pareto
29principle](https://en.wikipedia.org/wiki/Pareto_principle) and found that as we
30increase the frequency of something, or strive to perform actions to achieve
31some form of perfection, we run into [diminishing
32returns](https://en.wikipedia.org/wiki/Diminishing_returns).
33
34Even further, Harold Demsetz is noted as coining the term [the Nirvana
35fallacy](https://en.wikipedia.org/wiki/Nirvana_fallacy) in 1969, which shows the
36fallacy of comparing actual things with unrealistic, idealized alternatives.
37This is another trap that we may fall into, where we are constantly thinking of
38the ultimate solutions to problems, when something more realistic needs to be
39considered.
40
41Over and over throughout history, we've found that perfection is often
42unrealistic and unachievable. However, we push ourselves and our peers to "give
43100%" or "go the extra mile," while it may be that the better course is to give
44a valuable level of effort while considering the effects of further effort on
45the outcome. Working harder does not always help us achieve loftier goals.
46
47This has presented itself to me most recently during my time studying at my
48university. I was anxious and feeling the stresses of my courses, career, and
49personal life for quite a while, which was greatly affecting how well I was
50doing at school and my level of effort at work. One day, I happened to be
51talking to my father when he said something simple that hit home:
52
53> All you can do is show up and do your best. Worrying about the outcomes won't
54> affect the outcome itself.
55
56The thought was extremely straightforward and uncomplicated, yet it was
57something that I had lost sight of during my stress-filled years at school. Ever
58since then, I've found myself pausing and remembering that quote every time I
59get anxious or stressed. It helps to stop and think "Can I do anything to affect
60the outcome, or am I simply worrying over something I can't change?"
61
62# When Mediocrity Isn't Enough
63
64One problem with the philosophies presented in this post is that they are
65implemented far too often in situations where mediocrity simply isn't adequate.
66For example, let's take a look at digital user data, specifically
67personally-identifiable information (PII). As a cybersecurity auditor in the
68United States, I have found that most companies are concerned more with
69compliance than any actual safeguards over the privacy or protection of user
70data. Other than companies who have built their reputation on privacy and
71security, most companies will use
72[satisficing](https://en.wikipedia.org/wiki/Satisficing) as their primary
73decision-making strategy around user data.
74
75> Satisficing is a decision-making strategy or cognitive heuristic that entails
76> searching through the available alternatives until an acceptability threshold
77> is met.
78
79This means that each decision will be met with certain possible solutions until
80one of the solutions meets their minimum acceptable standards. For companies
81that deal with user data, the minimum-acceptable standards come from three
82areas:
83
841. Laws and regulations
852. Competitive pressure
863. Risk of monetary or reputation loss
87
88Working with project management or auditing, the primary concern here is the
89risk of legal ramifications. Since the primary risk comes from laws and
90regulations, companies will require that any project that involves user data
91must follow all the rules of those laws so that the company can protect itself
92from fines or other penalties.
93
94Following this, companies will consider best practices in order to place itself
95in a competitive position (e.g. Google vs. Apple) and review any recent or
96ongoing litigation against companies regarding user data. In a perfect company,
97management would then consider the ethical responsibilities of their
98organization and discuss their responsibilities over things like
99personally-identifiable information.
100
101However, as we mentioned above, most companies follow the idea of satisficing,
102which states that they have met the minimum acceptable standards and can now
103move on to other decisions. Modern business culture in the United States
104dictates that profits are the golden measure of how well a company or manager is
105performing, so we often don't think about our responsibilities beyond these
106basic standards.
107
108Not all situations demand excellence, but I believe that applying any philosophy
109as a broad stroke across one's life can be a mistake. We must be able to think
110critically about what we are doing as we do it and ask ourselves a few
111questions. Have I done everything I can in this situation? Is mediocrity an
112acceptable outcome, or should we strive for perfection, even if we can't attain
113it?
114
115Taking a few moments to think critically throughout our day, as we make
116decisions, can have a tremendous effect on the outcomes we create.
content/blog/2020-10-12-mediocrity.org added +124
@@ -0,0 +1,124 @@
1#+date: <2020-10-12>
2#+title: On the Pursuit of Mediocrity
3#+description:
4
5
6* Perfect is the Enemy of Good
7
8As the saying goes, "the best is the enemy of the good." As we strive
9for perfection, we often fail to realize the implications of such an
10undertaking. Attempting to reach perfection is often unrealistic. Even
11worse, it can get in the way of achieving a good outcome. In certain
12situations, we try so hard to achieve the ideal solution that we have
13burned the bridges that would have allowed us to reach a lesser yet
14still superb solution.
15
16Philosophers throughout history have inspected this plight from many
17viewpoints. Greek mythology speaks of the
18[[https://en.wikipedia.org/wiki/Golden_mean_(philosophy)][golden mean]],
19which uses the story of Icarus to illustrate that sometimes "the middle
20course" is the best solution. In this story, Daedalus, a famous artist
21of his time, built feathered wings for himself and his son so that they
22might escape the clutches of King Minos. Daedalus warns his beloved son
23whom he loved so much to "fly the middle course", between the sea spray
24and the sun's heat. Icarus did not heed his father; he flew up and up
25until the sun melted the wax off his wings. For not heeding the middle
26course, he fell into the sea and drowned.
27
28More recently, management scholars have explored the
29[[https://en.wikipedia.org/wiki/Pareto_principle][Pareto principle]] and
30found that as we increase the frequency of something, or strive to
31perform actions to achieve some form of perfection, we run into
32[[https://en.wikipedia.org/wiki/Diminishing_returns][diminishing
33returns]].
34
35Even further, Harold Demsetz is noted as coining the term
36[[https://en.wikipedia.org/wiki/Nirvana_fallacy][the Nirvana fallacy]]
37in 1969, which shows the fallacy of comparing actual things with
38unrealistic, idealized alternatives. This is another trap that we may
39fall into, where we are constantly thinking of the ultimate solutions to
40problems, when something more realistic needs to be considered.
41
42Over and over throughout history, we've found that perfection is often
43unrealistic and unachievable. However, we push ourselves and our peers
44to "give 100%" or "go the extra mile," while it may be that the better
45course is to give a valuable level of effort while considering the
46effects of further effort on the outcome. Working harder does not always
47help us achieve loftier goals.
48
49This has presented itself to me most recently during my time studying at
50my university. I was anxious and feeling the stresses of my courses,
51career, and personal life for quite a while, which was greatly affecting
52how well I was doing at school and my level of effort at work. One day,
53I happened to be talking to my father when he said something simple that
54hit home:
55
56#+begin_quote
57All you can do is show up and do your best. Worrying about the outcomes
58won't affect the outcome itself.
59#+end_quote
60
61The thought was extremely straightforward and uncomplicated, yet it was
62something that I had lost sight of during my stress-filled years at
63school. Ever since then, I've found myself pausing and remembering that
64quote every time I get anxious or stressed. It helps to stop and think
65"Can I do anything to affect the outcome, or am I simply worrying over
66something I can't change?"
67
68* When Mediocrity Isn't Enough
69
70One problem with the philosophies presented in this post is that they
71are implemented far too often in situations where mediocrity simply
72isn't adequate. For example, let's take a look at digital user data,
73specifically personally-identifiable information (PII). As a
74cybersecurity auditor in the United States, I have found that most
75companies are concerned more with compliance than any actual safeguards
76over the privacy or protection of user data. Other than companies who
77have built their reputation on privacy and security, most companies will
78use [[https://en.wikipedia.org/wiki/Satisficing][satisficing]] as their
79primary decision-making strategy around user data.
80
81#+begin_quote
82Satisficing is a decision-making strategy or cognitive heuristic that
83entails searching through the available alternatives until an
84acceptability threshold is met.
85#+end_quote
86
87This means that each decision will be met with certain possible
88solutions until one of the solutions meets their minimum acceptable
89standards. For companies that deal with user data, the
90minimum-acceptable standards come from three areas:
91
921. Laws and regulations
932. Competitive pressure
943. Risk of monetary or reputation loss
95
96Working with project management or auditing, the primary concern here is
97the risk of legal ramifications. Since the primary risk comes from laws
98and regulations, companies will require that any project that involves
99user data must follow all the rules of those laws so that the company
100can protect itself from fines or other penalties.
101
102Following this, companies will consider best practices in order to place
103itself in a competitive position (e.g. Google vs. Apple) and review any
104recent or ongoing litigation against companies regarding user data. In a
105perfect company, management would then consider the ethical
106responsibilities of their organization and discuss their
107responsibilities over things like personally-identifiable information.
108
109However, as we mentioned above, most companies follow the idea of
110satisficing, which states that they have met the minimum acceptable
111standards and can now move on to other decisions. Modern business
112culture in the United States dictates that profits are the golden
113measure of how well a company or manager is performing, so we often
114don't think about our responsibilities beyond these basic standards.
115
116Not all situations demand excellence, but I believe that applying any
117philosophy as a broad stroke across one's life can be a mistake. We must
118be able to think critically about what we are doing as we do it and ask
119ourselves a few questions. Have I done everything I can in this
120situation? Is mediocrity an acceptable outcome, or should we strive for
121perfection, even if we can't attain it?
122
123Taking a few moments to think critically throughout our day, as we make
124decisions, can have a tremendous effect on the outcomes we create.
content/blog/2020-12-27-website-redesign.md deleted −94
@@ -1,94 +0,0 @@
1+++
2date = 2020-12-27
3title = "Redesigning My Website: The 5 KB Result"
4description = ""
5draft = false
6+++
7
8# A Brief History
9
10As a form of continuous learning and entertainment, I've been running a handful
11of websites since 2016 when I took my first programming courses in college. I
12maintain one main website, the place I consider the official website to
13represent me. Under this site, I have a handful of subdirectories and
14subdomains.
15
16One of the parts I've enjoyed the most about web development is the aspect of
17designing an identity for a web page and working to find exciting ways to
18display the site's content. Inevitably, this means I've changed the designs for
19my websites more times than I could possibly count. Since I don't really host
20anything on my main webpage that's vital, it allows me the freedom to change
21things as inspiration strikes.
22
23Historically, I've relied on core utilities for spacing, components, and layouts
24from [Bootstrap](https://getbootstrap.com) and added custom CSS for fonts,
25accents, colors, and other items. I also tend to create sites with no border
26radius on items, visible borders, and content that takes up the entire screen
27(using whitespace inside components instead of whitespace around my components).
28
29# The Redesign Process
30
31About a week ago, I found myself wishing for a new design yet again. The prior
32design was largely inspired by IBM's [Carbon Design
33System](https://www.carbondesignsystem.com) and relied on jQuery, Bootstrap,
34along with some compressed [.webp](https://en.wikipedia.org/wiki/WebP) images.
35
36To anyone who knows my preferences toward web design - and even in my personal
37life - it should be no surprise that I immediately started looking for
38inspiration on minimalism. While there are some decent minimalistic designs on
39sites like
40[Dribbble](https://dribbble.com/search/shots/popular/web-design?q=minimalism),
41people seem to mostly discuss [brutalist web
42design](https://brutalist-web.design) when you ask about minimalism. While
43brutalist web design doesn't have to be minimal, it often is.
44
45I suppose, in a way, I did create a brutalist website since my HTML is semantic
46and accessible, hyperlinks are colored and underlined, and all native browser
47functions like scrolling and the back button work as expected. However, I didn't
48think about brutalism while designing these sites.
49
50The new design followed a simple design process. I walked through the screens on
51my blog and asked myself: "Is this element necessary for a user?" This allowed
52me to first start by removing all javascript, which had the sole purpose of
53allowing users to open a collapsed navbar on mobile. Replacing the collapsible
54navbar allowed me to remove both jQuery and Bootstrap's javascript.
55
56Next, I removed things like author names (since I'm literally the only person
57who will ever write on this site), multiple ways to click on a blog post card,
58blog post descriptions, and the scroll-to-top button. It also helped to move all
59categories to a single page, rather than have each category on its own page.
60
61The final big piece to finish the
62"[KonMari](https://en.wikipedia.org/wiki/Marie_Kondo#KonMari_method)"-like part
63of my process was to remove Bootstrap CSS in its entirety. However, this meant
64pulling out a few very useful classes, such as `.img-fluid` and the default font
65stacks to keep in my custom CSS.
66
67After removing all the unnecessary pieces, I was finally able to reorganize my
68content and add a very small amount of custom CSS to make everything pretty.
69This took a brief amount of time, effectively just consisting of me converting
70`<div>` tags into things like `<ul>` lists and choosing accent colors.
71
72# The Results
73
74## Reflection
75
76So, what did all of this reorganizing do to my webpages? Well, first, my
77websites are now **ridiculously fast**. Since the prior designs were also
78minimal and didn't have many images, they measured up in Firefox's Network
79Monitor around 300 KB - 600KB. After making the changes, my main site is at 5 KB
80transferred (22 KB total), and my blog is at 6.5 KB transferred (13 KB total).
81**That means the redesigned pages are less than 2% the size of the old
82designs.**
83
84Google Lighthouse ranks the new webpage as 100 in performance, accessibility,
85and best practices, with SEO at 92 since they think tap targets are not sized
86appropriately for mobile users. First contextual paints of the pages are under
870.8 seconds with 0 ms of blocking time. However, the blog subdomain ranks at 100
88for all four categories! First contextual paints of the blog homepage are under
891.0 seconds with 0 ms of blocking time, due to the fact that the CSS for my blog
90is within a separate CSS file, and the CSS for my main website is simply
91embedded in the HTML file.
92
93Now that everything is complete, I can confidently say I'm happy with the result
94and proud to look at the fastest set of websites I've created so far.
content/blog/2020-12-27-website-redesign.org added +100
@@ -0,0 +1,100 @@
1#+date: <2020-12-27>
2#+title: Redesigning My Website: The 5 KB Result
3#+description:
4
5
6* A Brief History
7
8As a form of continuous learning and entertainment, I've been running a
9handful of websites since 2016 when I took my first programming courses
10in college. I maintain one main website, the place I consider the
11official website to represent me. Under this site, I have a handful of
12subdirectories and subdomains.
13
14One of the parts I've enjoyed the most about web development is the
15aspect of designing an identity for a web page and working to find
16exciting ways to display the site's content. Inevitably, this means I've
17changed the designs for my websites more times than I could possibly
18count. Since I don't really host anything on my main webpage that's
19vital, it allows me the freedom to change things as inspiration strikes.
20
21Historically, I've relied on core utilities for spacing, components, and
22layouts from [[https://getbootstrap.com][Bootstrap]] and added custom
23CSS for fonts, accents, colors, and other items. I also tend to create
24sites with no border radius on items, visible borders, and content that
25takes up the entire screen (using whitespace inside components instead
26of whitespace around my components).
27
28* The Redesign Process
29
30About a week ago, I found myself wishing for a new design yet again. The
31prior design was largely inspired by IBM's
32[[https://www.carbondesignsystem.com][Carbon Design System]] and relied
33on jQuery, Bootstrap, along with some compressed
34[[https://en.wikipedia.org/wiki/WebP][.webp]] images.
35
36To anyone who knows my preferences toward web design - and even in my
37personal life - it should be no surprise that I immediately started
38looking for inspiration on minimalism. While there are some decent
39minimalistic designs on sites like
40[[https://dribbble.com/search/shots/popular/web-design?q=minimalism][Dribbble]],
41people seem to mostly discuss [[https://brutalist-web.design][brutalist
42web design]] when you ask about minimalism. While brutalist web design
43doesn't have to be minimal, it often is.
44
45I suppose, in a way, I did create a brutalist website since my HTML is
46semantic and accessible, hyperlinks are colored and underlined, and all
47native browser functions like scrolling and the back button work as
48expected. However, I didn't think about brutalism while designing these
49sites.
50
51The new design followed a simple design process. I walked through the
52screens on my blog and asked myself: "Is this element necessary for a
53user?" This allowed me to first start by removing all javascript, which
54had the sole purpose of allowing users to open a collapsed navbar on
55mobile. Replacing the collapsible navbar allowed me to remove both
56jQuery and Bootstrap's javascript.
57
58Next, I removed things like author names (since I'm literally the only
59person who will ever write on this site), multiple ways to click on a
60blog post card, blog post descriptions, and the scroll-to-top button. It
61also helped to move all categories to a single page, rather than have
62each category on its own page.
63
64The final big piece to finish the
65"[[https://en.wikipedia.org/wiki/Marie_Kondo#KonMari_method][KonMari]]"-like
66part of my process was to remove Bootstrap CSS in its entirety. However,
67this meant pulling out a few very useful classes, such as =.img-fluid=
68and the default font stacks to keep in my custom CSS.
69
70After removing all the unnecessary pieces, I was finally able to
71reorganize my content and add a very small amount of custom CSS to make
72everything pretty. This took a brief amount of time, effectively just
73consisting of me converting =<div>= tags into things like =<ul>= lists
74and choosing accent colors.
75
76* The Results
77
78** Reflection
79
80So, what did all of this reorganizing do to my webpages? Well, first, my
81websites are now *ridiculously fast*. Since the prior designs were also
82minimal and didn't have many images, they measured up in Firefox's
83Network Monitor around 300 KB - 600KB. After making the changes, my main
84site is at 5 KB transferred (22 KB total), and my blog is at 6.5 KB
85transferred (13 KB total). *That means the redesigned pages are less
86than 2% the size of the old designs.*
87
88Google Lighthouse ranks the new webpage as 100 in performance,
89accessibility, and best practices, with SEO at 92 since they think tap
90targets are not sized appropriately for mobile users. First contextual
91paints of the pages are under 0.8 seconds with 0 ms of blocking time.
92However, the blog subdomain ranks at 100 for all four categories! First
93contextual paints of the blog homepage are under 1.0 seconds with 0 ms
94of blocking time, due to the fact that the CSS for my blog is within a
95separate CSS file, and the CSS for my main website is simply embedded in
96the HTML file.
97
98Now that everything is complete, I can confidently say I'm happy with
99the result and proud to look at the fastest set of websites I've created
100so far.
content/blog/2020-12-28-neon-drive.md deleted −81
@@ -1,81 +0,0 @@
1+++
2date = 2020-12-28
3title = "Neon Drive: A Nostalgic 80s Arcade Racing Game"
4description = ""
5draft = false
6+++
7
8# Game Description
9
10[Neon Drive](https://store.steampowered.com/app/433910/Neon_Drive/) presents
11itself as a simple arcade-style game inspired by the arcade race games of the
121980s, yet it has managed to take up hours of my life without much effort. The
13game description, directly from the Steam page, is intriguing enough to entice
14anyone who's been looking for a good arcade racing game:
15
16> Neon Drive is a slick retro-futuristic arcade game that will make your brain
17> melt. You've been warned. From beautiful cityscapes and ocean roads to
18> exploding enemy spaceships, Neon Drive has it all.
19
20# Gameplay
21
22The game holds true to the
23[retro-futurism](https://en.wikipedia.org/wiki/Retrofuturism) style, including
24chrome female robots, pixelated arcade machines, and
25[outrun](https://teddit.net/r/outrun/) aesthetics.
26
27Each level of the game is shown as a separate arcade machine. Each arcade
28machine lets you play on Normal, Hard, Insane, Practice, and Free Run. To beat
29each arcade, you must reach the end of the level without crashing your car into
30the various obstacles on the course. Basic levels let you move left or right to
31avoid blocks in the road. Later levels put you through other tests, such as
32dodging traffic or blasting asteroids.
33
34The game uses synthwave music to keep you on track to make the correct moves by
35timing the beats of the songs to the correct moves on the screen. It reminds me
36of the early Guitar Hero games, as well as mobile apps like VOEZ - repetition
37and staying on-beat is the only way to win.
38
39# In-Game Screenshots
40
41Taking a look at the main menu, you can see that Neon Drive plays into every
42stereotype you can think of around retro-futuristic, synthwave arcades (in a
43good way).
44
45Once you get into the first level, we see that the choice of car fits right in
46with the stereotypical cars of the 80s, like the
47[DeLorean](https://en.wikipedia.org/wiki/DMC_DeLorean) or the [Ferrari
48F40](https://en.wikipedia.org/wiki/Ferrari_F40). Each new level comes with new
49color schemes and cars, so you should never get tired of the aesthetic.
50
51Personally, I love the orange and blue colors used in level 2:
52
53If you're the competitive type and getting 100% on all arcade machines isn't
54enough, there are leaderboards for the regular part of the game, and the
55endurance game mode.
56
57# Other Suggestions
58
59Neon Drive sits nicely within the well-founded cult genre of Outrun. Other games
60that I've enjoyed in this same spectrum are:
61
62- [Far Cry 3: Blood
63 Dragon](https://store.steampowered.com/app/233270/Far_Cry_3__Blood_Dragon/)
64- [Retrowave](https://store.steampowered.com/app/1239690/Retrowave/)
65- [Slipstream](https://store.steampowered.com/app/732810/Slipstream/)
66
67Although these games aren't necessarily in the same genre, they do have aspects
68that place them close enough to interest gamers that enjoyed Neon Drive:
69
70- [Black Ice](https://store.steampowered.com/app/311800/Black_Ice/)
71- [Cloudpunk](https://store.steampowered.com/app/746850/Cloudpunk/)
72- [Need for Speed:
73 Heat](https://store.steampowered.com/app/1222680/Need_for_Speed_Heat/)
74- [VirtuaVerse](https://store.steampowered.com/app/1019310/VirtuaVerse/)
75
76Of course, if all you really care about is the arcade aspect of these games, you
77can check out the [Atari
78Vault](https://store.steampowered.com/app/400020/Atari_Vault/) or any of the
79other classic games sold on Steam by companies like Namco, Atari. For something
80like Nintendo, you'd have to settle for buying used classic consoles or delve
81into the world of emulation.
content/blog/2020-12-28-neon-drive.org added +84
@@ -0,0 +1,84 @@
1#+date: <2020-12-28>
2#+title: Neon Drive: A Nostalgic 80s Arcade Racing Game
3#+description:
4
5
6* Game Description
7
8[[https://store.steampowered.com/app/433910/Neon_Drive/][Neon Drive]]
9presents itself as a simple arcade-style game inspired by the arcade
10race games of the 1980s, yet it has managed to take up hours of my life
11without much effort. The game description, directly from the Steam page,
12is intriguing enough to entice anyone who's been looking for a good
13arcade racing game:
14
15#+begin_quote
16Neon Drive is a slick retro-futuristic arcade game that will make your
17brain melt. You've been warned. From beautiful cityscapes and ocean
18roads to exploding enemy spaceships, Neon Drive has it all.
19#+end_quote
20
21* Gameplay
22
23The game holds true to the
24[[https://en.wikipedia.org/wiki/Retrofuturism][retro-futurism]] style,
25including chrome female robots, pixelated arcade machines, and
26[[https://teddit.net/r/outrun/][outrun]] aesthetics.
27
28Each level of the game is shown as a separate arcade machine. Each
29arcade machine lets you play on Normal, Hard, Insane, Practice, and Free
30Run. To beat each arcade, you must reach the end of the level without
31crashing your car into the various obstacles on the course. Basic levels
32let you move left or right to avoid blocks in the road. Later levels put
33you through other tests, such as dodging traffic or blasting asteroids.
34
35The game uses synthwave music to keep you on track to make the correct
36moves by timing the beats of the songs to the correct moves on the
37screen. It reminds me of the early Guitar Hero games, as well as mobile
38apps like VOEZ - repetition and staying on-beat is the only way to win.
39
40* In-Game Screenshots
41
42Taking a look at the main menu, you can see that Neon Drive plays into
43every stereotype you can think of around retro-futuristic, synthwave
44arcades (in a good way).
45
46Once you get into the first level, we see that the choice of car fits
47right in with the stereotypical cars of the 80s, like the
48[[https://en.wikipedia.org/wiki/DMC_DeLorean][DeLorean]] or the
49[[https://en.wikipedia.org/wiki/Ferrari_F40][Ferrari F40]]. Each new
50level comes with new color schemes and cars, so you should never get
51tired of the aesthetic.
52
53Personally, I love the orange and blue colors used in level 2:
54
55If you're the competitive type and getting 100% on all arcade machines
56isn't enough, there are leaderboards for the regular part of the game,
57and the endurance game mode.
58
59* Other Suggestions
60
61Neon Drive sits nicely within the well-founded cult genre of Outrun.
62Other games that I've enjoyed in this same spectrum are:
63
64- [[https://store.steampowered.com/app/233270/Far_Cry_3__Blood_Dragon/][Far
65 Cry 3: Blood Dragon]]
66- [[https://store.steampowered.com/app/1239690/Retrowave/][Retrowave]]
67- [[https://store.steampowered.com/app/732810/Slipstream/][Slipstream]]
68
69Although these games aren't necessarily in the same genre, they do have
70aspects that place them close enough to interest gamers that enjoyed
71Neon Drive:
72
73- [[https://store.steampowered.com/app/311800/Black_Ice/][Black Ice]]
74- [[https://store.steampowered.com/app/746850/Cloudpunk/][Cloudpunk]]
75- [[https://store.steampowered.com/app/1222680/Need_for_Speed_Heat/][Need
76 for Speed: Heat]]
77- [[https://store.steampowered.com/app/1019310/VirtuaVerse/][VirtuaVerse]]
78
79Of course, if all you really care about is the arcade aspect of these
80games, you can check out the
81[[https://store.steampowered.com/app/400020/Atari_Vault/][Atari Vault]]
82or any of the other classic games sold on Steam by companies like Namco,
83Atari. For something like Nintendo, you'd have to settle for buying used
84classic consoles or delve into the world of emulation.
content/blog/2020-12-29-zork.md deleted −80
@@ -1,80 +0,0 @@
1+++
2date = 2020-12-29
3title = "Zork: Let's Explore a Classic"
4description = ""
5draft = false
6+++
7
8# Download (Free)
9
10Before we get into the game itself, you should know that you can download Zork
11for free from Infocom's [download
12page](http://infocom-if.org/downloads/downloads.html). So feel free to boot it
13up and take a ride back to the 1980s with this masterpiece.
14
15# Game Description
16
17Zork is an interactive, text-based computer game originally released in 1980.
18This series, split into three separate games, introduced a robust and
19sophisticated text parser to gamers. People were largely used to the simple
20commands used in the popular game [Colossal Cave
21Adventure](https://en.wikipedia.org/wiki/Colossal_Cave_Adventure), but Zork
22allowed users to send more complex commands that included prepositions and
23conjunctions.
24
25Zork tracks your score as you explore the map, find tools, and collect trophy
26items (e.g., a jewel-encrusted egg). When you place your trophy items in the
27trophy case found in the Living Room area, you gain score points. Collecting the
28Twenty Treasures of Zork and placing them within the trophy case wins the game.
29However, you must explore the map, solve puzzles, and avoid being eaten by a
30grue to collect these treasures.
31
32# The Map
33
34Since Zork is a vast and complex game, it helps to have a map as you explore and
35collect your trophies. However, if you want to play the game as it was truly
36intended, you should try to play it without using the map.
37
38![Zork Map](https://img.cleberg.net/blog/20201229-zork/zork_map.jpg)
39
40_[Map Source](https://www.filfre.net/2012/01/exploring-zork-part-1/)_
41
42# In-Game Screenshots
43
44After playing the game (for the first time ever) for several weeks around 2014,
45I was finally able to beat the game with some online help to find the last
46couple items. As I was writing this post, I installed the game again to grab
47some screenshots to show off the true glory of this game. As noted in [Jimmy
48Maher's playthrough](https://www.filfre.net/2012/01/exploring-zork-part-1/), the
49original Zork games looked quite a bit different due to the older hardware of
50computers like the Apple II and multiple bug fixes that Infocom pushed out after
51the game's initial release. My play-through uses the [Zork
52Anthology](https://store.steampowered.com/app/570580/Zork_Anthology/) version,
53which utilizes DOSBox on Windows.
54
55The first screenshot here shows the introductory information, which doesn't
56include instructions of any kind for the player. If you haven't played text
57adventures before, try to use simple commands like "go west," "look around," or
58"hit troll with elvish sword."
59
60In this second screenshot, we see the player has entered the house and found the
61trophy case in the living room. The lantern and sword in this room allow the
62player to explore dark areas and attack enemies. If you don't use the lantern,
63you won't be able to see anything in dark areas, and you may be eaten by a grue.
64
65Finally, we see that the player has found the first treasure: a jewel-encrusted
66egg. These treasures can be taken back to the house and placed in the trophy
67case or carried until you feel like you want to put things away.
68
69# Conclusion
70
71It's been quite a few years since I first played Zork, but I clearly remember
72the late nights and bloodshot eyes that helped me find all the treasures. This
73game is well worth the time and effort, even though the text-based aspect may be
74off-putting to gamers who didn't have to grow up playing games without graphics.
75However, I believe that the strategy and skills learned in early video games
76like Zork can actually help you, even when playing newer games.
77
78If you do decide to play Zork, you can download Zork I, II, and III from
79Infocom's [download page](http://infocom-if.org/downloads/downloads.html) for
80free or search the internet for an online version.
content/blog/2020-12-29-zork.org added +86
@@ -0,0 +1,86 @@
1#+date: <2020-12-29>
2#+title: Zork: Let's Explore a Classic
3#+description:
4
5
6* Download (Free)
7
8Before we get into the game itself, you should know that you can
9download Zork for free from Infocom's
10[[http://infocom-if.org/downloads/downloads.html][download page]]. So
11feel free to boot it up and take a ride back to the 1980s with this
12masterpiece.
13
14* Game Description
15
16Zork is an interactive, text-based computer game originally released
17in 1980. This series, split into three separate games, introduced a
18robust and sophisticated text parser to gamers. People were largely used
19to the simple commands used in the popular game
20[[https://en.wikipedia.org/wiki/Colossal_Cave_Adventure][Colossal Cave
21Adventure]], but Zork allowed users to send more complex commands that
22included prepositions and conjunctions.
23
24Zork tracks your score as you explore the map, find tools, and collect
25trophy items (e.g., a jewel-encrusted egg). When you place your trophy
26items in the trophy case found in the Living Room area, you gain score
27points. Collecting the Twenty Treasures of Zork and placing them within
28the trophy case wins the game. However, you must explore the map, solve
29puzzles, and avoid being eaten by a grue to collect these treasures.
30
31* The Map
32
33Since Zork is a vast and complex game, it helps to have a map as you
34explore and collect your trophies. However, if you want to play the game
35as it was truly intended, you should try to play it without using the
36map.
37
38#+caption: Zork Map
39[[https://img.cleberg.net/blog/20201229-zork/zork_map.jpg]]
40
41/[[https://www.filfre.net/2012/01/exploring-zork-part-1/][Map Source]]/
42
43* In-Game Screenshots
44
45After playing the game (for the first time ever) for several weeks
46around 2014, I was finally able to beat the game with some online help
47to find the last couple items. As I was writing this post, I installed
48the game again to grab some screenshots to show off the true glory of
49this game. As noted in
50[[https://www.filfre.net/2012/01/exploring-zork-part-1/][Jimmy Maher's
51playthrough]], the original Zork games looked quite a bit different due
52to the older hardware of computers like the Apple II and multiple bug
53fixes that Infocom pushed out after the game's initial release. My
54play-through uses the
55[[https://store.steampowered.com/app/570580/Zork_Anthology/][Zork
56Anthology]] version, which utilizes DOSBox on Windows.
57
58The first screenshot here shows the introductory information, which
59doesn't include instructions of any kind for the player. If you haven't
60played text adventures before, try to use simple commands like "go
61west," "look around," or "hit troll with elvish sword."
62
63In this second screenshot, we see the player has entered the house and
64found the trophy case in the living room. The lantern and sword in this
65room allow the player to explore dark areas and attack enemies. If you
66don't use the lantern, you won't be able to see anything in dark areas,
67and you may be eaten by a grue.
68
69Finally, we see that the player has found the first treasure: a
70jewel-encrusted egg. These treasures can be taken back to the house and
71placed in the trophy case or carried until you feel like you want to put
72things away.
73
74* Conclusion
75
76It's been quite a few years since I first played Zork, but I clearly
77remember the late nights and bloodshot eyes that helped me find all the
78treasures. This game is well worth the time and effort, even though the
79text-based aspect may be off-putting to gamers who didn't have to grow
80up playing games without graphics. However, I believe that the strategy
81and skills learned in early video games like Zork can actually help you,
82even when playing newer games.
83
84If you do decide to play Zork, you can download Zork I, II, and III from
85Infocom's [[http://infocom-if.org/downloads/downloads.html][download
86page]] for free or search the internet for an online version.
content/blog/2021-01-01-seum.md deleted −71
@@ -1,71 +0,0 @@
1+++
2date = 2021-01-01
3title = "SEUM: Spreedrunners from Hell"
4description = ""
5draft = false
6+++
7
8# Game Description
9
10[SEUM: Speedrunners from
11Hell](https://store.steampowered.com/app/457210/SEUM_Speedrunners_from_Hell/) is
12an incredibly fast-paced mashup of a puzzle game and a free-running game.
13Created by [Pine Studio](https://pinestudio.co) in early 2016 and first released
14as a [demo on
15GameJolt](https://gamejolt.com/games/seum-speedrunners-from-hell-demo/154868),
16this game was successfully green-lit on Steam and has amassed a cult following
17on multiple consoles.
18
19Here's the game description straight from the developers:
20
21> Like a bastard child of Quake 3 and Super Meat Boy, SEUM: Speedrunners from
22> Hell is truly hardcore and focuses on speed and fast reaction.
23
24# Story
25
26SEUM does a great job setting the stage when you open the game for the first
27time, playing an animated video in the form of comic book images. You see Marty,
28the protagonist, sitting around drinking his beer as Satan busts down the door,
29cuts his hand off, and steals all his beer and bacon. As Satan is leaving, Marty
30whips a vinyl record across the room and cuts off one of Satan's hands. This
31hand is what allows you to use all the powers in the game.
32
33# Gameplay
34
35To accomplish each level, you will need to get to the final blue portal under a
36certain time limit. You can beat a level by getting to the portal before the
37time meter runs out or "Dominate" a level by beating it within a shorter time
38limit (noted by a bright red/orange color in the time meter).
39
40The portal is usually set behind locked gates, protected by dangerous defenses,
41or in hard-to-reach places. To reach the portal, you'll need to go through any
42existing orange portals, light all yellow beacons, avoid things like fireballs
43and blades, or use any satanic power orbs lying around. These special abilities
44include:
45
46- Gravity
47- Teleport
48- Rewind
49- Spawn platform
50- Roar (DLC)
51- Rocket (DLC)
52- Shadow world (DLC)
53
54For the main storyline, there are nine floors to beat. Each floor contains nine
55regular levels, one boss level, and one bonus level; although you don't
56technically need to beat all levels to advance to the next floor.
57# In-Game Screenshots
58
59The main menu gives you plenty of options for tuning your system, playing main
60levels, playing the DLC, or exploring the other game modes.
61
62Once you enter a level, you can look around and strategize before starting.
63Clicking any button will start the menu, and you'll have to restart if you die.
64One of the great things about SEUM is that it has great keyboard shortcuts. You
65can quickly smash `R` to restart the level or `M` to return to the level menu.
66
67Once you're able to win a level, you'll see the high scores and can watch
68replays of the best scores.
69
70Each regular level also contains a beer in a disguised location that may take
71some out-of-the-box thinking.
content/blog/2021-01-01-seum.org added +73
@@ -0,0 +1,73 @@
1#+date: <2021-01-01>
2#+title: SEUM: Spreedrunners from Hell
3#+description:
4
5
6* Game Description
7
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
14amassed a cult following on multiple consoles.
15
16Here's the game description straight from the developers:
17
18#+begin_quote
19Like a bastard child of Quake 3 and Super Meat Boy, SEUM: Speedrunners
20from Hell is truly hardcore and focuses on speed and fast reaction.
21#+end_quote
22
23* Story
24
25SEUM does a great job setting the stage when you open the game for the
26first time, playing an animated video in the form of comic book images.
27You see Marty, the protagonist, sitting around drinking his beer as
28Satan busts down the door, cuts his hand off, and steals all his beer
29and bacon. As Satan is leaving, Marty whips a vinyl record across the
30room and cuts off one of Satan's hands. This hand is what allows you to
31use all the powers in the game.
32
33* Gameplay
34
35To accomplish each level, you will need to get to the final blue portal
36under a certain time limit. You can beat a level by getting to the
37portal before the time meter runs out or "Dominate" a level by beating
38it within a shorter time limit (noted by a bright red/orange color in
39the time meter).
40
41The portal is usually set behind locked gates, protected by dangerous
42defenses, or in hard-to-reach places. To reach the portal, you'll need
43to go through any existing orange portals, light all yellow beacons,
44avoid things like fireballs and blades, or use any satanic power orbs
45lying around. These special abilities include:
46
47- Gravity
48- Teleport
49- Rewind
50- Spawn platform
51- Roar (DLC)
52- Rocket (DLC)
53- Shadow world (DLC)
54
55For the main storyline, there are nine floors to beat. Each floor
56contains nine regular levels, one boss level, and one bonus level;
57although you don't technically need to beat all levels to advance to the
58next floor. # In-Game Screenshots
59
60The main menu gives you plenty of options for tuning your system,
61playing main levels, playing the DLC, or exploring the other game modes.
62
63Once you enter a level, you can look around and strategize before
64starting. Clicking any button will start the menu, and you'll have to
65restart if you die. One of the great things about SEUM is that it has
66great keyboard shortcuts. You can quickly smash =R= to restart the level
67or =M= to return to the level menu.
68
69Once you're able to win a level, you'll see the high scores and can
70watch replays of the best scores.
71
72Each regular level also contains a beer in a disguised location that may
73take some out-of-the-box thinking.
content/blog/2021-01-04-fediverse.md deleted −100
@@ -1,100 +0,0 @@
1+++
2date = 2021-01-04
3title = "A Simple Guide to the Fediverse"
4description = ""
5draft = false
6+++
7
8# What is the Fediverse?
9
10The fediverse is a federated universe of servers commonly used for sharing
11content, like social media. So, instead of having to rely on a single
12organization to run the server (e.g. Facebook), the fediverse is a giant
13collection of servers across the world, owned by many people and organizations.
14
15Take a look at this depiction of a federated network. Each server in this photo
16is owned and run by different administrators/owners. Federated networks are best
17explained as email servers: you have an email account that exists on a server
18(e.g. Outlook), your friend has an account on a different server (e.g. GMail),
19and another friend has an account on a third server (e.g. ProtonMail). All three
20of you can talk and communicate back and forth without having to be on the same
21server. However, responsible email admins are there to set rules and control the
22traffic going in/out of the server.
23
24The main objective of this architecture is to decentralize the control within
25the internet connections. For example, if you run your own Mastodon instance,
26you and your users can't be censored or impacted in any way by authorities of
27another Mastodon instance. Some users have praised these features due to recent
28criticism of popular social media websites that may be over-censoring their
29users.
30
31This 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 I create
33a Mastodon instance and get a ton of users to sign up, I can shut the server
34down at any time. That means you're at risk of losing the content you've created
35unless you back it up, or the server backs it up for you. Also, depending on the
36software used (e.g. Mastodon, Pixelfed, etc.), censorship may still be an issue
37if the server admins decide they want to censor their users. Now, censorship
38isn't always a bad thing and can even benefit the community as a whole, but
39you'll want to determine which servers align with your idea of proper
40censorship.
41
42However, these are risks that we take when we sign up for any online platform.
43Whatever your reason is for trying out federated social networks, they are part
44of the future of the internet. However, the popularity of these services is
45still yet to be determined, especially with the increased difficulty
46understanding and signing up for these platforms. Perhaps increased regulation
47and litigation against current social media sites will push more users into the
48fediverse.
49
50# Federated Alternatives to Popular Sites
51
52The list below is a small guide that will show you federated alternatives to
53current popular websites. There are many more out there, so go and explore: you
54might just find the perfect home.
55
56## Reddit
57
58- [Lemmy](https://lemmy.ml/instances)
59
60## Twitter/Facebook/Tumblr
61
62- [Mastodon](https://joinmastodon.org)
63- [Diaspora](https://diasporafoundation.org)
64- [Friendica](https://friendi.ca)
65- [GNU Social](https://gnusocial.network)
66- [Pleroma](https://pleroma.social)
67
68## Instagram
69
70- [Pixelfed](https://pixelfed.org)
71
72## Slack/Discord
73
74- [Matrix](https://element.io)
75
76## Youtube/Vimeo
77
78- [Peertube](https://joinpeertube.org)
79
80## Spotify/Soundcloud
81
82- [Funkwhale](https://funkwhale.audio)
83
84## Podcasting
85
86- [Pubcast](https://pubcast.pub)
87
88## Medium/Blogger
89
90- [WriteFreely](https://writefreely.org)
91
92# Get Started
93
94The best way to get started is to simply sign up and learn as you go. If you're
95comfortable signing up through a Mastodon, Pleroma, or Friendica server, here is
96[a list of themed servers](https://fediverse.party/en/portal/servers) to choose
97from. If you're looking for something else, try a web search for a federated
98alternative to your favorite sites.
99
100Find a server that focuses on your passions and start there!
content/blog/2021-01-04-fediverse.org added +100
@@ -0,0 +1,100 @@
1#+date: <2021-01-04>
2#+title: A Simple Guide to the Fediverse
3#+description:
4
5
6* What is the Fediverse?
7
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
22traffic going in/out of the server.
23
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.
30
31This 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.
49
50* Federated Alternatives to Popular Sites
51
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.
55
56** Reddit
57
58- [[https://lemmy.ml/instances][Lemmy]]
59
60** Twitter/Facebook/Tumblr
61
62- [[https://joinmastodon.org][Mastodon]]
63- [[https://diasporafoundation.org][Diaspora]]
64- [[https://friendi.ca][Friendica]]
65- [[https://gnusocial.network][GNU Social]]
66- [[https://pleroma.social][Pleroma]]
67
68** Instagram
69
70- [[https://pixelfed.org][Pixelfed]]
71
72** Slack/Discord
73
74- [[https://element.io][Matrix]]
75
76** Youtube/Vimeo
77
78- [[https://joinpeertube.org][Peertube]]
79
80** Spotify/Soundcloud
81
82- [[https://funkwhale.audio][Funkwhale]]
83
84** Podcasting
85
86- [[https://pubcast.pub][Pubcast]]
87
88** Medium/Blogger
89
90- [[https://writefreely.org][WriteFreely]]
91
92* Get Started
93
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,
98try a web search for a federated alternative to your favorite sites.
99
100Find a server that focuses on your passions and start there!
content/blog/2021-01-07-ufw.md deleted −217
@@ -1,217 +0,0 @@
1+++
2date = 2021-01-07
3title = "Secure Your Network with the Uncomplicated Firewall (ufw)"
4description = ""
5draft = false
6+++
7
8# Uncomplicated Firewall
9
10Uncomplicated Firewall, also known as ufw, is a convenient and beginner-friendly
11way to enforce OS-level firewall rules. For those who are hosting servers or any
12device that is accessible to the world (i.e., by public IP or domain name), it's
13critical that a firewall is properly implemented and active.
14
15Ufw is available by default in all Ubuntu installations after 8.04 LTS. For
16other distributions, you can look to install ufw or check if there are
17alternative firewalls installed already. There are usually alternatives
18available, such as Fedora's `firewall` and the package available on most
19distributions: `iptables`. Ufw is considered a beginner-friendly front-end to
20iptables.
21
22[Gufw](https://gufw.org) is available as a graphical user interface (GUI)
23application for users who are uncomfortable setting up a firewall through a
24terminal.
25
26# Getting Help
27
28If you need help figuring out commands, remember that you can run the `--help`
29flag to get a list of options.
30
31```sh
32sudo ufw --help
33```
34
35# Set Default State
36
37The proper way to run a firewall is to set a strict default state and slowly
38open up ports that you want to allow. This helps prevent anything malicious from
39slipping through the cracks. The following command prevents all incoming traffic
40(other than the rules we specify later), but you can also set this for outgoing
41connections, if necessary.
42
43```sh
44sudo ufw default deny incoming
45```
46
47You should also allow outgoing traffic if you want to allow the device to
48communicate back to you or other parties. For example, media servers like Plex
49need to be able to send out data related to streaming the media.
50
51```sh
52sudo ufw default allow outgoing
53```
54
55# Adding Port Rules
56
57Now that we've disabled all incoming traffic by default, we need to open up some
58ports (or else no traffic would be able to come in). If you need to be able to
59`ssh` into the machine, you'll need to open up port 22.
60
61```sh
62sudo ufw allow 22
63```
64
65You can also issue more restrictive rules. The following rule will allow `ssh`
66connections only from machines on the local subnet.
67
68```sh
69sudo ufw allow proto tcp from 192.168.0.0/24 to any port 22
70```
71
72If you need to set a rule that isn't tcp, just append your connection type to
73the end of the rule.
74
75```sh
76sudo ufw allow 1900/udp
77```
78
79# Enable ufw
80
81Now that the firewall is configured and ready to go, you can enable the
82firewall.
83
84```sh
85sudo ufw enable
86```
87
88A restart may be required for the firewall to begin operating.
89
90```sh
91sudo reboot now
92```
93
94# Checking Status
95
96Now that the firewall is enabled, let's check and see what the rules look like.
97
98```sh
99sudo ufw status numbered
100```
101
102```txt
103Status: active
104
105 To Action From
106 -- ------ ----
107[ 1] 22 ALLOW IN Anywhere
108[ 2] 22 (v6) ALLOW IN Anywhere (v6)
109```
110
111# Deleting Rules
112
113If you need to delete a rule, you need to know the number associated with that
114rule. Let's delete the first rule in the table above. You'll be asked to confirm
115the deletion as part of this process.
116
117```sh
118sudo ufw delete 1
119```
120
121# Managing App Rules
122
123Luckily, there's a convenient way for installed applications to create files
124that ufw can easily implement so that you don't have to search and find which
125ports your application requires. To see if your device has any applications with
126pre-installed ufw rules, execute the following command:
127
128```sh
129sudo ufw app list
130```
131
132The results should look something like this:
133
134```txt
135Available applications:
136 OpenSSH
137 Samba
138 plexmediaserver
139 plexmediaserver-all
140 plexmediaserver-dlna
141```
142
143If you want to get more information on a specific app rule, use the `info`
144command.
145
146```sh
147sudo ufw app info plexmediaserver-dlna
148```
149
150You'll get a blurb of info back like this:
151
152```txt
153Profile: plexmediaserver-dlna
154Title: Plex Media Server (DLNA)
155Description: The Plex Media Server (additional DLNA capability only)
156
157Ports:
158 1900/udp
159 32469/tcp
160```
161
162You can add or delete app rules the same way that you'd add or delete specific
163port rules.
164
165```sh
166sudo ufw allow plexmediaserver-dlna
167```
168
169```sh
170sudo ufw delete RULE|NUM
171```
172
173# Creating App Rules
174
175If you'd like to create you own app rule, you'll need to create a file in the
176`/etc/ufw/applications.d` directory. Within the file you create, you need to
177make sure the content is properly formatted.
178
179For example, here are the contents my `plexmediaserver` file, which creates
180three distinct app rules for ufw:
181
182```config
183[plexmediaserver]
184title=Plex Media Server (Standard)
185description=The Plex Media Server
186ports=32400/tcp|3005/tcp|5353/udp|8324/tcp|32410:32414/udp
187
188[plexmediaserver-dlna]
189title=Plex Media Server (DLNA)
190description=The Plex Media Server (additional DLNA capability only)
191ports=1900/udp|32469/tcp
192
193[plexmediaserver-all]
194title=Plex Media Server (Standard + DLNA)
195description=The Plex Media Server (with additional DLNA capability)
196ports=32400/tcp|3005/tcp|5353/udp|8324/tcp|32410:32414/udp|1900/udp|32469/tcp
197```
198
199So, if I wanted to create a custom app rule called "mycustomrule," I'd create a
200file and add my content like this:
201
202```sh
203sudo nano /etc/ufw/applications.d/mycustomrule
204```
205
206```config
207[mycustomrule]
208title=My Custom Rule
209description=This is a temporary ufw app rule.
210ports=88/tcp|9100/udp
211```
212
213Then, I would just enable this rule in ufw.
214
215```sh
216sudo ufw allow mycustomrule
217```
content/blog/2021-01-07-ufw.org added +219
@@ -0,0 +1,219 @@
1#+date: <2021-01-07>
2#+title: Secure Your Network with the Uncomplicated Firewall (ufw)
3#+description:
4
5
6* Uncomplicated Firewall
7
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.
24
25* Getting Help
26
27If you need help figuring out commands, remember that you can run the
28=--help= flag to get a list of options.
29
30#+begin_src sh
31sudo ufw --help
32#+end_src
33
34* Set Default State
35
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.
41
42#+begin_src sh
43sudo ufw default deny incoming
44#+end_src
45
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.
50
51#+begin_src sh
52sudo ufw default allow outgoing
53#+end_src
54
55* Adding Port Rules
56
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.
60
61#+begin_src sh
62sudo ufw allow 22
63#+end_src
64
65You can also issue more restrictive rules. The following rule will allow
66=ssh= connections only from machines on the local subnet.
67
68#+begin_src sh
69sudo ufw allow proto tcp from 192.168.0.0/24 to any port 22
70#+end_src
71
72If you need to set a rule that isn't tcp, just append your connection
73type to the end of the rule.
74
75#+begin_src sh
76sudo ufw allow 1900/udp
77#+end_src
78
79* Enable ufw
80
81Now that the firewall is configured and ready to go, you can enable the
82firewall.
83
84#+begin_src sh
85sudo ufw enable
86#+end_src
87
88A restart may be required for the firewall to begin operating.
89
90#+begin_src sh
91sudo reboot now
92#+end_src
93
94* Checking Status
95
96Now that the firewall is enabled, let's check and see what the rules
97look like.
98
99#+begin_src sh
100sudo ufw status numbered
101#+end_src
102
103#+begin_src txt
104Status: active
105
106 To Action From
107 -- ------ ----
108[ 1] 22 ALLOW IN Anywhere
109[ 2] 22 (v6) ALLOW IN Anywhere (v6)
110#+end_src
111
112* Deleting Rules
113
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.
117
118#+begin_src sh
119sudo ufw delete 1
120#+end_src
121
122* Managing App Rules
123
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:
129
130#+begin_src sh
131sudo ufw app list
132#+end_src
133
134The results should look something like this:
135
136#+begin_src txt
137Available applications:
138 OpenSSH
139 Samba
140 plexmediaserver
141 plexmediaserver-all
142 plexmediaserver-dlna
143#+end_src
144
145If you want to get more information on a specific app rule, use the
146=info= command.
147
148#+begin_src sh
149sudo ufw app info plexmediaserver-dlna
150#+end_src
151
152You'll get a blurb of info back like this:
153
154#+begin_src txt
155Profile: plexmediaserver-dlna
156Title: Plex Media Server (DLNA)
157Description: The Plex Media Server (additional DLNA capability only)
158
159Ports:
160 1900/udp
161 32469/tcp
162#+end_src
163
164You can add or delete app rules the same way that you'd add or delete
165specific port rules.
166
167#+begin_src sh
168sudo ufw allow plexmediaserver-dlna
169#+end_src
170
171#+begin_src sh
172sudo ufw delete RULE|NUM
173#+end_src
174
175* Creating App Rules
176
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.
180
181For example, here are the contents my =plexmediaserver= file, which
182creates three distinct app rules for ufw:
183
184#+begin_src config
185[plexmediaserver]
186title=Plex Media Server (Standard)
187description=The Plex Media Server
188ports=32400/tcp|3005/tcp|5353/udp|8324/tcp|32410:32414/udp
189
190[plexmediaserver-dlna]
191title=Plex Media Server (DLNA)
192description=The Plex Media Server (additional DLNA capability only)
193ports=1900/udp|32469/tcp
194
195[plexmediaserver-all]
196title=Plex Media Server (Standard + DLNA)
197description=The Plex Media Server (with additional DLNA capability)
198ports=32400/tcp|3005/tcp|5353/udp|8324/tcp|32410:32414/udp|1900/udp|32469/tcp
199#+end_src
200
201So, if I wanted to create a custom app rule called "mycustomrule," I'd
202create a file and add my content like this:
203
204#+begin_src sh
205sudo nano /etc/ufw/applications.d/mycustomrule
206#+end_src
207
208#+begin_src config
209[mycustomrule]
210title=My Custom Rule
211description=This is a temporary ufw app rule.
212ports=88/tcp|9100/udp
213#+end_src
214
215Then, I would just enable this rule in ufw.
216
217#+begin_src sh
218sudo ufw allow mycustomrule
219#+end_src
content/blog/2021-02-19-macos.md deleted −197
@@ -1,197 +0,0 @@
1+++
2date = 2021-02-19
3title = "macOS: Testing Out a New OS"
4description = ""
5draft = false
6+++
7
8# Diving into macOS
9
10After spending nearly 15 years working with Windows and 8 years on Linux, I have
11experienced macOS for the first time. By chance, my spouse happened to buy a new
12MacBook and gifted me their 2013 model. Of course, I still consider my Linux
13desktop to be my daily driver and keep Windows around for gaming needs, but over
14the past week I've found myself using the MacBook more and more for things that
15don't require gaming specs or advanced dev tools.
16
17# Initial Thoughts
18
19Before I move on to the technical aspects of my set-up, I want to take some time
20and express my thoughts on the overall OS.
21
22As expected, the initial computer setup is a breeze with Mac's guided GUI
23installer.
24
25The desktop itself reminds me of GNOME more than anything else I've seen: even
26Pantheon from [ElementaryOS](https://elementary.io/), which people commonly
27refer to as the closest Linux distro to macOS. The desktop toolbar is great and
28far surpasses the utility of the GNOME toolbar due to the fact that the
29extensions and icons _actually work_. I launch macOS and immediately see my
30shortcuts for Tresorit, Bitwarden, and Mullvad pop up as the computer loads.
31
32Even further, the app dock is very useful and will be yet another familiarity
33for GNOME users. I know many people like panels instead of docks, but I've
34always found docks to have a more pleasing UI. However, I had to disable the
35"Show recent applications in Dock" preference; I can't stand items taking up
36precious screen space if I'm not currently using them. On that same note, it's
37taking me some time to get use to the fact that I have to manually quit an app
38or else it will still stay open/active in the dock, even if I've closed out all
39windows for that app (e.g. Firefox).
40
41Overall, I'm having a lot of fun and for users who spend a large majority of
42their time performing basic tasks like web browsing, writing, watching media,
43etc., macOS is a fantastic option.
44
45The rest of this post explains the technicalities of how I set up my CLI
46environment to make me feel more at-home, similar to the environments I set up
47on Fedora, Ubuntu, etc.
48
49# Making it Feel Like Home
50
51If you're someone who uses Linux primarily, no doubt your first thought when
52booting macOS will be the same as mine was: "Where is the terminal and how do I
53set up my favorite utilities?"
54
55Luckily, macOS hasn't completely hidden away the development tools from the
56average user. You can easily find the Terminal app in the Launchpad area, but
57it's probably not what you're used to. I was surprised (and happy) to see that
58the default shell is `zsh`, the shell I use on all of my Linux distros. However,
59the commands are not the same - even the ones you may think are native to the
60shell. Commands like `dir` do not exist, so other native commands like `ls -la`
61or `pwd` are more useful here.
62
63With only a few minutes of installing and tweaking a few packages, I was able to
64recreate a terminal environment that I feel very comfortable using. See the
65image below for a preview of the iTerm2 app with a split view between my macOS
66desktop shell and an SSH session into my server.
67
68# Xcode
69
70My first step was to search the web for any hints on how to get `zsh` back up to
71the state I like, with extensions, themes, etc. My first step was to install the
72CLI tools for [Xcode](https://developer.apple.com/xcode/), Apple's suite of
73development tools.
74
75```sh
76sudo xcode-select -r
77```
78
79```sh
80sudo xcode-select --install
81```
82
83# Homebrew
84
85Next up is to install [Homebrew](https://brew.sh), a nifty package manager for
86macOS.
87
88```sh
89/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
90```
91
92I ran into a permission error when installing Homebrew:
93
94```sh
95Error: Failed to link all completions, docs and manpages:
96 Permission denied @ rb_file_s_symlink - (../../../Homebrew/completions/zsh/_brew, /usr/local/share/zsh/site-functions/_brew)
97Failed during: /usr/local/bin/brew update --force --quiet
98```
99
100I found that the following permission modification worked like a charm. However,
101I noted that some users online discussed the fact that this solution may not
102work if your system has multiple users who use Homebrew.
103
104```sh
105sudo chown -R $(whoami) $(brew --prefix)/*
106```
107
108Next up is to ensure Homebrew is updated and cleaned.
109
110```sh
111brew update
112```
113
114```sh
115brew cleanup
116```
117
118# iTerm2
119
120Now that I've installed the basic utilities for development, I moved onto
121installing iTerm2, a much better terminal than the default.
122
123```sh
124brew install --cask iterm2
125```
126
127I also used the `Make iTerm2 Default Term` and `Install Shell Integration`
128options in the iTerm2 application menu to make sure I don't run into any issues
129later on with different terminals.
130
131We will also install `zsh` so we can use it in iTerm2.
132
133```sh
134brew install zsh
135```
136
137# Oh-My-Zsh
138
139I've shown the great aspects of [Oh My Zsh](https://ohmyz.sh) in other blog
140posts, so I'll skip over that speech for now. Simply install it and run an
141update.
142
143```sh
144sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
145```
146
147```sh
148omz update
149```
150
151Finally, restart the iTerm2 application to ensure all changes go into effect.
152
153# Oh-My-Zsh Themes
154
155Let's change the theme of the terminal to make it a little more friendly.
156
157```sh
158open ~/.zshrc
159```
160
161The third section of this file should contain a line like the code below. Change
162that theme to [any theme you
163want](https://github.com/ohmyzsh/ohmyzsh/wiki/Themes), save the file, and exit.
164
165```sh
166ZSH_THEME="af-magic"
167```
168
169After changing the `.zshrc` file, you'll need to close your terminal and re-open
170it to see the changes. Optionally, just open a new tab if you're using iTerm2,
171and you'll see the new shell config.
172
173# Oh-My-Zsh Plugins
174
175Of course, my customization of `zsh` would not be complete without
176[zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions). This
177will bring up commands you've run in the past as you type them. For example, if
178you've run `ssh user@192.168.1.99` before, the terminal will show this command
179as soon as you start typing it (e.g. `zsh u`), and you can hit the right arrow
180to autocomplete the command.
181
182```sh
183git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
184```
185
186```sh
187open ~/.zshrc
188```
189
190```sh
191# Scroll down the script and edit this line to add zsh-autosuggestions
192plugins=(git zsh-autosuggestions)
193```
194
195Remember: After changing the `.zshrc` file, you'll need to close your terminal
196and re-open it to see the changes. Optionally, just open a new tab if you're
197using iTerm2, and you'll see the new shell config.
content/blog/2021-02-19-macos.org added +203
@@ -0,0 +1,203 @@
1#+date: <2021-02-19>
2#+title: macOS: Testing Out a New OS
3#+description:
4
5
6* Diving into macOS
7
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.
15
16* Initial Thoughts
17
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.
49
50* Making it Feel Like Home
51
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?"
55
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.
63
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.
68
69* Xcode
70
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.
76
77#+begin_src sh
78sudo xcode-select -r
79#+end_src
80
81#+begin_src sh
82sudo xcode-select --install
83#+end_src
84
85* Homebrew
86
87Next up is to install [[https://brew.sh][Homebrew]], a nifty package
88manager for macOS.
89
90#+begin_src sh
91/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
92#+end_src
93
94I ran into a permission error when installing Homebrew:
95
96#+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
100#+end_src
101
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.
106
107#+begin_src sh
108sudo chown -R $(whoami) $(brew --prefix)/*
109#+end_src
110
111Next up is to ensure Homebrew is updated and cleaned.
112
113#+begin_src sh
114brew update
115#+end_src
116
117#+begin_src sh
118brew cleanup
119#+end_src
120
121* iTerm2
122
123Now that I've installed the basic utilities for development, I moved
124onto installing iTerm2, a much better terminal than the default.
125
126#+begin_src sh
127brew install --cask iterm2
128#+end_src
129
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.
133
134We will also install =zsh= so we can use it in iTerm2.
135
136#+begin_src sh
137brew install zsh
138#+end_src
139
140* Oh-My-Zsh
141
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.
145
146#+begin_src sh
147sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
148#+end_src
149
150#+begin_src sh
151omz update
152#+end_src
153
154Finally, restart the iTerm2 application to ensure all changes go into
155effect.
156
157* Oh-My-Zsh Themes
158
159Let's change the theme of the terminal to make it a little more
160friendly.
161
162#+begin_src sh
163open ~/.zshrc
164#+end_src
165
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.
170
171#+begin_src sh
172ZSH_THEME="af-magic"
173#+end_src
174
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.
178
179* Oh-My-Zsh Plugins
180
181Of 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.
187
188#+begin_src sh
189git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
190#+end_src
191
192#+begin_src sh
193open ~/.zshrc
194#+end_src
195
196#+begin_src sh
197# Scroll down the script and edit this line to add zsh-autosuggestions
198plugins=(git zsh-autosuggestions)
199#+end_src
200
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.
content/blog/2021-03-19-clone-github-repos.md → content/blog/2021-03-19-clone-github-repos.org renamed +64 −64
@@ -1,58 +1,57 @@
1+++
2date = 2021-03-19
3title = "How to Clonse All Repositories from a GitHub or Sourcehut Account"
4description = ""
5draft = false
6+++
1#+date: <2021-03-19>
2#+title: How to Clonse All Repositories from a GitHub or Sourcehut Account
3#+description:
74
8# Cloning from GitHub
95
10If you're like me and use a lot of different devices (and sometimes decide to
11just wipe your device and start with a new OS), you probably know the pain of
12cloning all your old code repositories down to your local file system.
6* Cloning from GitHub
137
14If you're using GitHub, you can easily clone all of your code back down in just
15seconds. First, create a bash script. I do so by opening a new file in `nano`,
16but you can use `gedit`, `vim`, or something else:
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.
1712
18```sh
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:
16
17#+begin_src sh
1918nano clone_github_repos.sh
20```
19#+end_src
2120
22Next, paste in the following information. Note that you can replace the word
23`users` in the first line with `orgs` and type an organization's name instead of
24a user's name.
21Next, paste in the following information. Note that you can replace the
22word =users= in the first line with =orgs= and type an organization's
23name instead of a user's name.
2524
26```sh
25#+begin_src sh
2726CNTX=users; NAME=YOUR-USERNAME; PAGE=1
2827curl "https://api.github.com/$CNTX/$NAME/repos?page=$PAGE&per_page=100" |
2928 grep -e 'git_url*' |
3029 cut -d " -f 4 |
3130 xargs -L1 git clone
32```
31#+end_src
3332
3433Finally, save the bash script and make it executable.
3534
36```sh
35#+begin_src sh
3736chmod a+x clone_github_repos.sh
38```
37#+end_src
3938
4039Now you can run the script and should see the cloning process begin.
4140
42```sh
41#+begin_src sh
4342./clone_github_repos.sh
44```
43#+end_src
4544
46# Cloning from Sourcehut
45* Cloning from Sourcehut
4746
48I haven't fully figured out how to directly incorporate Sourcehut's GraphQL API
49into a bash script yet, so this one will take two steps.
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.
5049
51First, log-in to Sourcehut and go to their [GraphQL playground for
52Git](https://git.sr.ht/graphql). Next, paste the following query into the left
53box:
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:
5453
55```sh
54#+begin_src sh
5655query {
5756 me {
5857 canonicalName
@@ -64,26 +63,27 @@ query {
6463 }
6564 }
6665}
67```
66#+end_src
6867
69The output on the right side will give you an object of all your repositories.
70Just grab that text and remove all the characters such as quotation marks and
71curly brackets. You will need a single-line list of space-separated values for
72the next step.
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.
7372
7473Now let's create the bash script:
7574
76```sh
75#+begin_src sh
7776nano clone_sourcehut_repos.sh
78```
77#+end_src
7978
80Next, paste the following bash script in with the list of repositories you
81obtained above and replace `your-username` with your username.
79Next, paste the following bash script in with the list of repositories
80you obtained above and replace =your-username= with your username.
8281
83Note that this uses the SSH-based Git cloning method (e.g. `git@git...`), so
84you'll need to ensure you have set up Sourcehut with your SSH key.
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.
8585
86```sh
86#+begin_src sh
8787repos=(repo1 repo2 repo3)
8888
8989# List all sub-directories in the current directory
@@ -92,36 +92,36 @@ do
9292 # Clone
9393 git clone git@git.sr.ht:~your-username/$repo
9494done
95```
95#+end_src
9696
9797Finally, save the bash script and make it executable.
9898
99```sh
99#+begin_src sh
100100chmod a+x clone_sourcehut_repos.sh
101```
101#+end_src
102102
103103Now you can run the script and should see the cloning process begin.
104104
105```sh
105#+begin_src sh
106106./clone_sourcehut_repos.sh
107```
107#+end_src
108108
109# Moving Repositories to a New Host
109* Moving Repositories to a New Host
110110
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:
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:
114114
115```sh
115#+begin_src sh
116116nano change_remote_urls.sh
117```
117#+end_src
118118
119Past the following information and be sure to change the URL information to
120whichever host you are moving to. For this example, I am looping through all of
121my cloned GitHub directories and changing them to Sourcehut (e.g.
122`<YOUR_NEW_REMOTE_URL>` -> `git@git.sr.ht:~myusername`).
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
122Sourcehut (e.g. =<YOUR_NEW_REMOTE_URL>= -> =git@git.sr.ht:~myusername=).
123123
124```sh
124#+begin_src sh
125125# List all sub-directories in the current directory
126126for dir in */
127127do
@@ -136,16 +136,16 @@ do
136136 # Go back to main directory
137137 cd ..
138138done
139```
139#+end_src
140140
141141Finally, save the bash script and make it executable.
142142
143```sh
143#+begin_src sh
144144chmod a+x change_remote_urls.sh
145```
145#+end_src
146146
147147Now you can run the script and should see the cloning process begin.
148148
149```sh
149#+begin_src sh
150150./change_remote_urls.sh
151```
151#+end_src
content/blog/2021-03-28-gemini-capsule.md deleted −171
@@ -1,171 +0,0 @@
1+++
2date = 2021-03-28
3title = "Launching a Gemini Capsule"
4description = ""
5draft = false
6+++
7
8# What is Gemini?
9
10[Gemini](https://gemini.circumlunar.space/) is an internet protocol introduced
11in June 2019 as an alternative to HTTP(S) or Gopher. In layman's terms, it's an
12alternative way to browse sites (called capsules) that requires a special
13browser. Since Gemini is not standardized as an internet standard, normal web
14browsers won't be able to load a Gemini capsule. Instead, you'll need to use [a
15Gemini-specific browser](https://gemini.circumlunar.space/clients.html).
16
17The content found within a Gemini page is called
18[Gemtext](https://gemini.circumlunar.space/docs/cheatsheet.gmi) and is
19_extremely_ basic (on purpose). Gemini only processes the text, no media content
20like images. However, you're able to style 3 levels of headings, regular text,
21links (which will display on their own line), quotes, and an unordered list.
22
23Here's a complete listing of valid Gemtext:
24
25````txt
26# Heading 1
27## Heading 2
28### Heading 3
29
30Regular text! Lorem ipsum dolor sit amet.
31
32=> https://example.com My Website
33=> gemini://example.com My Gemini Capsule
34
35> "If life were predictable it would cease to be life, and be without flavor." - Eleanor Roosevelt
36
37My List:
38** Item
39** Item
40
41```Anything between three backticks will be rendered as code.```
42````
43
44### Free Option
45
46There are probably numerous websites that allow you to create your personal
47Gemini capsule, but I'm going to focus on the two sites that I have personally
48tested. The first option below, Midnight Pub, allows you to create/edit any
49Gemini files you want in your account. This is essentially a GUI option with a
50built-in text box for editing. The second option below, Sourcehut, allows you to
51use a Git repository and automatic build process to deploy your personal Gemini
52capsule every time you push a commit.
53
54## Midnight Pub - Beginner Friendly
55
56[Midnight Pub](https://midnight.pub/) is a small, virtual community meant to
57reflect the atmosphere of wandering into a small alley pub. The site is built in
58Gemtext and has a server-side process to convert Gemtext to HTML if someone
59loads the site in an HTTP(S) browser.
60
61To create an account, you'll need to email the owner of the website to obtain a
62key. You can find their email on the Midnight Pub homepage. Once registered,
63head to [your account](https://midnight.pub/account) and select [manage
64site](https://midnight.pub/site). This is the screen where you can upload or
65create any files to be displayed on the internet.
66
67For example, I've created both an HTML file and a Gemini file. Remember that
68Gemini is automatically converted to HTML on the Pub, so you don't need an HTML
69version. For example, I created an HTML version to add in some extra styling.
70
71All you need to do is create a page like `index.gmi` and use your Gemini browser
72to head over to your-username.midnight.pub to see the result.
73
74That's all there is to it! Easy enough, right? Let's check out a more advanced
75version in the next section.
76
77# Paid Option
78
79As of 2021, Sourcehut has decided to require users to have a paid account in
80order to utilize their automated build system. For now, paid accounts can be as
81low as $2/month.
82
83## Sourcehut
84
85[Sourcehut](https://sourcehut.org/) is a collection of software development
86tools, but mostly surrounds their hosted Git repository service. Simply put,
87it's a minimal and more private alternative to services like GitHub.
88
89This walkthrough is more advanced and involves things like Git, SSH, the command
90line. If you don't think you know enough to do this, check out my walkthrough on
91creating a Gemini capsule for the Midnight Pub instead.
92
93The first thing you'll need to do is create an SSH key pair, if you don't
94already have one on your system. Once created, grab the contents of `id_rsa.pub`
95and add it to your Sourcehut account settings - this will allow you to push and
96pull code changes without using a username/password.
97
98```sh
99ssh keygen
100```
101
102Next up, let's create a repository with the proper name so that the Sourcehut
103build system will know we want them to host a website for us. Use the following
104format exactly:
105
106```sh
107mkdir your-username.srht.site && cd your-username.srht.site
108```
109
110Now that we've created the repo, let's initialize Git and add the proper remote
111URL.
112
113```sh
114git init
115```
116
117```sh
118git remote add origin git@git.sr.ht:~your-username/your-username.srht.site
119```
120
121Now that our repository is set up and configured, we will need to create at
122least two files:
123
124- `index.gmi`
125- `.build.yml`
126
127For your `.build.yml` file, use the following content and be sure to update the
128`site` line with your username!
129
130```yaml
131image: alpine/latest
132oauth: pages.sr.ht/PAGES:RW
133environment:
134 site: your-username.srht.site
135tasks:
136 - package: |
137 cd $site
138 tar -cvz . > ../site.tar.gz
139 - upload: |
140 acurl -f https://pages.sr.ht/publish/$site -Fcontent=@site.tar.gz -Fprotocol=GEMINI
141```
142
143For the `index.gmi` file, put whatever you want in there and save it. You could
144even just copy and paste the Gemtext cheatsheet.
145
146If you want to serve both HTML and Gemini files from this repository, just add a
147second command to the `upload` section:
148
149```yaml
150- upload: |
151 acurl -f https://pages.sr.ht/publish/$site -Fcontent=@site.tar.gz -Fprotocol=GEMINI
152 acurl -f https://pages.sr.ht/publish/$site -Fcontent=@site.tar.gz
153```
154
155Lastly, commit your changes and push them to the remote repo.
156
157```sh
158git add .; git commit -m "initial commit"; git push --set-upstream origin HEAD
159```
160
161If you've successfully created the files with the proper format, you'll see the
162terminal print a message that lets you know where the automatic build is taking
163place. For example, here's what the terminal tells me:
164
165```sh
166remote: Build started:
167remote: https://builds.sr.ht/~user/job/689803 [.build.yml]
168```
169
170Now that you've properly built your Sourcehut page, you can browse to
171your-username.srht.site in a Gemini browser and view the final results.
content/blog/2021-03-28-gemini-capsule.org added +178
@@ -0,0 +1,178 @@
1#+date: <2021-03-28>
2#+title: Launching a Gemini Capsule
3#+description:
4
5
6* What is Gemini?
7
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.
23
24Here's a complete listing of valid Gemtext:
25
26#+begin_src txt
27# Heading 1
28## Heading 2
29### Heading 3
30
31Regular text! Lorem ipsum dolor sit amet.
32
33=> https://example.com My Website
34=> gemini://example.com My Gemini Capsule
35
36> "If life were predictable it would cease to be life, and be without flavor." - Eleanor Roosevelt
37
38My List:
39,** Item
40,** Item
41
42```Anything between three backticks will be rendered as code.```
43#+end_src
44
45*** Free Option
46
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.
55
56** Midnight Pub - Beginner Friendly
57
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.
62
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.
69
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.
74
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.
77
78That's all there is to it! Easy enough, right? Let's check out a more
79advanced version in the next section.
80
81* Paid Option
82
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.
86
87** Sourcehut
88
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.
93
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.
98
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.
104
105#+begin_src sh
106ssh keygen
107#+end_src
108
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:
112
113#+begin_src sh
114mkdir your-username.srht.site && cd your-username.srht.site
115#+end_src
116
117Now that we've created the repo, let's initialize Git and add the proper
118remote URL.
119
120#+begin_src sh
121git init
122#+end_src
123
124#+begin_src sh
125git remote add origin git@git.sr.ht:~your-username/your-username.srht.site
126#+end_src
127
128Now that our repository is set up and configured, we will need to create
129at least two files:
130
131- =index.gmi=
132- =.build.yml=
133
134For your =.build.yml= file, use the following content and be sure to
135update the =site= line with your username!
136
137#+begin_src yaml
138image: alpine/latest
139oauth: pages.sr.ht/PAGES:RW
140environment:
141 site: your-username.srht.site
142tasks:
143 - package: |
144 cd $site
145 tar -cvz . > ../site.tar.gz
146 - upload: |
147 acurl -f https://pages.sr.ht/publish/$site -Fcontent=@site.tar.gz -Fprotocol=GEMINI
148#+end_src
149
150For the =index.gmi= file, put whatever you want in there and save it.
151You could even just copy and paste the Gemtext cheatsheet.
152
153If you want to serve both HTML and Gemini files from this repository,
154just add a second command to the =upload= section:
155
156#+begin_src yaml
157- upload: |
158 acurl -f https://pages.sr.ht/publish/$site -Fcontent=@site.tar.gz -Fprotocol=GEMINI
159 acurl -f https://pages.sr.ht/publish/$site -Fcontent=@site.tar.gz
160#+end_src
161
162Lastly, commit your changes and push them to the remote repo.
163
164#+begin_src sh
165git add .; git commit -m "initial commit"; git push --set-upstream origin HEAD
166#+end_src
167
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:
171
172#+begin_src sh
173remote: Build started:
174remote: https://builds.sr.ht/~user/job/689803 [.build.yml]
175#+end_src
176
177Now that you've properly built your Sourcehut page, you can browse to
178your-username.srht.site in a Gemini browser and view the final results.
content/blog/2021-03-28-vaporwave-vs-outrun.md deleted −126
@@ -1,126 +0,0 @@
1+++
2date = 2021-03-28
3title = "Vaporware vs. Outrun"
4description = ""
5draft = false
6+++
7
8# Overview
9
10When it comes to an aesthetic that originated primarily online, there tends to
11be a lot of confusion around what falls into each specific genre. This post
12discusses Vaporwave and Outrun, which have become almost synonymous in the minds
13of those online who aren't hardcore into these genres of media. More
14specifically, Outrun is largely an unknown term while all aesthetics in these
15two genres are usually attributed to Vaporwave. For example, take a look at the
16image search results for Vaporwave: the results include **a lot** of
17Outrun-themed images. You'll find a similar trend almost everywhere.
18
19![Vaporwave Search
20Results](https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/vaporwave-search-results.png)
21
22# Vaporwave
23
24![Macintosh Plus Album
25Cover](https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/macintosh-plus.png)
26
27Okay, so what is Vaporwave? I'm going to just copy-and-paste some general info
28from the Wikipedia article on
29[Vaporwave](https://en.wikipedia.org/wiki/Vaporwave), so that I'm not repeating
30everything you can already search for online:
31
32> Vaporwave is a microgenre of electronic music, a visual art style, and an
33> Internet meme that emerged in the early 2010s. It is defined partly by its
34> slowed-down, chopped and screwed samples of smooth jazz, elevator, R&B, and
35> lounge music from the 1980s and 1990s. The surrounding subculture is sometimes
36> associated with an ambiguous or satirical take on consumer capitalism and pop
37> culture, and tends to be characterized by a nostalgic or surrealist engagement
38> with the popular entertainment, technology and advertising of previous
39> decades. Visually, it incorporates early Internet imagery, late 1990s web
40> design, glitch art, anime, 3D-rendered objects, and cyberpunk tropes in its
41> cover artwork and music videos.
42
43This is an excellent summary, and it helps address my point here: there are
44specific aspects that make Vaporwave unique:
45
46## Time Frame
47
48The time frame for references, logos, etc. focuses mostly on the 1990s in
49Vaporwave. You'll see old school Pepsi logos, Microsoft 95 screens, tropical
50plants, classic marble sculptures, and many references from Japan's influence in
51the 90s.
52
53## Art
54
55The color scheme is generally a soft, light palette that uses pastel colors. The
56backdrop will often be in a light-pink or teal blue.
57
58## Music
59
60The musical genre of Vaporwave incorporates soft tunes, lounge music, and sound
61effects that will make the listener reminisce of the 90s. The sounds of the
62genre are generally slower-paced and calming. The major breakthrough artist for
63Vaporwave was [Macintosh Plus](https://en.wikipedia.org/wiki/Vektroid), who
64released the album [Floral
65Shoppe](https://archive.org/details/MACINTOSHPLUS-FLORALSHOPPE_complete) in
662011. Another more recent example is the artist
67[sadbert](https://sadbert.bandcamp.com/), whose latest album incorporates the
68upbeat tones of the 1999 Dilbert TV series.
69
70Notice that Vaporwave doesn't include things like racing cars, futuristic
71technology, chrome, or the deep orange/purple color scheme. Vaporwave is a focus
72on the idyllic state of the world when technology was becoming common in
73households, a reality that we have already experienced. Focus on the most
74aesthetically-pleasing parts of that past is a large part of Vaporwave.
75
76# Outrun
77
78![Outrun](https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/outrun.png)
79
80Now, let's get to Outrun. This one is a little trickier since the genre has
81largely been lumped under the Vaporwave title for so long. However, it stems
82from the [Synthwave](https://en.wikipedia.org/wiki/Synthwave) music genre and is
83likely named after the 1986 racer game, [Out
84Run](https://en.wikipedia.org/wiki/Out_Run).
85
86## Time Frame
87
88Outrun can be thought of as a retro-futuristic aesthetic born from the 1980s.
89
90## Art
91
92The color scheme uses a very dark color palette with the main colors being deep
93oranges, blues, and purples. Red edges are common around objects in Outrun art.
94The background of the Outrun aesthetic is almost always a neon grid like you'd
95expect to see in Tron or a 1980s arcade machine.
96
97Classic sports cars, chrome robots, computer generated graphics and fonts, and
98the occasional use of rain or palm trees can be found in Outrun art.
99
100## Music
101
102This aesthetic has a more aggressive and fast-paced style of music, which tends
103to match the subject of the art in this aesthetic.
104
105Outrun enthusiasts love what people in the 1980s thought the future would look
106like. Take a look at a common video game discussed in Outrun circles, Far Cry 3:
107Blood Dragon:
108
109![Fry Cry 3: Blood
110Dragon](https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/far-cry.png)
111
112Another example that doesn't force the color scheme as hard as some online art
113does is Kung Fury:
114
115![Kung
116Fury](https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/kung-fury.png)
117
118![Kung Fury Hacker
119Scene](https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/kung-fury-hacker.png)
120
121# Conclusion
122
123While Vaporwave and Outrun share similarities, they are two distinct aesthetics
124with many important distinctions. Someone who enjoys one may not necessarily
125enjoy the other, so it's important to make sure we properly describe the
126aesthetic we're looking for.
content/blog/2021-03-28-vaporwave-vs-outrun.org added +134
@@ -0,0 +1,134 @@
1#+date: <2021-03-28>
2#+title: Vaporware vs. Outrun
3#+description:
4
5
6* Overview
7
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.
17
18#+caption: Vaporwave Search Results
19[[https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/vaporwave-search-results.png]]
20
21* Vaporwave
22
23#+caption: Macintosh Plus Album Cover
24[[https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/macintosh-plus.png]]
25
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:
30
31#+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.
43#+end_quote
44
45This is an excellent summary, and it helps address my point here: there
46are specific aspects that make Vaporwave unique:
47
48** Time Frame
49
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.
54
55** Art
56
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.
59
60** Music
61
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.
79
80* Outrun
81
82#+caption: Outrun
83[[https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/outrun.png]]
84
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,
89[[https://en.wikipedia.org/wiki/Out_Run][Out Run]].
90
91** Time Frame
92
93Outrun can be thought of as a retro-futuristic aesthetic born from the
941980s.
95
96** Art
97
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.
103
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.
107
108** Music
109
110This aesthetic has a more aggressive and fast-paced style of music,
111which tends to match the subject of the art in this aesthetic.
112
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:
116
117#+caption: Fry Cry 3: Blood Dragon
118[[https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/far-cry.png]]
119
120Another example that doesn't force the color scheme as hard as some
121online art does is Kung Fury:
122
123#+caption: Kung Fury
124[[https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/kung-fury.png]]
125
126#+caption: Kung Fury Hacker Scene
127[[https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/kung-fury-hacker.png]]
128
129* Conclusion
130
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.
content/blog/2021-03-30-vps-web-server.md deleted −396
@@ -1,396 +0,0 @@
1+++
2date = 2021-03-30
3title = "How to Set Up a VPS Web Server"
4description = ""
5draft = false
6+++
7
8# Shared Hosting vs. VPS
9
10Choosing a place to host a website is one of the most confusing decisions for
11beginner web developers. Even for experienced web devs, choosing between
12different forms of web hosting can be a daunting choice.
13
14First, let's take a look at [shared web
15hosting](https://en.wikipedia.org/wiki/Shared_web_hosting_service). Shared web
16hosting is a product where you are purchasing a small piece of a web server that
17is being shared between many websites. As a result, the cost of shared hosting
18is extremely low. You won't have access to the server itself, which means you
19can't install your own software on the server, such as Docker. Usually, you are
20simply allowed to connect your domains to the server, set up domain security,
21and other small utilities.
22
23In contrast, a [virtual private
24server](https://en.wikipedia.org/wiki/Virtual_private_server) (VPS) is a virtual
25machine that replicates the environment of having a dedicated server to
26yourself. You are able to control the virtual server's host names, base file
27system, package manager, etc. Another great upside of a VPS is that since it's
28virtual, the company providing the VPS can dynamically increase the disk size,
29RAM size, or number of CPUs at any time. However, the virtual server is still
30physically located on a server that is shared between multiple virtual servers.
31
32The choice between shared hosting and VPS mostly depends on your skill level
33with system administration. If you're comforting working on a server that is
34mostly left up to you (or you're willing to learn), then a VPS is usually a
35better option. However, shared hosting is a fantastic option for people who
36don't want to have to learn how to manage their server.
37
38# My Situation
39
40I had used shared hosting for approximately 5 years before trying my first VPS.
41I manage a homelab and have had success running a server and performing typical
42sysadmin duties, but I was still hesitant to get a VPS. One fear was that I
43always struggled to properly set up the networking part of a server - DNS and
44hostname configurations were not my friend.
45
46As a little bit of background, I originally used
47[Siteground](https://www.siteground.com) for my initially shared hosting and
48stayed on that platform for at least a year. However, the UI was clunky, and I
49didn't like how they handled certain technical aspects, so I switched to
50[Namecheap](https://www.namecheap.com). Namecheap was great because it is the
51service I primarily use for purchasing domain names, which made it incredibly
52easy to link them to my hosting service. However, it was still mediocre shared
53hosting, and Namecheap is notorious for not letting you use [Let's
54Encrypt](https://letsencrypt.org) to obtain free SSL/TLS certificates; Namecheap
55wants to make you purchase certificates through their store.
56
57Finally, I settled down with [iWebFusion](https://www.iwebfusion.net) for about
58the last year of my shared hosting. This service was pretty great, came with
59free SSL/TLS, and I never had any complaints.
60
61However, I finally grew tired of not being able to install software on my own
62web server. I wanted to be able to try out things like
63[Postmill](https://postmill.xyz) or [Matrix](https://matrix.org). This is
64possible with a VPS, so I decided to grab a new domain name to try it out.
65
66# Getting Started: Buying a VPS
67
68The first step to moving over to a VPS is (you guessed it): finding a VPS
69provider. For my VPSs, I use [1984](https://1984hosting.com) and prefer their
70services much more than any alternative, due to their location (Iceland), their
71[privacy policy](https://1984hosting.com/GDPR/), their respect for GDPR, and the
72ability to remain anonymous if you pay in Bitcoin or Monero.
73
74[Njalla](https://njal.la) is another good, privacy-oriented option for VPS
75services.
76
77You'll have to decide what specifications you want on your VPS. For me, I only
78build and deploy low-resource HTML, PHP, and Python websites. This means I can
79survive on the smallest VPS: 1 CPU, 1GB of RAM, and 25GB SSD for $5.00 per
80month.
81
82As noted above, the great thing about a VPS is you can request your provider to
83increase the resources at any time.
84
85# Configuring DNS Settings
86
87Okay, so now let's get into some actual work that has to be done to get content
88moved from a shared host to a VPS. At this point, I'm assuming you have a shared
89host with website content that you can still access, and you've purchased a new
90VPS and can SSH into that server.
91
92The first change is minor, but it should be done immediately in order to get
93things moving: DNS settings. Go to wherever your DNS settings are handled. If
94your shared host also managed your DNS settings, you'll need to first move that
95DNS over to your new VPS provider. For me, I route my DNS through
96[Gandi](https://www.gandi.net).
97
98Once you know where your DNS settings are, go ahead and update the `A` records
99to match the public IP address of your VPS. For example:
100
101```txt
102A example.com xxx.xxx.xxx.xxx
103A subdomain xxx.xxx.xxx.xxx
104CNAME www example.com.
105```
106
107If you have any other records that require updates, such as MX or TXT records
108for a mail server, be sure to update those accordingly. Personally, I don't host
109my own mail server. I route all mail on my custom domains to
110[Migadu](https://www.migadu.com). Hosting your own email server can become
111complex quickly and is not for beginners.
112
113DNS changes can take up to 48 hours to propagate, so be sure to give it some
114time before assuming you've made an error.
115
116# Server Updates and Packages
117
118Now that the DNS settings have been changed, let's set up our server while we
119wait for the DNS to propagate. First up is to ssh into your server. If you've
120signed up with a service like DigitalOcean, you can add your SSH key to your
121account and to your VPS droplet so that you don't need a password in order to
122SSH.
123
124```sh
125ssh root@xxx.xxx.xxx.xxx
126```
127
128The VPS that is used in this blog post runs Ubuntu 20.04 with an Apache web
129server. If you're working on a different operating system (OS) or want a
130different web server, such as Nginx, you'll have to use different commands to
131set it up.
132
133First, let's update and upgrade our server.
134
135**NOTE:** Since we have logged in to the server as `root` for now, we don't need
136to use the `sudo` modifier before our commands.
137
138```sh
139apt update && apt upgrade -y
140```
141
142# Create A User Account
143
144While being able to use `root` can be beneficial at times, you shouldn't use
145`root` unless you have to.
146
147So let's set up a new user in our system. The `-m` option below tells the OS to
148create a home directory for the new user.
149
150```sh
151adduser USERNAME
152```
153
154Now, create a password for that user.
155
156```sh
157passwd USERNAME
158```
159
160Finally, add the user to the sudoers file, so they can perform priveleged
161commands.
162
163```sh
164usermod -a -G sudo USERNAME
165```
166
167If you are using SSH keys and not passwords, you'll need to copy your SSH key
168from your local machine to the VPS. If you haven't disabled password-based SSH
169yet, the easiest way to do this is `ssh-copy-id` from your local computer (not
170from the VPS):
171
172```sh
173ssh-copy-id testuser@xxx.xxx.xxx.xxx
174```
175
176If you've disabled password-based SSH, you'll need to manually copy your SSH key
177into the `~/.ssh/authorized_keys` file.
178
179# Install Software
180
181Our goal here is to host a web server, so the next step is to install the Apache
182web server and any other packages we need.
183
184From this point on, I will be logged in as a user (not `root`) and will need to
185use the `sudo` modifier for most commands.
186
187```sh
188sudo apt update; sudo apt upgrade -y; sudo apt autoremove -y
189sudo apt install apache2
190```
191
192If you need other language support, such as PHP, you'll need to install that
193too.
194
195```sh
196sudo apt install libapache2-mod-php php-dom
197sudo a2enmod php
198sudo systemctl restart apache2
199```
200
201# Website Files & Folders
202
203Next up is to create the directories for the domain(s) we want to be hosted on
204this web server.
205
206```sh
207cd /var/www
208sudo mkdir example.com
209```
210
211We have a folder for `example.com` now, so let's add an `index.html` file and
212put it within a specific `public_html` folder. You don't need this `public_html`
213if you don't want it, but it helps with organizing items related to
214`example.com` that you don't want to publish to the internet.
215
216```sh
217cd example.com
218sudo mkdir public_html && cd public_html
219sudo nano index.html
220```
221
222You can put anything you want in this `index.html` file. If you can't think of
223anything, paste this in there:
224
225```html
226<!DOCTYPE html>
227<html lang="en">
228 <head>
229 <meta charset="utf-8" />
230 <meta name="viewport" content="width=device-width, initial-scale=1" />
231 <title>Hello, world!</title>
232 </head>
233 <body>
234 <h1>Hello, world!</h1>
235 </body>
236</html>
237```
238
239If you want something to be served at `example.com/page01/file.txt`, you'll have
240to create the `page01` directory under the `example.com` directory. For example:
241
242```sh
243cd /var/www/example.com/public_html
244sudo mkdir page01
245sudo nano file.txt
246```
247
248# Apache Configuration
249
250Now, let's set up the files that will tell the server where to find the files
251for `example.com`. We will copy the default configuration file and create our
252own.
253
254```sh
255cd /etc/apache2/sites-available
256sudo cp 000-default.conf example.com.conf
257sudo nano example.com.conf
258```
259
260This configuration file will have a few default lines, but you'll need to edit
261it to look similar to this (settings may change based on your personal needs):
262
263```config
264<VirtualHost *:80>
265 ServerAdmin your-email@email-provider.com
266 ServerName example.com
267 ServerAlias www.example.com
268 DocumentRoot /var/www/example.com/public_html
269 ErrorLog ${APACHE_LOG_DIR}/error.log
270 CustomLog ${APACHE_LOG_DIR}/access.log combined
271</VirtualHost>
272```
273
274Now, enable the configuration for your new site, disable the default
275configuration, and reload the web server.
276
277```sh
278sudo a2ensite example.com.conf
279sudo a2dissite 000-default.conf
280sudo systemctl reload apache2
281```
282
283You can always run a test to make sure no errors or warnings are found in your
284configuration files.
285
286```sh
287sudo apache2ctl configtest
288```
289
290Now, restart the web server entirely. After this, you should be able to browse
291to `http://example.com` and see the HTML content you provided earlier. Note that
292SSL/TLS has not been enabled yet, so you won't be able to use the secure version
293yet (`https://example.com`).
294
295```sh
296sudo systemctl restart apache2
297```
298
299You can repeat this for as many websites as you need. Just create the domain
300folders in `/var/www/`, add the configuration file, enable the configuration,
301and restart `apache2`.
302
303# SSL/TLS Certificates: Serve Websites Over HTTPS
304
305In order to serve secure content, you'll need to obtain SSL/TLS certificates.
306Luckily, there's a free tool called [Certbot](https://certbot.eff.org) that
307helps us with the process.
308
309The first step is to install `snapd` and `core` for Ubuntu.
310
311```sh
312sudo apt install snapd
313sudo snap install core
314sudo snap refresh core
315```
316
317Next, install the `certbot` snap package.
318
319```sh
320sudo snap install --classic certbot
321```
322
323Execute the following command to ensure that the `certbot` command can be run.
324
325```sh
326sudo ln -s /snap/bin/certbot /usr/bin/certbot
327```
328
329Finally, you can run `certbot` one of two ways:
330
3311. run it and let it alter your Apache configuration files automatically to
332 enable HTTPS redirects.
3332. run it and only allow it to create certificates. You'll need to manually
334 alter the config files to enable HTTPS redirects.
335
336Run certbot and allow automatic config changes:
337
338```sh
339sudo certbot --apache
340```
341
342Run certbot for certificates only and don't allow it to alter config files:
343
344```sh
345sudo certbot certonly --apache
346```
347
348The Certbot packages on your system come with a cron job or systemd timer that
349will renew your certificates automatically before they expire. You will not need
350to run Certbot again unless you change your configuration. You can test
351automatic renewal for your certificates by running this command:
352
353```sh
354sudo certbot renew --dry-run
355```
356
357Now, test your domains by going to `https://example.com`.
358
359# Firewall Security
360
361To enable better security on your server, you'll need to enable a basic
362firewall. For Ubuntu, we'll use [the uncomplicated
363firewall](../secure-your-network-with-the-uncomplicated-firewall/).
364
365Now, add the following rules to the firewall allow SSH, Apache, and HTTP(S)
366connections. If you need to, you can enable different ports for specifics
367applications, SFTP, etc.
368
369```sh
370sudo ufw default deny incoming
371sudo ufw default allow outgoing
372sudo ufw allow OpenSSH
373sudo ufw allow Apache
374sudo ufw allow proto tcp from any to any port 80,443
375```
376
377Once you've added all the rules you need, enable the firewall.
378
379```sh
380sudo ufw enable
381```
382
383# Troubleshooting
384
385If you run into any issues during your VPS set-up, be sure to walk back through
386your actions and make sure you didn't miss any steps.
387
388Many websites have fantastic guides to setting up various web servers. This is
389one of the areas [where DigitalOcean
390shines](https://www.digitalocean.com/community/tutorials). For simpler or
391more Linux-oriented questions, I suggest using [Linuxize]
392(<https://linuxize.com>).
393
394If you're getting certain errors (e.g. `500 Internal Server Error`) and need to
395debug locally, you can view the `access.log` and `error.log` files in the
396`/var/log/apache/` directory.
content/blog/2021-03-30-vps-web-server.org added +411
@@ -0,0 +1,411 @@
1#+date: <2021-03-30>
2#+title: How to Set Up a VPS Web Server
3#+description:
4
5
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.
40
41* My Situation
42
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.
72
73* Getting Started: Buying a VPS
74
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.
81
82[[https://njal.la][Njalla]] is another good, privacy-oriented option for
83VPS services.
84
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.
89
90As noted above, the great thing about a VPS is you can request your
91provider to increase the resources at any time.
92
93* Configuring DNS Settings
94
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.
99
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]].
105
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:
108
109#+begin_src txt
110A example.com xxx.xxx.xxx.xxx
111A subdomain xxx.xxx.xxx.xxx
112CNAME www example.com.
113#+end_src
114
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.
120
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.
123
124* Server Updates and Packages
125
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.
131
132#+begin_src sh
133ssh root@xxx.xxx.xxx.xxx
134#+end_src
135
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.
140
141First, let's update and upgrade our server.
142
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.
145
146#+begin_src sh
147apt update && apt upgrade -y
148#+end_src
149
150* Create A User Account
151
152While being able to use =root= can be beneficial at times, you shouldn't
153use =root= unless you have to.
154
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.
157
158#+begin_src sh
159adduser USERNAME
160#+end_src
161
162Now, create a password for that user.
163
164#+begin_src sh
165passwd USERNAME
166#+end_src
167
168Finally, add the user to the sudoers file, so they can perform
169priveleged commands.
170
171#+begin_src sh
172usermod -a -G sudo USERNAME
173#+end_src
174
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):
179
180#+begin_src sh
181ssh-copy-id testuser@xxx.xxx.xxx.xxx
182#+end_src
183
184If you've disabled password-based SSH, you'll need to manually copy your
185SSH key into the =~/.ssh/authorized_keys= file.
186
187* Install Software
188
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.
191
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.
194
195#+begin_src sh
196sudo apt update; sudo apt upgrade -y; sudo apt autoremove -y
197sudo apt install apache2
198#+end_src
199
200If you need other language support, such as PHP, you'll need to install
201that too.
202
203#+begin_src sh
204sudo apt install libapache2-mod-php php-dom
205sudo a2enmod php
206sudo systemctl restart apache2
207#+end_src
208
209* Website Files & Folders
210
211Next up is to create the directories for the domain(s) we want to be
212hosted on this web server.
213
214#+begin_src sh
215cd /var/www
216sudo mkdir example.com
217#+end_src
218
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.
224
225#+begin_src sh
226cd example.com
227sudo mkdir public_html && cd public_html
228sudo nano index.html
229#+end_src
230
231You can put anything you want in this =index.html= file. If you can't
232think of anything, paste this in there:
233
234#+begin_src html
235<!DOCTYPE html>
236<html lang="en">
237 <head>
238 <meta charset="utf-8" />
239 <meta name="viewport" content="width=device-width, initial-scale=1" />
240 <title>Hello, world!</title>
241 </head>
242 <body>
243 <h1>Hello, world!</h1>
244 </body>
245</html>
246#+end_src
247
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:
251
252#+begin_src sh
253cd /var/www/example.com/public_html
254sudo mkdir page01
255sudo nano file.txt
256#+end_src
257
258* Apache Configuration
259
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.
263
264#+begin_src sh
265cd /etc/apache2/sites-available
266sudo cp 000-default.conf example.com.conf
267sudo nano example.com.conf
268#+end_src
269
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):
273
274#+begin_src config
275<VirtualHost *:80>
276 ServerAdmin your-email@email-provider.com
277 ServerName example.com
278 ServerAlias www.example.com
279 DocumentRoot /var/www/example.com/public_html
280 ErrorLog ${APACHE_LOG_DIR}/error.log
281 CustomLog ${APACHE_LOG_DIR}/access.log combined
282</VirtualHost>
283#+end_src
284
285Now, enable the configuration for your new site, disable the default
286configuration, and reload the web server.
287
288#+begin_src sh
289sudo a2ensite example.com.conf
290sudo a2dissite 000-default.conf
291sudo systemctl reload apache2
292#+end_src
293
294You can always run a test to make sure no errors or warnings are found
295in your configuration files.
296
297#+begin_src sh
298sudo apache2ctl configtest
299#+end_src
300
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
304able to use the secure version yet (=https://example.com=).
305
306#+begin_src sh
307sudo systemctl restart apache2
308#+end_src
309
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=.
313
314* SSL/TLS Certificates: Serve Websites Over HTTPS
315
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.
319
320The first step is to install =snapd= and =core= for Ubuntu.
321
322#+begin_src sh
323sudo apt install snapd
324sudo snap install core
325sudo snap refresh core
326#+end_src
327
328Next, install the =certbot= snap package.
329
330#+begin_src sh
331sudo snap install --classic certbot
332#+end_src
333
334Execute the following command to ensure that the =certbot= command can
335be run.
336
337#+begin_src sh
338sudo ln -s /snap/bin/certbot /usr/bin/certbot
339#+end_src
340
341Finally, you can run =certbot= one of two ways:
342
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.
347
348Run certbot and allow automatic config changes:
349
350#+begin_src sh
351sudo certbot --apache
352#+end_src
353
354Run certbot for certificates only and don't allow it to alter config
355files:
356
357#+begin_src sh
358sudo certbot certonly --apache
359#+end_src
360
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:
366
367#+begin_src sh
368sudo certbot renew --dry-run
369#+end_src
370
371Now, test your domains by going to =https://example.com=.
372
373* Firewall Security
374
375To 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]].
379
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.
383
384#+begin_src sh
385sudo ufw default deny incoming
386sudo ufw default allow outgoing
387sudo ufw allow OpenSSH
388sudo ufw allow Apache
389sudo ufw allow proto tcp from any to any port 80,443
390#+end_src
391
392Once you've added all the rules you need, enable the firewall.
393
394#+begin_src sh
395sudo ufw enable
396#+end_src
397
398* Troubleshooting
399
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.
402
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]]).
408
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.
content/blog/2021-04-17-gemini-server.md deleted −150
@@ -1,150 +0,0 @@
1+++
2date = 2021-04-17
3title = "Hosting a Gemini Server"
4description = ""
5draft = false
6+++
7
8# Similar Article Available
9
10To read more about Gemini and ways to test out this new protocol without your
11own server, see my previous post [Launching a Gemini
12Capsule](../launching-a-gemini-capsule/).
13
14# Preparation
15
16This guide assumes you have access to a server accessible to the world through a
17public IP address and that you own a domain name used for this Gemini capsule.
18
19# Getting Started with Agate
20
21We are going to use [Agate](https://github.com/mbrubeck/agate) for this
22tutorial. This is a basic Gemini server written in Rust. It takes very little
23time and maintenance to get it running.
24
25# Install Dependencies
26
27First, you will need to install the Rust package for your system. On Ubuntu, use
28the following commands (remember to use `sudo` if you are not the root user).
29The Rust installation will give you options to customize the installation; I
30used the default installation options.
31
32```sh
33sudo apt update && sudo apt upgrade -y
34curl https://sh.rustup.rs -sSf | sh
35```
36
37Remember to configure your shell with the new configuration:
38
39```sh
40source $HOME/.cargo/env
41```
42
43Before we install agate, make sure you have the `gcc` package installed:
44
45```sh
46sudo apt install gcc
47```
48
49Next, you'll need to install the agate executable with Rust's Cargo package
50maintainer:
51
52```sh
53cargo install agate
54```
55
56# Create Symlinks
57
58Once Cargo has finished installing all the required packages, symlink the
59executable to your $PATH.
60
61```sh
62sudo ln -s $HOME/.cargo/bin/agate /usr/local/bin/agate
63```
64
65# Using Agate's Built-In Installation Tool
66
67If you're running Ubuntu or Debian, use the Debian installation script found in
68Agate's GitHub repository, under the `tools/debian` folder.
69
70```sh
71git clone https://github.com/mbrubeck/agate
72cd agate/tools/debian
73sudo ./install.sh
74```
75
76# Configure the Gemini Service
77
78We have a little more to do, but since this script tries to immediately run the
79service, it will likely fail with an exit code. Let's add our finishing touches.
80Edit the following file and replace the hostname with your desired URL. You can
81also change the directory where content will be served.
82
83```sh
84sudo nano /etc/systemd/system/gemini.service
85```
86
87```sh
88# Edit these lines to whatever you want - see the next code block for my personal configuration.
89WorkingDirectory=/srv/gemini
90ExecStart=agate --hostname $(uname -n) --lang en
91```
92
93This is my personal config:
94
95```sh
96WorkingDirectory=/var/gemini/
97ExecStart=agate --hostname gemini.example.com --lang en
98```
99
100Since we've altered the systemd configuration files, we have to reload the
101daemon. Let's do that, restart our service, and check its status.
102
103```sh
104sudo systemctl daemon-reload
105sudo systemctl restart gemini.service
106sudo systemctl status gemini.service
107```
108
109# Fixing Systemd Errors
110
111If you're still getting errors, the installation process may not have properly
112enabled the gemini service. Fix it with the following commands.
113
114```sh
115sudo systemctl enable gemini.service
116sudo systemctl restart gemini.service
117sudo systemctl status gemini.service
118```
119
120# Firewall Rules
121
122Great! Our server is now functional and running. The first consideration now is
123that you need to be able to access port 1965 on the server. If you have a
124firewall enabled, you'll need to open that port up.
125
126```sh
127sudo ufw allow 1965
128sudo ufw reload
129```
130
131# Creating Content
132
133Let's create the Gemini capsule. Note that wherever you set the WorkingDirectory
134variable to earlier, Agate will expect you to put your Gemini capsule contents
135in a sub-folder called "content." So, I place my files in "/var/gmi/content."
136I'm going to create that folder now and put a file in there.
137
138```sh
139sudo mkdir /var/gemini/content
140sudo nano /var/gemini/content/index.gmi
141```
142
143You can put whatever you want in the "index.gmi" file, just make sure it's valid
144Gemtext.
145
146# The Results
147
148To view the results, you can use a Gemini browser, such as
149[Lagrange](https://gmi.skyjake.fi/lagrange/) or
150[amfora](https://github.com/makeworld-the-better-one/amfora).
content/blog/2021-04-17-gemini-server.org added +151
@@ -0,0 +1,151 @@
1#+date: <2021-04-17>
2#+title: Hosting a Gemini Server
3#+description:
4
5
6* Similar Article Available
7
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]].
11
12* Preparation
13
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.
17
18* Getting Started with Agate
19
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.
23
24* Install Dependencies
25
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.
30
31#+begin_src sh
32sudo apt update && sudo apt upgrade -y
33curl https://sh.rustup.rs -sSf | sh
34#+end_src
35
36Remember to configure your shell with the new configuration:
37
38#+begin_src sh
39source $HOME/.cargo/env
40#+end_src
41
42Before we install agate, make sure you have the =gcc= package installed:
43
44#+begin_src sh
45sudo apt install gcc
46#+end_src
47
48Next, you'll need to install the agate executable with Rust's Cargo
49package maintainer:
50
51#+begin_src sh
52cargo install agate
53#+end_src
54
55* Create Symlinks
56
57Once Cargo has finished installing all the required packages, symlink
58the executable to your $PATH.
59
60#+begin_src sh
61sudo ln -s $HOME/.cargo/bin/agate /usr/local/bin/agate
62#+end_src
63
64* Using Agate's Built-In Installation Tool
65
66If you're running Ubuntu or Debian, use the Debian installation script
67found in Agate's GitHub repository, under the =tools/debian= folder.
68
69#+begin_src sh
70git clone https://github.com/mbrubeck/agate
71cd agate/tools/debian
72sudo ./install.sh
73#+end_src
74
75* Configure the Gemini Service
76
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.
82
83#+begin_src sh
84sudo nano /etc/systemd/system/gemini.service
85#+end_src
86
87#+begin_src sh
88# Edit these lines to whatever you want - see the next code block for my personal configuration.
89WorkingDirectory=/srv/gemini
90ExecStart=agate --hostname $(uname -n) --lang en
91#+end_src
92
93This is my personal config:
94
95#+begin_src sh
96WorkingDirectory=/var/gemini/
97ExecStart=agate --hostname gemini.example.com --lang en
98#+end_src
99
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.
102
103#+begin_src sh
104sudo systemctl daemon-reload
105sudo systemctl restart gemini.service
106sudo systemctl status gemini.service
107#+end_src
108
109* Fixing Systemd Errors
110
111If you're still getting errors, the installation process may not have
112properly enabled the gemini service. Fix it with the following commands.
113
114#+begin_src sh
115sudo systemctl enable gemini.service
116sudo systemctl restart gemini.service
117sudo systemctl status gemini.service
118#+end_src
119
120* Firewall Rules
121
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.
125
126#+begin_src sh
127sudo ufw allow 1965
128sudo ufw reload
129#+end_src
130
131* Creating Content
132
133Let'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.
138
139#+begin_src sh
140sudo mkdir /var/gemini/content
141sudo nano /var/gemini/content/index.gmi
142#+end_src
143
144You can put whatever you want in the "index.gmi" file, just make sure
145it's valid Gemtext.
146
147* The Results
148
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]].
content/blog/2021-04-23-php-comment-system.md deleted −261
@@ -1,261 +0,0 @@
1+++
2date = 2021-04-23
3title = "Roll Your Own Static Commenting System in PHP"
4description = ""
5draft = false
6+++
7
8# The Terrible-ness of Commenting Systems
9
10The current state of affairs regarding interactive comment systems is, well,
11terrible. It is especially awful if you're a privacy conscious person who does
12not generally load third-party scripts or frames on the websites you visit.
13
14Even further, many comment systems are charging exorbitant fees for something
15that should be standard.
16
17Of course, there are some really terrible options:
18
19- Facebook Comments
20- Discourse
21
22There are some options that are better but still use too many scripts, frames,
23or social integrations on your web page that could impact some users:
24
25- Disqus
26- Isso
27- Remark42
28
29Lastly, I looked into a few unique ways of generating blog comments, such as
30using Twitter threads or GitHub issues to automatically post issues. However,
31these both rely on external third-party sites that I don't currently use.
32
33# Stay Static with Server-Side Comments
34
35The main issue for my personal use-case is that my blog is completely, 100%
36static. I use PHP on the back-end but website visitors only see HTML and a
37single CSS file. No external javascript and no embedded frames.
38
39So, how do we keep a site static and still allow users to interact with blog
40posts? The key actually pretty simple - I'm already using PHP, so why not rely
41on the classic HTML `<form>` and a PHP script to save the comments somewhere? As
42it turns out, this was a perfect solution for me.
43
44The second issue for my personal use-case is that I am trying to keep the
45contents of my website accessible over time, as described by @brandur, in his
46post entitled [Blog with Markdown + Git, and degrade gracefully through
47time](https://brandur.org/fragments/graceful-degradation-time) .
48
49This means I cannot rely on a database for comments, since I do not rely on a
50database for any other part of my websites.
51
52I blog in plain Markdown files, commit all articles to Git, and ensure that
53future readers will be able to see the source data long after I'm gone, or the
54website has gone offline. However, I still haven't committed any images served
55on my blog to Git, as I'm not entirely sold on Git LFS yet - for now, images can
56be found at [img.cleberg.net](https://img.cleberg.net).
57
58Saving my comments back to the Git repository ensures that another aspect of my
59site will degrade gracefully.
60
61# Create a Comment Form
62
63Okay, let's get started. The first step is to create an HTML form that users can
64see and utilize to submit comments. This is fairly easy and can be changed
65depending on your personal preferences.
66
67Take a look at the code block below for the form I currently use. Note that
68`<current-url>` is replaced automatically in PHP with the current post's URL, so
69that my PHP script used later will know which blog post the comment is related
70to.
71
72The form contains the following structure:
73
741. `<form>` - This is the form and will determine which PHP script to send the
75 comment to.
762. `<section hidden>` - This section is hidden from the user and is used to
77 ensure that we know which blog post sent the comment.
783. `<section>` Display Name (Optional) - Used to accept a display name, if
79 entered.
804. `<section>` Comment (Required) - Used to accept the user's full comment.
81 Markdown is allowed.
825. `<button>` - A button to submit the form.
83
84```html
85<form action="/comment.php" method="POST">
86 <h3>Leave a Comment</h3>
87 <section hidden>
88 <label class="form-label" for="postURL">Post URL</label>
89 <input
90 class="form-control"
91 id="postURL"
92 name="postURL"
93 type="text"
94 value="<current-url>"
95 />
96 </section>
97 <section>
98 <label class="form-label" for="userName">Display Name</label>
99 <input
100 class="form-control"
101 id="userName"
102 name="userName"
103 placeholder="John Doe"
104 type="text"
105 />
106 </section>
107 <section>
108 <label class="form-label" for="userContent">Your Comment</label>
109 <textarea
110 class="form-control"
111 id="userContent"
112 name="userContent"
113 rows="3"
114 placeholder="# Feel free to use Markdown"
115 aria-describedby="commentHelp"
116 required
117 ></textarea>
118 <div id="commentHelp" class="form-text">
119 Comments are saved as Markdown and cannot be edited or deleted.
120 </div>
121 </section>
122 <button type="submit">Submit</button>
123</form>
124```
125
126# Handle Comments via POST
127
128Now that we have a form and can submit comments, we need to create a PHP script
129so that the server can fetch the comment data and save it. Make sure your PHP
130script name matches the name you entered in the `action` field in your form.
131
132See the code block below for the contents of my `comment.php` script. We perform
133the following tasks in this script:
134
1351. Grab the POST data from the HTML form.
1362. Sanitize the comment data with `htmlentities`.
1373. Set the display name to `Anonymous` if it was left blank.
1384. Create a PHP object that combines all of this data.
1395. Check if our destination file `comments.json` exists.
1406. If so, convert the PHP object to JSON and save 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
143 code 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.
146
147```php
148// Get the content sent from the comment form
149$comment = htmlentities($_POST['userContent']);
150$post_url = $_POST['postURL'];
151
152// Set default values if blank
153if (isset($_POST['userName']) && trim($_POST['userName']) !== "") {
154 $username = $_POST['userName'];
155} else {
156 $username = 'Anonymous';
157}
158
159// Create an empty PHP object
160$user_object = new stdClass();
161
162// Add object content
163$user_object->timestamp = date('Y-m-d H:i:s');
164$user_object->username = $username;
165$user_object->comment = $comment;
166$user_object->post_url = $post_url;
167
168// Append JSON to file
169$file_name = 'comments.json';
170if (file_exists($file_name)) {
171 $source_data = file_get_contents($file_name);
172 $temp_array = json_decode($source_data);
173 array_push($temp_array, $user_object);
174 $json_data = json_encode($temp_array, JSON_PRETTY_PRINT);
175 file_put_contents($file_name, $json_data);
176} else {
177 die('Error: The "comments.json" file does not exist.');
178}
179
180// Send the user back
181header('Location: ' . $post_url . '#comments');
182```
183
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`.
187
188```sh
189chgrp -R www-data /path/to/website/
190chmod -R g+w comments.json
191```
192
193# Display User Comments
194
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.
197
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.
200
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.
204
205```php
206$query = 'your-blog-post.html';
207
208// Load saved comments
209$comments_file = 'comments.json';
210$comments_raw = file_get_contents($comments_file);
211$comments = json_decode($comments_raw);
212$comment_section = '<section id="comments" class="comments"><h3>Comments</h3>';
213foreach ($comments as $comment) {
214 if ($comment->post_url == "https://example.com/post/" . $query) {
215 // Assign metadata to variables
216 $comment_timestamp = $comment->timestamp;
217 $comment_username = $comment->username;
218 $comment_content = $comment->comment;
219
220 // Parse the comment, in case it contains Markdown
221 $comment_md = Parsedown::instance()->text($comment_content);
222 $comment_html = new DOMDocument();
223 $comment_html->loadHTML($comment_md);
224 $comment_html_links = $comment_html->getElementsByTagName('a');
225 foreach ($comment_html_links as $comment_html_link) {
226 $comment_html_link->setAttribute('rel', 'noreferrer');
227 $comment_html_link->setAttribute('target', '_blank');
228 }
229 $comment_secured_html = $comment_html->saveHTML();
230
231 // Apply metadata to comments section
232 $comment_section .= '<div class="user-comment"><div class="row"><label>Timestamp:</label><p>' . $comment_timestamp . '</p></div><div class="row"><label>Name:</label><p>' . $comment_username . '</p></div><div class="row markdown"><label>Comment:</label><div class="comment-markdown">' . $comment_secured_html . '</div></div></div>';
233 }
234}
235
236echo $comment_section;
237```
238
239# Bonus: Create a 'Recent Comments' Page
240
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.
245
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/`.
248
249# Possible Enhancements
250
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:
253
254- Create a secure moderator page with user authentication at
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-23-php-comment-system.org added +272
@@ -0,0 +1,272 @@
1#+date: <2021-04-23>
2#+title: Roll Your Own Static Commenting System in PHP
3#+description:
4
5
6* The Terrible-ness of Commenting Systems
7
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.
12
13Even further, many comment systems are charging exorbitant fees for
14something that should be standard.
15
16Of course, there are some really terrible options:
17
18- Facebook Comments
19- Discourse
20
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:
24
25- Disqus
26- Isso
27- Remark42
28
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.
33
34* Stay Static with Server-Side Comments
35
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.
40
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.
45
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]] .
51
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.
54
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]].
61
62Saving my comments back to the Git repository ensures that another
63aspect of my site will degrade gracefully.
64
65* Create a Comment Form
66
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.
70
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.
75
76The form contains the following structure:
77
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.
865. =<button>= - A button to submit the form.
87
88#+begin_src html
89<form action="/comment.php" method="POST">
90 <h3>Leave a Comment</h3>
91 <section hidden>
92 <label class="form-label" for="postURL">Post URL</label>
93 <input
94 class="form-control"
95 id="postURL"
96 name="postURL"
97 type="text"
98 value="<current-url>"
99 />
100 </section>
101 <section>
102 <label class="form-label" for="userName">Display Name</label>
103 <input
104 class="form-control"
105 id="userName"
106 name="userName"
107 placeholder="John Doe"
108 type="text"
109 />
110 </section>
111 <section>
112 <label class="form-label" for="userContent">Your Comment</label>
113 <textarea
114 class="form-control"
115 id="userContent"
116 name="userContent"
117 rows="3"
118 placeholder="# Feel free to use Markdown"
119 aria-describedby="commentHelp"
120 required
121 ></textarea>
122 <div id="commentHelp" class="form-text">
123 Comments are saved as Markdown and cannot be edited or deleted.
124 </div>
125 </section>
126 <button type="submit">Submit</button>
127</form>
128#+end_src
129
130* Handle Comments via POST
131
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.
136
137See the code block below for the contents of my =comment.php= script. We
138perform the following tasks in this script:
139
1401. Grab the POST data from the HTML form.
1412. Sanitize the comment data with =htmlentities=.
1423. Set the display name to =Anonymous= if it was left blank.
1434. Create a PHP object that combines all of this data.
1445. 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.
151
152#+begin_src php
153// Get the content sent from the comment form
154$comment = htmlentities($_POST['userContent']);
155$post_url = $_POST['postURL'];
156
157// Set default values if blank
158if (isset($_POST['userName']) && trim($_POST['userName']) !== "") {
159 $username = $_POST['userName'];
160} else {
161 $username = 'Anonymous';
162}
163
164// Create an empty PHP object
165$user_object = new stdClass();
166
167// Add object content
168$user_object->timestamp = date('Y-m-d H:i:s');
169$user_object->username = $username;
170$user_object->comment = $comment;
171$user_object->post_url = $post_url;
172
173// Append JSON to file
174$file_name = 'comments.json';
175if (file_exists($file_name)) {
176 $source_data = file_get_contents($file_name);
177 $temp_array = json_decode($source_data);
178 array_push($temp_array, $user_object);
179 $json_data = json_encode($temp_array, JSON_PRETTY_PRINT);
180 file_put_contents($file_name, $json_data);
181} else {
182 die('Error: The "comments.json" file does not exist.');
183}
184
185// Send the user back
186header('Location: ' . $post_url . '#comments');
187#+end_src
188
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=.
192
193#+begin_src sh
194chgrp -R www-data /path/to/website/
195chmod -R g+w comments.json
196#+end_src
197
198* Display User Comments
199
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.
203
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.
207
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.
212
213#+begin_src php
214$query = 'your-blog-post.html';
215
216// Load saved comments
217$comments_file = 'comments.json';
218$comments_raw = file_get_contents($comments_file);
219$comments = json_decode($comments_raw);
220$comment_section = '<section id="comments" class="comments"><h3>Comments</h3>';
221foreach ($comments as $comment) {
222 if ($comment->post_url == "https://example.com/post/" . $query) {
223 // Assign metadata to variables
224 $comment_timestamp = $comment->timestamp;
225 $comment_username = $comment->username;
226 $comment_content = $comment->comment;
227
228 // Parse the comment, in case it contains Markdown
229 $comment_md = Parsedown::instance()->text($comment_content);
230 $comment_html = new DOMDocument();
231 $comment_html->loadHTML($comment_md);
232 $comment_html_links = $comment_html->getElementsByTagName('a');
233 foreach ($comment_html_links as $comment_html_link) {
234 $comment_html_link->setAttribute('rel', 'noreferrer');
235 $comment_html_link->setAttribute('target', '_blank');
236 }
237 $comment_secured_html = $comment_html->saveHTML();
238
239 // Apply metadata to comments section
240 $comment_section .= '<div class="user-comment"><div class="row"><label>Timestamp:</label><p>' . $comment_timestamp . '</p></div><div class="row"><label>Name:</label><p>' . $comment_username . '</p></div><div class="row markdown"><label>Comment:</label><div class="comment-markdown">' . $comment_secured_html . '</div></div></div>';
241 }
242}
243
244echo $comment_section;
245#+end_src
246
247* Bonus: Create a 'Recent Comments' Page
248
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.
253
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/=.
257
258* Possible Enhancements
259
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:
263
264- 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.
content/blog/2021-04-28-photography.md deleted −68
@@ -1,68 +0,0 @@
1+++
2date = 2021-04-28
3title = "Jumping Back Into Photography"
4description = ""
5draft = false
6+++
7
8# Why Photography?
9
10I've often wondered why photography is as enticing as it is. You can see
11billions of people around the world taking photographs every single moment of
12the day. New technology often boasts about their photographic capabilities,
13especially smartphones. I would even assume that we live in a world where there
14is never a moment in which a photograph is not being taken somewhere on Earth.
15
16As for myself, I would simply say that I enjoy preserving a memory in physical
17(or digital) form. I've never had the best memory when it comes to recalling
18details of places and people gone by, so it helps to have a frame of reference
19lying around.
20
21Regardless of the reason, I think most people would agree that you simply cannot
22have too many hobbies.
23
24# Older Cameras
25
26I started playing around with the idea of photography when my family purchased a
27Fujifilm camera for family-specific events. I don't recall the specific model,
28but I do recall it was a point-and-shoot camera without an interchangeable lens.
29However, it was of great value to someone, like myself, who couldn't afford any
30other camera. I took about 10,000 shots with that camera over a 3-5 year span.
31Most notably, all of my trips to California were documented through this camera.
32
33When possible, I would borrow my sister's camera, which is a Sony SLT-A58. This
34camera was great and allowed for some of my best early shots, especially those
35taken in Utah's and Nevada's parks.
36
37# My Current Kit
38
39I've finally come to a point in my life where I have the disposable income to
40invest in a solid photography kit. I played around with the idea of a lot of
41different cameras, different types, new vs used, etc. Finally, I settled on the
42[Sony α7 III](https://en.wikipedia.org/wiki/Sony_%CE%B17_III). This camera is
43mirror-less and uses a full-frame image sensor at 24 megapixels. I don't create
44large prints, and I am mostly focused on preserving memories in high quality for
45the next 5-10 years with this camera, so the specifications here are just
46perfect for me.
47
48For lenses, I decided to buy two lenses that could carry me through most
49situations:
50
51- [Vario-Tessar T\*\* FE 24-70 mm F4 ZA
52 OSS](https://electronics.sony.com/imaging/lenses/full-frame-e-mount/p/sel2470z)
53- [Tamron 70-300mm f4.5-6.3 Di III
54 RXD](https://www.tamron-usa.com/product/lenses/a047.html)
55
56In addition, I grabbed a couple [HGX Prime
5767mm](https://www.promaster.com/Product/6725) protection filters for the lenses.
58
59As I delve further into photography and pick up more skills, I will most likely
60go back and grab a lens with a higher f-stop value, such as f/1.8. I toyed with
61the idea of grabbing a 50 mm at `f/1.8`, but decided to keep things in a
62reasonable price range instead.
63
64Finally, I made sure to buy a photography-specific backpack with a rain guard,
65and the zipper on the back panel, to protect the equipment while wearing the
66bag. If you've ever had to haul around a DSLR (or camera of similar heft) in a
67bag that only has a shoulder strap, you'll know the pain it can cause. Putting
68all my equipment in a backpack was an easy decision.
content/blog/2021-04-28-photography.org added +71
@@ -0,0 +1,71 @@
1#+date: <2021-04-28>
2#+title: Jumping Back Into Photography
3#+description:
4
5
6* Why Photography?
7
8I'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.
14
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.
19
20Regardless of the reason, I think most people would agree that you
21simply cannot have too many hobbies.
22
23* Older Cameras
24
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.
32
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.
36
37* My Current Kit
38
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.
48
49For lenses, I decided to buy two lenses that could carry me through most
50situations:
51
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]]
56
57In addition, I grabbed a couple
58[[https://www.promaster.com/Product/6725][HGX Prime 67mm]] protection
59filters for the lenses.
60
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.
65
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.
content/blog/2021-05-30-changing-git-authors.md → content/blog/2021-05-30-changing-git-authors.org renamed +26 −28
@@ -1,33 +1,31 @@
1+++
2date = 2021-05-30
3title = "Changing Git Authors"
4description = ""
5draft = false
6+++
7
8# Changing Git Author/Email Based on Previously Committed Email
9
10Here's the dilemma: You've been committing changes to your git repository with
11an incorrect name or email (or multiple repositories), and now you want to fix
12it. Luckily, there's a semi-reliable way to fix that. While I have never
13experienced issues with this method, some people have warned that it can mess
14with historical hashes and integrity of commits, so use this method only if
15you're okay accepting that risk.
1#+date: <2021-05-30>
2#+title: Changing Git Authors
3#+description:
4
5
6* Changing Git Author/Email Based on Previously Committed Email
7
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.
1614
1715Okay, let's create the bash script:
1816
19```sh
17#+begin_src sh
2018nano change_git_authors.sh
21```
19#+end_src
2220
23The following information can be pasted directly into your bash script. The only
24changes you need to make are to the following variables:
21The following information can be pasted directly into your bash script.
22The only changes you need to make are to the following variables:
2523
26- `OLD_EMAIL`
27- `CORRECT_NAME`
28- `CORRECT_EMAIL`
24- =OLD_EMAIL=
25- =CORRECT_NAME=
26- =CORRECT_EMAIL=
2927
30```sh
28#+begin_src sh
3129#!/bin/sh
3230
3331# List all sub-directories in the current directory
@@ -60,16 +58,16 @@ do
6058
6159 cd ..
6260done
63```
61#+end_src
6462
6563Finally, save the bash script and make it executable.
6664
67```sh
65#+begin_src sh
6866chmod a+x change_git_authors.sh
69```
67#+end_src
7068
7169Now you can run the script and should see the process begin.
7270
73```sh
71#+begin_src sh
7472./change_git_authors.sh
75```
73#+end_src
content/blog/2021-07-15-delete-gitlab-repos.md → content/blog/2021-07-15-delete-gitlab-repos.org renamed +41 −42
@@ -1,53 +1,51 @@
1+++
2date = 2021-07-15
3title = "How t oDelete All GitLab Repositories"
4description = ""
5draft = false
6+++
1#+date: <2021-07-15>
2#+title: How to Delete All GitLab Repositories
3#+description:
74
8# Background
95
10Have you ever used GitLab to host your source code, moved to a different host,
11and wanted to delete everything from your GitLab account? Well, this post covers
12any scenario where you would want to delete all repositories from your GitLab
13account.
6* Background
147
15For me, I currently maintain around 30 repositories and don't like to manually
16delete them whenever I switch host. GitHub has a few different tools online to
17delete all repositories for you, but I have not found anything similar for
18GitLab, so I needed an alternative solution.
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.
1912
20# Use a Python Script
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.
2117
22## Requirements
18* Use a Python Script
2319
24Before we look at the script, make sure you know your GitLab username. Next,
25[create an authorization
26token](https://gitlab.com/-/profile/personal_access_tokens) so that the Python
27script can delete your repositories. Don't lose this token or else you'll need
28to create a new one.
20** Requirements
2921
30## Create the Script
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.
3127
32To run a Python script, you must first create it. Open a terminal and enter the
33following commands in whichever directory you prefer to store the script. You
34can do the same things in a file manager if you prefer.
28** Create the Script
3529
36```sh
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.
33
34#+begin_src sh
3735mkdir delete-gitlab
38```
36#+end_src
3937
40```sh
38#+begin_src sh
4139cd delete-gitlab
42```
40#+end_src
4341
44```sh
42#+begin_src sh
4543nano main.py
46```
44#+end_src
4745
48Enter the following code into your `main.py` script.
46Enter the following code into your =main.py= script.
4947
50```python
48#+begin_src python
5149import request
5250import json
5351
@@ -101,15 +99,16 @@ def main():
10199
102100if __name__ == "__main__":
103101 main()
104```
102#+end_src
105103
106Now that you have the proper information, replace `{user-id}` with your GitLab
107username and `{auth-token}` with the authorization token you created earlier.
104Now that you have the proper information, replace ={user-id}= with your
105GitLab username and ={auth-token}= with the authorization token you
106created earlier.
108107
109Finally, simply run the script and watch the output. You can also use PyCharm
110Community Edition to edit and run the Python script if you don't want to work in
111a terminal.
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.
112111
113```sh
112#+begin_src sh
114113python3 main.py
115```
114#+end_src
content/blog/2021-08-25-audit-sampling.md → content/blog/2021-08-25-audit-sampling.org renamed +126 −124
@@ -1,52 +1,51 @@
1+++
2date = 2021-08-25
3title = "Audit Sampling with Python"
4description = ""
5draft = false
6+++
1#+date: <2021-08-25>
2#+title: Audit Sampling with Python
3#+description:
74
8# Introduction
5
6* Introduction
97
108For anyone who is familiar with internal auditing, external auditing, or
11consulting, you will understand how tedious audit testing can become when you
12are required to test large swaths of data. When we cannot establish an automated
13means of testing an entire population, we generate samples to represent the
14population of data. This helps ensure we can have a small enough data pool to
15test and that our results still represent the population.
16
17However, sampling data within the world of audit still seems to confuse quite a
18lot of people. While some audit-focused tools have introduced sampling
19functionality (e.g. Wdesk), many audit departments and firms cannot use software
20like this due to certain constraints, such as the team's budget or knowledge.
21Here is where this article comes in: we're going to use
22[Python](https://www.python.org), a free and open-source programming language,
23to generate random samples from a dataset in order to suffice numerous audit
24situations.
25
26# Audit Requirements for Sampling
27
28Before we get into the details of how to sample with Python, I want to make sure
29I discuss the different requirements that auditors may have of samples used
30within their projects.
31
32## Randomness
33
34First, let's discuss randomness. When testing out new technology to help assist
35with audit sampling, you need to understand exactly how your samples are being
36generated. For example, if the underlying function is just picking every 57th
37element from a list, that's not truly random; it's a systematic form of
38sampling. Luckily, since Python is open-source, we have access to its codebase.
39Through this blog post, I will be using the [pandas](https://pandas.pydata.org)
40module in order to generate the random samples. More specifically, I will be
41using the
42[pandas.DataFrame.sample](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.sample.html)
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.
24
25* Audit Requirements for Sampling
26
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.
30
31** Randomness
32
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]]
4342function provided by Pandas.
4443
45Now that you know what you're using, you can always check out the code behind
46`pandas.DataFrame.sample`. This function does a lot of work, but we really only
47care about the following snippets of code:
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:
4847
49```python
48#+begin_src python
5049# Process random_state argument
5150rs = com.random_state(random_state)
5251
@@ -58,62 +57,65 @@ if ignore_index:
5857result.index = ibase.default_index(len(result))
5958
6059return result
61```
62
63The block of code above shows you that if you assign a `random_state` argument
64when you run the function, that will be used as a seed number in the random
65generation and will allow you to reproduce a sample, given that nothing else
66changes. This is critical to the posterity of audit work. After all, how can you
67say your audit process is adequately documented if the next person can't run the
68code and get the same sample? The final piece here on randomness is to look at
69the [choice](https://docs.python.org/3/library/random.html#random.choice)
70function used above. This is the crux of the generation and can also be examined
71for more detailed analysis on its reliability. As far as auditing goes, we will
72trust that these functions are mathematically random.
73
74## Sample Sizes
75
76As mentioned in the intro, sampling is only an effective method of auditing when
77it truly represents the entire population. While some audit departments or firms
78may consider certain judgmental sample sizes to be adequate, you may need to
79rely on statistically-significant confidence levels of sample testing at certain
80points. I will demonstrate both here. For statistically-significant confidence
81levels, most people will assume a 90% - 99% confidence level. In order to
82actually calculate the correct sample size, it is best to use statistical tools
83due to the tedious math work required. For example, for a population of 1000,
84and a 90% confidence level that no more than 5% of the items are nonconforming,
85you would sample 45 items.
86
87However, in my personal experience, many audit departments and firms do not use
88statistical sampling. Most people use a predetermined, often proprietary, table
89that will instruct auditors which sample sizes to choose. This allows for
90uniform testing and reduces overall workload. See the table below for a common
91implementation of sample sizes:
60#+end_src
61
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.
74
75** Sample Sizes
76
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:
9294
9395Control Frequency Sample Size - High Risk Sample Size - Low Risk
9496
95---
97--------------
9698
9799More Than Daily 40 25 Daily 40 25 Weekly 12 5 Monthly 5 3 Quarterly 2 2
98100Semi-Annually 1 1 Annually 1 1 Ad-hoc 1 1
99101
100### Sampling with Python & Pandas
102*** Sampling with Python & Pandas
101103
102In this section, I am going to cover a few basic audit situations that require
103sampling. While some situations may require more effort, the syntax,
104organization, and intellect used remain largely the same. If you've never used
105Python before, note that lines starting with a '`#`' symbol are called comments,
106and they will be skipped by Python. I highly recommend taking a quick tutorial
107online to understand the basics of Python if any of the code below is confusing
108to you.
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.
109111
110## Simple Random Sample
112** Simple Random Sample
111113
112First, let's look at a simple, random sample. The code block below will import
113the `pandas` module, load a data file, sample the data, and export the sample to
114a file.
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.
115117
116```python
118#+begin_src python
117119# Import the Pandas module
118120import pandas
119121
@@ -131,14 +133,14 @@ sample = df.sample(n=25, random_state=0)
131133
132134# Save the sample to Excel
133135sample.to_excel(file_output)
134```
136#+end_src
135137
136## Simple Random Sample: Using Multiple Input Files
138** Simple Random Sample: Using Multiple Input Files
137139
138Now that we've created a simple sample, let's create a sample from multiple
139files.
140Now that we've created a simple sample, let's create a sample from
141multiple files.
140142
141```python
143#+begin_src python
142144# Import the Pandas module
143145import pandas
144146
@@ -165,15 +167,15 @@ sample = pandas.concat([sample_01, sample_02, sample_03], ignore_index=True)
165167
166168# Save the sample to Excel
167169sample.to_excel(file_output)
168```
170#+end_src
169171
170## Stratified Random Sample
172** Stratified Random Sample
171173
172Well, what if you need to sample distinct parts of a single file? For example,
173let's write some code to separate our data by "Region" and sample those regions
174independently.
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.
175177
176```python
178#+begin_src python
177179# Import the Pandas module
178180import pandas
179181
@@ -199,16 +201,16 @@ sample = pandas.concat([sample_east, sample_west], ignore_index=True)
199201
200202# Save the sample to Excel
201203sample.to_excel(file_output)
202```
204#+end_src
203205
204## Stratified Systematic Sample
206** Stratified Systematic Sample
205207
206This next example is quite useful if you need audit coverage over a certain time
207period. This code will generate samples for each month in the data and combine
208them all together at the end. Obviously, this code can be modified to stratify
209by something other than months, if needed.
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.
210212
211```python
213#+begin_src python
212214# Import the Pandas module
213215import pandas
214216
@@ -248,21 +250,21 @@ def monthly_stratified_sample(df: pandas.DataFrame, date_column: str, num_select
248250sample_size = 3
249251sample = monthly_stratified_sample(df, 'Date of Sale', sample_size)
250252sample.to_excel(file_output)
251```
252
253### Documenting the Results
254
255Once you've generated a proper sample, there are a few things left to do in
256order to properly ensure your process is reproducible.
257
2581. Document the sample. Make sure the resulting file is readable and includes
259 the documentation listed in the next bullet.
2602. Include documentation around the data source, extraction techniques, any
261 modifications made to the data, and be sure to include a copy of the script
262 itself.
2633. Whenever possible, perform a completeness and accuracy test to ensure your
264 sample is coming from a complete and accurate population. To ensure
265 completeness, compare the record count from the data source to the record
266 count loaded into Python. To ensure accuracy, test a small sample against the
267 source data (e.g., test 5 sales against the database to see if the details
268 are accurate).
253#+end_src
254
255*** Documenting the Results
256
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).
content/blog/2021-10-09-apache-redirect.md deleted −45
@@ -1,45 +0,0 @@
1+++
2date = 2021-10-09
3title = "Apache Redirect HTML Files to a Directory"
4description = ""
5draft = false
6+++
7
8# The Problem
9
10After recently switching static site generators (SSG), my blog URLs changed with
11no option to preserve the classic `.html` extension at the end of my blog post
12URLs.
13
14I really disliked using my old SSG ([Jekyll](https://jekyllrb.com)) and prefer
15my new tool ([Zola](https://www.getzola.org)) much more, so I was determined to
16figure out a way to get the proper redirect set up so that people who find my
17posts online aren't constantly met by 404 errors.
18
19# The Solution
20
21To solve this problem, I really needed to solve two pieces:
22
231. Redirect all blog post URL requests from `/blog/some-post.html` to
24 `/blog/some-post/`.
252. Ensure that no other `.html` files are redirected, such as `index.html`.
26
27After _a lot_ of tweaking and testing, I believe I have finally found the
28solution. The solution is shown below.
29
30```conf
31RewriteEngine On
32RewriteCond %{REQUEST_URI} !\index.html$ [NC]
33RewriteRule ^(.*).html$ https://example.com/$1 [R=301,L]
34```
35
36This piece of code in the Apache `.conf` or `.htaccess` file will do the
37following:
38
391. Turn on the RewriteEngine so that we can modify URLs.
402. 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 to
42 exclude the file extension.
434. The final piece is adding the trailing slash (`/`) at the end of the URL -
44 you'll notice that I don't have an Apache rule for that since Apache handles
45 that automatically.
content/blog/2021-10-09-apache-redirect.org added +45
@@ -0,0 +1,45 @@
1#+date: <2021-10-09>
2#+title: Apache Redirect HTML Files to a Directory
3#+description:
4
5
6* The Problem
7
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.
11
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.
17
18* The Solution
19
20To solve this problem, I really needed to solve two pieces:
21
221. Redirect all blog post URL requests from =/blog/some-post.html= to
23 =/blog/some-post/=.
242. Ensure that no other =.html= files are redirected, such as
25 =index.html=.
26
27After /a lot/ of tweaking and testing, I believe I have finally found
28the solution. The solution is shown below.
29
30#+begin_src conf
31RewriteEngine On
32RewriteCond %{REQUEST_URI} !\index.html$ [NC]
33RewriteRule ^(.*).html$ https://example.com/$1 [R=301,L]
34#+end_src
35
36This piece of code in the Apache =.conf= or =.htaccess= file will do the
37following:
38
391. Turn on the RewriteEngine so that we can modify URLs.
402. 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.
content/blog/2021-12-04-cisa.md deleted −193
@@ -1,193 +0,0 @@
1+++
2date = 2021-12-04
3title = "I Passed the CISA!"
4description = ""
5draft = false
6+++
7
8# What is the CISA?
9
10For those of you lucky enough not to be knee-deep in the world of IT/IS
11Auditing, [CISA](https://www.isaca.org/credentialing/cisa) stands for Certified
12Information Systems Auditor. This certification and exam are part of ISACA's
13suite of certifications. As I often explain it to people like my family, it
14basically means you're employed to use your knowledge of information systems,
15regulations, common threats, risks, etc. in order to assess an organization's
16current control of their risk. If a risk isn't controlled (and the company
17doesn't want to accept the risk), an IS auditor will suggest implementing a
18control to address that risk.
19
20Now, the CISA certification itself is, in my opinion, the main certification for
21this career. While certifications such as the CPA or CISSP are beneficial,
22nothing matches the power of the CISA for an IS auditor when it comes to getting
23hired, getting a raise/bonus, or earning respect in the field.
24
25However, to be honest, I am a skeptic of most certifications. I understand the
26value they hold in terms of how much you need to commit to studying or learning
27on the job, as well as the market value for certifications such as the CISA. But
28I also have known some very ~~incompetent~~ _less than stellar_ auditors who
29have CPAs, CISAs, CIAs, etc.
30
31The same goes for most industries: if a person is good at studying, they can
32earn the certification. However, that knowledge means nothing unless you're
33actually able to use it in real life and perform as expected of a certification
34holder. The challenge comes when people are hired or connected strictly because
35of their certifications or resume; you need to see a person work before you can
36assume them having a CISA means they're better than someone without the CISA.
37
38Okay, rant over. Certifications are generally accepted as a measuring stick of
39commitment and quality of an employee, so I am accepting it too.
40
41# Exam Content
42
43The CISA is broken down into five sections, each weighted with a percentage of
44test questions that may appear.
45
46Since the exam contains 150 questions, here's how those sections break down:
47
48| Exam Section | Percentage of Exam | Questions |
49| --------------- | ------------------ | --------- |
50| 1 | 21% | 32 |
51| 2 | 17% | 26 |
52| 3 | 12% | 18 |
53| 4 | 23% | 34 |
54| 5 | 27% | 40 |
55| **Grand Total** | **100%** | **150** |
56
57# My Studying Habits
58
59This part is a little hard for me to break down into specific detail due to the
60craziness of the last year. While I officially purchased my studying materials
61in December 2020 and opened them to "start studying" in January 2021, I really
62wasn't able to study much due to the demands of my job and personal life.
63
64Let me approach this from a few different viewpoints.
65
66## Study Materials
67
68Let's start by discussing the study materials I purchased. I'll be referring to
69#1 as the CRM and #2 as the QAE.
70
711. [CISA Review Manual, 27th Edition |
72 Print](https://store.isaca.org/s/store#/store/browse/detail/a2S4w000004KoCbEAK)
732. [CISA Review Questions, Answers & Explanations Manual, 12th Edition |
74 Print](https://store.isaca.org/s/store#/store/browse/detail/a2S4w000004KoCcEAK)
75
76The CRM is an excellent source of information and could honestly be used as a
77reference for most IS auditors as a learning reference during their daily audit
78responsibilities. However, it is **full** of information and can be overloading
79if you're not good at filtering out useless information while studying.
80
81The QAE is the real star of the show here. This book contains 1000 questions,
82separated by exam section, and a practice exam. My only complaint about the QAE
83is that each question is immediately followed with the correct answer and
84explanations below it, which means I had to use something to constantly cover
85the answers while I was studying.
86
87I didn't use the online database version of the QAE, but I've heard that it's
88easier to use than the printed book. However, it is more expensive ($299
89database vs $129 book) which might be important if you're paying for materials
90yourself.
91
92In terms of question difficulty, I felt that the QAE was a good representation
93of the actual exam. I've seen a lot of people online say it wasn't accurate to
94the exam or that it was much easier/harder, but I disagree with all of those.
95The exam was fairly similar to the QAE, just focusing on whichever topics they
96chose for my version of the exam.
97
98If you understand the concepts, skim the CRM (and read in-depth on topics you
99struggle with), and use the QAE to continue practicing exam-like questions, you
100should be fine. I didn't use any online courses, videos, etc. - the ISACA
101materials are more than enough.
102
103## Studying Process
104
105While I was able to briefly read through sections 1 and 2 in early 2021, I had
106to stop and take a break from February/March to September. I switched jobs in
107September, which allowed me a lot more free time to study.
108
109In September, I studied sections 3-5, took notes, and did a quick review of the
110section topics. Once I felt comfortable with my notes, I took a practice exam
111from the QAE manual and scored 70% (105/150).
112
113Here's a breakdown of my initial practice exam:
114
115| Exam Section | Incorrect | Correct | Grand Total | Percent |
116| --------------- | --------- | ------- | ----------- | ------- |
117| 1 | 8 | 25 | 33 | 76% |
118| 2 | 5 | 20 | 25 | 80% |
119| 3 | 6 | 12 | 18 | 67% |
120| 4 | 10 | 23 | 33 | 70% |
121| 5 | 16 | 25 | 41 | 61% |
122| **Grand Total** | **45** | **105** | **150** | **70%** |
123
124As I expected, my toughest sections were related to project management,
125development, implementation, and security.
126
127This just leaves October and November. For these months, I tried to practice
128every few days, doing 10 questions for each section, until the exam. This came
129out to 13 practice sessions, ~140 questions per section, and ~700 questions
130total.
131
132While some practice sessions were worse and some were better, the final results
133were similar to my practice exam results. As you can see below, my averages were
134slightly worse than my practice exam. However, I got in over 700 questions of
135practice and, most importantly, _I read through the explanations every time I
136answered incorrectly and learned from my mistakes_.
137
138| Exam Section | Incorrect | Correct | Grand Total | Percent |
139| --------------- | --------- | ------- | ----------- | ------- |
140| 1 | 33 | 108 | 141 | 77% |
141| 2 | 33 | 109 | 142 | 77% |
142| 3 | 55 | 89 | 144 | 62% |
143| 4 | 52 | 88 | 140 | 63% |
144| 5 | 55 | 85 | 140 | 61% |
145| **Grand Total** | **228** | **479** | **707** | **68%** |
146
147# Results
148
149Now, how do the practice scores reflect my actual results? After all, it's hard
150to tell how good a practice regimen is unless you see how it turns out.
151
152| Exam Section | Section Name | Score |
153| ------------ | ---------------------------------------------------------------- | ----- |
154| 1 | Information Systems Auditing Process | 678 |
155| 2 | Governance and Management of IT | 590 |
156| 3 | Information Systems Acquisition, Development, and Implementation | 721 |
157| 4 | Information Systems Operations and Business Resilience | 643 |
158| 5 | Protection of Information Assets | 511 |
159
160Now, in order to pass the CISA, you need at least 450 on a sliding scale of
161200-800. Personally, I really have no clue what an average CISA score is. After
162a _very_ brief look online, I can see that the high end is usually in the low
163700s. In addition, only about 50-60% of people pass the exam.
164
165Given this information, I feel great about my scores. 616 may not be phenomenal,
166and I wish I had done better on sections 2 & 5, but my practicing seems to have
167worked very well overall.
168
169However, the practice results do not conform to the actual results. Section 2
170was one of my highest practice sections and was my second-lowest score in the
171exam. Conversely, section 3 was my second-lowest practice section and turned out
172to be my highest actual score!
173
174After reflecting, it is obvious that if you have any background on the CISA
175topics at all, the most important part of studying is doing practice questions.
176You really need to understand how to read the questions critically and pick the
177best answer.
178
179# Looking Forward
180
181I am extremely happy that I was finally able to pass the CISA. Looking to the
182future, I'm not sure what's next in terms of professional learning. My current
183company offers internal learning courses, so I will most likely focus on that if
184I need to gain more knowledge in certain areas.
185
186To be fair, even if you pass the CISA, it's hard to become an expert on any
187specific topic found within. My career may take me in a different direction, and
188I might need to focus more on security or networking certifications (or possibly
189building a better analysis/visualization portfolio if I want to go into data
190analysis/science).
191
192All I know is that I am content at the moment and extremely proud of my
193accomplishment.
content/blog/2021-12-04-cisa.org added +205
@@ -0,0 +1,205 @@
1#+date: <2021-12-04>
2#+title: I Passed the CISA!
3#+description:
4
5
6* What is the CISA?
7
8For 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.
42
43* Exam Content
44
45The CISA is broken down into five sections, each weighted with a
46percentage of test questions that may appear.
47
48Since the exam contains 150 questions, here's how those sections break
49down:
50
51| Exam Section | Percentage of Exam | Questions |
52|---------------+--------------------+-----------|
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* |
59
60* My Studying Habits
61
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.
67
68Let me approach this from a few different viewpoints.
69
70** Study Materials
71
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.
108
109** Studying Process
110
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.
115
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).
119
120Here's a breakdown of my initial practice exam:
121
122| Exam Section | Incorrect | Correct | Grand Total | Percent |
123|---------------+-----------+---------+-------------+---------|
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%* |
130
131As I expected, my toughest sections were related to project management,
132development, implementation, and security.
133
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.
138
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/.
145
146| Exam Section | Incorrect | Correct | Grand Total | Percent |
147|---------------+-----------+---------+-------------+---------|
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%* |
154
155* Results
156
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.
160
161| Exam Section | Section Name | Score |
162|--------------+------------------------------------------------------------------+-------|
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.
189
190* Looking Forward
191
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).
203
204All I know is that I am content at the moment and extremely proud of my
205accomplishment.
content/blog/2022-02-10-leaving-the-office.md deleted −207
@@ -1,207 +0,0 @@
1+++
2date = 2022-02-10
3title = "Leaving Office-Based Work in the Past"
4description = ""
5draft = false
6+++
7
8# The Working World is Changing
9
10There has been a trend for the past few years of companies slowly realizing that
11the pandemic is not just a temporary state that will go away eventually and let
12everything return to the way it was before. In terms of business and employment,
13this means that more and more jobs are being offered as permanently remote
14roles.
15
16I had always dreamt of working from home but thought of it as a fantasy,
17especially since I did not want to move over into the software development
18field. However, I have found that almost all roles being sent to me via
19recruiters are permanently remote (although most are limited to US citizens or
20even region-locked for companies who only operate in select states).
21
22I decided to take a look back at my relatively short career so far and compare
23the positive and negative effects of the different work environments I've been
24in.
25
26# In-Person Offices
27
28## Retail Internship
29
30I started my first job as a management intern at a busy retail pharmacy, working
31my 40-hour weeks on my feet. As these retail stores don't believe in resting or
32sitting down, you can guarantee that you will spend entire shifts standing,
33walking, or running around the store. Unfortunately, I worked at a time when our
34store didn't have enough managers, so I spent the majority of my tenure at the
35store running and breaking a sweat.
36
37Now, things aren't all bad in retail stores like this. It is definitely tiring
38and inefficient to force employees to work constantly, or pretend to work if
39there's nothing to do, and not allow anyone to sit down. However, if you are
40able to operate a retail store with a limited crew and provide enough comfort
41and support, I believe these jobs could be both comfortable and efficient.
42
43## Semi-Private Cubicles
44
45After about a year, I was able to find another internship - this time, it was in
46my field of interest: internal auditing. This was for a life insurance company
47that was well over 100 years old. The age of the company shows if you work
48there, as most people in management are well into their 40s-60s with little to
49no youthful leadership in the company. Likewise, they owned a large headquarters
50in a nice area of town with plenty of space, parking, etc.
51
52One upside is that each person gets their own large L-shaped desk, formed into
53cubicles that house 4 desks/employees. These "pods" of 4-person cubicles are
54linked throughout each floor of the headquarters (except the sales people, who
55had that open-floor concept going on). The walls of the cubicle were tall and
56provided a lot of privacy and sound-proofing, except when I used the standing
57desk feature (I'm over 6 feet tall, so probably not an issue for most people).
58
59I loved this environment, it allowed me to focus on my work with minimal
60distractions, but also allowed easy access, so I could spin around in my chair
61and chat with my friends without leaving my chair. This is the closest I've been
62to a home office environment (which is my personal favorite, as I'll get to
63later in this post).
64
65## Semi-Open Floor Concept
66
67When I shifted to my first full-time internal audit job out of college, I was
68working at a company that was headquartered on a floor in a downtown high-rise
69building. The company was only about 20 years old when I worked there and were
70trying a lot of new things to attract young talent, one of which was a semi-open
71floor concept for the office. My department worked just around the hallway
72corner from the executive offices and used that "modern" layout young tech
73companies started using in the 2000s/2010s.
74
75Each desk was brief, and you could look most coworkers in the face without
76moving from your chair, I hated this so much. Directly to my left was the Chief
77Audit Executive (our department's leading boss), and his desk was pointed so
78that his face would stare straight at my desk all day. I spent more time
79thinking about who was looking at me or checking on me than actually working.
80
81The other annoying part of the open concept they used was that the kitchen area
82and pathways were too close to everyone's desks (since the desks were spread
83out, to provide space or something), so noise and conversation would be constant
84throughout the day while you try to work. For someone like me, who needs silence
85to get work done, that was a non-starter.
86
87## Hotel Office Concept
88
89I currently work for a company remotely (for now) and travel to the office every
90once in a while for events and to help coach the staff underneath me. The office
91I visit uses the hotel desk concept, where you need to check in at a touch
92screen when you enter the office and "rent" a desk for the day. The same goes
93for offices and meeting rooms.
94
95These desks are flat-top only and do not have any walls at all. In addition,
96they're stacked with one row of 4 desks facing another row of 4 desks. These
97pairs of desk rows are repeated through the office.
98
99This means that when I go, I need to rent a random desk or try to remember the
100unique ID numbers on desks I like. Once I rent it, I have to make sure no one
101sat down in that desk without renting it. Then, I can sit down and work, but
102will probably need to adjust the monitors so that I'm not staring in the face of
103the person across from me all day. Finally, I need to wear headphones as this
104environment does nothing to provide you with peace or quiet.
105
106Luckily, you can rent offices with doors that offer quiet and privacy, which can
107be very nice if you have a lot of meetings or webinars on a certain day.
108
109# Home Office
110
111Okay, now let's finally get to the home office concept. I have worked from home
112for a little over two years at this point, across three different
113jobs/employers. Over this time, I have experimented with a plethora of different
114organizational ideas, desks, and room layouts to find what works best for me.
115
116These things might not apply to you, and that's fine. Everyone has a different
117situation, and I really don't think you'll know what works until you try.
118
119## Tip #1
120
121Let's start with my top rule for a home office:
122
123> If you live with others, working in a shared space is not effective.
124
125It just does not work. If you have another person sleeping in your bedroom, it
126is difficult to manage your work schedule with their sleeping/work/school
127schedule. If they wake up after you need to start work, you might wake them up
128or have to suffer the agony of staring at bright screens in a dark room.
129
130In a similar vein, working from a location such as the living room likely won't
131work either. Distractions will come far more frequently: televisions, cooking,
132cleaning, deliveries, etc. If you're like me, you'll end up playing a game
133instead of actually doing any work.
134
135## Tip #2
136
137Okay, the second thing I've discovered that works for me:
138
139> Use the pomodoro method (or something similar) to balance work tasks with
140> personal tasks.
141
142I use a very casual version of the pomodoro method where I will work for 1-2
143hours (usually set in strict intervals like 1, 1.5, 2 hours) and then will allow
144myself 30-60 minutes for personal tasks. This schedule works for me, since my
145work schedule really only comes to 3-6 hours of work per day.
146
147In this case, I'll work through my list of tasks for an hour or two and then
148give myself personal time to get drinks and food, wash dishes, put clothes in
149the washer, get the mail, etc. If you're in a convenient location, this usually
150gives time for things like getting groceries (as long as you're not a slow
151shopper).
152
153## Tip #3
154
155While I listed this one as number three, I don't think I'd accomplish anything
156without it:
157
158> Document everything: even things you didn't before - such as task lists and
159> notes from casual calls or meetings.
160
161I've noticed that staying in an office gave me more constant reminders of
162outstanding tasks or facts I had learned in a conversation. Translating
163everything to a digital world has made me lose a bit of that focus (perhaps
164since I don't have visual reminders?).
165
166Keeping a running task list of all things I have to do - even potential tasks! -
167has helped me keep up without missing anything small. Likewise, keeping notes
168for ALL meetings and calls, no matter how casual/quick, has improved my
169retention immensely. Beyond helping my mental recollection, it has saved me
170numerous times when I need to do a keyword search for some topic that was
171discussed 6+ months ago.
172
173## Tip #4
174
175Okay, last one for now.
176
177> Keep your work area clean.
178
179This one is straightforward, but I know some people struggle with cleanliness or
180may not believe it makes a difference. Trust me, keeping your desk area clean
181and organized makes a huge difference, both mentally and emotionally.
182
183Just think about it, you walk into your home office and see a clean desk with a
184laptop, dock, monitors, keyboard, mouse, and a notepad with a pen on top.
185
186Now imagine the opposite, there's an office with the same equipment, but there
187are clothes hanging on the chair, empty drink bottles, candy wrappers and dirty
188plates. This can take both a mental and emotional toll by bringing constant
189disarray and stress into your working environment.
190
191Just keep things clean each day, and you won't need to do any big cleaning days
192to recover.
193
194# My Preferences
195
196I've talked about the different environments I've worked in and expressed some
197honest thoughts on pros or cons to each, but what do I prefer? Well, if you're
198reading along, you should be able to tell that I much prefer a home office above
199all else.
200
201Being able to control my own day and allot my time as needed has brought a
202calmness to my life and has allowed me to maximize each day. I feel far more
203effective and efficient in a home office than any other office, especially
204open-office layouts.
205
206If I do need to return to an office part-time in the future, I really hope the
207office will have privacy and quietness in order for me to get my work done.
content/blog/2022-02-10-leaving-the-office.org added +231
@@ -0,0 +1,231 @@
1#+date: <2022-02-10>
2#+title: Leaving Office-Based Work in the Past
3#+description:
4
5
6* The Working World is Changing
7
8There has been a trend for the past few years of companies slowly
9realizing that the pandemic is not just a temporary state that will go
10away eventually and let everything return to the way it was before. In
11terms of business and employment, this means that more and more jobs are
12being offered as permanently remote roles.
13
14I had always dreamt of working from home but thought of it as a fantasy,
15especially since I did not want to move over into the software
16development field. However, I have found that almost all roles being
17sent to me via recruiters are permanently remote (although most are
18limited to US citizens or even region-locked for companies who only
19operate in select states).
20
21I decided to take a look back at my relatively short career so far and
22compare the positive and negative effects of the different work
23environments I've been in.
24
25* In-Person Offices
26
27** Retail Internship
28
29I started my first job as a management intern at a busy retail pharmacy,
30working my 40-hour weeks on my feet. As these retail stores don't
31believe in resting or sitting down, you can guarantee that you will
32spend entire shifts standing, walking, or running around the store.
33Unfortunately, I worked at a time when our store didn't have enough
34managers, so I spent the majority of my tenure at the store running and
35breaking a sweat.
36
37Now, things aren't all bad in retail stores like this. It is definitely
38tiring and inefficient to force employees to work constantly, or pretend
39to work if there's nothing to do, and not allow anyone to sit down.
40However, if you are able to operate a retail store with a limited crew
41and provide enough comfort and support, I believe these jobs could be
42both comfortable and efficient.
43
44** Semi-Private Cubicles
45
46After about a year, I was able to find another internship - this time,
47it was in my field of interest: internal auditing. This was for a life
48insurance company that was well over 100 years old. The age of the
49company shows if you work there, as most people in management are well
50into their 40s-60s with little to no youthful leadership in the company.
51Likewise, they owned a large headquarters in a nice area of town with
52plenty of space, parking, etc.
53
54One upside is that each person gets their own large L-shaped desk,
55formed into cubicles that house 4 desks/employees. These "pods" of
564-person cubicles are linked throughout each floor of the headquarters
57(except the sales people, who had that open-floor concept going on). The
58walls of the cubicle were tall and provided a lot of privacy and
59sound-proofing, except when I used the standing desk feature (I'm over 6
60feet tall, so probably not an issue for most people).
61
62I loved this environment, it allowed me to focus on my work with minimal
63distractions, but also allowed easy access, so I could spin around in my
64chair and chat with my friends without leaving my chair. This is the
65closest I've been to a home office environment (which is my personal
66favorite, as I'll get to later in this post).
67
68** Semi-Open Floor Concept
69
70When I shifted to my first full-time internal audit job out of college,
71I was working at a company that was headquartered on a floor in a
72downtown high-rise building. The company was only about 20 years old
73when I worked there and were trying a lot of new things to attract young
74talent, one of which was a semi-open floor concept for the office. My
75department worked just around the hallway corner from the executive
76offices and used that "modern" layout young tech companies started using
77in the 2000s/2010s.
78
79Each desk was brief, and you could look most coworkers in the face
80without moving from your chair, I hated this so much. Directly to my
81left was the Chief Audit Executive (our department's leading boss), and
82his desk was pointed so that his face would stare straight at my desk
83all day. I spent more time thinking about who was looking at me or
84checking on me than actually working.
85
86The other annoying part of the open concept they used was that the
87kitchen area and pathways were too close to everyone's desks (since the
88desks were spread out, to provide space or something), so noise and
89conversation would be constant throughout the day while you try to work.
90For someone like me, who needs silence to get work done, that was a
91non-starter.
92
93** Hotel Office Concept
94
95I currently work for a company remotely (for now) and travel to the
96office every once in a while for events and to help coach the staff
97underneath me. The office I visit uses the hotel desk concept, where you
98need to check in at a touch screen when you enter the office and "rent"
99a desk for the day. The same goes for offices and meeting rooms.
100
101These desks are flat-top only and do not have any walls at all. In
102addition, they're stacked with one row of 4 desks facing another row of
1034 desks. These pairs of desk rows are repeated through the office.
104
105This means that when I go, I need to rent a random desk or try to
106remember the unique ID numbers on desks I like. Once I rent it, I have
107to make sure no one sat down in that desk without renting it. Then, I
108can sit down and work, but will probably need to adjust the monitors so
109that I'm not staring in the face of the person across from me all day.
110Finally, I need to wear headphones as this environment does nothing to
111provide you with peace or quiet.
112
113Luckily, you can rent offices with doors that offer quiet and privacy,
114which can be very nice if you have a lot of meetings or webinars on a
115certain day.
116
117* Home Office
118
119Okay, now let's finally get to the home office concept. I have worked
120from home for a little over two years at this point, across three
121different jobs/employers. Over this time, I have experimented with a
122plethora of different organizational ideas, desks, and room layouts to
123find what works best for me.
124
125These things might not apply to you, and that's fine. Everyone has a
126different situation, and I really don't think you'll know what works
127until you try.
128
129** Tip #1
130
131Let's start with my top rule for a home office:
132
133#+begin_quote
134If you live with others, working in a shared space is not effective.
135#+end_quote
136
137It just does not work. If you have another person sleeping in your
138bedroom, it is difficult to manage your work schedule with their
139sleeping/work/school schedule. If they wake up after you need to start
140work, you might wake them up or have to suffer the agony of staring at
141bright screens in a dark room.
142
143In a similar vein, working from a location such as the living room
144likely won't work either. Distractions will come far more frequently:
145televisions, cooking, cleaning, deliveries, etc. If you're like me,
146you'll end up playing a game instead of actually doing any work.
147
148** Tip #2
149
150Okay, the second thing I've discovered that works for me:
151
152#+begin_quote
153Use the pomodoro method (or something similar) to balance work tasks
154with personal tasks.
155#+end_quote
156
157I use a very casual version of the pomodoro method where I will work for
1581-2 hours (usually set in strict intervals like 1, 1.5, 2 hours) and
159then will allow myself 30-60 minutes for personal tasks. This schedule
160works for me, since my work schedule really only comes to 3-6 hours of
161work per day.
162
163In this case, I'll work through my list of tasks for an hour or two and
164then give myself personal time to get drinks and food, wash dishes, put
165clothes in the washer, get the mail, etc. If you're in a convenient
166location, this usually gives time for things like getting groceries (as
167long as you're not a slow shopper).
168
169** Tip #3
170
171While I listed this one as number three, I don't think I'd accomplish
172anything without it:
173
174#+begin_quote
175Document everything: even things you didn't before - such as task lists
176and notes from casual calls or meetings.
177#+end_quote
178
179I've noticed that staying in an office gave me more constant reminders
180of outstanding tasks or facts I had learned in a conversation.
181Translating everything to a digital world has made me lose a bit of that
182focus (perhaps since I don't have visual reminders?).
183
184Keeping a running task list of all things I have to do - even potential
185tasks! - has helped me keep up without missing anything small. Likewise,
186keeping notes for ALL meetings and calls, no matter how casual/quick,
187has improved my retention immensely. Beyond helping my mental
188recollection, it has saved me numerous times when I need to do a keyword
189search for some topic that was discussed 6+ months ago.
190
191** Tip #4
192
193Okay, last one for now.
194
195#+begin_quote
196Keep your work area clean.
197#+end_quote
198
199This one is straightforward, but I know some people struggle with
200cleanliness or may not believe it makes a difference. Trust me, keeping
201your desk area clean and organized makes a huge difference, both
202mentally and emotionally.
203
204Just think about it, you walk into your home office and see a clean desk
205with a laptop, dock, monitors, keyboard, mouse, and a notepad with a pen
206on top.
207
208Now imagine the opposite, there's an office with the same equipment, but
209there are clothes hanging on the chair, empty drink bottles, candy
210wrappers and dirty plates. This can take both a mental and emotional
211toll by bringing constant disarray and stress into your working
212environment.
213
214Just keep things clean each day, and you won't need to do any big
215cleaning days to recover.
216
217* My Preferences
218
219I've talked about the different environments I've worked in and
220expressed some honest thoughts on pros or cons to each, but what do I
221prefer? Well, if you're reading along, you should be able to tell that I
222much prefer a home office above all else.
223
224Being able to control my own day and allot my time as needed has brought
225a calmness to my life and has allowed me to maximize each day. I feel
226far more effective and efficient in a home office than any other office,
227especially open-office layouts.
228
229If I do need to return to an office part-time in the future, I really
230hope the office will have privacy and quietness in order for me to get
231my work done.
content/blog/2022-02-10-njalla-dns-api.md → content/blog/2022-02-10-njalla-dns-api.org renamed +81 −79
@@ -1,104 +1,106 @@
1+++
2date = 2022-02-10
3title = "Dynamic DNS with Njalla API"
4description = ""
5draft = false
6+++
1#+date: <2022-02-10>
2#+title: Dynamic DNS with Njalla API
3#+description:
74
8# Njalla's API
95
10As noted in my recent post about [switching to Njalla from
11Cloudflare](file:///blog/ditching-cloudflare/), I was searching for a way to
12replace my very easy-to-use bash script to [update Cloudflare's DNS via their
13API](file:///blog/cloudflare-dns-api/).
6* Njalla's API
147
15To reiterate what I said in those posts, this is a common necessity for those of
16us who have non-static IP addresses that can change at any moment due to ISP
17policy.
8As noted in my recent post about
9[[file:///blog/ditching-cloudflare/][switching to Njalla from
10Cloudflare]], I was searching for a way to replace my very easy-to-use
11bash script to [[file:///blog/cloudflare-dns-api/][update Cloudflare's
12DNS via their API]].
1813
19In order to keep a home server running smoothly, the server admin needs to have
20a process to constantly monitor their public IP address and update their
21domain's DNS records if it changes.
14To reiterate what I said in those posts, this is a common necessity for
15those of us who have non-static IP addresses that can change at any
16moment due to ISP policy.
2217
23This post explains how to use Python to update Njalla's DNS records whenever a
24machine's public IP address changes.
18In order to keep a home server running smoothly, the server admin needs
19to have a process to constantly monitor their public IP address and
20update their domain's DNS records if it changes.
2521
26## Creating a Token
22This post explains how to use Python to update Njalla's DNS records
23whenever a machine's public IP address changes.
2724
28To use Njalla's API, you will first need to create a token that will be used to
29authenticate you every time you call the API. Luckily, this is very easy to do
30if you have an account with Njalla.
25** Creating a Token
3126
32Simply go the [API Settings](https://njal.la/settings/api/) page and click the
33`Add Token` button. Next, enter a name for the token and click `Add`.
27To use Njalla's API, you will first need to create a token that will be
28used to authenticate you every time you call the API. Luckily, this is
29very easy to do if you have an account with Njalla.
3430
35Finally, click the `Manage` button next to your newly created token and copy the
36`API Token` field.
31Simply go the [[https://njal.la/settings/api/][API Settings]] page and
32click the =Add Token= button. Next, enter a name for the token and click
33=Add=.
3734
38## Finding the Correct API Request
35Finally, click the =Manage= button next to your newly created token and
36copy the =API Token= field.
3937
40Once you have a token, you're ready to call the Njalla API for any number of
41requests. For a full listing of available requests, see the [Njalla API
42Documentation](https://njal.la/api/).
38** Finding the Correct API Request
4339
44For this demo, we are using the `list-records` and `edit-record` requests.
40Once you have a token, you're ready to call the Njalla API for any
41number of requests. For a full listing of available requests, see the
42[[https://njal.la/api/][Njalla API Documentation]].
4543
46The `list-records` request requires the following payload to be sent when
47calling the API:
44For this demo, we are using the =list-records= and =edit-record=
45requests.
46
47The =list-records= request requires the following payload to be sent
48when calling the API:
4849
49```txt
50#+begin_src txt
5051params: {
5152 domain: string
5253}
53```
54#+end_src
5455
55The `edit-record` request requires the following payload to be sent when calling
56the API:
56The =edit-record= request requires the following payload to be sent when
57calling the API:
5758
58```txt
59#+begin_src txt
5960params: {
6061 domain: string
6162 id: int
6263 content: string
6364}
64```
65#+end_src
6566
66# Server Set-Up
67* Server Set-Up
6768
68To create this script, we will be using Python. By default, I use Python 3 on my
69servers, so please note that I did not test this in Python 2, and I do not know
70if Python 2 will work for this.
69To create this script, we will be using Python. By default, I use Python
703 on my servers, so please note that I did not test this in Python 2,
71and I do not know if Python 2 will work for this.
7172
72## Creating the Script
73** Creating the Script
7374
74First, find a suitable place to create your script. Personally, I just create a
75directory called `ddns` in my home directory:
75First, find a suitable place to create your script. Personally, I just
76create a directory called =ddns= in my home directory:
7677
77```sh
78#+begin_src sh
7879mkdir ~/ddns
79```
80#+end_src
8081
8182Next, create a Python script file:
8283
83```sh
84#+begin_src sh
8485nano ~/ddns/ddns.py
85```
86#+end_src
8687
87The following code snippet is quite long, so I won't go into depth on each part.
88However, I suggest you read through the entire script before running it; it is
89quite simple and contains comments to help explain each code block.
88The following code snippet is quite long, so I won't go into depth on
89each part. However, I suggest you read through the entire script before
90running it; it is quite simple and contains comments to help explain
91each code block.
9092
91:warning: **Note**: You will need to update the following variables for this to
92work:
93:warning: *Note*: You will need to update the following variables for
94this to work:
9395
94- `token`: This is the Njalla API token you created earlier.
95- `user_domain`: This is the top-level domain you want to modify.
96- `include_subdomains`: Set this to `True` if you also want to modify
97 subdomains found under the TLD.
98- `subdomains`: If `include_subdomains` = `True`, you can include your list of
99 subdomains to be modified here.
96- =token=: This is the Njalla API token you created earlier.
97- =user_domain=: This is the top-level domain you want to modify.
98- =include_subdomains=: Set this to =True= if you also want to modify
99 subdomains found under the TLD.
100- =subdomains=: If =include_subdomains= = =True=, you can include your
101 list of subdomains to be modified here.
100102
101```python
103#+begin_src python
102104#!/usr/bin/python
103105# -*- coding: utf-8 -*-
104106# Import Python modules
@@ -177,29 +179,29 @@ for record in data['records']:
177179 'does not match Njalla's value of',
178180 record['content'], '. Updating...')
179181 update_record(user_domain, record['id'], ipv6)
180```
182#+end_src
181183
182## Running the Script
184** Running the Script
183185
184Once you've created the script and are ready to test it, run the following
185command:
186Once you've created the script and are ready to test it, run the
187following command:
186188
187```sh
189#+begin_src sh
188190python3 ~/ddns/ddns.py
189```
191#+end_src
190192
191## Setting the Script to Run Automatically
193** Setting the Script to Run Automatically
192194
193To make sure the scripts run automatically, add it to the `cron` file so that it
194will run on a schedule. To do this, open the `cron` file:
195To make sure the scripts run automatically, add it to the =cron= file so
196that it will run on a schedule. To do this, open the =cron= file:
195197
196```sh
198#+begin_src sh
197199crontab -e
198```
200#+end_src
199201
200In the cron file, paste the following at the bottom of the editor in order to
201check the IP every five minutes:
202In the cron file, paste the following at the bottom of the editor in
203order to check the IP every five minutes:
202204
203```sh
204*/5 ** ** ** ** python3 /home/<your_username>/ddns/ddns.py
205```
205#+begin_src sh
206,*/5 ** ** ** ** python3 /home/<your_username>/ddns/ddns.py
207#+end_src
content/blog/2022-02-16-debian-and-nginx.md deleted −164
@@ -1,164 +0,0 @@
1+++
2date = 2022-02-16
3title = "Migrating to a New Web Server Setup with Debian, Nginx, and Agate"
4description = ""
5draft = false
6+++
7
8# Server OS: Debian
9
10I've used various Linux distributions throughout the years, but I've never used
11anything except Ubuntu for my servers. Why? I really have no idea, mostly just
12comfort around the commands and software availability.
13
14However, I have always wanted to try Debian as a server OS after testing it out
15in a VM a few years ago (side-note: I'd love to try Alpine too, but I always
16struggle with compatibility). So, I decided to launch a new VPS and use
17[Debian](https://www.debian.org) 11 as the OS. Spoiler alert: it feels identical
18to Ubuntu for my purposes.
19
20I did the normal things when first launching the VPS, such as adding a new user,
21locking down SSH, etc. If you want to see that level of detail, read my other
22post about [How to Set Up a VPS Web
23Server](https://cleberg.net/blog/how-to-set-up-a-vps-web-server/).
24
25All of this has been similar, apart from small things such as the location of
26users' home folders. No complaints at all from me - Debian seems great.
27
28# Web Server: Nginx
29
30Once I had the baseline server configuration set-up for Debian, I moved on to
31trying out [Nginx](https://nginx.org) as my web server software. This required
32me to install the `nginx` and `ufw` packages, as well as setting up the initial
33UFW config:
34
35```sh
36sudo apt install nginx ufw
37sudo ufw allow 'Nginx Full'
38sudo ufw allow SSH
39sudo ufw enable
40sudo ufw status
41sudo systemctl status nginx
42```
43
44Once I had the firewall set, I moved on to creating the directories and files
45for my website. This is very easy and is basically the same as setting up an
46Apache server, so no struggles here.
47
48```sh
49sudo mkdir -p /var/www/your_domain/html
50sudo chown -R $USER:$USER /var/www/your_domain/html
51sudo chmod -R 755 /var/www/your_domain
52nano /var/www/your_domain/html/index.html
53```
54
55The next part, creating the Nginx configuration files, is quite a bit different
56from Apache. First, you need to create the files in the `sites-available` folder
57and symlink it the `sites-enabled` folder.
58
59Creating the config file for your domain:
60
61```sh
62sudo nano /etc/nginx/sites-available/your_domain
63```
64
65Default content for an Nginx config file:
66
67```sh
68server {
69 listen 80;
70 listen [::]:80;
71
72 root /var/www/your_domain/html;
73 index index.html index.htm index.nginx-debian.html;
74
75 server_name your_domain www.your_domain;
76
77 location / {
78 try_files $uri $uri/ =404;
79 }
80}
81```
82
83Finally, symlink it together:
84
85```sh
86sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/
87```
88
89This will make your site available to the public (as long as you have
90`your_domain` DNS records pointed at the server's IP address)!
91
92Next, I used [certbot](https://certbot.eff.org/) to issue an HTTPS certificate
93for my domains using the following commands:
94
95```sh
96sudo apt install snapd; sudo snap install core; sudo snap refresh core
97sudo snap install --classic certbot
98sudo ln -s /snap/bin/certbot /usr/bin/certbot
99sudo certbot --nginx
100```
101
102Now that certbot ran successfully and updated my Nginx config files to include a
103`443` server block of code, I went back in and edited the config file to include
104security HTTP headers. This part is optional, but is recommended for security
105purposes; you can even test a website's HTTP header security at [Security
106Headers](https://securityheaders.com/).
107
108The configuration below shows a set-up where you only want your website to serve
109content from its own domain, except for images and scripts, which may come from
110`nullitics.com`. All other content would be blocked from loading in a browser.
111
112```sh
113sudo nano /etc/nginx/sites-available/your_domain
114```
115
116```sh
117server {
118 ...
119 add_header Content-Security-Policy "default-src 'none'; img-src 'self' https://nullitics.com; script-src 'self' https://nullitics.com; style-src 'self'; font-src 'self'";
120 add_header X-Content-Type-Options "nosniff";
121 add_header X-XSS-Protection "1; mode=block";
122 add_header X-Frame-Options "DENY";
123 add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
124 add_header Referrer-Policy "no-referrer";
125 ...
126}
127```
128
129```sh
130sudo systemctl restart nginx
131```
132
133## Nginx vs. Apache
134
135As I stated at the beginning, my historical hesitation with trying Nginx was
136that the differences in configuration formats scared me away from leaving
137Apache. However, I prefer Nginx to Apache for a few reasons:
138
1391. Nginx uses only one config file (`your_domain`) vs. Apache's two-file
140 approach for HTTP vs. HTTPS (`your_domain.conf` and
141 `your_domain-le-ssl.conf`).
1422. Symlinking new configurations files and reloading Nginx are way easier than
143 Apache's process of having to enable headers with `a2enmod mod_headers`,
144 enable PHP with `a2enmod php` (plus any other mods you need), and then
145 enabling sites with `a2ensite`, and THEN reloading Apache.
1463. The contents of the Nginx config files seem more organized and logical with
147 the curly-bracket approach. This is a minor reason, but everything just felt
148 cleaner while I was installing my sites and that had a big quality of life
149 impact on the installation for me.
150
151They're both great software packages, but Nginx just seems more organized and
152easier to use these days. I will certainly be exploring the Nginx docs to see
153what other fun things I can do with all of this.
154
155# Gemini Server: Agate
156
157Finally, I set up the Agate software on this server again to host my Gemini
158server content, using Rust as I have before. You can read my other post for more
159information on installing Agate: [Hosting a Gemini
160Server](https://cleberg.net/blog/hosting-a-gemini-server/).
161
162All in all, Debian + Nginx is very slick and I prefer it over my old combination
163of Ubuntu + Apache (although it's really just Nginx > Apache for me, since
164Debian seems mostly the same as Ubuntu is so far).
content/blog/2022-02-16-debian-and-nginx.org added +167
@@ -0,0 +1,167 @@
1#+date: <2022-02-16>
2#+title: Migrating to a New Web Server Setup with Debian, Nginx, and Agate
3#+description:
4
5
6* Server OS: Debian
7
8I've used various Linux distributions throughout the years, but I've
9never used anything except Ubuntu for my servers. Why? I really have no
10idea, mostly just comfort around the commands and software availability.
11
12However, I have always wanted to try Debian as a server OS after testing
13it out in a VM a few years ago (side-note: I'd love to try Alpine too,
14but I always struggle with compatibility). So, I decided to launch a new
15VPS and use [[https://www.debian.org][Debian]] 11 as the OS. Spoiler
16alert: it feels identical to Ubuntu for my purposes.
17
18I did the normal things when first launching the VPS, such as adding a
19new user, locking down SSH, etc. If you want to see that level of
20detail, read my other post about
21[[https://cleberg.net/blog/how-to-set-up-a-vps-web-server/][How to Set
22Up a VPS Web Server]].
23
24All of this has been similar, apart from small things such as the
25location of users' home folders. No complaints at all from me - Debian
26seems great.
27
28* Web Server: Nginx
29
30Once I had the baseline server configuration set-up for Debian, I moved
31on to trying out [[https://nginx.org][Nginx]] as my web server software.
32This required me to install the =nginx= and =ufw= packages, as well as
33setting up the initial UFW config:
34
35#+begin_src sh
36sudo apt install nginx ufw
37sudo ufw allow 'Nginx Full'
38sudo ufw allow SSH
39sudo ufw enable
40sudo ufw status
41sudo systemctl status nginx
42#+end_src
43
44Once I had the firewall set, I moved on to creating the directories and
45files for my website. This is very easy and is basically the same as
46setting up an Apache server, so no struggles here.
47
48#+begin_src sh
49sudo mkdir -p /var/www/your_domain/html
50sudo chown -R $USER:$USER /var/www/your_domain/html
51sudo chmod -R 755 /var/www/your_domain
52nano /var/www/your_domain/html/index.html
53#+end_src
54
55The next part, creating the Nginx configuration files, is quite a bit
56different from Apache. First, you need to create the files in the
57=sites-available= folder and symlink it the =sites-enabled= folder.
58
59Creating the config file for your domain:
60
61#+begin_src sh
62sudo nano /etc/nginx/sites-available/your_domain
63#+end_src
64
65Default content for an Nginx config file:
66
67#+begin_src sh
68server {
69 listen 80;
70 listen [::]:80;
71
72 root /var/www/your_domain/html;
73 index index.html index.htm index.nginx-debian.html;
74
75 server_name your_domain www.your_domain;
76
77 location / {
78 try_files $uri $uri/ =404;
79 }
80}
81#+end_src
82
83Finally, symlink it together:
84
85#+begin_src sh
86sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/
87#+end_src
88
89This will make your site available to the public (as long as you have
90=your_domain= DNS records pointed at the server's IP address)!
91
92Next, I used [[https://certbot.eff.org/][certbot]] to issue an HTTPS
93certificate for my domains using the following commands:
94
95#+begin_src sh
96sudo apt install snapd; sudo snap install core; sudo snap refresh core
97sudo snap install --classic certbot
98sudo ln -s /snap/bin/certbot /usr/bin/certbot
99sudo certbot --nginx
100#+end_src
101
102Now that certbot ran successfully and updated my Nginx config files to
103include a =443= server block of code, I went back in and edited the
104config file to include security HTTP headers. This part is optional, but
105is recommended for security purposes; you can even test a website's HTTP
106header security at [[https://securityheaders.com/][Security Headers]].
107
108The configuration below shows a set-up where you only want your website
109to serve content from its own domain, except for images and scripts,
110which may come from =nullitics.com=. All other content would be blocked
111from loading in a browser.
112
113#+begin_src sh
114sudo nano /etc/nginx/sites-available/your_domain
115#+end_src
116
117#+begin_src sh
118server {
119 ...
120 add_header Content-Security-Policy "default-src 'none'; img-src 'self' https://nullitics.com; script-src 'self' https://nullitics.com; style-src 'self'; font-src 'self'";
121 add_header X-Content-Type-Options "nosniff";
122 add_header X-XSS-Protection "1; mode=block";
123 add_header X-Frame-Options "DENY";
124 add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
125 add_header Referrer-Policy "no-referrer";
126 ...
127}
128#+end_src
129
130#+begin_src sh
131sudo systemctl restart nginx
132#+end_src
133
134** Nginx vs. Apache
135
136As I stated at the beginning, my historical hesitation with trying Nginx
137was that the differences in configuration formats scared me away from
138leaving Apache. However, I prefer Nginx to Apache for a few reasons:
139
1401. Nginx uses only one config file (=your_domain=) vs. Apache's two-file
141 approach for HTTP vs. HTTPS (=your_domain.conf= and
142 =your_domain-le-ssl.conf=).
1432. Symlinking new configurations files and reloading Nginx are way
144 easier than Apache's process of having to enable headers with
145 =a2enmod mod_headers=, enable PHP with =a2enmod php= (plus any other
146 mods you need), and then enabling sites with =a2ensite=, and THEN
147 reloading Apache.
1483. The contents of the Nginx config files seem more organized and
149 logical with the curly-bracket approach. This is a minor reason, but
150 everything just felt cleaner while I was installing my sites and that
151 had a big quality of life impact on the installation for me.
152
153They're both great software packages, but Nginx just seems more
154organized and easier to use these days. I will certainly be exploring
155the Nginx docs to see what other fun things I can do with all of this.
156
157* Gemini Server: Agate
158
159Finally, I set up the Agate software on this server again to host my
160Gemini server content, using Rust as I have before. You can read my
161other post for more information on installing Agate:
162[[https://cleberg.net/blog/hosting-a-gemini-server/][Hosting a Gemini
163Server]].
164
165All in all, Debian + Nginx is very slick and I prefer it over my old
166combination of Ubuntu + Apache (although it's really just Nginx > Apache
167for me, since Debian seems mostly the same as Ubuntu is so far).
content/blog/2022-02-17-exiftool.md deleted −59
@@ -1,59 +0,0 @@
1+++
2date = 2022-02-17
3title = "Stripping Image Metadata with Exiftool"
4description = ""
5draft = false
6+++
7
8## Why Strip Metadata?
9
10Okay, so you want to strip metadata from your photos. Perhaps you take pictures
11of very rare birds, and the location metadata is a gold mine for poachers, or
12perhaps you're just privacy-oriented like me and prefer to strip metadata from
13publicly-available images.
14
15There are various components of image metadata that you may want to delete
16before releasing a photo to the public. Here's an incomplete list of things I
17could easily see just by inspecting a photo on my laptop:
18
19- Location (Latitude & Longitude)
20- Dimensions
21- Device Make & Model
22- Color Space
23- Color Profile
24- Focal Length
25- Alpha Channel
26- Red Eye
27- Metering Mode
28- F Number
29
30Regardless of your reasoning, I'm going to explain how I used the `exiftool`
31package in Linux to automatically strip metadata from all images in a directory
32(+ subdirectories).
33
34## Installing `exiftool`
35
36First things first: we need to install the tool. I'm running Debian 11 on my
37server (Ubuntu will work the same), so the command is as simple as:
38
39```sh
40sudo apt install exiftool
41```
42
43There are different tools that can accomplish the same thing across
44distributions, but I really only care to test out this one package.
45
46## Recursively Strip Data
47
48I actually use this tool extensively to strip any photos uploaded to the website
49that serves all the images for my blog (`img.cleberg.net`).
50
51The following command is incredibly useful and can be modified to include any
52image extensions that `exiftool` supports:
53
54```sh
55exiftool -r -all= -ext jpg -ext png /path/to/directory/
56```
57
58The output of the command will let you know how many directories were scanned,
59how many images were updated, and how many images were unchanged.
content/blog/2022-02-17-exiftool.org added +59
@@ -0,0 +1,59 @@
1#+date: <2022-02-17>
2#+title: Stripping Image Metadata with Exiftool
3#+description:
4
5
6** Why Strip Metadata?
7
8Okay, so you want to strip metadata from your photos. Perhaps you take
9pictures of very rare birds, and the location metadata is a gold mine
10for poachers, or perhaps you're just privacy-oriented like me and prefer
11to strip metadata from publicly-available images.
12
13There are various components of image metadata that you may want to
14delete before releasing a photo to the public. Here's an incomplete list
15of things I could easily see just by inspecting a photo on my laptop:
16
17- Location (Latitude & Longitude)
18- Dimensions
19- Device Make & Model
20- Color Space
21- Color Profile
22- Focal Length
23- Alpha Channel
24- Red Eye
25- Metering Mode
26- F Number
27
28Regardless of your reasoning, I'm going to explain how I used the
29=exiftool= package in Linux to automatically strip metadata from all
30images in a directory (+ subdirectories).
31
32** Installing =exiftool=
33
34First things first: we need to install the tool. I'm running Debian 11
35on my server (Ubuntu will work the same), so the command is as simple
36as:
37
38#+begin_src sh
39sudo apt install exiftool
40#+end_src
41
42There are different tools that can accomplish the same thing across
43distributions, but I really only care to test out this one package.
44
45** Recursively Strip Data
46
47I actually use this tool extensively to strip any photos uploaded to the
48website that serves all the images for my blog (=img.cleberg.net=).
49
50The following command is incredibly useful and can be modified to
51include any image extensions that =exiftool= supports:
52
53#+begin_src sh
54exiftool -r -all= -ext jpg -ext png /path/to/directory/
55#+end_src
56
57The output of the command will let you know how many directories were
58scanned, how many images were updated, and how many images were
59unchanged.
content/blog/2022-02-20-nginx-caching.md deleted −65
@@ -1,65 +0,0 @@
1+++
2date = 2022-02-20
3title = "Caching Static Content with Nginx"
4description = ""
5draft = false
6+++
7
8# Update Your Nginx Config to Cache Static Files
9
10If you run a website on Nginx that serves static content (i.e., content that is
11not dynamic and changing with interactions from the user), you would likely
12benefit from caching that content on the client-side. If you're used to Apache
13and looking for the Nginx equivalent, this post should help.
14
15Luckily, setting up the cache is as easy as identifying the file types you want
16to cache and determining the expiration length. To include more file types,
17simply use the bar separator (`|`) and type the new file extension you want to
18include.
19
20```config
21server {
22 ...
23
24 location ~** .(css|js|jpg|jpeg|gif|png|ico)$ {
25 expires 30d;
26 }
27
28 ...
29}
30```
31
32I have seen some people who prefer to set `expires` as `365d` or even `max`, but
33that is only for stable, infrequently changing websites. As my site often
34changes (i.e., I'm never content with my website), I need to know that my
35readers are seeing the new content without waiting too long.
36
37So, I went ahead and set the expiration date at `30d`, which is short enough to
38refresh for readers but long enough that clients/browsers won't be re-requesting
39the static files too often, hopefully resulting in faster loading times, as
40images should be the only thing slowing down my site.
41
42# Testing Results
43
44To test my changes to the Nginx configuration, I used the [HTTP Header
45Live](https://addons.mozilla.org/en-US/firefox/addon/http-header-live/)
46extension on my Gecko browser and used the sidebar to inspect the headers of a
47recent image from my blog.
48
49In the image below, you can see that the `Cache-Control` header is now present
50and set to 2592000, which is 30 days represented in seconds (30 days \_ 24
51hours/day \_ 60 minutes/hour \*\* 60 seconds/minute = 2,592,000 seconds).
52
53The `Expires` field is now showing 22 March 2022, which is 30 days from the day
54of this post, 20 February 2022.
55
56# Caveats
57
58Remember that this caching system is **client-side**, which means that content
59is only cached for as long as a client allows it. For example, my browser purges
60all caches, data, etc. upon exit, so this caching policy will only work as long
61as my browser remains open and running.
62
63If you need to test updates to your site, you'll need to clear the cache to see
64updates for any file extension you configured. This can often be done with the
65`Shift + F5` or `Ctrl + F5` key combinations in most browsers.
content/blog/2022-02-20-nginx-caching.org added +68
@@ -0,0 +1,68 @@
1#+date: <2022-02-20>
2#+title: Caching Static Content with Nginx
3#+description:
4
5
6* Update Your Nginx Config to Cache Static Files
7
8If you run a website on Nginx that serves static content (i.e., content
9that is not dynamic and changing with interactions from the user), you
10would likely benefit from caching that content on the client-side. If
11you're used to Apache and looking for the Nginx equivalent, this post
12should help.
13
14Luckily, setting up the cache is as easy as identifying the file types
15you want to cache and determining the expiration length. To include more
16file types, simply use the bar separator (=|=) and type the new file
17extension you want to include.
18
19#+begin_src config
20server {
21 ...
22
23 location ~** .(css|js|jpg|jpeg|gif|png|ico)$ {
24 expires 30d;
25 }
26
27 ...
28}
29#+end_src
30
31I have seen some people who prefer to set =expires= as =365d= or even
32=max=, but that is only for stable, infrequently changing websites. As
33my site often changes (i.e., I'm never content with my website), I need
34to know that my readers are seeing the new content without waiting too
35long.
36
37So, I went ahead and set the expiration date at =30d=, which is short
38enough to refresh for readers but long enough that clients/browsers
39won't be re-requesting the static files too often, hopefully resulting
40in faster loading times, as images should be the only thing slowing down
41my site.
42
43* Testing Results
44
45To test my changes to the Nginx configuration, I used the
46[[https://addons.mozilla.org/en-US/firefox/addon/http-header-live/][HTTP
47Header Live]] extension on my Gecko browser and used the sidebar to
48inspect the headers of a recent image from my blog.
49
50In the image below, you can see that the =Cache-Control= header is now
51present and set to 2592000, which is 30 days represented in seconds (30
52days _ 24 hours/day _ 60 minutes/hour ** 60 seconds/minute = 2,592,000
53seconds).
54
55The =Expires= field is now showing 22 March 2022, which is 30 days from
56the day of this post, 20 February 2022.
57
58* Caveats
59
60Remember that this caching system is *client-side*, which means that
61content is only cached for as long as a client allows it. For example,
62my browser purges all caches, data, etc. upon exit, so this caching
63policy will only work as long as my browser remains open and running.
64
65If you need to test updates to your site, you'll need to clear the cache
66to see updates for any file extension you configured. This can often be
67done with the =Shift + F5= or =Ctrl + F5= key combinations in most
68browsers.
content/blog/2022-02-22-tuesday.md deleted −35
@@ -1,35 +0,0 @@
1+++
2date = 2022-02-22
3title = "Tuesday"
4description = ""
5draft = false
6+++
7
8# Tuesday, Twosday
9
10I'm taking a break from my usual technology-related posts and writing about
11something a little more enjoyable today.
12
13Today is Tuesday, February 22nd, 2022. Today is 02-22-2022. Today is Twosday.
14
15Let's look at everything that fell in place today:
16
171. Written in the `m-dd-yy` or `dd-m-yy` formats, today is 2-22-22 or 22-2-22,
18 which is a neat little palindrome in either format. (The last ubiquitous
19 six-digit palindrome was 1-11-11.)
202. Today is Tuesday, which is why everyone is using the nickname Twosday to call
21 out these similarities.
223. Falling on Tuesday means today is the 2nd day of the week (for most cultures.
23 For the US, it's the 3rd day of the week since we start on Sunday).
244. The only culture I could find with a connection to a `2` is that some Slavic
25 languages derived their version of Tuesday from the Old Church Slavonic word
26 `въторъ`, meaning "the second."
275. Written in the classic monospaced, digital font (think of digital clocks from
28 the 80s/90s), there is nice symmetry to the numbers ([view the image
29 here](https://img.cleberg.net/blog/20220222-tuesday/digital_font.png)!).
306. This one isn't naturally-occurring, but it seems people around the world are
31 celebrating the day. For example, a group is putting together [a wedding of
32 222 couples at the California State Capitol in
33 Sacramento](https://www.eventbrite.com/e/2-22-22-a-collective-wedding-ceremony-at-the-state-capitol-tickets-211434605597),
34 concluding at exactly 2:22 PM. These couples will record their marriage dates
35 as 2-22-22 2:22 PM. Tickets were on sale for $222.22.
content/blog/2022-02-22-tuesday.org added +38
@@ -0,0 +1,38 @@
1#+date: <2022-02-22>
2#+title: Tuesday
3#+description:
4
5
6* Tuesday, Twosday
7
8I'm taking a break from my usual technology-related posts and writing
9about something a little more enjoyable today.
10
11Today is Tuesday, February 22nd, 2022. Today is 02-22-2022. Today is
12Twosday.
13
14Let's look at everything that fell in place today:
15
161. Written in the =m-dd-yy= or =dd-m-yy= formats, today is 2-22-22 or
17 22-2-22, which is a neat little palindrome in either format. (The
18 last ubiquitous six-digit palindrome was 1-11-11.)
192. Today is Tuesday, which is why everyone is using the nickname Twosday
20 to call out these similarities.
213. Falling on Tuesday means today is the 2nd day of the week (for most
22 cultures. For the US, it's the 3rd day of the week since we start on
23 Sunday).
244. The only culture I could find with a connection to a =2= is that some
25 Slavic languages derived their version of Tuesday from the Old Church
26 Slavonic word =въторъ=, meaning "the second."
275. Written in the classic monospaced, digital font (think of digital
28 clocks from the 80s/90s), there is nice symmetry to the numbers
29 ([[https://img.cleberg.net/blog/20220222-tuesday/digital_font.png][view
30 the image here]]!).
316. This one isn't naturally-occurring, but it seems people around the
32 world are celebrating the day. For example, a group is putting
33 together
34 [[https://www.eventbrite.com/e/2-22-22-a-collective-wedding-ceremony-at-the-state-capitol-tickets-211434605597][a
35 wedding of 222 couples at the California State Capitol in
36 Sacramento]], concluding at exactly 2:22 PM. These couples will
37 record their marriage dates as 2-22-22 2:22 PM. Tickets were on sale
38 for $222.22.
content/blog/2022-03-02-reliable-notes.md deleted −127
@@ -1,127 +0,0 @@
1+++
2date = 2022-03-02
3title = "Easy, Reliable Note-Taking"
4description = ""
5draft = false
6+++
7
8# Choosing Durable File Formats
9
10> TL;DR: Write in a format that can be easily rendered and read in plain-text
11> mode (e.g., `.txt`, `.md`, etc.).
12
13As I've written more and more over the years, I've found that my love of
14note-taking is always growing. Everything I learn or need to remember can be
15written down in a note and saved digitally, with no cost to myself. Unlike paper
16copies that need physical storage space, digital files simply need space on your
17local disk or cloud storage, which is fairly abundant these days.
18
19However, I had a historical struggle with migration of notes between different
20apps that require different formats and applied proprietary styling. This meant
21that I had to go through each note during migration and edit the file to look
22presentable again.
23
24For the last year or two, I have written everything exclusively in
25[Markdown](https://en.wikipedia.org/wiki/Markdown) format. Small notes,
26long-form writings, and even these blog posts are all written in Markdown.
27
28Why Markdown? While I do appreciate the simplicity of plain-text files without
29any formatting, I often need visual cues such as heading and code blocks to keep
30my thoughts straight. Markdown provides a minimal set of styling indicators for
31me to style my notes without adding any proprietary, embedded data into the
32files. If I want a top-level heading, I simply add a hash (`#`) before the line.
33An added bonus is that even if a system doesn't understand Markdown, it will
34render it as plain-text and I can read it just as easily.
35
36For example, here's how TextEdit on macOS will open and display a Markdown file
37in plain-text, since it does contain any features to preview Markdown as HTML:
38
39![Plain Text
40Markdown](https://img.cleberg.net/blog/20220302-easy-reliable-note-taking/plain_markdown.png)
41
42## Saving & Syncing Files
43
44In order to read and edit my notes across platforms, I use my personal cloud
45storage through Tresorit due to its native integration with macOS and iOS file
46managers. In addition, Tresorit works well on Debian-based Linux distros, which
47I used before macOS (and will likely switch back to in a few years).
48
49You can use whatever sync software you want - syncing plain-text or markdown
50files is incredibly easy and fast, since the files are generally tiny.
51
52Since the cloud storage syncs files automatically, there is no need for me to
53sync anything manually or kick-off a sync job to update my files. This means
54that I can edit on mobile, and it takes about 5-10 seconds to see the changes on
55desktop.
56
57### Version Control with Git
58
59A different approach I've contemplated is storing my notes and attachments is
60using a hosted Git repository to track changes to the files. However, I don't
61want to rely on an external service that could potentially see into my data,
62even if the repository is private.
63
64I might just do `git init` locally and then commit my changes each time I write
65or update a note, but that seems to be a lot of work just for tracking changes -
66which I don't necessarily care to know.
67
68### Backups!
69
70One small addition to the storage/sync conversation is the idea of backups.
71Personally, I manually create periodic backups of my entire cloud storage,
72compress it into an archive, and store it on my home server.
73
74To improve my workflow, I am going to be exploring options to automatically
75compress the mounted cloud directory and send it over to my server on a set
76schedule.
77
78## Writing on Desktop
79
80> **Update (06.14.22)**: Since writing this post, I have reverted to simply
81> keeping my `notes` folder open and opening notes individually in TextEdit for
82> a more minimal and relaxing writing experience on the desktop.
83
84The bulk of my writing occurs in a desktop environment, with a full keyboard
85layout and wide screen. I don't illustrate with a smart pen, I rarely use
86embedded images, and I love being able to see all of my notes/directories in a
87sidebar.
88
89With this simple set of requirements, I chose [Obsidian](https://obsidian.md) as
90my desktop text editor. Obsidian has some in-depth tools like a graph view,
91command palette, mentions, etc., but I've found that using it as a simple
92Markdown editor is incredibly easy and straightforward.
93
94Here's an example of how my Markdown notes look when opened in plain-text mode:
95
96![Obsidian Markdown Source
97Mode](https://img.cleberg.net/blog/20220302-easy-reliable-note-taking/obsidian_source_mode.png)
98
99Here's the "live preview" version, where the Markdown is rendered into its HTML
100format:
101
102![Obsidian Markdown Live
103Preview](https://img.cleberg.net/blog/20220302-easy-reliable-note-taking/obsidian_live_preview.png)
104
105### Programming on Desktop
106
107While I was writing this, I realized I should specify that I don't use the same
108editor for writing notes and for writing code. For programming purposes, I use
109[VSCodium](https://vscodium.com) as my development IDE.
110
111## Writing on Mobile
112
113Personally, I write very little on mobile, except when needing to take important
114notes on-the-go. Any long-form writing, journals, etc. are done at home, where I
115always have my laptop available.
116
117I wanted a simple and foolproof editor for iOS, preferably open-source. After a
118long journey of testing the few (& terrible) open-source iOS note-taking apps, I
119finally found a phenomenal one:
120[Runestone](https://github.com/simonbs/runestone). This app is fantastic for
121note-taking, has plenty of optional features, and integrates natively with the
122iOS file manager.
123
124This app opens the iOS file manager and allows you to click any file you want,
125opens it up in an editor, and lets me save and close out of that note.
126
127Quite simple but effective.
content/blog/2022-03-02-reliable-notes.org added +156
@@ -0,0 +1,156 @@
1#+date: <2022-03-02>
2#+title: Easy, Reliable Note-Taking
3#+description:
4
5* Choosing Durable File Formats
6:PROPERTIES:
7:CUSTOM_ID: choosing-durable-file-formats
8:END:
9
10#+begin_quote
11TL;DR: Write in a format that can be easily rendered and read in
12plain-text mode (e.g., =.txt=, =.md=, etc.).
13#+end_quote
14
15As I've written more and more over the years, I've found that my love of
16note-taking is always growing. Everything I learn or need to remember
17can be written down in a note and saved digitally, with no cost to
18myself. Unlike paper copies that need physical storage space, digital
19files simply need space on your local disk or cloud storage, which is
20fairly abundant these days.
21
22However, I had a historical struggle with migration of notes between
23different apps that require different formats and applied proprietary
24styling. This meant that I had to go through each note during migration
25and edit the file to look presentable again.
26
27For the last year or two, I have written everything exclusively in
28[[https://en.wikipedia.org/wiki/Markdown][Markdown]] format. Small
29notes, long-form writings, and even these blog posts are all written in
30Markdown.
31
32Why Markdown? While I do appreciate the simplicity of plain-text files
33without any formatting, I often need visual cues such as heading and
34code blocks to keep my thoughts straight. Markdown provides a minimal
35set of styling indicators for me to style my notes without adding any
36proprietary, embedded data into the files. If I want a top-level
37heading, I simply add a hash (=#=) before the line. An added bonus is
38that even if a system doesn't understand Markdown, it will render it as
39plain-text and I can read it just as easily.
40
41For example, here's how TextEdit on macOS will open and display a
42Markdown file in plain-text, since it does contain any features to
43preview Markdown as HTML:
44
45#+caption: Plain Text Markdown
46[[https://img.cleberg.net/blog/20220302-easy-reliable-note-taking/plain_markdown.png]]
47
48** Saving & Syncing Files
49:PROPERTIES:
50:CUSTOM_ID: saving-syncing-files
51:END:
52In order to read and edit my notes across platforms, I use my personal
53cloud storage through Tresorit due to its native integration with macOS
54and iOS file managers. In addition, Tresorit works well on Debian-based
55Linux distros, which I used before macOS (and will likely switch back to
56in a few years).
57
58You can use whatever sync software you want - syncing plain-text or
59markdown files is incredibly easy and fast, since the files are
60generally tiny.
61
62Since the cloud storage syncs files automatically, there is no need for
63me to sync anything manually or kick-off a sync job to update my files.
64This means that I can edit on mobile, and it takes about 5-10 seconds to
65see the changes on desktop.
66
67*** Version Control with Git
68:PROPERTIES:
69:CUSTOM_ID: version-control-with-git
70:END:
71A different approach I've contemplated is storing my notes and
72attachments is using a hosted Git repository to track changes to the
73files. However, I don't want to rely on an external service that could
74potentially see into my data, even if the repository is private.
75
76I might just do =git init= locally and then commit my changes each time
77I write or update a note, but that seems to be a lot of work just for
78tracking changes - which I don't necessarily care to know.
79
80*** Backups!
81:PROPERTIES:
82:CUSTOM_ID: backups
83:END:
84One small addition to the storage/sync conversation is the idea of
85backups. Personally, I manually create periodic backups of my entire
86cloud storage, compress it into an archive, and store it on my home
87server.
88
89To improve my workflow, I am going to be exploring options to
90automatically compress the mounted cloud directory and send it over to
91my server on a set schedule.
92
93** Writing on Desktop
94:PROPERTIES:
95:CUSTOM_ID: writing-on-desktop
96:END:
97
98#+begin_quote
99*Update (06.14.22)*: Since writing this post, I have reverted to simply
100keeping my =notes= folder open and opening notes individually in
101TextEdit for a more minimal and relaxing writing experience on the
102desktop.
103#+end_quote
104
105The bulk of my writing occurs in a desktop environment, with a full
106keyboard layout and wide screen. I don't illustrate with a smart pen, I
107rarely use embedded images, and I love being able to see all of my
108notes/directories in a sidebar.
109
110With this simple set of requirements, I chose
111[[https://obsidian.md][Obsidian]] as my desktop text editor. Obsidian
112has some in-depth tools like a graph view, command palette, mentions,
113etc., but I've found that using it as a simple Markdown editor is
114incredibly easy and straightforward.
115
116Here's an example of how my Markdown notes look when opened in
117plain-text mode:
118
119#+caption: Obsidian Markdown Source Mode
120[[https://img.cleberg.net/blog/20220302-easy-reliable-note-taking/obsidian_source_mode.png]]
121
122Here's the "live preview" version, where the Markdown is rendered into
123its HTML format:
124
125#+caption: Obsidian Markdown Live Preview
126[[https://img.cleberg.net/blog/20220302-easy-reliable-note-taking/obsidian_live_preview.png]]
127
128*** Programming on Desktop
129:PROPERTIES:
130:CUSTOM_ID: programming-on-desktop
131:END:
132While I was writing this, I realized I should specify that I don't use
133the same editor for writing notes and for writing code. For programming
134purposes, I use [[https://vscodium.com][VSCodium]] as my development
135IDE.
136
137** Writing on Mobile
138:PROPERTIES:
139:CUSTOM_ID: writing-on-mobile
140:END:
141Personally, I write very little on mobile, except when needing to take
142important notes on-the-go. Any long-form writing, journals, etc. are
143done at home, where I always have my laptop available.
144
145I wanted a simple and foolproof editor for iOS, preferably open-source.
146After a long journey of testing the few (& terrible) open-source iOS
147note-taking apps, I finally found a phenomenal one:
148[[https://github.com/simonbs/runestone][Runestone]]. This app is
149fantastic for note-taking, has plenty of optional features, and
150integrates natively with the iOS file manager.
151
152This app opens the iOS file manager and allows you to click any file you
153want, opens it up in an editor, and lets me save and close out of that
154note.
155
156Quite simple but effective.
content/blog/2022-03-03-financial-database.md → content/blog/2022-03-03-financial-database.org renamed +132 −113
@@ -1,66 +1,75 @@
1+++
2date = 2022-03-03
3title = "Maintaining a Personal Financial Database"
4description = ""
5draft = false
6+++
7
8# Personal Financial Tracking
9
10For the last 6-ish years, I've tracked my finances in a spreadsheet. This is
11common practice in the business world, but any good dev will cringe at the
12thought of storing long-term data in a spreadsheet. A spreadsheet is not for
13long-term storage or as a source of data to pull data/reports.
14
15As I wanted to expand the functionality of my financial data (e.g., adding more
16reports), I decided to migrate the data into a database. To run reports, I would
17query the database and use a language like Python or Javascript to process the
18data, perform calculations, and visualize the data.
19
20# SQLite
21
22When choosing the type of database I wanted to use for this project, I was split
23between three options:
24
251. MySQL: The database I have the most experience with and have used for years.
262. PostgreSQL: A database I'm new to, but want to learn.
273. SQLite: A database that I've used for a couple projects and have moderate
28 experience.
29
30I ended up choosing SQLite since it can be maintained within a single `.sqlite`
31file, which allows me more flexibility for storage and backup. I keep this file
32in my cloud storage and pull it up whenever needed.
33
34## GUI Editing
35
36Since I didn't want to try and import 1000--1500 records into my new database
37via the command line, I opted to use [DB Browser for SQLite
38(DB4S)](https://sqlitebrowser.org/) as a GUI tool. This application is
39excellent, and I don't see myself going back to the CLI when working in this
40database.
1#+date: <2022-03-03>
2#+title: Maintaining a Personal Financial Database
3#+description:
4
5
6* Personal Financial Tracking
7:PROPERTIES:
8:CUSTOM_ID: personal-financial-tracking
9:END:
10For the last 6-ish years, I've tracked my finances in a spreadsheet.
11This is common practice in the business world, but any good dev will
12cringe at the thought of storing long-term data in a spreadsheet. A
13spreadsheet is not for long-term storage or as a source of data to pull
14data/reports.
15
16As I wanted to expand the functionality of my financial data (e.g.,
17adding more reports), I decided to migrate the data into a database. To
18run reports, I would query the database and use a language like Python
19or Javascript to process the data, perform calculations, and visualize
20the data.
21
22* SQLite
23:PROPERTIES:
24:CUSTOM_ID: sqlite
25:END:
26When choosing the type of database I wanted to use for this project, I
27was split between three options:
28
291. MySQL: The database I have the most experience with and have used for
30 years.
312. PostgreSQL: A database I'm new to, but want to learn.
323. SQLite: A database that I've used for a couple projects and have
33 moderate experience.
34
35I ended up choosing SQLite since it can be maintained within a single
36=.sqlite= file, which allows me more flexibility for storage and backup.
37I keep this file in my cloud storage and pull it up whenever needed.
38
39** GUI Editing
40:PROPERTIES:
41:CUSTOM_ID: gui-editing
42:END:
43Since I didn't want to try and import 1000--1500 records into my new
44database via the command line, I opted to use
45[[https://sqlitebrowser.org/][DB Browser for SQLite (DB4S)]] as a GUI
46tool. This application is excellent, and I don't see myself going back
47to the CLI when working in this database.
4148
4249DB4S allows you to copy a range of cells from a spreadsheet and paste it
43straight into the SQL table. I used this process for all 36 accounts, 1290
44account statements, and 126 pay statements. Overall, I'm guessing this took
45anywhere between 4--8 hours. In comparison, it probably took me 2-3 days to
46initially create the spreadsheet.
47
48## Schema
50straight into the SQL table. I used this process for all 36 accounts,
511290 account statements, and 126 pay statements. Overall, I'm guessing
52this took anywhere between 4--8 hours. In comparison, it probably took
53me 2-3 days to initially create the spreadsheet.
4954
50The schema for this database is actually extremely simple and involves only
51three tables (for now):
55** Schema
56:PROPERTIES:
57:CUSTOM_ID: schema
58:END:
59The schema for this database is actually extremely simple and involves
60only three tables (for now):
5261
531. Accounts
542. Statements
553. Payroll
621. Accounts
632. Statements
643. Payroll
5665
57**Accounts**
66*Accounts*
5867
59The Accounts table contains summary information about an account, such as a car
60loan or a credit card. By viewing this table, you can find high-level data, such
61as interest rate, credit line, or owner.
68The Accounts table contains summary information about an account, such
69as a car loan or a credit card. By viewing this table, you can find
70high-level data, such as interest rate, credit line, or owner.
6271
63```sql
72#+begin_src sql
6473CREATE TABLE "Accounts" (
6574 "AccountID" INTEGER NOT NULL UNIQUE,
6675 "AccountType" TEXT,
@@ -72,16 +81,16 @@ CREATE TABLE "Accounts" (
7281 "Co-Owner" TEXT,
7382 PRIMARY KEY("AccountID" AUTOINCREMENT)
7483)
75```
84#+end_src
7685
77**Statements**
86*Statements*
7887
79The Statements table uses the same unique identifier as the Accounts table,
80meaning you can join the tables to find a monthly statement for any of the
81accounts listed in the Accounts table. Each statement has an account ID,
82statement date, and total balance.
88The Statements table uses the same unique identifier as the Accounts
89table, meaning you can join the tables to find a monthly statement for
90any of the accounts listed in the Accounts table. Each statement has an
91account ID, statement date, and total balance.
8392
84```sql
93#+begin_src sql
8594CREATE TABLE "Statements" (
8695 "StatementID" INTEGER NOT NULL UNIQUE,
8796 "AccountID" INTEGER,
@@ -90,16 +99,16 @@ CREATE TABLE "Statements" (
9099 PRIMARY KEY("StatementID" AUTOINCREMENT),
91100 FOREIGN KEY("AccountID") REFERENCES "Accounts"("AccountID")
92101)
93```
102#+end_src
94103
95**Payroll**
104*Payroll*
96105
97The Payroll table is a separate entity, unrelated to the Accounts or Statements
98tables. This table contains all information you would find on a pay statement
99from an employer. As you change employers or obtain new perks/benefits, just add
100new columns to adapt to the new data.
106The Payroll table is a separate entity, unrelated to the Accounts or
107Statements tables. This table contains all information you would find on
108a pay statement from an employer. As you change employers or obtain new
109perks/benefits, just add new columns to adapt to the new data.
101110
102```sql
111#+begin_src sql
103112CREATE TABLE "Payroll" (
104113 "PaycheckID" INTEGER NOT NULL UNIQUE,
105114 "PayDate" TEXT,
@@ -129,23 +138,28 @@ CREATE TABLE "Payroll" (
129138 "TaxState" NUMERIC,
130139 PRIMARY KEY("PaycheckID" AUTOINCREMENT)
131140)
132```
133
134## Python Reporting
135
136Once I created the database tables and imported all my data, the only step left
137was to create a process to report and visualize on various aspects of the data.
138
139In order to explore and create the reports I'm interested in, I utilized a
140two-part process involving Jupyter Notebooks and Python scripts.
141
142### Step 1: Jupyter Notebooks
143
141#+end_src
142
143** Python Reporting
144:PROPERTIES:
145:CUSTOM_ID: python-reporting
146:END:
147Once I created the database tables and imported all my data, the only
148step left was to create a process to report and visualize on various
149aspects of the data.
150
151In order to explore and create the reports I'm interested in, I utilized
152a two-part process involving Jupyter Notebooks and Python scripts.
153
154*** Step 1: Jupyter Notebooks
155:PROPERTIES:
156:CUSTOM_ID: step-1-jupyter-notebooks
157:END:
144158When I need to explore data, try different things, and re-run my code
145cell-by-cell, I use Jupyter Notebooks. For example, I explored the `Accounts`
146table until I found the following useful information:
159cell-by-cell, I use Jupyter Notebooks. For example, I explored the
160=Accounts= table until I found the following useful information:
147161
148```python
162#+begin_src python
149163import sqlite3
150164import pandas as pd
151165import matplotlib
@@ -165,14 +179,17 @@ matplotlib.rcParams['legend.labelcolor'] = 'black'
165179
166180# Display graph
167181df.groupby(['AccountType']).sum().plot.pie(title='Credit Line by Account Type', y='CreditLine', figsize=(5,5), autopct='%1.1f%%')
168```
169
170### Step 2: Python Scripts
182#+end_src
171183
172Once I explored enough through the notebooks and had a list of reports I wanted,
173I moved on to create a Python project with the following structure:
184*** Step 2: Python Scripts
185:PROPERTIES:
186:CUSTOM_ID: step-2-python-scripts
187:END:
188Once I explored enough through the notebooks and had a list of reports I
189wanted, I moved on to create a Python project with the following
190structure:
174191
175```txt
192#+begin_src txt
176193finance/
177194├── notebooks/
178195│ │ ├── account_summary.ipynb
@@ -189,23 +206,24 @@ finance/
189206├── process.py
190207├── requirements.txt
191208└── README.md
192```
209#+end_src
193210
194211This structure allows me to:
195212
1961. Compile all required python packages into `requirements.txt` for easy
197 installation if I move to a new machine.
1982. Activate a virtual environment in `venv/` so I don't need to maintain a
199 system-wide Python environment just for this project.
2003. Keep my `notebooks/` folder to continuously explore the data as I see fit.
2014. Maintain a local copy of the database in `src/` for easy access.
2025. Export reports, images, HTML files, etc. to `public/`.
2131. Compile all required python packages into =requirements.txt= for easy
214 installation if I move to a new machine.
2152. Activate a virtual environment in =venv/= so I don't need to maintain
216 a system-wide Python environment just for this project.
2173. Keep my =notebooks/= folder to continuously explore the data as I see
218 fit.
2194. Maintain a local copy of the database in =src/= for easy access.
2205. Export reports, images, HTML files, etc. to =public/=.
203221
204Now, onto the differences between the code in a Jupyter Notebook and the actual
205Python files. To create the report in the Notebook snippet above, I created the
206following function inside `process.py`:
222Now, onto the differences between the code in a Jupyter Notebook and the
223actual Python files. To create the report in the Notebook snippet above,
224I created the following function inside =process.py=:
207225
208```python
226#+begin_src python
209227# Create summary pie chart
210228def summary_data(accounts: pandas.DataFrame) -> None:
211229 accounts_01 = accounts[accounts["Owner"] == "Person01"]
@@ -231,19 +249,20 @@ def summary_data(accounts: pandas.DataFrame) -> None:
231249 )
232250 )
233251 summary_chart.figure.savefig(f"public/summary_chart_{x}.png", dpi=1200)
234```
252#+end_src
235253
236254The result? A high-quality pie chart that is read directly by the
237`public/index.html` template I use.
255=public/index.html= template I use.
238256
239257Other charts generated by this project include:
240258
241- Charts of account balances over time.
242- Line chart of effective tax rate (taxes divided by taxable income).
243- Salary projections and error limits using past income and inflation rates.
244- Multi-line chart of gross income, taxable income, and net income.
259- Charts of account balances over time.
260- Line chart of effective tax rate (taxes divided by taxable income).
261- Salary projections and error limits using past income and inflation
262 rates.
263- Multi-line chart of gross income, taxable income, and net income.
245264
246The best thing about this project? I can improve it at any given time, shaping
247it into whatever helps me the most for that time. I imagine that I will be
248introducing an asset tracking table soon to track the depreciating value of
249cars, houses, etc. Who knows what's next?
265The best thing about this project? I can improve it at any given time,
266shaping it into whatever helps me the most for that time. I imagine that
267I will be introducing an asset tracking table soon to track the
268depreciating value of cars, houses, etc. Who knows what's next?
content/blog/2022-03-08-plex-migration.md deleted −218
@@ -1,218 +0,0 @@
1+++
2date = 2022-03-08
3title = "Migrating Plex to a New Server with Nvidia GPU Transcoding"
4description = ""
5draft = false
6+++
7
8# Migration Phases
9
10I recently decided to migrate my server from an old OptiPlex desktop machine to
11a custom-built tower with better hardware in every category. In order to do
12this, I would need to properly migrate a full Plex installation.
13
14The second part of this migration is that the new server uses an Nvidia GPU and
15does not have any integrated graphics, which requires extra work for
16installation, but provides much better hardware transcoding options for Plex.
17
18Therefore, I have broken this migration down into three phases:
19
201. Configure the New Server
212. Migrate Plex Data & Devices
223. Configure GPU Transcoding
23
24# Phase 1: Configure the New Server
25
26## Choosing an OS
27
28In order to migrate Plex to my new server, I first needed to choose an
29appropriate operating system (OS) and install it on the machine. Given that I
30have encountered numerous issues installing other Linux distributions properly
31with Nvidia graphics, I chose [Ubuntu
32Server](https://ubuntu.com/download/server).
33
34The first step is to create a bootable USB with Ubuntu Server. This is easy with
35[Etcher](https://www.balena.io/etcher/), an app that runs on many different
36platforms. Just download the Ubuntu Server `.iso` image, launch Etcher, and
37install the `.iso` on the USB.
38
39Once the USB is created, insert it into my server, reboot, and click `Esc` (or
40any of the `F1-12` keys) until the BIOS menu appears. Finally, launch the USB
41boot drive.
42
43## Booting with Nvidia
44
45In order to install Ubuntu Server with an Nvidia Graphics card (and no
46integrated graphics on this device for some reason), you'll have to configure
47the boot menu to allow different graphics drivers to be loaded.
48
49When booting from the USB, the machine will launch the initial installation
50menu. From this menu, type `e` to view the default command options that come
51with the device - it's a good idea to take a photo of this screen, so you can
52enter these commands on the next screen (along with adding support for Nvidia).
53
54Finally, type `Ctrl + C` to enter the command line. From this command line,
55enter the commands found on the `e` screen. _Remember to add `nomodeset` to the
56`linux ...` line so that your Nvidia device will display the installation
57screens properly!_
58
59Here's an example of the commands I pulled from the `e` screen and entered on
60the command line.
61
62```sh
63setparams 'Install Ubuntu Server'
64setgfxpayload=keep
65linux /casper/vmlinuz quiet nomodeset ---
66initrd /casper/initrd
67boot
68```
69
70Once the machine is rebooted, enter the `e` screen again and add `nomodeset` to
71the `linux ...` line again and press `Ctrl + X` to save the boot options.
72
73The machine is now fully installed and can properly display on an external
74display using the Nvidia GPU.
75
76Always remember to update and upgrade on a new installation:
77
78```sh
79sudo apt update; sudo apt upgrade -y; sudo apt autoremove -y
80```
81
82# Phase 2: Migrate Plex Data & Devices
83
84This phase uses the great Plex article on migrations ([Move an Installation to
85Another
86System](https://support.plex.tv/articles/201370363-move-an-install-to-another-system/))
87and adds a bit more information to help with commands and context.
88
89## Terminology
90
91**Source:** The original server that is being replaced.\
92**Destination:** The new server.\
93**Client:** Any application that can be used to modify settings for both
94source/destination.
95
96## Step 01: [Client] Update Settings
97
98Open up a Plex app and _disable_ the `Account` > `Library` > `Empty trash
99automatically after every scan` preference for the source server.
100
101## Step 02: [Destination] Install Plex
102
103Open up the [Plex Media Server download
104page](https://www.plex.tv/media-server-downloads/) and copy the link for the
105appropriate platform.
106
107Execute the following commands on the destination server to install Plex:
108
109```sh
110wget <url>
111sudo dpkg -i <filename>
112sudo systemctl stop plexmediaserver.service
113```
114
115## Step 03: [Source] Stop Plex & Migrate Data
116
117First, stop the Plex service so that no data is created or modified during the
118migration.
119
120```sh
121sudo systemctl stop plexmediaserver.service
122```
123
124Next, copy the data to the new server. To find where the Plex data directory is
125located, Plex has another excellent article available: [Where is the Plex Media
126Server data directory
127located?](https://support.plex.tv/articles/202915258-where-is-the-plex-media-server-data-directory-located/).
128
129There are many ways to copy the data to the new server and will largely depend
130on the size of the folder being copied. Personally, my data folder was ~23GB and
131I opted to simply use the `scp` command to copy the files over SSH.
132
133This process was throttled by the old server's slow HDD and ports and took
134approximately 90 minutes to complete. In comparison, moving the data from the
135new server's `home/user/` directory to the `/var/.../Plex Media Server`
136directory took 2-3 minutes.
137
138```sh
139scp -r "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server" your_user@xxx.xxx.xxx.xxx:"'/path/to/destination/'"
140```
141
142## Step 04: [Destination] Update File Permissions
143
144In case you move the data directory to a common area on the new server, it will
145have to be moved to the proper location before Plex can function properly:
146
147```sh
148mv "Plex Media Server" /var/lib/plexmediaserver/Library/Application Support/
149```
150
151To ensure permissions were retained properly, the server will need to show that
152all files and folders in the data directory are owned by `plex:plex` (or
153whichever user is running the Plex application).
154
155```sh
156sudo chown -R plex:plex "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server"
157```
158
159Finally, start the service and check the status.
160
161```sh
162sudo systemctl start plexmediaserver.service
163sudo systemctl status plexmediaserver.service
164```
165
166## Step 05: [Client] Update Libraries & Metadata
167
168The first step - now that the new server is up and running - is to sign out of
169the client and sign back in. Once this is done, update any library locations, if
170necessary. This was unnecessary in my case since I simply moved my storage
171drives from the source server to the destination server.
172
173Next, perform the following actions in the client:
174
1751. On the left sidebar, click `More` > Three-Dot Menu > `Scan Library Files`
1762. _Enable_ the `Account` > `Library` > `Empty trash automatically after every
177scan` preference for the source server.
1783. On the left sidebar, click `More` > Three-Dot Menu > `Manage Server` > `Empty
179 Trash`
1804. On the left sidebar, click `More` > Three-Dot Menu > `Manage Server` > `Clean
181 Bundles`
1825. On the left sidebar, click `More` > Three-Dot Menu > `Manage Server` >
183 `Optimize Database`
184
185Finally, double-check the Remote Access settings to make sure no changes have
186caused issues with accessing the server from outside the network.
187
188In my case, I use a single port forwarding rule in my router and needed to
189update the Local LAN IP Address to the new server IP address.
190
191# Phase 3: Configure GPU Transcoding
192
193The final piece to the migration is enabling hardware transcoding so that Plex
194can fully utilize the new Nvidia GPU available in the server. The first step is
195to install Nvidia graphics drivers. This process may take a few minutes, but the
196commands are pretty simple:
197
198```sh
199sudo add-apt-repository ppa:graphics-drivers/ppa
200sudo apt update
201sudo apt-get install ubuntu-drivers-common
202sudo ubuntu-drivers autoinstall
203```
204
205Finally, reboot so that the changes are loaded:
206
207```sh
208sudo reboot now
209```
210
211To ensure that the Nvidia graphics drivers are working properly, run the
212following command to view the available GPUs, statistics, and processes:
213
214```sh
215sudo nvidia-smi
216```
217
218Finally, enable hardware transcoding settings in the Plex application to finish the process.
content/blog/2022-03-08-plex-migration.org added +252
@@ -0,0 +1,252 @@
1#+date: <2022-03-08>
2#+title: Migrating Plex to a New Server with Nvidia GPU Transcoding
3#+description:
4
5
6* Migration Phases
7:PROPERTIES:
8:CUSTOM_ID: migration-phases
9:END:
10I recently decided to migrate my server from an old OptiPlex desktop
11machine to a custom-built tower with better hardware in every category.
12In order to do this, I would need to properly migrate a full Plex
13installation.
14
15The second part of this migration is that the new server uses an Nvidia
16GPU and does not have any integrated graphics, which requires extra work
17for installation, but provides much better hardware transcoding options
18for Plex.
19
20Therefore, I have broken this migration down into three phases:
21
221. Configure the New Server
232. Migrate Plex Data & Devices
243. Configure GPU Transcoding
25
26* Phase 1: Configure the New Server
27:PROPERTIES:
28:CUSTOM_ID: phase-1-configure-the-new-server
29:END:
30** Choosing an OS
31:PROPERTIES:
32:CUSTOM_ID: choosing-an-os
33:END:
34In order to migrate Plex to my new server, I first needed to choose an
35appropriate operating system (OS) and install it on the machine. Given
36that I have encountered numerous issues installing other Linux
37distributions properly with Nvidia graphics, I chose
38[[https://ubuntu.com/download/server][Ubuntu Server]].
39
40The first step is to create a bootable USB with Ubuntu Server. This is
41easy with [[https://www.balena.io/etcher/][Etcher]], an app that runs on
42many different platforms. Just download the Ubuntu Server =.iso= image,
43launch Etcher, and install the =.iso= on the USB.
44
45Once the USB is created, insert it into my server, reboot, and click
46=Esc= (or any of the =F1-12= keys) until the BIOS menu appears. Finally,
47launch the USB boot drive.
48
49** Booting with Nvidia
50:PROPERTIES:
51:CUSTOM_ID: booting-with-nvidia
52:END:
53In order to install Ubuntu Server with an Nvidia Graphics card (and no
54integrated graphics on this device for some reason), you'll have to
55configure the boot menu to allow different graphics drivers to be
56loaded.
57
58When booting from the USB, the machine will launch the initial
59installation menu. From this menu, type =e= to view the default command
60options that come with the device - it's a good idea to take a photo of
61this screen, so you can enter these commands on the next screen (along
62with adding support for Nvidia).
63
64Finally, type =Ctrl + C= to enter the command line. From this command
65line, enter the commands found on the =e= screen. /Remember to add
66=nomodeset= to the =linux ...= line so that your Nvidia device will
67display the installation screens properly!/
68
69Here's an example of the commands I pulled from the =e= screen and
70entered on the command line.
71
72#+begin_src sh
73setparams 'Install Ubuntu Server'
74setgfxpayload=keep
75linux /casper/vmlinuz quiet nomodeset ---
76initrd /casper/initrd
77boot
78#+end_src
79
80Once the machine is rebooted, enter the =e= screen again and add
81=nomodeset= to the =linux ...= line again and press =Ctrl + X= to save
82the boot options.
83
84The machine is now fully installed and can properly display on an
85external display using the Nvidia GPU.
86
87Always remember to update and upgrade on a new installation:
88
89#+begin_src sh
90sudo apt update; sudo apt upgrade -y; sudo apt autoremove -y
91#+end_src
92
93* Phase 2: Migrate Plex Data & Devices
94:PROPERTIES:
95:CUSTOM_ID: phase-2-migrate-plex-data-devices
96:END:
97This phase uses the great Plex article on migrations
98([[https://support.plex.tv/articles/201370363-move-an-install-to-another-system/][Move
99an Installation to Another System]]) and adds a bit more information to
100help with commands and context.
101
102** Terminology
103:PROPERTIES:
104:CUSTOM_ID: terminology
105:END:
106*Source:* The original server that is being replaced.\\
107*Destination:* The new server.\\
108*Client:* Any application that can be used to modify settings for both
109source/destination.
110
111** Step 01: [Client] Update Settings
112:PROPERTIES:
113:CUSTOM_ID: step-01-client-update-settings
114:END:
115Open up a Plex app and /disable/ the =Account= > =Library= >
116=Empty trash automatically after every scan= preference for the source
117server.
118
119** Step 02: [Destination] Install Plex
120:PROPERTIES:
121:CUSTOM_ID: step-02-destination-install-plex
122:END:
123Open up the [[https://www.plex.tv/media-server-downloads/][Plex Media
124Server download page]] and copy the link for the appropriate platform.
125
126Execute the following commands on the destination server to install
127Plex:
128
129#+begin_src sh
130wget <url>
131sudo dpkg -i <filename>
132sudo systemctl stop plexmediaserver.service
133#+end_src
134
135** Step 03: [Source] Stop Plex & Migrate Data
136:PROPERTIES:
137:CUSTOM_ID: step-03-source-stop-plex-migrate-data
138:END:
139First, stop the Plex service so that no data is created or modified
140during the migration.
141
142#+begin_src sh
143sudo systemctl stop plexmediaserver.service
144#+end_src
145
146Next, copy the data to the new server. To find where the Plex data
147directory is located, Plex has another excellent article available:
148[[https://support.plex.tv/articles/202915258-where-is-the-plex-media-server-data-directory-located/][Where
149is the Plex Media Server data directory located?]].
150
151There are many ways to copy the data to the new server and will largely
152depend on the size of the folder being copied. Personally, my data
153folder was ~23GB and I opted to simply use the =scp= command to copy the
154files over SSH.
155
156This process was throttled by the old server's slow HDD and ports and
157took approximately 90 minutes to complete. In comparison, moving the
158data from the new server's =home/user/= directory to the
159=/var/.../Plex Media Server= directory took 2-3 minutes.
160
161#+begin_src sh
162scp -r "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server" your_user@xxx.xxx.xxx.xxx:"'/path/to/destination/'"
163#+end_src
164
165** Step 04: [Destination] Update File Permissions
166:PROPERTIES:
167:CUSTOM_ID: step-04-destination-update-file-permissions
168:END:
169In case you move the data directory to a common area on the new server,
170it will have to be moved to the proper location before Plex can function
171properly:
172
173#+begin_src sh
174mv "Plex Media Server" /var/lib/plexmediaserver/Library/Application Support/
175#+end_src
176
177To ensure permissions were retained properly, the server will need to
178show that all files and folders in the data directory are owned by
179=plex:plex= (or whichever user is running the Plex application).
180
181#+begin_src sh
182sudo chown -R plex:plex "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server"
183#+end_src
184
185Finally, start the service and check the status.
186
187#+begin_src sh
188sudo systemctl start plexmediaserver.service
189sudo systemctl status plexmediaserver.service
190#+end_src
191
192** Step 05: [Client] Update Libraries & Metadata
193:PROPERTIES:
194:CUSTOM_ID: step-05-client-update-libraries-metadata
195:END:
196The first step - now that the new server is up and running - is to sign
197out of the client and sign back in. Once this is done, update any
198library locations, if necessary. This was unnecessary in my case since I
199simply moved my storage drives from the source server to the destination
200server.
201
202Next, perform the following actions in the client:
203
2041. On the left sidebar, click =More= > Three-Dot Menu >
205 =Scan Library Files=
2062. /Enable/ the =Account= > =Library= >
207 =Empty trash automatically after every scan= preference for the
208 source server.
2093. On the left sidebar, click =More= > Three-Dot Menu > =Manage Server=
210 > =Empty Trash=
2114. On the left sidebar, click =More= > Three-Dot Menu > =Manage Server=
212 > =Clean Bundles=
2135. On the left sidebar, click =More= > Three-Dot Menu > =Manage Server=
214 > =Optimize Database=
215
216Finally, double-check the Remote Access settings to make sure no changes
217have caused issues with accessing the server from outside the network.
218
219In my case, I use a single port forwarding rule in my router and needed
220to update the Local LAN IP Address to the new server IP address.
221
222* Phase 3: Configure GPU Transcoding
223:PROPERTIES:
224:CUSTOM_ID: phase-3-configure-gpu-transcoding
225:END:
226The final piece to the migration is enabling hardware transcoding so
227that Plex can fully utilize the new Nvidia GPU available in the server.
228The first step is to install Nvidia graphics drivers. This process may
229take a few minutes, but the commands are pretty simple:
230
231#+begin_src sh
232sudo add-apt-repository ppa:graphics-drivers/ppa
233sudo apt update
234sudo apt-get install ubuntu-drivers-common
235sudo ubuntu-drivers autoinstall
236#+end_src
237
238Finally, reboot so that the changes are loaded:
239
240#+begin_src sh
241sudo reboot now
242#+end_src
243
244To ensure that the Nvidia graphics drivers are working properly, run the
245following command to view the available GPUs, statistics, and processes:
246
247#+begin_src sh
248sudo nvidia-smi
249#+end_src
250
251Finally, enable hardware transcoding settings in the Plex application to
252finish the process.
content/blog/2022-03-23-cloudflare-dns-api.md → content/blog/2022-03-23-cloudflare-dns-api.org renamed +48 −45
@@ -1,39 +1,42 @@
1+++
2date = 2022-03-23
3title = "Dynamic DNS with Cloudflare API"
4description = ""
5draft = false
6+++
1#+date: <2022-03-23>
2#+title: Dynamic DNS with Cloudflare API
3#+description:
74
8# DDNS: Dynamic DNS
95
10If you're hosting a service from a location with dynamic DNS (where your IP may
11change at any time), you must have a solution to update the DNS so that you can
12access your service even when the IP of the server changes.
6* DDNS: Dynamic DNS
7:PROPERTIES:
8:CUSTOM_ID: ddns-dynamic-dns
9:END:
10If you're hosting a service from a location with dynamic DNS (where your
11IP may change at any time), you must have a solution to update the DNS
12so that you can access your service even when the IP of the server
13changes.
1314
14The process below uses the [Cloudflare API](https://api.cloudflare.com/) to
15update DNS `A` and `AAAA` records with the server's current IP. If you use
16another DNS provider, you will have to find a way to update your DNS (or find a
17way to get a static IP).
15The process below uses the [[https://api.cloudflare.com/][Cloudflare
16API]] to update DNS =A= and =AAAA= records with the server's current IP.
17If you use another DNS provider, you will have to find a way to update
18your DNS (or find a way to get a static IP).
1819
19First, install `jq` since we will use it in the next script:
20First, install =jq= since we will use it in the next script:
2021
21```sh
22#+begin_src sh
2223sudo apt install jq
23```
24#+end_src
2425
25Next, create a location for your DDNS update scripts and open the first script:
26Next, create a location for your DDNS update scripts and open the first
27script:
2628
27```sh
29#+begin_src sh
2830mkdir ~/ddns
2931nano ~/ddns/update.sh
30```
32#+end_src
3133
32The following `update.sh` script will take all of your domains and subdomains
33and check Cloudflare to see if the current `A` and `AAAA` records match your
34server's IP address. If not, it will update the records.
34The following =update.sh= script will take all of your domains and
35subdomains and check Cloudflare to see if the current =A= and =AAAA=
36records match your server's IP address. If not, it will update the
37records.
3538
36```sh
39#+begin_src sh
3740# file: update.sh
3841#!/bin/bash
3942
@@ -55,20 +58,20 @@ do
5558 echo -e "\nUpdating $subdomain..."
5659 zone_name=$domain dns_record=$subdomain /home/<your-username>/ddns/ddns.sh
5760done
58```
61#+end_src
5962
60Next, open up the `ddns.sh` script. Paste the following into the script and
61update the `api_token` and `email` variables.
63Next, open up the =ddns.sh= script. Paste the following into the script
64and update the =api_token= and =email= variables.
6265
63```sh
66#+begin_src sh
6467nano ~/ddns/ddns.sh
65```
68#+end_src
6669
67**Note**: If you want your DNS records to be proxied through Cloudflare, find
68and update the following snippet: `"proxied":false}"` to say `true` instead of
69`false`.
70*Note*: If you want your DNS records to be proxied through Cloudflare,
71find and update the following snippet: ="proxied":false}"= to say =true=
72instead of =false=.
7073
71```sh
74#+begin_src sh
7275# file: ddns.sh
7376#!/bin/bash
7477# based on https://gist.github.com/Tras2/cba88201b17d765ec065ccbedfb16d9a
@@ -161,30 +164,30 @@ then
161164else
162165 echo "There is a problem with either the email or the password"
163166fi
164```
167#+end_src
165168
166169Once the script is saved and closed, make the scripts executable:
167170
168```sh
171#+begin_src sh
169172chmod +x ~/ddns/ddns.sh
170173chmod +x ~/ddns/update.sh
171```
174#+end_src
172175
173176You can test the script by running it manually:
174177
175```sh
178#+begin_src sh
176179./update.sh
177```
180#+end_src
178181
179To make sure the scripts run automatically, add it to the `cron` file so that it
180will run on a schedule. To do this, open the cron file:
182To make sure the scripts run automatically, add it to the =cron= file so
183that it will run on a schedule. To do this, open the cron file:
181184
182```sh
185#+begin_src sh
183186crontab -e
184```
187#+end_src
185188
186189In the cron file, paste the following at the bottom of the editor:
187190
188```sh
189*/5 ** ** ** ** bash /home/<your_username>/ddns/update.sh
190```
191#+begin_src sh
192,*/5 ** ** ** ** bash /home/<your_username>/ddns/update.sh
193#+end_src
content/blog/2022-03-23-nextcloud-on-ubuntu.md → content/blog/2022-03-23-nextcloud-on-ubuntu.org renamed +93 −73
@@ -1,80 +1,92 @@
1+++
2date = 2022-03-23
3title = "Self-Hosting Nextcloud"
4description = ""
5draft = false
6+++
1#+date: <2022-03-23>
2#+title: Self-Hosting Nextcloud
3#+description:
74
8# What is Nextcloud?
95
10[Nextcloud](https://nextcloud.com/) is a self-hosted solution for storage,
11communications, editing, calendar, contacts, and more.
6* What is Nextcloud?
7:PROPERTIES:
8:CUSTOM_ID: what-is-nextcloud
9:END:
10[[https://nextcloud.com/][Nextcloud]] is a self-hosted solution for
11storage, communications, editing, calendar, contacts, and more.
1212
1313This tutorial assumes that you have an Ubuntu server and a domain name
1414configured to point toward the server.
1515
16# Install Dependencies
17
16* Install Dependencies
17:PROPERTIES:
18:CUSTOM_ID: install-dependencies
19:END:
1820To start, you will need to install the packages that Nextcloud requires:
1921
20```sh
22#+begin_src sh
2123sudo apt install apache2 mariadb-server libapache2-mod-php7.4
2224sudo apt install php7.4-gd php7.4-mysql php7.4-curl php7.4-mbstring php7.4-intl
2325sudo apt install php7.4-gmp php7.4-bcmath php-imagick php7.4-xml php7.4-zip
24```
25
26# Set Up MySQL
26#+end_src
2727
28Next, you will need to log in to MySQL as the `root` user of the machine.
28* Set Up MySQL
29:PROPERTIES:
30:CUSTOM_ID: set-up-mysql
31:END:
32Next, you will need to log in to MySQL as the =root= user of the
33machine.
2934
30```sh
35#+begin_src sh
3136sudo mysql -uroot -p
32```
37#+end_src
3338
34Once you've logged in, you must create a new user so that Nextcloud can manage
35the database. You will also create a `nextcloud` database and assign privileges:
39Once you've logged in, you must create a new user so that Nextcloud can
40manage the database. You will also create a =nextcloud= database and
41assign privileges:
3642
37```sql
43#+begin_src sql
3844CREATE USER 'username'@'localhost' IDENTIFIED BY 'password';
3945CREATE DATABASE IF NOT EXISTS nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
4046GRANT ALL PRIVILEGES ON nextcloud.** TO 'username'@'localhost';
4147FLUSH PRIVILEGES;
4248quit;
43```
44
45# Download & Install Nextcloud
46
47To download Nextcloud, go the [Nextcloud downloads
48page](https://nextcloud.com/install/#instructions-server), click on `Archive
49File` and right-click the big blue button to copy the link.
50
51Then, go to your server and enter the following commands to download, unzip, and
52move the files to your destination directory. This example uses `example.com` as
53the destination, but you can put it wherever you want to server your files from.
54
55```sh
49#+end_src
50
51* Download & Install Nextcloud
52:PROPERTIES:
53:CUSTOM_ID: download-install-nextcloud
54:END:
55To download Nextcloud, go the
56[[https://nextcloud.com/install/#instructions-server][Nextcloud
57downloads page]], click on =Archive File= and right-click the big blue
58button to copy the link.
59
60Then, go to your server and enter the following commands to download,
61unzip, and move the files to your destination directory. This example
62uses =example.com= as the destination, but you can put it wherever you
63want to server your files from.
64
65#+begin_src sh
5666wget https://download.nextcloud.com/server/releases/nextcloud-23.0.3.zip
5767sudo apt install unzip
5868unzip nextcloud-23.0.3.zip
5969sudo cp -r nextcloud /var/www/example.com
60```
70#+end_src
6171
62# Configure the Apache Web Server
63
64Now that the database is set up and Nextcloud is installed, you need to set up
65the Apache configuration files to tell the server how to handle requests for
66`example.com/nextcloud`.
72* Configure the Apache Web Server
73:PROPERTIES:
74:CUSTOM_ID: configure-the-apache-web-server
75:END:
76Now that the database is set up and Nextcloud is installed, you need to
77set up the Apache configuration files to tell the server how to handle
78requests for =example.com/nextcloud=.
6779
6880First, open the following file in the editor:
6981
70```sh
82#+begin_src sh
7183sudo nano /etc/apache2/sites-available/nextcloud.conf
72```
84#+end_src
7385
74Once the editor is open, paste the following information in. Then, save and
75close the file.
86Once the editor is open, paste the following information in. Then, save
87and close the file.
7688
77```config
89#+begin_src config
7890<VirtualHost *:80>
7991 DocumentRoot /var/www/example.com
8092 ServerName example.com
@@ -93,57 +105,65 @@ close the file.
93105 </IfModule>
94106 </Directory>
95107</VirtualHost>
96```
108#+end_src
97109
98110Once the file is saved, enable it with Apache:
99111
100```sh
112#+begin_src sh
101113sudo a2ensite nextcloud.conf
102```
114#+end_src
103115
104116Next, enable the Apache mods required by Nextcloud:
105117
106```sh
118#+begin_src sh
107119sudo a2enmod rewrite headers env dir mime
108```
120#+end_src
109121
110Finally, restart Apache. If any errors arise, you must solve those before
111continuing.
122Finally, restart Apache. If any errors arise, you must solve those
123before continuing.
112124
113```sh
125#+begin_src sh
114126sudo systemctl restart apache2
115```
127#+end_src
116128
117129For the app to work, you must have the correct file permissions on your
118`nextcloud` directory. Set the owner to be `www-data`:
130=nextcloud= directory. Set the owner to be =www-data=:
119131
120```sh
132#+begin_src sh
121133sudo chown -R www-data:www-data /var/www/example.com/nextcloud/
122```
123
124# DNS
134#+end_src
125135
136* DNS
137:PROPERTIES:
138:CUSTOM_ID: dns
139:END:
126140If you do not have a static IP address, you will need to update your DNS
127settings (at your DNS provider) whenever your dynamic IP address changes.
128
129For an example on how I do that with Cloudflare, see my other post: [Updating
130Dynamic DNS with Cloudflare API](../updating-dynamic-dns-with-cloudflare-api/)
141settings (at your DNS provider) whenever your dynamic IP address
142changes.
131143
132# Certbot
144For an example on how I do that with Cloudflare, see my other post:
145[[../updating-dynamic-dns-with-cloudflare-api/][Updating Dynamic DNS
146with Cloudflare API]]
133147
134If you want to serve Nextcloud from HTTPS rather than plain HTTP, use the
135following commands to issue Let's Encrypt SSL certificates:
148* Certbot
149:PROPERTIES:
150:CUSTOM_ID: certbot
151:END:
152If you want to serve Nextcloud from HTTPS rather than plain HTTP, use
153the following commands to issue Let's Encrypt SSL certificates:
136154
137```sh
155#+begin_src sh
138156sudo apt install snapd
139157sudo snap install core
140158sudo snap refresh core
141159sudo snap install --classic certbot
142160sudo ln -s /snap/bin/certbot /usr/bin/certbot
143161sudo certbot --apache
144```
145
146# Results
147
148Voilà! You're all done and should be able to access Nextcloud from your domain
149or IP address.
162#+end_src
163
164* Results
165:PROPERTIES:
166:CUSTOM_ID: results
167:END:
168Voilà! You're all done and should be able to access Nextcloud from your
169domain or IP address.
content/blog/2022-03-24-server-hardening.md deleted −345
@@ -1,345 +0,0 @@
1+++
2date = 2022-03-24
3title = "Hardening a Public-Facing Home Server"
4description = ""
5draft = false
6+++
7
8# Post Updates
9
10> After reviewing this post today (2022-10-04), I noticed quite a few gaps in my
11> write-up and wanted to add a few things, even though this blog is really just
12> a retrospective and knowledge dump for myself. I left things intact and simply
13> crossed them out (~~like this~~) for posterity.
14
15# Planning Data Flows & Security
16
17## My Personal Data Flow
18
19```txt
20 ┌───────┐ ┌─────────────────┐
21 ┌──► VLAN1 ├───► Private Devices │
22 │ └───────┘ └─────────────────┘
23┌──────────┐ ┌────────┐ ┌──────────┐ ┌────────┐ │
24│ Internet ├───► Router ├───► Firewall ├───► Switch ├──┤
25└──────────┘ └────────┘ └──────────┘ └────────┘ │
26 │ ┌───────┐ ┌───────────────┐
27 └──► VLAN2 ├───► Public Server │
28 └───────┘ └───────────────┘
29```
30
31## Thought Process
32
33To serve content from your home server and harden your security posture, you
34have to think about the transport of data from `server` to `client`.
35
36Let's start with the actual server itself. Think about the following:
37
38- Do I have a firewall enabled? Do I need to update this to allow new ports or
39 IPs?
40- Do I have an IPS/IDS that may prevent outside traffic?
41- Do I have any other security software installed?
42- Are the services hosted inside Docker containers, behind a reverse proxy, or
43 virtualized? If so, are they configured to allow outside traffic?
44
45Once the data leaves the server, where does it go? In my case, it goes to a
46managed switch. In this case, I asked the following:
47
48- What configurations is the switch using?
49- Am I using VLANs?
50 - Yes, I am using 802.1Q VLANs.
51- Are the VLANs configured properly?
52 - Yes, as shown in the Switch section below, I have a separate VLAN to
53 allow outside traffic to and from the server alone. No other devices,
54 except for a service port, and in that VLAN.
55
56At this point, the data has been processed through the switch. Where does it go
57next? In my case, it's pretty simple: it goes to the router/modem device.
58
59- Does my ISP block any ports that I need?
60 - This is an important step that a lot of people run into when
61 self-hosting at home. Use an online port-checker tool for your IP or
62 call your ISP if you think ports are blocked.
63- Is there a router firewall?
64 - Yes, I checked that it's configured to allow the ports I need to run my
65 services publicly. Common web servers and reverse proxies require ports
66 80 and 443, but other services like media servers or games can require
67 unique ports, so be sure to check the documentation for your service(s).
68- Are there any other settings affecting inbound/outbound traffic?
69 - Schedules or access blocks
70 - Static Routing
71 - QoS
72 - Port Forwarding
73 - DMZ Hosting
74 - Remote Management (this can sometimes mess with services that also
75 require the use of ports 80 and 443)
76
77Once the data leaves my router, it goes to the upstream ISP and can be accessed
78publicly.
79
80### Server
81
82+The services I run on my server are installed straight into the OS, without any
83use of Docker or VMs, so I don't need any extra application configuration to
84make them accessible to the outside world.+
85
86As of 2022-10-04, the paragraph above is no longer true as I now run a reverse
87proxy with Nginx and host many services inside Docker. However, it doesn't
88change anything regarding this post as I still just need to open ports 80 & 443
89and create the necessary website configuration files.
90
91When creating new services - either installed directly on bare metal or within
92something like Docker - I ensure that I read through the documentation
93thoroughly to understand a few key things: - What network activities should this
94app perform (if any)? Using which ports and protocols? - Does this app require
95any commands/services to be run as `root`? - Does this app log errors,
96authentication failures/successes, or anything else that would be useful for an
97investigation?
98
99For extra security, I use limit all incoming connections to SSH connections
100through my server firewall (`ufw`) and disable common SSH settings. After all of
101that, I use `fail2ban` as a preventative measure against brute-force login
102attempts.
103
104As another piece of security, you can randomize your SSH port to ensure that
105random scanners or attackers can't easily try to force their way into your
106network. For example, you can edit the port rules in your server to block all
107connection requests to port `22` but forward all remote connections from port
108`12345` to your server's port `22`. Then you just need to SSH to your network
109via your randomized port.
110
111## `ufw`
112
113To see how to configure `ufw`, see my other post: [Secure Your Network with the
114Uncomplicated
115Firewall](../secure-your-network-with-the-uncomplicated-firewall/).
116
117The general notion with an on-device firewall is that you want to deny all
118incoming connections by default and then selectively open certain ports for
119services or users that you know need access.
120
121If you know that you will only be logging into this server from a certain set or
122list of IPs, you can always set the firewall to only allow connections to port
12322 from those IPs.
124
125For a quick start to only allow SSH connections to the server, use this:
126
127```sh
128sudo ufw default deny incoming
129sudo ufw default allow outgoing
130sudo ufw allow 22
131sudo ufw enable
132```
133
134## `ssh`
135
1361. Using SSH Keys
137
138 First, make sure you have an SSH keypair generated on the device(s) that
139 you'll be using to log in to the server. If you don't have an SSH key, run
140 this command:
141
142 ```sh
143 ssh-keygen
144 ```
145
146 Now that we have an SSH key, copy it to the server with the following
147 command, which will ask for the user's password before accepting the key:
148
149 ```sh
150 ssh-copy-id my_user@my_server
151 ```
152
153 If you have multiple keys, you'll need to specify which to use. After it's
154 complete, `ssh` back into the server as that user and make sure it doesn't
155 ask for a password.
156
1572. Disable Password & Root Authentication
158
159 Now that we can access the server without a password, we will disable
160 password authentication and disable anyone from using `ssh` to login as
161 `root`.
162
163 To do this, open the `sshd_config` file:
164
165 ```sh
166 sudo nano /etc/ssh/sshd_config
167 ```
168
169 You'll need to update the parameters to the values below. If one of these
170 rules is commented-out or doesn't exist, create the rule at the bottom of
171 the file.
172
173 ```config
174 PermitRootLogin no
175 PasswordAuthentication no
176 PubkeyAuthentication yes
177 ```
178
179 Finally, restart the `ssh` service:
180
181 ```sh
182 sudo systemctl restart sshd.service
183 ```
184
185 To test that everything's working so far, open ANOTHER terminal and try
186 logging in as `root` over SSH. It is very important that you keep your
187 current SSH session open and test with an additional session, or you will
188 lock yourself out at some point and will need to use a recovery method
189 (e.g., hooking monitor up to home server) to get yourself back in.
190
1913. Enable MFA for `ssh`
192
193 This part is optional, but I highly recommend it. So far, we've ensured that
194 no one can log into our user on the server without using our secret key, and
195 we've ensured that no one can log in remotely as `root`. Next, you can
196 enable MFA authentication for `ssh` connections.
197
198 This process involves editing a couple files and installing an MFA package,
199 so I will not include all the details in this post. To see how to configure
200 MFA for `ssh`, see my other post: [Enabling MFA for
201 SSH](../enable-totp-mfa-for-ssh/).
202
203## `fail2ban`
204
205I haven't written a post on how I use `fail2ban`, but it's quite simple. I use
206the default `sshd` jail, but you can always create new jails for respective
207applications or ports. For example, if you use Nginx as your web server, you can
208use the `nginx-http-auth` jail.
209
210In order to get it up and running, use the following commands:
211
212```sh
213sudo apt install fail2ban
214sudo fail2ban-client start sshd
215sudo fail2ban-client status sshd
216```
217
218This should be used as a last-resort defense and shouldn't be a replacement for
219the security measures mentioned above.
220
221# Switch
222
223Between the router and any local devices is my managed switch, which is used to
224create VLANs. The example below shows how I would isolate the VLANs if I were
225starting to host a single service at home.
226
227## 802.1Q VLAN Configuration
228
229In this configuration, port 8 is the public server that needs to be accessed
230from the outside. Port 23 is my 'dedicated service port' for this server. In
231order to SSH to this server, I need to plug my laptop into port 23 or else I
232cannot SSH. Otherwise, I'd need to hook up a monitor and keyboard directly to
233the server to manage it.
234
235|
236
237| VLAN ID | VLAN Name | Member Ports | Tagged Ports | Untagged Ports |
238| ------- | --------- | ------------ | ------------ | -------------- |
239| 1 | Default | 1-24 | 1-24 | |
240| 2 | Server | 1,8,23 | 1,8,23 | |
241
242## 802.1Q VLAN PVID Setting
243
244Once the VLAN is created, I simply add the `VLAN ID` of `2` as the `PVID` for
245any related ports (in this case, see that ports `8` and `23` have a PVID of
246`2`).
247
248| Port | PVID |
249| ---- | ---- |
250| 1 | 1 |
251| 2 | 1 |
252| 3 | 1 |
253| 4 | 1 |
254| 5 | 1 |
255| 6 | 1 |
256| 7 | 1 |
257| 8 | 2 |
258| 9 | 1 |
259| 10 | 1 |
260| 11 | 1 |
261| 12 | 1 |
262| 13 | 1 |
263| 14 | 1 |
264| 15 | 1 |
265| 16 | 1 |
266| 17 | 1 |
267| 18 | 1 |
268| 19 | 1 |
269| 20 | 1 |
270| 21 | 1 |
271| 22 | 1 |
272| 23 | 2 |
273| 24 | 1 |
274
275# Router
276
277On my router, the configuration was as easy as opening the firewall settings and
278unblocking the ports I needed for my services (e.g., HTTP/S, Plex, SSH, MySQL,
279etc.).
280
281_Since I'm relying on an ISP-provided modem/router combo for now (not by
282choice), I do not use any other advanced settings on my router that would
283inhibit any valid traffic to these services._
284
285The paragraph above regarding the ISP-owned router is no longer accurate as I
286now use the Ubiquiti Unifi Dream Machine Pro as my router. Within this router, I
287enabled port forwarding/firewall rules, segregate the network based on the
288device, and enable traffic restrictions (e.g., silently drop traffic from
289certain countries and threat categories).
290
291If you have the option with your ISP, I recommend using a personal router with
292software that you are familiar with so that you can explore all the options
293available to you.
294
295# Physical Security
296
297One large piece of self-hosting that people generally don't discuss online is
298physical security. However, physical security is very important for everyone who
299hosts a server like this. Exactly _how_ important it is depends on the server
300use/purpose.
301
302If you self-host customer applications that hold protected data (HIPAA, GDPR,
303COPPA, etc.), then physical security is extremely important and cannot be
304ignored. If you simply host a blog and some hobby sites, then it's a relatively
305minor consideration, but one you still need to think about.
306
307## Location
308
309The first consideration is quite simple: location.
310
311- Is the server within a property you own or housed on someone else's
312 property?
313- Is it nearby (in your house, in your work office, in your neighbor's garage,
314 in a storage unit, etc.)?
315- Do you have 24/7 access to the server?
316- Are there climate considerations, such as humidity, fires, tornadoes,
317 monsoons?
318- Do you have emergency equipment nearby in case of emergency?
319
320## Hardware Ownership
321
322Secondly, consider the hardware itself:
323
324- Do you own the server in its entirety?
325- Are any other users able to access the server, even if your data/space is
326 segregated?
327- If you're utilizing a third party, do they have any documentation to show
328 responsibility? This could be a SOC 1/2/3 report, ISO compliance report,
329 internal security/safety documentation.
330
331## Physical Controls
332
333Regardless of who owns the hardware, ensure that there are adequate safeguards
334in place, if necessary. These usually don't apply to small home servers and are
335usually covered already if you're utilizing a third party.
336
337These can include:
338
339- Server bezel locks
340- Server room locks - physical, digital, or biometric authentication
341- Security cameras
342- Raised floors/lowered ceilings with proper guards/gates in-place within the
343 floors or ceilings
344- Security personnel
345- Log sheets and/or guest badges
content/blog/2022-03-24-server-hardening.org added +341
@@ -0,0 +1,341 @@
1#+date: <2022-03-24>
2#+title: Hardening a Public-Facing Home Server
3#+description:
4
5
6* Post Updates
7
8#+begin_quote
9After reviewing this post today (2022-10-04), I noticed quite a few gaps in my
10write-up and wanted to add a few things, even though this blog is really just a
11retrospective and knowledge dump for myself. I left things intact and simply
12crossed them out (+like this+) for posterity.
13#+end_quote
14
15* Planning Data Flows & Security
16
17** My Personal Data Flow
18
19#+begin_src
20 ┌───────┐ ┌─────────────────┐
21 ┌──► VLAN1 ├───► Private Devices │
22 │ └───────┘ └─────────────────┘
23┌──────────┐ ┌────────┐ ┌──────────┐ ┌────────┐ │
24│ Internet ├───► Router ├───► Firewall ├───► Switch ├──┤
25└──────────┘ └────────┘ └──────────┘ └────────┘ │
26 │ ┌───────┐ ┌───────────────┐
27 └──► VLAN2 ├───► Public Server │
28 └───────┘ └───────────────┘
29#+end_src
30
31** Thought Process
32
33To serve content from your home server and harden your security posture, you
34have to think about the transport of data from =server= to =client=.
35
36Let's start with the actual server itself. Think about the following:
37
38- Do I have a firewall enabled? Do I need to update this to allow new ports or
39 IPs?
40- Do I have an IPS/IDS that may prevent outside traffic?
41- Do I have any other security software installed?
42- Are the services hosted inside Docker containers, behind a reverse proxy, or
43 virtualized? If so, are they configured to allow outside traffic?
44
45Once the data leaves the server, where does it go? In my case, it goes to a
46managed switch. In this case, I asked the following:
47
48- What configurations is the switch using?
49- Am I using VLANs?
50 - Yes, I am using 802.1Q VLANs.
51- Are the VLANs configured properly?
52 - Yes, as shown in the Switch section below, I have a separate VLAN to allow
53 outside traffic to and from the server alone. No other devices, except for a
54 service port, and in that VLAN.
55
56At this point, the data has been processed through the switch. Where does it go
57next? In my case, it's pretty simple: it goes to the router/modem device.
58
59- Does my ISP block any ports that I need?
60 - This is an important step that a lot of people run into when self-hosting at
61 home. Use an online port-checker tool for your IP or call your ISP if you
62 think ports are blocked.
63- Is there a router firewall?
64 - Yes, I checked that it's configured to allow the ports I need to run my
65 services publicly. Common web servers and reverse proxies require ports 80
66 and 443, but other services like media servers or games can require unique
67 ports, so be sure to check the documentation for your service(s).
68- Are there any other settings affecting inbound/outbound traffic?
69 - Schedules or access blocks
70 - Static Routing
71 - QoS
72 - Port Forwarding
73 - DMZ Hosting
74 - Remote Management (this can sometimes mess with services that also require
75 the use of ports 80 and 443)
76
77Once the data leaves my router, it goes to the upstream ISP and can be accessed
78publicly.
79
80*** Server
81
82The services I run on my server are installed straight into the OS, without any
83use of Docker or VMs, so I don't need any extra application configuration to
84make them accessible to the outside world.+
85
86As of 2022-10-04, the paragraph above is no longer true as I now run a reverse
87proxy with Nginx and host many services inside Docker. However, it doesn't
88change anything regarding this post as I still just need to open ports 80 & 443
89and create the necessary website configuration files.
90
91When creating new services - either installed directly on bare metal or within
92something like Docker - I ensure that I read through the documentation
93thoroughly to understand a few key things: - What network activities should this
94app perform (if any)? Using which ports and protocols? - Does this app require
95any commands/services to be run as =root=? - Does this app log errors,
96authentication failures/successes, or anything else that would be useful for an
97investigation?
98
99For extra security, I use limit all incoming connections to SSH connections
100through my server firewall (=ufw=) and disable common SSH settings. After all of
101that, I use =fail2ban= as a preventative measure against brute-force login
102attempts.
103
104As another piece of security, you can randomize your SSH port to ensure that
105random scanners or attackers can't easily try to force their way into your
106network. For example, you can edit the port rules in your server to block all
107connection requests to port =22= but forward all remote connections from port
108=12345= to your server's port =22=. Then you just need to SSH to your network
109via your randomized port.
110
111** =ufw=
112
113To see how to configure =ufw=, see my other post: [[/blog/ufw.html][Secure Your
114Network with the Uncomplicated Firewall]].
115
116The general notion with an on-device firewall is that you want to deny all
117incoming connections by default and then selectively open certain ports for
118services or users that you know need access.
119
120If you know that you will only be logging into this server from a certain set or
121list of IPs, you can always set the firewall to only allow connections to port
12222 from those IPs.
123
124For a quick start to only allow SSH connections to the server, use this:
125
126#+begin_src sh
127sudo ufw default deny incoming
128sudo ufw default allow outgoing
129sudo ufw allow 22
130sudo ufw enable
131#+end_src
132
133** =ssh=
134
1351. Using SSH Keys
136
137 First, make sure you have an SSH keypair generated on the device(s) that
138 you'll be using to log in to the server. If you don't have an SSH key, run
139 this command:
140
141 #+begin_src sh
142 ssh-keygen
143 #+end_src
144
145 Now that we have an SSH key, copy it to the server with the following
146 command, which will ask for the user's password before accepting the key:
147
148 #+begin_src sh
149 ssh-copy-id my_user@my_server
150 #+end_src
151
152 If you have multiple keys, you'll need to specify which to use. After it's
153 complete, =ssh= back into the server as that user and make sure it doesn't
154 ask for a password.
155
1562. Disable Password & Root Authentication
157
158 Now that we can access the server without a password, we will disable
159 password authentication and disable anyone from using =ssh= to login as
160 =root=.
161
162 To do this, open the =sshd_config= file:
163
164 #+begin_src sh
165 sudo nano /etc/ssh/sshd_config
166 #+end_src
167
168 You'll need to update the parameters to the values below. If one of these
169 rules is commented-out or doesn't exist, create the rule at the bottom of the
170 file.
171
172 #+begin_src config
173 PermitRootLogin no
174 PasswordAuthentication no
175 PubkeyAuthentication yes
176 #+end_src
177
178 Finally, restart the =ssh= service:
179
180 #+begin_src sh
181 sudo systemctl restart sshd.service
182 #+end_src
183
184 To test that everything's working so far, open ANOTHER terminal and try
185 logging in as =root= over SSH. It is very important that you keep your
186 current SSH session open and test with an additional session, or you will
187 lock yourself out at some point and will need to use a recovery method (e.g.,
188 hooking monitor up to home server) to get yourself back in.
189
1903. Enable MFA for =ssh=
191
192 This part is optional, but I highly recommend it. So far, we've ensured that
193 no one can log into our user on the server without using our secret key, and
194 we've ensured that no one can log in remotely as =root=. Next, you can enable
195 MFA authentication for =ssh= connections.
196
197 This process involves editing a couple files and installing an MFA package,
198 so I will not include all the details in this post. To see how to configure
199 MFA for =ssh=, see my other post: [[/blog/ssh-mfa.html][Enabling MFA for
200 SSH]].
201
202** =fail2ban=
203
204I haven't written a post on how I use =fail2ban=, but it's quite simple. I use
205the default =sshd= jail, but you can always create new jails for respective
206applications or ports. For example, if you use Nginx as your web server, you can
207use the =nginx-http-auth= jail.
208
209In order to get it up and running, use the following commands:
210
211#+begin_src sh
212sudo apt install fail2ban
213sudo fail2ban-client start sshd
214sudo fail2ban-client status sshd
215#+end_src
216
217This should be used as a last-resort defense and shouldn't be a replacement for
218the security measures mentioned above.
219
220* Switch
221
222Between the router and any local devices is my managed switch, which is used to
223create VLANs. The example below shows how I would isolate the VLANs if I were
224starting to host a single service at home.
225
226** 802.1Q VLAN Configuration
227
228In this configuration, port 8 is the public server that needs to be accessed
229from the outside. Port 23 is my 'dedicated service port' for this server. In
230order to SSH to this server, I need to plug my laptop into port 23 or else I
231cannot SSH. Otherwise, I'd need to hook up a monitor and keyboard directly to
232the server to manage it.
233
234| VLAN ID | VLAN Name | Member Ports | Tagged Ports | Untagged Ports |
235|---------+-----------+--------------+--------------+----------------|
236| 1 | Default | 1-24 | 1-24 | |
237| 2 | Server | 1,8,23 | 1,8,23 | |
238
239** 802.1Q VLAN PVID Setting
240
241Once the VLAN is created, I simply add the =VLAN ID= of =2= as the =PVID= for
242any related ports (in this case, see that ports =8= and =23= have a PVID of
243=2=).
244
245| Port | PVID |
246|------+------|
247| 1 | 1 |
248| 2 | 1 |
249| 3 | 1 |
250| 4 | 1 |
251| 5 | 1 |
252| 6 | 1 |
253| 7 | 1 |
254| 8 | 2 |
255| 9 | 1 |
256| 10 | 1 |
257| 11 | 1 |
258| 12 | 1 |
259| 13 | 1 |
260| 14 | 1 |
261| 15 | 1 |
262| 16 | 1 |
263| 17 | 1 |
264| 18 | 1 |
265| 19 | 1 |
266| 20 | 1 |
267| 21 | 1 |
268| 22 | 1 |
269| 23 | 2 |
270| 24 | 1 |
271
272* Router
273
274On my router, the configuration was as easy as opening the firewall settings and
275unblocking the ports I needed for my services (e.g., HTTP/S, Plex, SSH, MySQL,
276etc.).
277
278Since I'm relying on an ISP-provided modem/router combo for now (not by
279choice), I do not use any other advanced settings on my router that would
280inhibit any valid traffic to these services.
281
282The paragraph above regarding the ISP-owned router is no longer accurate as I
283now use the Ubiquiti Unifi Dream Machine Pro as my router. Within this router, I
284enabled port forwarding/firewall rules, segregate the network based on the
285device, and enable traffic restrictions (e.g., silently drop traffic from
286certain countries and threat categories).
287
288If you have the option with your ISP, I recommend using a personal router with
289software that you are familiar with so that you can explore all the options
290available to you.
291
292* Physical Security
293
294One large piece of self-hosting that people generally don't discuss online is
295physical security. However, physical security is very important for everyone who
296hosts a server like this. Exactly /how/ important it is depends on the server
297use/purpose.
298
299If you self-host customer applications that hold protected data (HIPAA, GDPR,
300COPPA, etc.), then physical security is extremely important and cannot be
301ignored. If you simply host a blog and some hobby sites, then it's a relatively
302minor consideration, but one you still need to think about.
303
304** Location
305
306The first consideration is quite simple: location.
307
308- Is the server within a property you own or housed on someone else's property?
309- Is it nearby (in your house, in your work office, in your neighbor's garage,
310 in a storage unit, etc.)?
311- Do you have 24/7 access to the server?
312- Are there climate considerations, such as humidity, fires, tornadoes,
313 monsoons?
314- Do you have emergency equipment nearby in case of emergency?
315
316** Hardware Ownership
317
318Secondly, consider the hardware itself:
319
320- Do you own the server in its entirety?
321- Are any other users able to access the server, even if your data/space is
322 segregated?
323- If you're utilizing a third party, do they have any documentation to show
324 responsibility? This could be a SOC 1/2/3 report, ISO compliance report,
325 internal security/safety documentation.
326
327** Physical Controls
328
329Regardless of who owns the hardware, ensure that there are adequate safeguards
330in place, if necessary. These usually don't apply to small home servers and are
331usually covered already if you're utilizing a third party.
332
333These can include:
334
335- Server bezel locks
336- Server room locks - physical, digital, or biometric authentication
337- Security cameras
338- Raised floors/lowered ceilings with proper guards/gates in-place within the
339 floors or ceilings
340- Security personnel
341- Log sheets and/or guest badges
content/blog/2022-03-26-ssh-mfa.md → content/blog/2022-03-26-ssh-mfa.org renamed +89 −89
@@ -1,82 +1,82 @@
1+++
2date = 2022-03-26
3title = "Enable TOTP MFA for SSH"
4description = ""
5draft = false
6+++
1#+date: <2022-03-26>
2#+title: Enable TOTP MFA for SSH
3#+description:
74
8# Why Do I Need MFA for SSH?
95
10If you are a sysadmin of a server anywhere (that includes at home!), you may
11want an added layer of protection against intruders. This is not a replacement
12for other security measures, such as:
6* Why Do I Need MFA for SSH?
137
14- Disable root SSH
15- Disable SSH password authentication
16- Allow only certain users to login via SSH
17- Allow SSH only from certain IPs
8If you are a sysadmin of a server anywhere (that includes at home!), you
9may want an added layer of protection against intruders. This is not a
10replacement for other security measures, such as:
1811
19However, MFA can be added as an additional security measure to ensure that your
20server is protected. This is especially important if you need to allow password
21authentication for SSH.
12- Disable root SSH
13- Disable SSH password authentication
14- Allow only certain users to login via SSH
15- Allow SSH only from certain IPs
2216
23For more guidance on server security measures, see my other post: [Hardening a
24Public-Facing Home Server](../hardening-a-public-facing-home-server/).
17However, MFA can be added as an additional security measure to ensure
18that your server is protected. This is especially important if you need
19to allow password authentication for SSH.
2520
26# Install MFA PAM Module
21For more guidance on server security measures, see my other post:
22[[../hardening-a-public-facing-home-server/][Hardening a Public-Facing
23Home Server]].
2724
28PAM, which stands for Pluggable Authentication Module, is an authentication
29infrastructure used on Linux systems to authenticate a user. In order to use
30this technology, let's install the `libpam-google-authenticator` package:
25* Install MFA PAM Module
3126
32```sh
27PAM, which stands for Pluggable Authentication Module, is an
28authentication infrastructure used on Linux systems to authenticate a
29user. In order to use this technology, let's install the
30=libpam-google-authenticator= package:
31
32#+begin_src sh
3333sudo apt-get update
34```
34#+end_src
3535
36```sh
36#+begin_src sh
3737sudo apt-get install libpam-google-authenticator
38```
38#+end_src
3939
40# Initialize the PAM Module
40* Initialize the PAM Module
4141
42## Interactive Method
42** Interactive Method
4343
44Once the package is installed, initialize it and following the interactive
45prompts to generate your OTP or TOTP:
44Once the package is installed, initialize it and following the
45interactive prompts to generate your OTP or TOTP:
4646
47```sh
47#+begin_src sh
4848google-authenticator
49```
49#+end_src
5050
51If you are not sure how to answer, read the prompts carefully and think about
52having to how each situation would affect your normal login attempts. If you are
53still not sure, use my default responses below.
51If you are not sure how to answer, read the prompts carefully and think
52about having to how each situation would affect your normal login
53attempts. If you are still not sure, use my default responses below.
5454
55```txt
55#+begin_src txt
5656OUTPUT
5757
5858Do you want authentication tokens to be time-based (y/n) y
59```
59#+end_src
6060
61At this point, use an authenticator app somewhere one of your devices to scan
62the QR code. Any future login attempts after our upcoming configuration changes
63will require that TOTP.
61At this point, use an authenticator app somewhere one of your devices to
62scan the QR code. Any future login attempts after our upcoming
63configuration changes will require that TOTP.
6464
65```txt
65#+begin_src txt
6666OUTPUT
6767
6868Do you want me to update your "/home/user/.google_authenticator" file? (y/n) y
69```
69#+end_src
7070
71```txt
71#+begin_src txt
7272OUTPUT
7373
7474Do you want to disallow multiple uses of the same authentication
7575token? This restricts you to one login about every 30s, but it increases
7676your chances to notice or even prevent man-in-the-middle attacks (y/n) y
77```
77#+end_src
7878
79```txt
79#+begin_src txt
8080OUTPUT
8181
8282By default, a new token is generated every 30 seconds by the mobile app.
@@ -89,30 +89,30 @@ code, the next code) to 17 permitted codes (the 8 previous codes, the current
8989code, and the 8 next codes). This will permit for a time skew of up to 4 minutes
9090between client and server.
9191Do you want to do so? (y/n) n
92```
92#+end_src
9393
94```txt
94#+begin_src txt
9595OUTPUT
9696
9797If the computer that you are logging into isn't hardened against brute-force
9898login attempts, you can enable rate-limiting for the authentication module.
9999By default, this limits attackers to no more than 3 login attempts every 30s.
100100Do you want to enable rate-limiting? (y/n) y
101```
101#+end_src
102102
103## Non-Interactive Method
103** Non-Interactive Method
104104
105If you need to do this quickly, know your responses to the prompts, or are
106setting this up for numerous users, the non-interactive method can be much
107faster:
105If you need to do this quickly, know your responses to the prompts, or
106are setting this up for numerous users, the non-interactive method can
107be much faster:
108108
109```sh
109#+begin_src sh
110110google-authenticator -t -d -f -r 3 -R 30 -w 3
111```
111#+end_src
112112
113113The options referenced above are as follows:
114114
115```txt
115#+begin_src txt
116116google-authenticator [<options>]
117117 -h, --help Print this message
118118 -c, --counter-based Set up counter-based (HOTP) verification
@@ -132,65 +132,65 @@ google-authenticator [<options>]
132132 -w, --window-size=W Set window of concurrently valid codes
133133 -W, --minimal-window Disable window of concurrently valid codes
134134 -e, --emergency-codes=N Number of emergency codes to generate
135```
135#+end_src
136136
137This fully configures the authenticator, saves it to a file, and then outputs
138the secret key, QR code, and recovery codes. (If you add the flag `-q`, then
139there won't be any output). If you use this command in an automated fashion,
140make sure your script captures the secret key and/or recovery codes and makes
141them available to the user.
137This fully configures the authenticator, saves it to a file, and then
138outputs the secret key, QR code, and recovery codes. (If you add the
139flag =-q=, then there won't be any output). If you use this command in
140an automated fashion, make sure your script captures the secret key
141and/or recovery codes and makes them available to the user.
142142
143# PAM Configuration Settings
143* PAM Configuration Settings
144144
145Once you've enabled the T/OTP and have it saved to an MFA app on your phone or
146other device, open the PAM `sshd` file:
145Once you've enabled the T/OTP and have it saved to an MFA app on your
146phone or other device, open the PAM =sshd= file:
147147
148```sh
148#+begin_src sh
149149sudo nano /etc/pam.d/sshd
150```
150#+end_src
151151
152You need to do two things in this file. First, add the following lines to the
153bottom of the file:
152You need to do two things in this file. First, add the following lines
153to the bottom of the file:
154154
155```config
155#+begin_src config
156156auth required pam_google_authenticator.so nullok
157157auth required pam_permit.so
158```
158#+end_src
159159
160160Second, comment-out the following line near the top of the file.
161161
162If you leave this line uncommented, every SSH login attempt will ask for the
163following three authentication factors:
162If you leave this line uncommented, every SSH login attempt will ask for
163the following three authentication factors:
164164
1651. Publickey
1662. Password
1673. T/OTP code
1651. Publickey
1662. Password
1673. T/OTP code
168168
169```config
169#+begin_src config
170170#@include common-auth
171```
171#+end_src
172172
173# SSH Configuration Settings
173* SSH Configuration Settings
174174
175Finally, edit the `sshd_config` file again:
175Finally, edit the =sshd_config= file again:
176176
177```sh
177#+begin_src sh
178178sudo nano /etc/ssh/sshd_config
179```
179#+end_src
180180
181You'll need to change `ChallengeResponseAuthentication` to yes and add the
182`AuthenticationMethods` line to the bottom of the file.
181You'll need to change =ChallengeResponseAuthentication= to yes and add
182the =AuthenticationMethods= line to the bottom of the file.
183183
184```config
184#+begin_src config
185185ChallengeResponseAuthentication yes
186186AuthenticationMethods publickey,password publickey,keyboard-interactive
187```
187#+end_src
188188
189Finally, restart the `ssh` service:
189Finally, restart the =ssh= service:
190190
191```sh
191#+begin_src sh
192192sudo systemctl restart sshd.service
193```
193#+end_src
194194
195195The next time you log in, you should be greeted with a verification code
196196request!
content/blog/2022-04-02-nginx-reverse-proxy.md deleted −226
@@ -1,226 +0,0 @@
1+++
2date = 2022-04-02
3title = "Set-Up a Reverse Proxy with Nginx"
4description = ""
5draft = false
6+++
7
8# What is a Reverse Proxy?
9
10A reverse proxy is a server that is placed between local servers or services and
11clients/users (e.g., the internet). The reverse proxy intercepts all requests
12from clients at the network edge and uses its configuration files to determine
13where each request should be sent.
14
15## A Brief Example
16
17For example, let's say that I run three servers in my home:
18
19- Server01 (`example.com`)
20- Server02 (`service01.example.com`)
21- Server03 (`service02.example.com`)
22
23I also run a reverse proxy in my home that intercepts all public traffic:
24
25- Reverse Proxy
26
27Assume that I have a domain name (`example.com`) that allows clients to request
28websites or services from my home servers.
29
30In this case, the reverse proxy will intercept all traffic from `example.com`
31that enters my network and determine if the client is requesting valid data,
32based on my configuration.
33
34If the user is requesting `example.com` and my configuration files say that
35Server~01~ holds that data, Nginx will send the user to Server~01~. If I were to
36change the configuration so that `example.com` is routed to Server~02~, that
37same user would be sent to Server~02~ instead.
38
39```txt
40┌──────┐ ┌───────────┐
41│ User │─┐ ┌──► Server_01 │
42└──────┘ │ │ └───────────┘
43 │ ┌──────────┐ ┌───────────────┐ │ ┌───────────┐
44 ├────► Internet ├───► Reverse Proxy ├─────├──► Server_02 │
45 │ └──────────┘ └───────────────┘ │ └───────────┘
46┌──────┐ │ │ ┌───────────┐
47│ User │─┘ └──► Server_03 │
48└──────┘ └───────────┘
49```
50
51# Reverse Proxy Options
52
53There are a lot of options when it comes to reverse proxy servers, so I'm just
54going to list a few of the options I've heard recommended over the last few
55years:
56
57- [Nginx](https://nginx.com)
58- [Caddy](https://caddyserver.com)
59- [Traefik](https://traefik.io/)
60- [HAProxy](https://www.haproxy.org/)
61- [Squid](https://ubuntu.com/server/docs/proxy-servers-squid)
62
63In this post, we will be using Nginx as our reverse proxy, running on Ubuntu
64Server 20.04.4 LTS.
65
66# Nginx Reverse Proxy Example
67
68## Local Applications
69
70You may be like me and have a lot of applications running on your local network
71that you'd like to expose publicly with a domain.
72
73In my case, I have services running in multiple Docker containers within a
74single server and want a way to visit those services from anywhere with a URL.
75For example, on my local network, [Dashy](https://dashy.to) runs through port
764000 (`localhost:4000`) and [Uptime
77Kuma](https://github.com/louislam/uptime-kuma) runs through port 3001
78(`localhost:3001`).
79
80In order to expose these services to the public, I will need to do the
81following:
82
831. Set up DNS records for a domain or subdomain (one per service) to point
84 toward the IP address of the server.
852. Open up the server network's HTTP and HTTPS ports (80 & 443) so that the
86 reverse proxy can accept traffic and determine where to send it.
873. Install the reverse proxy software.
884. Configure the reverse proxy to recognize which service should get traffic
89 from any of the domains or subdomains.
90
91## Step 1: DNS Configuration
92
93To start, update your DNS configuration so that you have an `A` record for each
94domain or subdomain.
95
96The `A` records should point toward the public IP address of the server. If you
97don't know the public IP address, log in to the server and run the following
98command:
99
100```sh
101curl ifconfig.co
102```
103
104In the DNS example below, `xxx.xxx.xxx.xxx` is the public IP address of the
105server.
106
107```conf
108example.com A xxx.xxx.xxx.xxx
109uptime.example.com A xxx.xxx.xxx.xxx
110dashy.example.com A xxx.xxx.xxx.xxx
111www CNAME example.com
112```
113
114Finally, ensure the DNS has propagated correctly with [DNS
115Checker](https://dnschecker.org) by entering your domains or subdomains in the
116search box and ensuring the results are showing the correct IP address.
117
118## Step 2: Open Network Ports
119
120This step will be different depending on which router you have in your home. If
121you're not sure, try to visit [192.168.1.1](http://192.168.1.1) in your browser.
122Login credentials are usually written on a sticker somewhere on your
123modem/router.
124
125Once you're able to log in to your router, find the Port Forwarding settings.
126You will need to forward ports `80` and `443` to whichever machine is running
127the reverse proxy.
128
129In my case, the table below shows the port-forwarding rules I've created. In
130this table, `xxx.xxx.xxx.xxx` is the local device IP of the reverse proxy
131server, it will probably be an IP between `192.168.1.1` and `192.168.1.255`.
132
133| NAME | FROM PORT | DEST PORT/IP | ENABLED |
134| ----- | --------- | --------------- | ------- |
135| HTTP | 80 | xxx.xxx.xxx.xxx | TRUE |
136| HTTPS | 443 | xxx.xxx.xxx.xxx | TRUE |
137
138Once configured, these rules will direct all web traffic to your reverse proxy.
139
140## Step 3: Nginx Installation
141
142To install Nginx, simply run the following command:
143
144```sh
145sudo apt install nginx
146```
147
148If you have a firewall enabled, open up ports `80` and `443` on your server so
149that Nginx can accept web traffic from the router.
150
151For example, if you want to use `ufw` for web traffic and SSH, run the following
152commands:
153
154```sh
155sudo ufw allow 'Nginx Full'
156sudo ufw allow SSH
157sudo ufw enable
158```
159
160## Step 4: Nginx Configuration
161
162Now that we have domains pointing toward the server, the only step left is to
163configure the reverse proxy to direct traffic from domains to local services.
164
165To start, you'll need to create a configuration file for each domain in
166`/etc/nginx/sites-available/`. They will look identical except for the
167`server_name` variable and the `proxy_pass` port.
168
169Dashy:
170
171```sh
172nano /etc/nginx/sites-available/dashy.example.com
173```
174
175```config
176server {
177 listen 80;
178 server_name dashy.example.com;
179
180 location / {
181 proxy_pass http://localhost:4000;
182 }
183}
184```
185
186Uptime:
187
188```sh
189nano /etc/nginx/sites-available/uptime.example.com
190```
191
192```config
193server {
194 listen 80;
195 server_name uptime.example.com;
196
197 location / {
198 proxy_pass http://localhost:3001;
199 }
200}
201```
202
203Once the configuration files are created, you will need to enable them with the
204`symlink` command:
205
206```sh
207sudo ln -s /etc/nginx/sites-available/dashy.example.com /etc/nginx/sites-enabled/
208```
209
210Voilà! Your local services should now be available through their URLs.
211
212# HTTPS with Certbot
213
214If you've followed along, you'll notice that your services are only available
215via HTTP (not HTTPS).
216
217If you want to enable HTTPS for your new domains, you will need to generate
218SSL/TLS certificates for them. The easiest way to generate certificates on Nginx
219is [Certbot](https://certbot.eff.org):
220
221```sh
222sudo apt install snapd; sudo snap install core; sudo snap refresh core
223sudo snap install --classic certbot
224sudo ln -s /snap/bin/certbot /usr/bin/certbot
225sudo certbot --nginx
226```
content/blog/2022-04-02-nginx-reverse-proxy.org added +230
@@ -0,0 +1,230 @@
1#+date: <2022-04-02>
2#+title: Set-Up a Reverse Proxy with Nginx
3#+description:
4
5
6* What is a Reverse Proxy?
7
8A reverse proxy is a server that is placed between local servers or
9services and clients/users (e.g., the internet). The reverse proxy
10intercepts all requests from clients at the network edge and uses its
11configuration files to determine where each request should be sent.
12
13** A Brief Example
14
15For example, let's say that I run three servers in my home:
16
17- Server01 (=example.com=)
18- Server02 (=service01.example.com=)
19- Server03 (=service02.example.com=)
20
21I also run a reverse proxy in my home that intercepts all public
22traffic:
23
24- Reverse Proxy
25
26Assume that I have a domain name (=example.com=) that allows clients to
27request websites or services from my home servers.
28
29In this case, the reverse proxy will intercept all traffic from
30=example.com= that enters my network and determine if the client is
31requesting valid data, based on my configuration.
32
33If the user is requesting =example.com= and my configuration files say
34that Server_{01} holds that data, Nginx will send the user to
35Server_{01}. If I were to change the configuration so that =example.com=
36is routed to Server_{02}, that same user would be sent to Server_{02}
37instead.
38
39#+begin_src txt
40┌──────┐ ┌───────────┐
41│ User │─┐ ┌──► Server_01 │
42└──────┘ │ │ └───────────┘
43 │ ┌──────────┐ ┌───────────────┐ │ ┌───────────┐
44 ├────► Internet ├───► Reverse Proxy ├─────├──► Server_02 │
45 │ └──────────┘ └───────────────┘ │ └───────────┘
46┌──────┐ │ │ ┌───────────┐
47│ User │─┘ └──► Server_03 │
48└──────┘ └───────────┘
49#+end_src
50
51* Reverse Proxy Options
52
53There are a lot of options when it comes to reverse proxy servers, so
54I'm just going to list a few of the options I've heard recommended over
55the last few years:
56
57- [[https://nginx.com][Nginx]]
58- [[https://caddyserver.com][Caddy]]
59- [[https://traefik.io/][Traefik]]
60- [[https://www.haproxy.org/][HAProxy]]
61- [[https://ubuntu.com/server/docs/proxy-servers-squid][Squid]]
62
63In this post, we will be using Nginx as our reverse proxy, running on
64Ubuntu Server 20.04.4 LTS.
65
66* Nginx Reverse Proxy Example
67
68** Local Applications
69
70You may be like me and have a lot of applications running on your local
71network that you'd like to expose publicly with a domain.
72
73In my case, I have services running in multiple Docker containers within
74a single server and want a way to visit those services from anywhere
75with a URL. For example, on my local network,
76[[https://dashy.to][Dashy]] runs through port 4000 (=localhost:4000=)
77and [[https://github.com/louislam/uptime-kuma][Uptime Kuma]] runs
78through port 3001 (=localhost:3001=).
79
80In order to expose these services to the public, I will need to do the
81following:
82
831. Set up DNS records for a domain or subdomain (one per service) to
84 point toward the IP address of the server.
852. Open up the server network's HTTP and HTTPS ports (80 & 443) so that
86 the reverse proxy can accept traffic and determine where to send it.
873. Install the reverse proxy software.
884. Configure the reverse proxy to recognize which service should get
89 traffic from any of the domains or subdomains.
90
91** Step 1: DNS Configuration
92
93To start, update your DNS configuration so that you have an =A= record
94for each domain or subdomain.
95
96The =A= records should point toward the public IP address of the server.
97If you don't know the public IP address, log in to the server and run
98the following command:
99
100#+begin_src sh
101curl ifconfig.co
102#+end_src
103
104In the DNS example below, =xxx.xxx.xxx.xxx= is the public IP address of
105the server.
106
107#+begin_src conf
108example.com A xxx.xxx.xxx.xxx
109uptime.example.com A xxx.xxx.xxx.xxx
110dashy.example.com A xxx.xxx.xxx.xxx
111www CNAME example.com
112#+end_src
113
114Finally, ensure the DNS has propagated correctly with
115[[https://dnschecker.org][DNS Checker]] by entering your domains or
116subdomains in the search box and ensuring the results are showing the
117correct IP address.
118
119** Step 2: Open Network Ports
120
121This step will be different depending on which router you have in your
122home. If you're not sure, try to visit
123[[http://192.168.1.1][192.168.1.1]] in your browser. Login credentials
124are usually written on a sticker somewhere on your modem/router.
125
126Once you're able to log in to your router, find the Port Forwarding
127settings. You will need to forward ports =80= and =443= to whichever
128machine is running the reverse proxy.
129
130In my case, the table below shows the port-forwarding rules I've
131created. In this table, =xxx.xxx.xxx.xxx= is the local device IP of the
132reverse proxy server, it will probably be an IP between =192.168.1.1=
133and =192.168.1.255=.
134
135| NAME | FROM PORT | DEST PORT/IP | ENABLED |
136|-------+-----------+-----------------+---------|
137| HTTP | 80 | xxx.xxx.xxx.xxx | TRUE |
138| HTTPS | 443 | xxx.xxx.xxx.xxx | TRUE |
139
140Once configured, these rules will direct all web traffic to your reverse
141proxy.
142
143** Step 3: Nginx Installation
144
145To install Nginx, simply run the following command:
146
147#+begin_src sh
148sudo apt install nginx
149#+end_src
150
151If you have a firewall enabled, open up ports =80= and =443= on your
152server so that Nginx can accept web traffic from the router.
153
154For example, if you want to use =ufw= for web traffic and SSH, run the
155following commands:
156
157#+begin_src sh
158sudo ufw allow 'Nginx Full'
159sudo ufw allow SSH
160sudo ufw enable
161#+end_src
162
163** Step 4: Nginx Configuration
164
165Now that we have domains pointing toward the server, the only step left
166is to configure the reverse proxy to direct traffic from domains to
167local services.
168
169To start, you'll need to create a configuration file for each domain in
170=/etc/nginx/sites-available/=. They will look identical except for the
171=server_name= variable and the =proxy_pass= port.
172
173Dashy:
174
175#+begin_src sh
176nano /etc/nginx/sites-available/dashy.example.com
177#+end_src
178
179#+begin_src config
180server {
181 listen 80;
182 server_name dashy.example.com;
183
184 location / {
185 proxy_pass http://localhost:4000;
186 }
187}
188#+end_src
189
190Uptime:
191
192#+begin_src sh
193nano /etc/nginx/sites-available/uptime.example.com
194#+end_src
195
196#+begin_src config
197server {
198 listen 80;
199 server_name uptime.example.com;
200
201 location / {
202 proxy_pass http://localhost:3001;
203 }
204}
205#+end_src
206
207Once the configuration files are created, you will need to enable them
208with the =symlink= command:
209
210#+begin_src sh
211sudo ln -s /etc/nginx/sites-available/dashy.example.com /etc/nginx/sites-enabled/
212#+end_src
213
214Voilà! Your local services should now be available through their URLs.
215
216* HTTPS with Certbot
217
218If you've followed along, you'll notice that your services are only
219available via HTTP (not HTTPS).
220
221If you want to enable HTTPS for your new domains, you will need to
222generate SSL/TLS certificates for them. The easiest way to generate
223certificates on Nginx is [[https://certbot.eff.org][Certbot]]:
224
225#+begin_src sh
226sudo apt install snapd; sudo snap install core; sudo snap refresh core
227sudo snap install --classic certbot
228sudo ln -s /snap/bin/certbot /usr/bin/certbot
229sudo certbot --nginx
230#+end_src
content/blog/2022-04-09-pinetime.md deleted −134
@@ -1,134 +0,0 @@
1+++
2date = 2022-04-09
3title = "PineTime: An Open-Source Smart Watch"
4description = ""
5draft = false
6+++
7
8# PineTime Product Information
9
10## Overview
11
12The [PineTime](https://www.pine64.org/pinetime/) is an open-source smartwatch,
13created by [PINE64](https://www.pine64.org). Originally announced in September
142019, this ARM-based watch is a fantastic option for users who want the benefits
15of a modern smartwatch with the backing of open-source components and software.
16
17## Product Specifications
18
19I won't dive into too many details that you can find on [the product
20page](https://www.pine64.org/pinetime/), but I wanted to point out the prices
21for each watch and the primary functions:
22
231. Price:
24
25 - $26.99 (Sealed)
26 - $24.99 (Dev Kit)
27 - $51.98 (One Sealed + One Dev Kit)
28
292. Primary Functionality:
30
31 - Clock (+ Smartphone Sync)
32 - Pedometer
33 - Heart Rate Monitor
34 - Sleep Monitor
35 - Calories burned
36 - Messaging
37 - Smartphone Notifications
38 - Media Controls
39
40# Unboxing
41
42Now, my PineTime was ordered on 2022-02-17, shipped on 2022-02-22, and was
43delivered on 2022-03-23. With the current delays on shipping times around the
44world (and the semiconductor shortage), a month for delivery from China seems
45reasonable to me.
46
47The packaging is simple, and the watch comes with instructions, technical
48information, the watch, and a charger (it does not include a USB wall adapter).
49
50The watch itself was able to turn on immediately when I pulled it out of the
51box, but the battery was depleted and required charging right away.
52
53# Software
54
55## Watch OS: InfiniTime
56
57While turning on the watch for the first time, some of the main design choices
58you can see in the watch OS,
59[InfiniTime](https://wiki.pine64.org/wiki/InfiniTime), are:
60
61- A square bezel, not too thin against the sides of the watch.
62- A simple, rubber band.
63- Basic font and screen pixel design.
64- Swipe gestures to access other screens.
65
66The OS itself is fantastic in terms of functionality for me. It does exactly
67what a smartwatch should do - track time, steps, heart rates, and connect to
68another smart device, without being overly burdensome to the user.
69
70My only gripe so far is that it's _really_ difficult to swipe to different
71screens, such as pulling down the notification tray. I'm not sure if this is an
72OS or hardware issue, but it makes it quite hard to quickly move around the
73screens.
74
75However, my absolute favorite design choice is that the button the side turns
76the screen on and off and tilting/waving my wrist doesn't accidentally turn on
77the screen. With other watches, I absolutely hated not being able to turn off
78the raise-to-wake or wave features (i.e., blinding myself while wearing a watch
79at night because I moved my arm).
80
81## iOS App: InfiniLink
82
83Since I am using iOS as my primary mobile device OS, I am using the
84[InfiniLink](https://github.com/xan-m/InfiniLink) app to connect my watch.
85
86This app provides the following for PineTime owners:
87
88- Firmware updates
89- Steps
90- Charts
91- Notifications
92
93Another big feature of InfiniLink is the ability to track pedometer steps in a
94collection of beautiful graphs, with the option to change your step goal and add
95in manual steps.
96
97Finally, there are charts to display the battery percentage and heart rates over
98time. This area also comes with an option to clear data.
99
100# Final Thoughts
101
102## Pros
103
104After wearing my watch for a few weeks, I have mostly positive thoughts about
105the watch so far. In the past, I have owned smartwatches by FitBit, Fossil,
106Apple, etc. - **but I prefer the PineTime over all of those watches**.
107
108The PineTime strips out all the unnecessary features and performs the functions
109that it provides effectively and efficiently.
110
111The battery life is amazing on this device. By default, the watch seems to last
112anywhere from a few days to a week before dying.
113
114And of course, it's open source and backed by some of the most dedicated
115enthusiasts and developers I've seen. Watching the Matrix channel, forums, and
116website have been exciting to see.
117
118## Cons
119
120If I had to complain about anything, it would simply be the small bugs in some
121features that can be contributed to the companion apps more than the watch
122itself.
123
124A regular user would want native notification support out-of-the-box, which is
125the biggest item not working for me at the moment.
126
127My only other complaint is that the battery indicator on the watch doesn't seem
128accurate when it's nearing depletion - it seems that there's a bit of battery
129life left and then my watch is dead very suddenly after. This could just be me
130misinterpreting the battery level icons, but it has fooled me a few times into
131thinking I had more battery left than I actually did.
132
133Other than those small items, I really do love this watch and am glad I replaced
134my Apple Watch with the PineTime.
content/blog/2022-04-09-pinetime.org added +138
@@ -0,0 +1,138 @@
1#+date: <2022-04-09>
2#+title: PineTime: An Open-Source Smart Watch
3#+description:
4
5
6* PineTime Product Information
7
8** Overview
9
10The [[https://www.pine64.org/pinetime/][PineTime]] is an open-source
11smartwatch, created by [[https://www.pine64.org][PINE64]]. Originally
12announced in September 2019, this ARM-based watch is a fantastic option
13for users who want the benefits of a modern smartwatch with the backing
14of open-source components and software.
15
16** Product Specifications
17
18I won't dive into too many details that you can find on
19[[https://www.pine64.org/pinetime/][the product page]], but I wanted to
20point out the prices for each watch and the primary functions:
21
221. Price:
23
24 - $26.99 (Sealed)
25 - $24.99 (Dev Kit)
26 - $51.98 (One Sealed + One Dev Kit)
27
282. Primary Functionality:
29
30 - Clock (+ Smartphone Sync)
31 - Pedometer
32 - Heart Rate Monitor
33 - Sleep Monitor
34 - Calories burned
35 - Messaging
36 - Smartphone Notifications
37 - Media Controls
38
39* Unboxing
40
41Now, my PineTime was ordered on 2022-02-17, shipped on 2022-02-22, and
42was delivered on 2022-03-23. With the current delays on shipping times
43around the world (and the semiconductor shortage), a month for delivery
44from China seems reasonable to me.
45
46The packaging is simple, and the watch comes with instructions,
47technical information, the watch, and a charger (it does not include a
48USB wall adapter).
49
50The watch itself was able to turn on immediately when I pulled it out of
51the box, but the battery was depleted and required charging right away.
52
53* Software
54
55** Watch OS: InfiniTime
56
57While turning on the watch for the first time, some of the main design
58choices you can see in the watch OS,
59[[https://wiki.pine64.org/wiki/InfiniTime][InfiniTime]], are:
60
61- A square bezel, not too thin against the sides of the watch.
62- A simple, rubber band.
63- Basic font and screen pixel design.
64- Swipe gestures to access other screens.
65
66The OS itself is fantastic in terms of functionality for me. It does
67exactly what a smartwatch should do - track time, steps, heart rates,
68and connect to another smart device, without being overly burdensome to
69the user.
70
71My only gripe so far is that it's /really/ difficult to swipe to
72different screens, such as pulling down the notification tray. I'm not
73sure if this is an OS or hardware issue, but it makes it quite hard to
74quickly move around the screens.
75
76However, my absolute favorite design choice is that the button the side
77turns the screen on and off and tilting/waving my wrist doesn't
78accidentally turn on the screen. With other watches, I absolutely hated
79not being able to turn off the raise-to-wake or wave features (i.e.,
80blinding myself while wearing a watch at night because I moved my arm).
81
82** iOS App: InfiniLink
83
84Since I am using iOS as my primary mobile device OS, I am using the
85[[https://github.com/xan-m/InfiniLink][InfiniLink]] app to connect my
86watch.
87
88This app provides the following for PineTime owners:
89
90- Firmware updates
91- Steps
92- Charts
93- Notifications
94
95Another big feature of InfiniLink is the ability to track pedometer
96steps in a collection of beautiful graphs, with the option to change
97your step goal and add in manual steps.
98
99Finally, there are charts to display the battery percentage and heart
100rates over time. This area also comes with an option to clear data.
101
102* Final Thoughts
103
104** Pros
105
106After wearing my watch for a few weeks, I have mostly positive thoughts
107about the watch so far. In the past, I have owned smartwatches by
108FitBit, Fossil, Apple, etc. - *but I prefer the PineTime over all of
109those watches*.
110
111The PineTime strips out all the unnecessary features and performs the
112functions that it provides effectively and efficiently.
113
114The battery life is amazing on this device. By default, the watch seems
115to last anywhere from a few days to a week before dying.
116
117And of course, it's open source and backed by some of the most dedicated
118enthusiasts and developers I've seen. Watching the Matrix channel,
119forums, and website have been exciting to see.
120
121** Cons
122
123If I had to complain about anything, it would simply be the small bugs
124in some features that can be contributed to the companion apps more than
125the watch itself.
126
127A regular user would want native notification support out-of-the-box,
128which is the biggest item not working for me at the moment.
129
130My only other complaint is that the battery indicator on the watch
131doesn't seem accurate when it's nearing depletion - it seems that
132there's a bit of battery life left and then my watch is dead very
133suddenly after. This could just be me misinterpreting the battery level
134icons, but it has fooled me a few times into thinking I had more battery
135left than I actually did.
136
137Other than those small items, I really do love this watch and am glad I
138replaced my Apple Watch with the PineTime.
content/blog/2022-06-01-ditching-cloudflare.md deleted −89
@@ -1,89 +0,0 @@
1+++
2date = 2022-06-01
3title = "Ditching Cloudflare for Njalla"
4description = ""
5draft = false
6+++
7
8# Registrar
9
10After spending a year or so using Cloudflare for DNS only - no proxying or
11applications - I spent the last few months using Cloudflare Tunnels and
12Cloudflare Access to protect my self-hosted websites and applications via their
13proxy traffic model.
14
15However, I have never liked using Cloudflare due to their increasingly large
16share of control over web traffic, as well as their business model of being a
17MITM for all of your traffic.
18
19So, as of today, I have switched over to [Njalla](https://njal.la) as my
20registrar and DNS manager. I was able to easily transfer my domains over
21rapidly, with only one domain taking more than 15-30 minutes to propagate.
22
23+I do still have two domains sitting at Cloudflare for the moment while I decide
24if they're worth the higher rates (one domain is 30€ and the other is 45€).+
25
26> **Update (2022.06.03)**: I ended up transferring my final two domains over to
27> Njalla, clearing my Cloudflare account of personal data, and deleting the
28> Cloudflare account entirely. _I actually feel relieved to have moved on to a
29> provider I trust._
30
31# DNS
32
33As noted above, I'm using Njalla exclusively for DNS configurations on my
34domains.
35
36However, the transfer process was not ideal. As soon as the domains transferred
37over, I switched the nameservers from Cloudflare to Njalla and lost most of the
38associated DNS records. So, the majority of the time spent during the migration
39was simply re-typing all the DNS records back in one-by-one.
40
41This would be much simpler if I were able to edit the plain-text format of the
42DNS configuration. I was able to do that at a past registrar (perhaps it was
43[Gandi.net](https://gandi.net/)?) and it made life a lot easier.
44
45## Dynamic DNS Updates
46
47I have built an easy Python script to run (or set-up in `cron` to run
48automatically) that will check my server's IPv4 and IPv6, compare it to Njalla,
49and update the DNS records if they don't match. You can see the full script and
50process in my other post: [Updating Dynamic DNS with Njalla
51API](../njalla-dns-api/).
52
53I haven't used this other method, but I do know that you can create `Dynamic`
54DNS records with Njalla that [work for updating dynamic
55subdomains](https://njal.la/docs/ddns/).
56
57## Njalla's DNS Tool
58
59One neat upside to Njalla is that they have a [DNS lookup
60tool](https://check.njal.la/dns/) that provides a lot of great information for
61those of you (AKA: me) who hate using the `dig` command.
62
63This was very useful for monitoring a couple of my transferred domains to see
64when the changes in nameservers, records, and DNSSEC went into effect.
65
66# Tunnel
67
68Cloudflare Tunnel is a service that acts as a reverse-proxy (hosted on
69Cloudflare's servers) and allowed me to mask the private IP address of the
70server hosting my various websites and apps.
71
72However, as I was moving away from Cloudflare, I was not able to find a suitable
73replacement that was both inexpensive and simple. So, I simply went back to
74hosting [my own reverse proxy with
75Nginx](file:///blog/set-up-nginx-reverse-proxy/). With the recent additions of
76Unifi hardware in my server/network rack, I am much more protected against spam
77and malicious attacks at the network edge than I was before I switched to
78Cloudflare.
79
80# Access
81
82Cloudflare Access, another app I used in combination with Cloudflare Tunnel,
83provided an authentication screen that required you to enter valid credentials
84before Cloudflare would forward you to the actual website or app (if the
85website/app has their own authentication, you'd then have to authenticate a
86second time).
87
88I did not replace this service with anything since I only host a handful of
89non-sensitive apps that don't require duplicate authentication.
content/blog/2022-06-01-ditching-cloudflare.org added +95
@@ -0,0 +1,95 @@
1#+date: <2022-06-01>
2#+title: Ditching Cloudflare for Njalla
3#+description:
4
5
6* Registrar
7
8After spending a year or so using Cloudflare for DNS only - no proxying
9or applications - I spent the last few months using Cloudflare Tunnels
10and Cloudflare Access to protect my self-hosted websites and
11applications via their proxy traffic model.
12
13However, I have never liked using Cloudflare due to their increasingly
14large share of control over web traffic, as well as their business model
15of being a MITM for all of your traffic.
16
17So, as of today, I have switched over to [[https://njal.la][Njalla]] as
18my registrar and DNS manager. I was able to easily transfer my domains
19over rapidly, with only one domain taking more than 15-30 minutes to
20propagate.
21
22+I do still have two domains sitting at Cloudflare for the moment while
23I decide if they're worth the higher rates (one domain is 30€ and the
24other is 45€).+
25
26#+begin_quote
27*Update (2022.06.03)*: I ended up transferring my final two domains over
28to Njalla, clearing my Cloudflare account of personal data, and deleting
29the Cloudflare account entirely. /I actually feel relieved to have moved
30on to a provider I trust./
31#+end_quote
32
33* DNS
34
35As noted above, I'm using Njalla exclusively for DNS configurations on
36my domains.
37
38However, the transfer process was not ideal. As soon as the domains
39transferred over, I switched the nameservers from Cloudflare to Njalla
40and lost most of the associated DNS records. So, the majority of the
41time spent during the migration was simply re-typing all the DNS records
42back in one-by-one.
43
44This would be much simpler if I were able to edit the plain-text format
45of the DNS configuration. I was able to do that at a past registrar
46(perhaps it was [[https://gandi.net/][Gandi.net]]?) and it made life a
47lot easier.
48
49** Dynamic DNS Updates
50
51I have built an easy Python script to run (or set-up in =cron= to run
52automatically) that will check my server's IPv4 and IPv6, compare it to
53Njalla, and update the DNS records if they don't match. You can see the
54full script and process in my other post: [[../njalla-dns-api/][Updating
55Dynamic DNS with Njalla API]].
56
57I haven't used this other method, but I do know that you can create
58=Dynamic= DNS records with Njalla that
59[[https://njal.la/docs/ddns/][work for updating dynamic subdomains]].
60
61** Njalla's DNS Tool
62
63One neat upside to Njalla is that they have a
64[[https://check.njal.la/dns/][DNS lookup tool]] that provides a lot of
65great information for those of you (AKA: me) who hate using the =dig=
66command.
67
68This was very useful for monitoring a couple of my transferred domains
69to see when the changes in nameservers, records, and DNSSEC went into
70effect.
71
72* Tunnel
73
74Cloudflare Tunnel is a service that acts as a reverse-proxy (hosted on
75Cloudflare's servers) and allowed me to mask the private IP address of
76the server hosting my various websites and apps.
77
78However, as I was moving away from Cloudflare, I was not able to find a
79suitable replacement that was both inexpensive and simple. So, I simply
80went back to hosting [[file:///blog/set-up-nginx-reverse-proxy/][my own
81reverse proxy with Nginx]]. With the recent additions of Unifi hardware
82in my server/network rack, I am much more protected against spam and
83malicious attacks at the network edge than I was before I switched to
84Cloudflare.
85
86* Access
87
88Cloudflare Access, another app I used in combination with Cloudflare
89Tunnel, provided an authentication screen that required you to enter
90valid credentials before Cloudflare would forward you to the actual
91website or app (if the website/app has their own authentication, you'd
92then have to authenticate a second time).
93
94I did not replace this service with anything since I only host a handful
95of non-sensitive apps that don't require duplicate authentication.
content/blog/2022-06-07-self-hosting-freshrss.md deleted −238
@@ -1,238 +0,0 @@
1+++
2date = 2022-06-07
3title = "Self-Hosting FreshRSS"
4description = ""
5draft = false
6+++
7
8# Why RSS?
9
10After noticing that I have collected 50+ blogs as bookmarks, I decided to
11migrate back to using RSS feeds to stay up-to-date with my favorite websites.
12Using RSS allows me to read all of these posts in a single app (on both mobile &
13desktop) and allows me to be notified when new posts are available.
14
15However, I ran into one issue: syncing subscriptions and read/unread posts
16across devices. Since I want to be able to easily read on both mobile and
17desktop, I decided to look for a self-hosted RSS solution.
18
19Thus, I found [FreshRSS](https://www.freshrss.org/) and was able to successfully
20install it on my server in about 30 minutes.
21
22# Documentation
23
24While it's certainly not robust, the [FreshRSS
25documentation](https://freshrss.github.io/FreshRSS/) is helpful for figuring out
26basic information about the service.
27
28However, I wanted to install this service as a Docker container and stumbled
29across the [Docker
30README](https://github.com/FreshRSS/FreshRSS/tree/edge/Docker) within the GitHub
31repository.
32
33This README was the documentation I actually needed. However, as you'll see
34below, I still had to manually edit one file (`config.php`) to access the API
35externally via my RSS apps.
36
37# Installation
38
39## DNS
40
41The first step, as required by any external web service, was assigning a domain
42name to use. I chose to use a subdomain, like `rss.example.com`.
43
44To assign this, I created an `A` record in my DNS settings with the IPv4 address
45of the server and an `AAAA` record with the IPv6 address of the server. Note:
46assigning an IPv6 (`AAAA`) record is optional, but I like to enable IPV6 for my
47services.
48
49```config
50rss.example.com A xxx.xxx.xxx.xxx
51rss.example.com AAAA xxxx:xxxx: ... :xxxx
52```
53
54## Docker
55
56I initially tried to set up a `docker-compose.yml` file with a `.env` file
57because I prefer to have a file I can look back at later to see how I initially
58started the container, but it simply wouldn't work for me. I'm not sure why, but
59I assume I wasn't telling `docker-compose` where the `.env` file was.
60
61Regardless, I chose to simply run the service with `docker run`. See the
62following command for my `docker run` configuration:
63
64```sh
65sudo docker run -d --restart unless-stopped --log-opt max-size=10m \
66 -p 8080:80 \
67 -e TZ=America/Chicago \
68 -e 'CRON_MIN=1,31' \
69 -v freshrss_data:/var/www/FreshRSS/data \
70 -v freshrss_extensions:/var/www/FreshRSS/extensions \
71 --name freshrss \
72 freshrss/freshrss
73```
74
75This started the container successfully and allowed me to visit the FreshRSS
76instance at `localhost:8080`.
77
78## Fresh RSS Set-Up
79
80I **HIGHLY** suggest that you set up your user account prior to exposing this
81service to the public. It's unlikely that someone is trying to access the exact
82domain or IP/port you're assigning here, but as soon as you expose this service,
83the first person to open the URL will be able to create the admin user.
84
85In order to set up your FreshRSS service, open the `localhost:8080` URL in your
86browser (you may need to use a local IP instead of `localhost` if you're
87accessing the page from a different machine on the network - e.g.,
88`192.168.1.20:8080`).
89
90Once the page loads, set up your default user with a strong username and
91password. You may also choose to configure other settings prior to exposing this
92service.
93
94## Nginx Reverse-Proxy
95
96In order to access this service outside my home, I needed to set up a
97reverse-proxy to connect `localhost:8080` to `rss.example.com`.
98
99First, I created a new Nginx configuration file:
100
101```sh
102sudo nano /etc/nginx/sites-available/rss.example.com
103```
104
105Within the config file, I pasted the following code:
106
107```config
108upstream freshrss {
109 server 127.0.0.1:8080;
110 keepalive 64;
111}
112
113server {
114 server_name rss.example.com;
115 listen 80;
116
117 location / {
118 # The final `/` is important.
119 proxy_pass http://localhost:8080/;
120 add_header X-Frame-Options SAMEORIGIN;
121 add_header X-XSS-Protection "1; mode=block";
122 proxy_redirect off;
123 proxy_buffering off;
124 proxy_set_header Host $host;
125 proxy_set_header X-Real-IP $remote_addr;
126 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
127 proxy_set_header X-Forwarded-Proto $scheme;
128 proxy_set_header X-Forwarded-Port $server_port;
129 proxy_read_timeout 90;
130
131 # Forward the Authorization header for the Google Reader API.
132 proxy_set_header Authorization $http_authorization;
133 proxy_pass_header Authorization;
134 }
135}
136```
137
138Finally, restart Nginx and you will be able to access your service via HTTP:
139
140```sh
141sudo systemctl restart nginx.service
142```
143
144## HTTPS
145
146However, I don't want to access my RSS feeds via HTTP. I want it available only
147via HTTPS. In order to do this, I ran the [certbot](https://certbot.eff.org/)
148program to generate SSL certificates for me:
149
150```sh
151sudo certbot --nginx
152```
153
154This process will automatically generate an SSL certificate for you and modify
155the Nginx configuration file to include a redirect from HTTP to HTTPS.
156
157# Post-Installation Fixes
158
159At this point, we have a functional FreshRSS website, available from anywhere
160and secured with HTTPS. However, attempting to connect this service to an RSS
161app resulted in many errors regarding unavailable URLs and incorrect
162credentials.
163
164## API Set-Up
165
166First, you need to open your user profile in FreshRSS (`Settings` > `Profile`)
167and set an API password in the field at the bottom. This is the password you
168will need to provide to your RSS apps.
169
170Once that is set and saved, click the link below the API password field to open
171the API check tool. It should look something like `https://localhost:8080/api/`
172or `https://rss.example.com/api/`.
173
174Within this page, you _should_ see your correct external URL and "PASS" at the
175bottom of each API type. This would mean everything is set up correctly, and you
176can now move on and login to any RSS apps that support self-hosted options.
177
178In my case, the URL showed an internal URL and I had a warning that the
179`base_url` variable may be misconfigured. If this is the case, see the next
180section for a fix.
181
182## Base URL Fix
183
184In order to fix the `base_url` for the API, I opened up my docker container with
185the following command:
186
187```sh
188sudo docker exec -it freshrss bash
189```
190
191Within this container, update the packages and install an editor:
192
193```sh
194apt-get update
195apt-get install nano
196```
197
198Finally, open up `config.php` in the `data` directory:
199
200```sh
201nano data/config.php
202```
203
204Within `config.php`, you will need to update the `base_url` variable and update
205it to match your external URL. In my case, I simply commented-out the incorrect
206URL with `//` and added the correct one on a new line:
207
208```php
209<?php
210 return array (
211 ...
212 // 'base_url' => 'http://localhost:8080',
213 'base_url' => 'https://rss.example.com',
214 ...
215 )
216>
217```
218
219You can now exit the file with `Ctrl + x`, press `y` to save the file, and then
220click `Enter` to keep the same file name.
221
222Finally, just exit out of the docker container:
223
224```sh
225exit
226```
227
228Next, just restart the container:
229
230```sh
231sudo docker restart freshrss
232```
233
234Voilà! Your API check should now "PASS" and you should be able to use one of the
235API URLs in your RSS apps.
236
237In my case, I use [NetNewsWire](https://netnewswire.com) on my desktop and
238phone.
content/blog/2022-06-07-self-hosting-freshrss.org added +243
@@ -0,0 +1,243 @@
1#+date: <2022-06-07>
2#+title: Self-Hosting FreshRSS
3#+description:
4
5
6* Why RSS?
7
8After noticing that I have collected 50+ blogs as bookmarks, I decided
9to migrate back to using RSS feeds to stay up-to-date with my favorite
10websites. Using RSS allows me to read all of these posts in a single app
11(on both mobile & desktop) and allows me to be notified when new posts
12are available.
13
14However, I ran into one issue: syncing subscriptions and read/unread
15posts across devices. Since I want to be able to easily read on both
16mobile and desktop, I decided to look for a self-hosted RSS solution.
17
18Thus, I found [[https://www.freshrss.org/][FreshRSS]] and was able to
19successfully install it on my server in about 30 minutes.
20
21* Documentation
22
23While it's certainly not robust, the
24[[https://freshrss.github.io/FreshRSS/][FreshRSS documentation]] is
25helpful for figuring out basic information about the service.
26
27However, I wanted to install this service as a Docker container and
28stumbled across the
29[[https://github.com/FreshRSS/FreshRSS/tree/edge/Docker][Docker README]]
30within the GitHub repository.
31
32This README was the documentation I actually needed. However, as you'll
33see below, I still had to manually edit one file (=config.php=) to
34access the API externally via my RSS apps.
35
36* Installation
37
38** DNS
39
40The first step, as required by any external web service, was assigning a
41domain name to use. I chose to use a subdomain, like =rss.example.com=.
42
43To assign this, I created an =A= record in my DNS settings with the IPv4
44address of the server and an =AAAA= record with the IPv6 address of the
45server. Note: assigning an IPv6 (=AAAA=) record is optional, but I like
46to enable IPV6 for my services.
47
48#+begin_src config
49rss.example.com A xxx.xxx.xxx.xxx
50rss.example.com AAAA xxxx:xxxx: ... :xxxx
51#+end_src
52
53** Docker
54
55I initially tried to set up a =docker-compose.yml= file with a =.env=
56file because I prefer to have a file I can look back at later to see how
57I initially started the container, but it simply wouldn't work for me.
58I'm not sure why, but I assume I wasn't telling =docker-compose= where
59the =.env= file was.
60
61Regardless, I chose to simply run the service with =docker run=. See the
62following command for my =docker run= configuration:
63
64#+begin_src sh
65sudo docker run -d --restart unless-stopped --log-opt max-size=10m \
66 -p 8080:80 \
67 -e TZ=America/Chicago \
68 -e 'CRON_MIN=1,31' \
69 -v freshrss_data:/var/www/FreshRSS/data \
70 -v freshrss_extensions:/var/www/FreshRSS/extensions \
71 --name freshrss \
72 freshrss/freshrss
73#+end_src
74
75This started the container successfully and allowed me to visit the
76FreshRSS instance at =localhost:8080=.
77
78** Fresh RSS Set-Up
79
80I *HIGHLY* suggest that you set up your user account prior to exposing
81this service to the public. It's unlikely that someone is trying to
82access the exact domain or IP/port you're assigning here, but as soon as
83you expose this service, the first person to open the URL will be able
84to create the admin user.
85
86In order to set up your FreshRSS service, open the =localhost:8080= URL
87in your browser (you may need to use a local IP instead of =localhost=
88if you're accessing the page from a different machine on the network -
89e.g., =192.168.1.20:8080=).
90
91Once the page loads, set up your default user with a strong username and
92password. You may also choose to configure other settings prior to
93exposing this service.
94
95** Nginx Reverse-Proxy
96
97In order to access this service outside my home, I needed to set up a
98reverse-proxy to connect =localhost:8080= to =rss.example.com=.
99
100First, I created a new Nginx configuration file:
101
102#+begin_src sh
103sudo nano /etc/nginx/sites-available/rss.example.com
104#+end_src
105
106Within the config file, I pasted the following code:
107
108#+begin_src config
109upstream freshrss {
110 server 127.0.0.1:8080;
111 keepalive 64;
112}
113
114server {
115 server_name rss.example.com;
116 listen 80;
117
118 location / {
119 # The final `/` is important.
120 proxy_pass http://localhost:8080/;
121 add_header X-Frame-Options SAMEORIGIN;
122 add_header X-XSS-Protection "1; mode=block";
123 proxy_redirect off;
124 proxy_buffering off;
125 proxy_set_header Host $host;
126 proxy_set_header X-Real-IP $remote_addr;
127 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
128 proxy_set_header X-Forwarded-Proto $scheme;
129 proxy_set_header X-Forwarded-Port $server_port;
130 proxy_read_timeout 90;
131
132 # Forward the Authorization header for the Google Reader API.
133 proxy_set_header Authorization $http_authorization;
134 proxy_pass_header Authorization;
135 }
136}
137#+end_src
138
139Finally, restart Nginx and you will be able to access your service via
140HTTP:
141
142#+begin_src sh
143sudo systemctl restart nginx.service
144#+end_src
145
146** HTTPS
147
148However, I don't want to access my RSS feeds via HTTP. I want it
149available only via HTTPS. In order to do this, I ran the
150[[https://certbot.eff.org/][certbot]] program to generate SSL
151certificates for me:
152
153#+begin_src sh
154sudo certbot --nginx
155#+end_src
156
157This process will automatically generate an SSL certificate for you and
158modify the Nginx configuration file to include a redirect from HTTP to
159HTTPS.
160
161* Post-Installation Fixes
162
163At this point, we have a functional FreshRSS website, available from
164anywhere and secured with HTTPS. However, attempting to connect this
165service to an RSS app resulted in many errors regarding unavailable URLs
166and incorrect credentials.
167
168** API Set-Up
169
170First, you need to open your user profile in FreshRSS (=Settings= >
171=Profile=) and set an API password in the field at the bottom. This is
172the password you will need to provide to your RSS apps.
173
174Once that is set and saved, click the link below the API password field
175to open the API check tool. It should look something like
176=https://localhost:8080/api/= or =https://rss.example.com/api/=.
177
178Within this page, you /should/ see your correct external URL and "PASS"
179at the bottom of each API type. This would mean everything is set up
180correctly, and you can now move on and login to any RSS apps that
181support self-hosted options.
182
183In my case, the URL showed an internal URL and I had a warning that the
184=base_url= variable may be misconfigured. If this is the case, see the
185next section for a fix.
186
187** Base URL Fix
188
189In order to fix the =base_url= for the API, I opened up my docker
190container with the following command:
191
192#+begin_src sh
193sudo docker exec -it freshrss bash
194#+end_src
195
196Within this container, update the packages and install an editor:
197
198#+begin_src sh
199apt-get update
200apt-get install nano
201#+end_src
202
203Finally, open up =config.php= in the =data= directory:
204
205#+begin_src sh
206nano data/config.php
207#+end_src
208
209Within =config.php=, you will need to update the =base_url= variable and
210update it to match your external URL. In my case, I simply commented-out
211the incorrect URL with =//= and added the correct one on a new line:
212
213#+begin_src php
214<?php
215 return array (
216 ...
217 // 'base_url' => 'http://localhost:8080',
218 'base_url' => 'https://rss.example.com',
219 ...
220 )
221>
222#+end_src
223
224You can now exit the file with =Ctrl + x=, press =y= to save the file,
225and then click =Enter= to keep the same file name.
226
227Finally, just exit out of the docker container:
228
229#+begin_src sh
230exit
231#+end_src
232
233Next, just restart the container:
234
235#+begin_src sh
236sudo docker restart freshrss
237#+end_src
238
239Voilà! Your API check should now "PASS" and you should be able to use
240one of the API URLs in your RSS apps.
241
242In my case, I use [[https://netnewswire.com][NetNewsWire]] on my desktop
243and phone.
content/blog/2022-06-16-terminal-lifestyle.md deleted −189
@@ -1,189 +0,0 @@
1+++
2date = 2022-06-16
3title = "A Terminal Lifestyle"
4description = ""
5draft = false
6+++
7
8# Text-Based Simplicity
9
10I've detailed my views on web-based minimalism and related topics in other posts
11throughout the years; e.g., JavaScript/CSS bloat slowing down websites that are
12essentially a text document. However, I have never really expanded beyond
13talking about the web and describing how I focus on minimizing distractions in
14other digital environments.
15
16This post is going to set the baseline for how I _try_ to live my digital life.
17It does not necessarily get into my physical life, which is often harder to
18control and contain all the noise in our modern world.
19
20While there are new things to do every day in our digital world, I find that
21keeping a core set of values and interests can ground you and keep you mindful
22of _why_ you are participating in the digital world. For example, if - at your
23core - you have no interest in what strangers think about random topics, it
24would be unwise to start participating in social media. However, I am someone
25who has been dragged in by effective advertising to participate in communities
26that I realize I do not care for.
27
28I won't dive much further into explaining the philosophy of all this, but I will
29link a few helpful articles that may pique your interest if you're in search of
30more meaningful experiences:
31
32- [Mindfulness](https://en.wikipedia.org/wiki/Mindfulness)
33- [Minimalism](https://en.wikipedia.org/wiki/Minimalism)
34- [Stoicism](https://en.wikipedia.org/wiki/Stoicism)
35
36# Living Life in the Terminal
37
38My personal approach to reducing digital distractions and increasing my focus on
39the task at hand is to use a terminal for as much as I possibly can.
40
41Most days, this means that I have a few tabs open constantly in my terminal:
42
431. A web browser
442. A chat client
453. An email client
464. An RSS feed reader
475. A local shell for navigating my computer's files
486. A remote shell for managing servers and other machines
49
50Beyond this, I rarely open other tabs or GUI applications, unless absolutely
51necessary. If you look, you may be surprised what can be accomplished in the
52terminal.
53
54For example, I have moved my music and entertainment downloads to the terminal,
55along with my device VPN connections. I am exploring options for moving my RSS
56subscriptions to something like [Newsboat](https://newsboat.org/), so that I can
57read my daily articles without all the fuss.
58
59Now that we have some examples out of the way, let's dive into the specifics.
60
61## Browsing the Web
62
63I'm going to start off with a hard topic for those who prefer to live in the
64terminal: web browsing. This task is made hard mostly by websites and web apps
65that require JavaScript to run. The other difficult part is that if you're using
66a text-based browser, that means images won't load (hopefully that's obvious).
67
68I am using [Lynx](https://lynx.invisible-island.net), a text-based browser that
69runs quickly and easily in the terminal. Lynx allows me to browser most websites
70by simply typing `g` and then typing in the URL I want.
71
72If you need a search engine while in Lynx, I recommend [DuckDuckGo
73(Lite)](https://lite.duckduckgo.com/lite/), which allows you to search the web
74using their text-only interface.
75
76Eventually, you will run into websites that don't work (or are just too ugly and
77messy) in a text-only mode, and you'll be forced to switch over to a GUI browser
78to look at that site. Personally, I don't mind this as it doesn't happen as
79often as I thought it would.
80
81The only time I need to do this is when I want to browse an image/video-focused
82webpage or if I need to log in to a site, and it doesn't support a text-only
83login page. For example, I am able to easily log in to
84[Sourcehut](https://sr.ht) in lynx.
85
86## Chatting with Friends
87
88After web browsing activities, my main form of terminal communication is Matrix.
89I use the [gomuks](https://docs.mau.fi/gomuks/) client currently.
90
91This was incredibly easy to install on macOS (but I will need to see if it'll be
92just as easy on Linux when my new laptop arrives):
93
94```sh
95brew install gomuks
96```
97
98Once you launch gomuks, it will sync and require your username and password to
99login. After doing so, the only problem I ran into was verifying my gomuks
100client so that I could participate in rooms with E2EE.
101
102Finally, I was able to verify the session by opening the Element desktop app (I
103assume you can do this in the browser and mobile app too, but I'm not sure) and
104manually verifying myself with this process:
105
1061. Open the Element desktop app
1072. Open a room I was a member of
1083. Open the `Room Info` pane
1094. Open the `People` menu and search for myself
1105. Click on my profile name
1116. Click on the session link under the `Security` section and follow the prompts
112 to manually verify the session
113
114Overall, I like gomuks and am able to enjoy all the features I was using in
115Element. The only hiccup I have occurred is manually downloading images to view
116them, which can be annoying.
117
118## Email
119
120Moving email to the terminal has been the hardest of the tasks for me. Unlike
121web browsing, where I can simply decide to not look at a website that does not
122work in the terminal, I cannot simply ignore emails sent to me.
123
124Personally, I am experimenting with [neomutt](https://neomutt.org/) as a
125potential email client.
126
127However, this requires a **TON** of configuration and tweaking to get right.
128Even when I was able to set up neomutt, configure my email account, and
129customize a few personal preferences, a lot of emails still do not display
130correctly (mostly due to HTML and images).
131
132I won't get into the details of configuring `neomutt`; I mostly followed this
133blog post: [Email in the Terminal: Configuring
134Neomutt](https://gideonwolfe.com/posts/workflow/neomutt/intro/).
135
136Finally, I have yet to figure out how to connect my GPG keys to `neomutt`, but
137that's a problem for another day.
138
139## RSS Feed Reader
140
141I have just started using [Newsboat](https://newsboat.org/) to read articles in
142my terminal and have found quick success with it.
143
144The configuration was super easy for this app; I simply installed the app,
145created a file for URLs, and imported my OPML subscriptions that I had exported
146out of my old feed reader:
147
148```sh
149brew install newsboat
150```
151
152```sh
153touch ~/.newsboat/urls
154```
155
156```sh
157newsboat -i=my_subscriptions.opml
158```
159
160## Writing & Programming
161
162Unfortunately, the weak link in my terminal-based environment right now is my
163grasp of the possibilities of editing files within a shell.
164
165I am used to the easy extensions found in VSCodium and Kate, so I am slowly
166learning how to mold the default editing tools to my needs. Currently, this
167means I am using `nano` with the following configuration:
168
169```config
170set breaklonglines
171set autoindent
172set linenumbers
173set tabstospaces
174set tabsize 2
175set fill 80
176```
177
178This configuration allows nano to automatically hard-wrap lines at 80
179characters, autoindent the wrapped lines (if the previous line was indented),
180use 2 spaces per tab, and display line numbers within each file I open.
181
182I am currently looking to see if `vim` or `emacs` would be more useful for my
183current needs, but I'm not in any rush, so I don't expect to find an answer
184anytime soon.
185
186With my current life demands, I am not programming at the moment and have not
187explored the best terminal set-up for programming. However, I have seen many
188peers find success configuring `vim` and `emacs`, so that's where I will start
189when I pick my projects back up.
content/blog/2022-06-16-terminal-lifestyle.org added +201
@@ -0,0 +1,201 @@
1#+date: <2022-06-16>
2#+title: A Terminal Lifestyle
3#+description:
4
5
6* Text-Based Simplicity
7
8I've detailed my views on web-based minimalism and related topics in
9other posts throughout the years; e.g., JavaScript/CSS bloat slowing
10down websites that are essentially a text document. However, I have
11never really expanded beyond talking about the web and describing how I
12focus on minimizing distractions in other digital environments.
13
14This post is going to set the baseline for how I /try/ to live my
15digital life. It does not necessarily get into my physical life, which
16is often harder to control and contain all the noise in our modern
17world.
18
19While there are new things to do every day in our digital world, I find
20that keeping a core set of values and interests can ground you and keep
21you mindful of /why/ you are participating in the digital world. For
22example, if - at your core - you have no interest in what strangers
23think about random topics, it would be unwise to start participating in
24social media. However, I am someone who has been dragged in by effective
25advertising to participate in communities that I realize I do not care
26for.
27
28I won't dive much further into explaining the philosophy of all this,
29but I will link a few helpful articles that may pique your interest if
30you're in search of more meaningful experiences:
31
32- [[https://en.wikipedia.org/wiki/Mindfulness][Mindfulness]]
33- [[https://en.wikipedia.org/wiki/Minimalism][Minimalism]]
34- [[https://en.wikipedia.org/wiki/Stoicism][Stoicism]]
35
36* Living Life in the Terminal
37
38My personal approach to reducing digital distractions and increasing my
39focus on the task at hand is to use a terminal for as much as I possibly
40can.
41
42Most days, this means that I have a few tabs open constantly in my
43terminal:
44
451. A web browser
462. A chat client
473. An email client
484. An RSS feed reader
495. A local shell for navigating my computer's files
506. A remote shell for managing servers and other machines
51
52Beyond this, I rarely open other tabs or GUI applications, unless
53absolutely necessary. If you look, you may be surprised what can be
54accomplished in the terminal.
55
56For example, I have moved my music and entertainment downloads to the
57terminal, along with my device VPN connections. I am exploring options
58for moving my RSS subscriptions to something like
59[[https://newsboat.org/][Newsboat]], so that I can read my daily
60articles without all the fuss.
61
62Now that we have some examples out of the way, let's dive into the
63specifics.
64
65** Browsing the Web
66
67I'm going to start off with a hard topic for those who prefer to live in
68the terminal: web browsing. This task is made hard mostly by websites
69and web apps that require JavaScript to run. The other difficult part is
70that if you're using a text-based browser, that means images won't load
71(hopefully that's obvious).
72
73I am using [[https://lynx.invisible-island.net][Lynx]], a text-based
74browser that runs quickly and easily in the terminal. Lynx allows me to
75browser most websites by simply typing =g= and then typing in the URL I
76want.
77
78If you need a search engine while in Lynx, I recommend
79[[https://lite.duckduckgo.com/lite/][DuckDuckGo (Lite)]], which allows
80you to search the web using their text-only interface.
81
82Eventually, you will run into websites that don't work (or are just too
83ugly and messy) in a text-only mode, and you'll be forced to switch over
84to a GUI browser to look at that site. Personally, I don't mind this as
85it doesn't happen as often as I thought it would.
86
87The only time I need to do this is when I want to browse an
88image/video-focused webpage or if I need to log in to a site, and it
89doesn't support a text-only login page. For example, I am able to easily
90log in to [[https://sr.ht][Sourcehut]] in lynx.
91
92** Chatting with Friends
93
94After web browsing activities, my main form of terminal communication is
95Matrix. I use the [[https://docs.mau.fi/gomuks/][gomuks]] client
96currently.
97
98This was incredibly easy to install on macOS (but I will need to see if
99it'll be just as easy on Linux when my new laptop arrives):
100
101#+begin_src sh
102brew install gomuks
103#+end_src
104
105Once you launch gomuks, it will sync and require your username and
106password to login. After doing so, the only problem I ran into was
107verifying my gomuks client so that I could participate in rooms with
108E2EE.
109
110Finally, I was able to verify the session by opening the Element desktop
111app (I assume you can do this in the browser and mobile app too, but I'm
112not sure) and manually verifying myself with this process:
113
1141. Open the Element desktop app
1152. Open a room I was a member of
1163. Open the =Room Info= pane
1174. Open the =People= menu and search for myself
1185. Click on my profile name
1196. Click on the session link under the =Security= section and follow the
120 prompts to manually verify the session
121
122Overall, I like gomuks and am able to enjoy all the features I was using
123in Element. The only hiccup I have occurred is manually downloading
124images to view them, which can be annoying.
125
126** Email
127
128Moving email to the terminal has been the hardest of the tasks for me.
129Unlike web browsing, where I can simply decide to not look at a website
130that does not work in the terminal, I cannot simply ignore emails sent
131to me.
132
133Personally, I am experimenting with [[https://neomutt.org/][neomutt]] as
134a potential email client.
135
136However, this requires a *TON* of configuration and tweaking to get
137right. Even when I was able to set up neomutt, configure my email
138account, and customize a few personal preferences, a lot of emails still
139do not display correctly (mostly due to HTML and images).
140
141I won't get into the details of configuring =neomutt=; I mostly followed
142this blog post:
143[[https://gideonwolfe.com/posts/workflow/neomutt/intro/][Email in the
144Terminal: Configuring Neomutt]].
145
146Finally, I have yet to figure out how to connect my GPG keys to
147=neomutt=, but that's a problem for another day.
148
149** RSS Feed Reader
150
151I have just started using [[https://newsboat.org/][Newsboat]] to read
152articles in my terminal and have found quick success with it.
153
154The configuration was super easy for this app; I simply installed the
155app, created a file for URLs, and imported my OPML subscriptions that I
156had exported out of my old feed reader:
157
158#+begin_src sh
159brew install newsboat
160#+end_src
161
162#+begin_src sh
163touch ~/.newsboat/urls
164#+end_src
165
166#+begin_src sh
167newsboat -i=my_subscriptions.opml
168#+end_src
169
170** Writing & Programming
171
172Unfortunately, the weak link in my terminal-based environment right now
173is my grasp of the possibilities of editing files within a shell.
174
175I am used to the easy extensions found in VSCodium and Kate, so I am
176slowly learning how to mold the default editing tools to my needs.
177Currently, this means I am using =nano= with the following
178configuration:
179
180#+begin_src config
181set breaklonglines
182set autoindent
183set linenumbers
184set tabstospaces
185set tabsize 2
186set fill 80
187#+end_src
188
189This configuration allows nano to automatically hard-wrap lines at 80
190characters, autoindent the wrapped lines (if the previous line was
191indented), use 2 spaces per tab, and display line numbers within each
192file I open.
193
194I am currently looking to see if =vim= or =emacs= would be more useful
195for my current needs, but I'm not in any rush, so I don't expect to find
196an answer anytime soon.
197
198With my current life demands, I am not programming at the moment and
199have not explored the best terminal set-up for programming. However, I
200have seen many peers find success configuring =vim= and =emacs=, so
201that's where I will start when I pick my projects back up.
content/blog/2022-06-22-daily-poetry.md deleted −213
@@ -1,213 +0,0 @@
1+++
2date = 2022-06-22
3title = "Daily Plaintext Poetry via Email"
4description = ""
5draft = false
6+++
7
8# Source Code
9
10I don't want to bury the lede here, so if you'd like to see the full source code
11I use to email myself plaintext poems daily, visit the repository:
12[daily-poem.git](https://git.sr.ht/~cyborg/daily-poem).
13
14# My Daily Dose of Poetry
15
16Most of my programming projects are small, random projects that are made
17strictly to fix some small problem I have or enhance my quality of life.
18
19In this case, I was looking for a simply and easy way to get a daily dose of
20literature or poetry to read in the mornings.
21
22However, I don't want to sign up for a random mailing list on just any website.
23I also don't want to have to work to find the reading content each morning, as I
24know I would simply give up and stop reading daily.
25
26Thus, I found a way to deliver poetry to myself in plain-text format, on a daily
27basis, and scheduled to deliver automatically.
28
29# Prerequisites
30
31This solution uses Python and email, so the following process requires the
32following to be installed:
33
341. An SMTP server, which can be as easy as installing `mailutils` if you're on a
35 Debian-based distro.
362. Python (& pip!)
373. The following Python packages: `email`, `smtplib`, `json`, and `requests`
38
39# Breaking Down the Logic
40
41I want to break down the logic for this program, as it's quite simple and
42informational.
43
44## Required Packages
45
46This program starts with a simple import of the required packages, so I wanted
47to explain why each package is used:
48
49```python
50from email.mime.text import MIMEText # Required for translating MIMEText
51import smtplib # Required to process the SMTP mail delivery
52import json # Required to parse the poetry API results
53import requests # Required to send out a request to the API
54```
55
56## Sending the API Request
57
58Next, we need to actually send the API request. In my case, I'm calling a random
59poem from the entire API. If you want, you can call specific poems or authors
60from this API.
61
62```python
63json_data = requests.get('https://poetrydb.org/random').json()
64```
65
66This gives us the following result in JSON:
67
68```json
69[
70 {
71 "title": "Sonnet XXII: With Fools and Children",
72 "author": "Michael Drayton",
73 "lines": [
74 "To Folly",
75 "",
76 "With fools and children, good discretion bears;",
77 "Then, honest people, bear with Love and me,",
78 "Nor older yet, nor wiser made by years,",
79 "Amongst the rest of fools and children be;",
80 "Love, still a baby, plays with gauds and toys,",
81 "And, like a wanton, sports with every feather,",
82 "And idiots still are running after boys,",
83 "Then fools and children fitt'st to go together.",
84 "He still as young as when he first was born,",
85 "No wiser I than when as young as he;",
86 "You that behold us, laugh us not to scorn;",
87 "Give Nature thanks you are not such as we.",
88 "Yet fools and children sometimes tell in play",
89 "Some, wise in show, more fools indeed than they."
90 ],
91 "linecount": "15"
92 }
93]
94```
95
96## Parsing the API Results
97
98In order to parse this into a readable format, we need to use the `json` package
99and extract the fields we want. In the example below, I am grabbing every field
100presented by the API.
101
102For the actual poem content, we need to loop over each line in the `lines`
103variable since each line is a separate string by default.
104
105> You _could_ also extract the title or author and make another call out to the
106> API to avoid having to build the plaintext poem with a loop, but it just
107> doesn't make sense to me to send multiple requests when we can create a simple
108> loop on our local machine to work with the data we already have.
109>
110> For
111> [example](https://poetrydb.org/title/Sonnet%20XXII:%20With%20Fools%20and%20Children/lines.text),
112> look at the raw data response of this link to see the poem's lines returned in
113> plaintext.
114
115```python
116title = json_data[0]['title']
117author = json_data[0]['author']
118line_count = json_data[0]['linecount']
119lines = ''
120for line in json_data[0]['lines']:
121 lines = lines + line + "\n"
122```
123
124## Composing the Email
125
126Now that I have all the data I need, I just need to compose it into a message
127and prepare the message metadata.
128
129For my daily email, I want to see the title of the poem first, followed by the
130author, then a blank line, and finally the full poem. This code snippet combines
131that data and packages it into a MIMEText container, ready to be emailed.
132
133```python
134msg_body = title + "\n" + author + "\n\n" + lines
135msg = MIMEText(msg_body)
136```
137
138Before we send the email, we need to prepare the metadata (subject, from, to,
139etc.):
140
141```python
142sender_email = 'example@server.local'
143recipient_emails = ['user@example.com']
144msg['Subject'] = 'Your Daily Poem (' + line_count + ' lines)'
145msg['From'] = sender_email
146msg['To'] = recipient_email
147```
148
149## Sending the Email
150
151Now that I have everything ready to be emailed, the last step is to simply
152connect to an SMTP server and send the email out to the recipients. In my case,
153I installed `mailutils` on Ubuntu and let my SMTP server be `localhost`.
154
155```python
156smtp_server = 'localhost'
157s = smtplib.SMTP(smtp_server)
158s.sendmail(sender_email, recipient_emails, msg.as_string())
159s.quit()
160```
161
162# The Result!
163
164Instead of including a screenshot, I've copied the contents of the email that
165was delivered to my inbox below since I set this process up in plaintext format.
166
167```txt
168Date: Wed, 22 Jun 2022 14:37:19 +0000 (UTC)
169From: REDACTED
170To: REDACTED
171Subject: Your Daily Poem (36 lines)
172MIME-Version: 1.0
173Content-Transfer-Encoding: 8bit
174Content-Type: text/plain; charset=utf-8
175
176Sonnet XXII: With Fools and Children
177Michael Drayton
178
179With fools and children, good discretion bears;
180Then, honest people, bear with Love and me,
181Nor older yet, nor wiser made by years,
182Amongst the rest of fools and children be;
183Love, still a baby, plays with gauds and toys,
184And, like a wanton, sports with every feather,
185And idiots still are running after boys,
186Then fools and children fitt'st to go together.
187He still as young as when he first was born,
188No wiser I than when as young as he;
189You that behold us, laugh us not to scorn;
190Give Nature thanks you are not such as we.
191Yet fools and children sometimes tell in play
192Some, wise in show, more fools indeed than they.
193```
194
195# Scheduling the Daily Email
196
197Last, but not least, is scheduling this Python script with `crontab`. To
198schedule a script to run daily, you can add it to the `crontab` file. To do
199this, open `crontab` in editing mode:
200
201```sh
202crontab -e
203```
204
205In the file, simply paste the following snippet at the bottom of the file and
206ensure that the file path is correctly pointing to wherever you saved your
207Python script:
208
209```config
2100 8 ** ** ** python3 /home/<your_user>/dailypoem/main.py
211```
212
213We have now set up the script and scheduled it to run daily at 08:00!
content/blog/2022-06-22-daily-poetry.org added +218
@@ -0,0 +1,218 @@
1#+date: <2022-06-22>
2#+title: Daily Plaintext Poetry via Email
3#+description:
4
5
6* Source Code
7
8I don't want to bury the lede here, so if you'd like to see the full
9source code I use to email myself plaintext poems daily, visit the
10repository: [[https://git.sr.ht/~cyborg/daily-poem][daily-poem.git]].
11
12* My Daily Dose of Poetry
13
14Most of my programming projects are small, random projects that are made
15strictly to fix some small problem I have or enhance my quality of life.
16
17In this case, I was looking for a simply and easy way to get a daily
18dose of literature or poetry to read in the mornings.
19
20However, I don't want to sign up for a random mailing list on just any
21website. I also don't want to have to work to find the reading content
22each morning, as I know I would simply give up and stop reading daily.
23
24Thus, I found a way to deliver poetry to myself in plain-text format, on
25a daily basis, and scheduled to deliver automatically.
26
27* Prerequisites
28
29This solution uses Python and email, so the following process requires
30the following to be installed:
31
321. An SMTP server, which can be as easy as installing =mailutils= if
33 you're on a Debian-based distro.
342. Python (& pip!)
353. The following Python packages: =email=, =smtplib=, =json=, and
36 =requests=
37
38* Breaking Down the Logic
39
40I want to break down the logic for this program, as it's quite simple
41and informational.
42
43** Required Packages
44
45This program starts with a simple import of the required packages, so I
46wanted to explain why each package is used:
47
48#+begin_src python
49from email.mime.text import MIMEText # Required for translating MIMEText
50import smtplib # Required to process the SMTP mail delivery
51import json # Required to parse the poetry API results
52import requests # Required to send out a request to the API
53#+end_src
54
55** Sending the API Request
56
57Next, we need to actually send the API request. In my case, I'm calling
58a random poem from the entire API. If you want, you can call specific
59poems or authors from this API.
60
61#+begin_src python
62json_data = requests.get('https://poetrydb.org/random').json()
63#+end_src
64
65This gives us the following result in JSON:
66
67#+begin_src json
68[
69 {
70 "title": "Sonnet XXII: With Fools and Children",
71 "author": "Michael Drayton",
72 "lines": [
73 "To Folly",
74 "",
75 "With fools and children, good discretion bears;",
76 "Then, honest people, bear with Love and me,",
77 "Nor older yet, nor wiser made by years,",
78 "Amongst the rest of fools and children be;",
79 "Love, still a baby, plays with gauds and toys,",
80 "And, like a wanton, sports with every feather,",
81 "And idiots still are running after boys,",
82 "Then fools and children fitt'st to go together.",
83 "He still as young as when he first was born,",
84 "No wiser I than when as young as he;",
85 "You that behold us, laugh us not to scorn;",
86 "Give Nature thanks you are not such as we.",
87 "Yet fools and children sometimes tell in play",
88 "Some, wise in show, more fools indeed than they."
89 ],
90 "linecount": "15"
91 }
92]
93#+end_src
94
95** Parsing the API Results
96
97In order to parse this into a readable format, we need to use the =json=
98package and extract the fields we want. In the example below, I am
99grabbing every field presented by the API.
100
101For the actual poem content, we need to loop over each line in the
102=lines= variable since each line is a separate string by default.
103
104#+begin_quote
105You /could/ also extract the title or author and make another call out
106to the API to avoid having to build the plaintext poem with a loop, but
107it just doesn't make sense to me to send multiple requests when we can
108create a simple loop on our local machine to work with the data we
109already have.
110
111For
112[[https://poetrydb.org/title/Sonnet%20XXII:%20With%20Fools%20and%20Children/lines.text][example]],
113look at the raw data response of this link to see the poem's lines
114returned in plaintext.
115#+end_quote
116
117#+begin_src python
118title = json_data[0]['title']
119author = json_data[0]['author']
120line_count = json_data[0]['linecount']
121lines = ''
122for line in json_data[0]['lines']:
123 lines = lines + line + "\n"
124#+end_src
125
126** Composing the Email
127
128Now that I have all the data I need, I just need to compose it into a
129message and prepare the message metadata.
130
131For my daily email, I want to see the title of the poem first, followed
132by the author, then a blank line, and finally the full poem. This code
133snippet combines that data and packages it into a MIMEText container,
134ready to be emailed.
135
136#+begin_src python
137msg_body = title + "\n" + author + "\n\n" + lines
138msg = MIMEText(msg_body)
139#+end_src
140
141Before we send the email, we need to prepare the metadata (subject,
142from, to, etc.):
143
144#+begin_src python
145sender_email = 'example@server.local'
146recipient_emails = ['user@example.com']
147msg['Subject'] = 'Your Daily Poem (' + line_count + ' lines)'
148msg['From'] = sender_email
149msg['To'] = recipient_email
150#+end_src
151
152** Sending the Email
153
154Now that I have everything ready to be emailed, the last step is to
155simply connect to an SMTP server and send the email out to the
156recipients. In my case, I installed =mailutils= on Ubuntu and let my
157SMTP server be =localhost=.
158
159#+begin_src python
160smtp_server = 'localhost'
161s = smtplib.SMTP(smtp_server)
162s.sendmail(sender_email, recipient_emails, msg.as_string())
163s.quit()
164#+end_src
165
166* The Result!
167
168Instead of including a screenshot, I've copied the contents of the email
169that was delivered to my inbox below since I set this process up in
170plaintext format.
171
172#+begin_src txt
173Date: Wed, 22 Jun 2022 14:37:19 +0000 (UTC)
174From: REDACTED
175To: REDACTED
176Subject: Your Daily Poem (36 lines)
177MIME-Version: 1.0
178Content-Transfer-Encoding: 8bit
179Content-Type: text/plain; charset=utf-8
180
181Sonnet XXII: With Fools and Children
182Michael Drayton
183
184With fools and children, good discretion bears;
185Then, honest people, bear with Love and me,
186Nor older yet, nor wiser made by years,
187Amongst the rest of fools and children be;
188Love, still a baby, plays with gauds and toys,
189And, like a wanton, sports with every feather,
190And idiots still are running after boys,
191Then fools and children fitt'st to go together.
192He still as young as when he first was born,
193No wiser I than when as young as he;
194You that behold us, laugh us not to scorn;
195Give Nature thanks you are not such as we.
196Yet fools and children sometimes tell in play
197Some, wise in show, more fools indeed than they.
198#+end_src
199
200* Scheduling the Daily Email
201
202Last, but not least, is scheduling this Python script with =crontab=. To
203schedule a script to run daily, you can add it to the =crontab= file. To
204do this, open =crontab= in editing mode:
205
206#+begin_src sh
207crontab -e
208#+end_src
209
210In the file, simply paste the following snippet at the bottom of the
211file and ensure that the file path is correctly pointing to wherever you
212saved your Python script:
213
214#+begin_src config
2150 8 ** ** ** python3 /home/<your_user>/dailypoem/main.py
216#+end_src
217
218We have now set up the script and scheduled it to run daily at 08:00!
content/blog/2022-06-24-fedora-i3.md deleted −153
@@ -1,153 +0,0 @@
1+++
2date = 2022-06-24
3title = "Rebooting My Love Affair with Linux"
4description = ""
5draft = false
6+++
7
8# Leaving macOS
9
10As I noted [in a recent post](../foss-macos-apps), I have been planning on
11migrating from macOS back to a Linux-based OS. I am happy to say that I have
12finally completed my migration and am now stuck in the wonderful world of Linux
13again.
14
15My decision to leave macOS really came down to just a few important things:
16
17- Apple Security (Gatekeeper) restricting me from running any software I want.
18 Even if you disable Gatekeeper and allow software to bypass the rest of the
19 device installation security, you still have to repeat that process every
20 time the allowed software is updated.
21- macOS sends out nearly constant connections, pings, telemetry, etc. to a
22 myriad of mysterious Apple services. I'm not even going to dive into how
23 many macOS apps have constant telemetry on, as well.
24- Lastly, I just _really_ missed the customization and freedom that comes with
25 Linux. Being able to switch to entirely new kernel, OS, or desktop within
26 minutes is a freedom I took for granted when I switched to macOS.
27
28Now that I've covered macOS, I'm going to move on to more exciting topics: my
29personal choice of OS, DE, and various customizations I'm using.
30
31# Fedora
32
33After trying a ton of distros (I think I booted and tested around 20-25
34distros), I finally landed on [Fedora Linux](https://getfedora.org/). I have
35quite a bit of experience with Fedora and enjoy the `dnf` package manager.
36Fedora allows me to keep up-to-date with recent software (I'm looking at you,
37Debian), but still provides a level of stability you don't find in every distro.
38
39In a very close second place was Arch Linux, as well as its spin-off: Garuda
40Linux (Garuda w/ sway is _beautiful_). Arch is great for compatibility and the
41massive community it has, but I have just never had the time to properly sit
42down and learn the methodology behind their packaging systems.
43
44Basically, everything else I tested was unacceptable in at least one way or
45another. Void (`glibc`) was great, but doesn't support all the software I need.
46Slackware worked well as a tui, but I wasn't skilled enough to get a tiling
47window manager (WM) working on it.
48
49## i3
50
51One of the reasons I settled on Fedora is that it comes with an official i3
52spin. Being able to use a tiling WM, such as i3 or sway, is one of the biggest
53things I wanted to do as soon as I adopted Linux again.
54
55I will probably set up a dotfile repository soon, so that I don't lose any of my
56configurations, but nothing big has been configured thus far.
57
58The two main things I have updated in i3wm are natural scrolling and binding my
59brightness keys to the `brightnessctl` program.
60
611. Natural Scrolling
62
63 You can enable natural scrolling by opening the following file:
64
65 ```sh
66 sudo nano /usr/share/X11/xorg.conf.d/40-libinput.conf
67 ```
68
69 Within the `40-libinput.conf` file, find the following input sections and
70 enable the natural scrolling option.
71
72 This is the `pointer` section:
73
74 ```conf
75 Section "InputClass"
76 Identifier "libinput pointer catchall"
77 MatchIsPointer "on"
78 MatchDevicePath "/dev/input/event*"
79 Driver "libinput"
80 Option "NaturalScrolling" "True"
81 EndSection
82 ```
83
84 This is the `touchpad` section:
85
86 ```conf
87 Section "InputClass"
88 Identifier "libinput touchpad catchall"
89 MatchIsTouchpad "on"
90 MatchDevicePath "/dev/input/event*"
91 Driver "libinput"
92 Option "NaturalScrolling" "True"
93 EndSection
94 ```
95
962. Enabling Brightness Keys
97
98 Likewise, enabling brightness key functionality is as simple as binding the
99 keys to the `brightnessctl` program.
100
101 To do this, open up your i3 config file. Mine is located here:
102
103 ```sh
104 nano /home/<my-user>/.config/i3/config
105 ```
106
107 ```conf
108 # Use brightnessctl to adjust brightness.
109 bindsym XF86MonBrightnessDown exec --no-startup-id brightnessctl --min-val=2 -q set 3%-
110 bindsym XF86MonBrightnessUp exec --no-startup-id brightnessctl -q set 3%+
111 ```
112
1133. `polybar`
114
115 Instead of using the default `i3status` bar, I have opted to use `polybar`
116 instead (as you can also see in the screenshot above).
117
118 My config for this menu bar is basically just the default settings with
119 modified colors and an added battery block to quickly show me the machine's
120 battery info.
121
1224. `alacritty`
123
124 Not much to say on this part yet, as I haven't configured it much, but I
125 installed `alacritty` as my default terminal, and I am using `zsh` and the
126 shell.
127
128# Software Choices
129
130Again, I'm not going to say much that I haven't said yet in other blog posts, so
131I'll just do a quick rundown of the apps I installed immediately after I set up
132the environment.
133
134Flatpak Apps:
135
136- Cryptomator
137- pCloud
138- Signal
139
140Fedora Packages:
141
142- gomuks
143- neomutt
144- neofetch
145- Firefox
146 - uBlock Origin
147 - Bitwarden
148 - Stylus
149 - Privacy Redirect
150
151Other:
152
153- exiftool
content/blog/2022-06-24-fedora-i3.org added +156
@@ -0,0 +1,156 @@
1#+date: <2022-06-24>
2#+title: Rebooting My Love Affair with Linux
3#+description:
4
5
6* Leaving macOS
7
8As I noted [[../foss-macos-apps][in a recent post]], I have been
9planning on migrating from macOS back to a Linux-based OS. I am happy to
10say that I have finally completed my migration and am now stuck in the
11wonderful world of Linux again.
12
13My decision to leave macOS really came down to just a few important
14things:
15
16- Apple Security (Gatekeeper) restricting me from running any software I
17 want. Even if you disable Gatekeeper and allow software to bypass the
18 rest of the device installation security, you still have to repeat
19 that process every time the allowed software is updated.
20- macOS sends out nearly constant connections, pings, telemetry, etc. to
21 a myriad of mysterious Apple services. I'm not even going to dive into
22 how many macOS apps have constant telemetry on, as well.
23- Lastly, I just /really/ missed the customization and freedom that
24 comes with Linux. Being able to switch to entirely new kernel, OS, or
25 desktop within minutes is a freedom I took for granted when I switched
26 to macOS.
27
28Now that I've covered macOS, I'm going to move on to more exciting
29topics: my personal choice of OS, DE, and various customizations I'm
30using.
31
32* Fedora
33
34After trying a ton of distros (I think I booted and tested around 20-25
35distros), I finally landed on [[https://getfedora.org/][Fedora Linux]].
36I have quite a bit of experience with Fedora and enjoy the =dnf= package
37manager. Fedora allows me to keep up-to-date with recent software (I'm
38looking at you, Debian), but still provides a level of stability you
39don't find in every distro.
40
41In a very close second place was Arch Linux, as well as its spin-off:
42Garuda Linux (Garuda w/ sway is /beautiful/). Arch is great for
43compatibility and the massive community it has, but I have just never
44had the time to properly sit down and learn the methodology behind their
45packaging systems.
46
47Basically, everything else I tested was unacceptable in at least one way
48or another. Void (=glibc=) was great, but doesn't support all the
49software I need. Slackware worked well as a tui, but I wasn't skilled
50enough to get a tiling window manager (WM) working on it.
51
52** i3
53
54One of the reasons I settled on Fedora is that it comes with an official
55i3 spin. Being able to use a tiling WM, such as i3 or sway, is one of
56the biggest things I wanted to do as soon as I adopted Linux again.
57
58I will probably set up a dotfile repository soon, so that I don't lose
59any of my configurations, but nothing big has been configured thus far.
60
61The two main things I have updated in i3wm are natural scrolling and
62binding my brightness keys to the =brightnessctl= program.
63
641. Natural Scrolling
65
66 You can enable natural scrolling by opening the following file:
67
68 #+begin_src sh
69 sudo nano /usr/share/X11/xorg.conf.d/40-libinput.conf
70 #+end_src
71
72 Within the =40-libinput.conf= file, find the following input sections
73 and enable the natural scrolling option.
74
75 This is the =pointer= section:
76
77 #+begin_src conf
78 Section "InputClass"
79 Identifier "libinput pointer catchall"
80 MatchIsPointer "on"
81 MatchDevicePath "/dev/input/event*"
82 Driver "libinput"
83 Option "NaturalScrolling" "True"
84 EndSection
85 #+end_src
86
87 This is the =touchpad= section:
88
89 #+begin_src conf
90 Section "InputClass"
91 Identifier "libinput touchpad catchall"
92 MatchIsTouchpad "on"
93 MatchDevicePath "/dev/input/event*"
94 Driver "libinput"
95 Option "NaturalScrolling" "True"
96 EndSection
97 #+end_src
98
992. Enabling Brightness Keys
100
101 Likewise, enabling brightness key functionality is as simple as
102 binding the keys to the =brightnessctl= program.
103
104 To do this, open up your i3 config file. Mine is located here:
105
106 #+begin_src sh
107 nano /home/<my-user>/.config/i3/config
108 #+end_src
109
110 #+begin_src conf
111 # Use brightnessctl to adjust brightness.
112 bindsym XF86MonBrightnessDown exec --no-startup-id brightnessctl --min-val=2 -q set 3%-
113 bindsym XF86MonBrightnessUp exec --no-startup-id brightnessctl -q set 3%+
114 #+end_src
115
1163. =polybar=
117
118 Instead of using the default =i3status= bar, I have opted to use
119 =polybar= instead (as you can also see in the screenshot above).
120
121 My config for this menu bar is basically just the default settings
122 with modified colors and an added battery block to quickly show me
123 the machine's battery info.
124
1254. =alacritty=
126
127 Not much to say on this part yet, as I haven't configured it much,
128 but I installed =alacritty= as my default terminal, and I am using
129 =zsh= and the shell.
130
131* Software Choices
132
133Again, I'm not going to say much that I haven't said yet in other blog
134posts, so I'll just do a quick rundown of the apps I installed
135immediately after I set up the environment.
136
137Flatpak Apps:
138
139- Cryptomator
140- pCloud
141- Signal
142
143Fedora Packages:
144
145- gomuks
146- neomutt
147- neofetch
148- Firefox
149 - uBlock Origin
150 - Bitwarden
151 - Stylus
152 - Privacy Redirect
153
154Other:
155
156- exiftool
content/blog/2022-07-01-git-server.md deleted −624
@@ -1,624 +0,0 @@
1+++
2date = 2022-07-01
3title = "Self-Hosting a Personal Git Server"
4description = ""
5draft = false
6+++
7
8# My Approach to Self-Hosting Git
9
10I have often tried to self-host my Git repositories, but have always fallen
11short when I tried to find a suitable web interface to show on the front-end.
12
13After a few years, I have finally found a combination of methods that allow me
14to easily self-host my projects, view them on the web, and access them from
15anywhere.
16
17Before I dive into the details, I want to state a high-level summary of my
18self-hosted Git approach:
19
20- This method uses the `ssh://` (read & write) and `git://` (read-only)
21 protocols for push and pull access.
22 - For the `git://` protocol, I create a `git-daemon-export-ok` file in any
23 repository that I want to be cloneable by anyone.
24 - The web interface I am using (`cgit`) allows simple HTTP cloning by
25 default. I do not disable this setting as I want beginners to be able to
26 clone one of my repositories even if they don't know the proper method.
27- I am not enabling Smart HTTPS for any repositories. Updates to repositories
28 must be pushed via SSH.
29- Beyond the actual repository management, I am using `cgit` for the front-end
30 web interface.
31 - If you use the `scan-path=<path>` configuration in the `cgitrc`
32 configuration file to automatically find repositories, you can't exclude
33 a repository from `cgit` if it's stored within the path that `cgit`
34 reads. To host private repositories, you'd need to set up another
35 directory that `cgit` can't read.
36
37# Assumptions
38
39For the purposes of this walkthrough, I am assuming you have a URL
40(`git.example.com`) or IP address (`207.84.26.991`) addressed to the server that
41you will be using to host your git repositories.
42
43# Adding a Git User
44
45In order to use the SSH method associated with git, we will need to add a user
46named `git`. If you have used the SSH method for other git hosting sites, you
47are probably used to the following syntax:
48
49```sh
50git clone [user@]server:project.git
51```
52
53The syntax above is an `scp`-like syntax for using SSH on the `git` user on the
54server to access your repository.
55
56Let's delete any remnants of an old `git` user, if any, and create the new user
57account:
58
59```sh
60sudo deluser --remove-home git
61sudo adduser git
62```
63
64## Import Your SSH Keys to the Git User
65
66Once the `git` user is created, you will need to copy your public SSH key on
67your local development machine to the `git` user on the server.
68
69If you don't have an SSH key yet, create one with this command:
70
71```sh
72ssh-keygen
73```
74
75Once you create the key pair, the public should be saved to `~/.ssh/id_rsa.pub`.
76
77If your server still has password-based authentication available, you can copy
78it over to your user's home directory like this:
79
80```sh
81ssh-copy-id git@server
82```
83
84Otherwise, copy it over to any user that you can access.
85
86```sh
87scp ~/.ssh/id_rsa.pub your_user@your_server:
88```
89
90Once on the server, you will need to copy the contents into the `git` user's
91`authorized_keys` file:
92
93```sh
94cat id_rsa.pub > /home/git/.ssh/authorized_keys
95```
96
97## (Optional) Disable Password-Based SSH
98
99If you want to lock down your server and ensure that no one can authenticate in
100via SSH with a password, you will need to edit your SSH configuration.
101
102```sh
103sudo nano /etc/ssh/sshd_config
104```
105
106Within this file, find the following settings and set them to the values I am
107showing below:
108
109```conf
110PermitRootLogin no
111PasswordAuthentication no
112AuthenticationMethods publickey
113```
114
115You may have other Authentication Methods required in your personal set-up, so
116the key here is just to ensure that `AuthenticationMethods` does not allow
117passwords.
118
119### Setting up the Base Directory
120
121Now that we have set up a `git` user to handle all transport methods, we need to
122set up the directory that we will be using as our base of all repositories.
123
124In my case, I am using `/git` as my source folder. To create this folder and
125assign it to the user we created, execute the following commands:
126
127```sh
128sudo mkdir /git
129sudo chown -R git:git /git
130```
131
132### Creating a Test Repository
133
134On your server, switch over to the `git` user in order to start managing git
135files.
136
137```sh
138su git
139```
140
141Once logged-in as the `git` user, go to your base directory and create a test
142repository.
143
144```sh
145cd /git
146mkdir test.git && cd test.git
147git init --bare
148```
149
150If you want to make this repo viewable/cloneable to the public via the `git://`
151protocol, you need to create a `git-daemon-export-ok` file inside the
152repository.
153
154```sh
155touch git-daemon-export-ok
156```
157
158# Change the Login Shell for `git`
159
160To make sure that the `git` user is only used for git operations and nothing
161else, you need to change the user's login shell. To do this, simply use the
162`chsh` command:
163
164```sh
165sudo chsh git
166```
167
168The interactive prompt will ask which shell you want the `git` user to use. You
169must use the following value:
170
171```sh
172/usr/bin/git-shell
173```
174
175Once done, no one will be able to SSH to the `git` user or execute commands
176other than the standard git commands.
177
178# Opening the Firewall
179
180Don't forget to open up ports on the device firewall and network firewall if you
181want to access these repositories publicly. If you're using default ports,
182forward ports `22` (ssh) and `9418` (git) from your router to your server's IP
183address.
184
185If your server also has a firewall, ensure that the firewall allows the same
186ports that are forwarded from the router. For example, if you use `ufw`:
187
188```sh
189sudo ufw allow 22
190sudo ufw allow 9418
191```
192
193## Non-Standard SSH Ports
194
195If you use a non-standard port for SSH, such as `9876`, you will need to create
196an SSH configuration file on your local development machine in order to connect
197to your server's git repositories.
198
199To do this, you'll need to define your custom port on your client machine in
200your `~/.ssh/config` file:
201
202```sh
203nano ~/.ssh/config
204```
205
206```conf
207Host git.example.com
208 # HostName can be a URL or an IP address
209 HostName git.example.com
210 Port 9876
211 User git
212```
213
214## Testing SSH
215
216There are two main syntaxes you can use to manage git over SSH:
217
218- `git clone [user@]server:project.git`
219- `git clone ssh://[user@]server/project.git`
220
221I prefer the first, which is an `scp`-like syntax. To test it, try to clone the
222test repository you set up on the server:
223
224```sh
225git clone git@git.example.com:/git/test.git
226```
227
228# Enabling Read-Only Access
229
230If you want people to be able to clone any repository where you've placed a
231`git-daemon-export-ok` file, you will need to start the git daemon.
232
233To do this on a system with `systemd`, create a service file:
234
235```sh
236sudo nano /etc/systemd/system/git-daemon.service
237```
238
239Inside the `git-daemon.service` file, paste the following:
240
241```conf
242[Unit]
243Description=Start Git Daemon
244
245[Service]
246ExecStart=/usr/bin/git daemon --reuseaddr --base-path=/git/ /git/
247
248Restart=always
249RestartSec=500ms
250
251StandardOutput=syslog
252StandardError=syslog
253SyslogIdentifier=git-daemon
254
255User=git
256Group=git
257
258[Install]
259WantedBy=multi-user.target
260```
261
262Once created, enable and start the service:
263
264```sh
265sudo systemctl enable git-daemon.service
266sudo systemctl start git-daemon.service
267```
268
269To clone read-only via the `git://` protocol, you can use the following syntax:
270
271```sh
272git clone git://git.example.com/test.git
273```
274
275# Migrating Repositories
276
277At this point, we have a working git server that works with both SSH and
278read-only access.
279
280For each of the repositories I had hosted a different provider, I executed the
281following commands in order to place a copy on my server as my new source of
282truth:
283
284Server:
285
286```sh
287su git
288mkdir /git/<REPOSITORY_NAME>.git && cd /git/<REPOSITORY_NAME>.git
289git init --bare
290
291# If you want to make this repo viewable/cloneable to the public
292touch git-daemon-export-ok
293```
294
295Client:
296
297```sh
298git clone git@<PREVIOUS_HOST>:<REPOSITORY_NAME>
299git remote set-url origin git@git.EXAMPLE.COM:/git/<REPOSITORY_NAME>.git
300git push
301```
302
303# Optional Web View: `cgit`
304
305If you want a web viewer for your repositories, you can use various tools, such
306as `gitweb`, `cgit`, or `klaus`. I chose `cgit` due to its simple interface and
307fairly easy set-up (compared to others). Not to mention that the [Linux kernel
308uses `cgit`](https://git.kernel.org/).
309
310## Docker Compose
311
312Instead of using my previous method of using a `docker run` command, I've
313updated this section to use `docker-compose` instead for an easier installation
314and simpler management and configuration.
315
316In order to use Docker Compose, you will set up a `docker-compose.yml` file to
317automatically connect resources like the repositories, `cgitrc`, and various
318files or folders to the `cgit` container you're creating:
319
320```sh
321mkdir ~/cgit && cd ~/cgit
322nano docker-compose.yml
323```
324
325```conf
326# docker-compose.yml
327version: '3'
328
329services:
330 cgit:
331 image: invokr/cgit
332 volumes:
333 - /git:/git
334 - ./cgitrc:/etc/cgitrc
335 - ./logo.png:/var/www/htdocs/cgit/logo.png
336 - ./favicon.png:/var/www/htdocs/cgit/favicon.png
337 - ./filters:/var/www/htdocs/cgit/filters
338 ports:
339 - "8763:80"
340 restart: always
341```
342
343Then, just start the container:
344
345```sh
346sudo docker-compose up -d
347```
348
349Once it's finished installing, you can access the site at `<SERVER_IP>:8763` or
350use a reverse-proxy service to forward `cgit` to a URL, such as
351`git.example.com`. See the next section for more details on reverse proxying a
352URL to a local port.
353
354## Nginx Reverse Proxy
355
356I am using Nginx as my reverse proxy so that the `cgit` Docker container can use
357`git.example.com` as its URL. To do so, I simply created the following
358configuration file:
359
360```sh
361sudo nano /etc/nginx/sites-available/git.example.com
362```
363
364```conf
365server {
366 listen 80;
367 server_name git.example.com;
368
369 if ($host = git.example.com) {
370 return 301 https://$host$request_uri;
371 }
372
373 return 404;
374}
375
376server {
377 server_name git.example.com;
378 listen 443 ssl http2;
379
380 location / {
381 # The final `/` is important.
382 proxy_pass http://localhost:8763/;
383 add_header X-Frame-Options SAMEORIGIN;
384 add_header X-XSS-Protection "1; mode=block";
385 proxy_redirect off;
386 proxy_buffering off;
387 proxy_set_header Host $host;
388 proxy_set_header X-Real-IP $remote_addr;
389 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
390 proxy_set_header X-Forwarded-Proto $scheme;
391 proxy_set_header X-Forwarded-Port $server_port;
392 }
393
394 # INCLUDE ANY SSL CERTS HERE
395 include /etc/letsencrypt/options-ssl-nginx.conf;
396 ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
397}
398```
399
400Once created, symlink it and restart the web server.
401
402```sh
403sudo ln -s /etc/nginx/sites-available/git.example.com /etc/nginx/sites-enabled/
404sudo systemctl restart nginx.service
405```
406
407As we can see below, my site at `git.example.com` is available and running:
408
409## Settings Up Git Details
410
411Once you have `cgit` running, you can add some small details, such as repository
412owners and descriptions by editing the following files within each repository.
413
414Alternatively, you can use the `cgitrc` file to edit these details if you only
415care to edit them for the purpose of seeing them on your website.
416
417The `description` file within the repository on your server will display the
418description online.
419
420```sh
421cd /git/example.git
422nano description
423```
424
425You can add a `[gitweb]` block to the `config` file in order to display the
426owner of the repository.
427
428```sh
429cd /git/example.git
430nano config
431```
432
433```conf
434[gitweb]
435 owner = "YourName"
436```
437
438Note that you can ignore the configuration within each repository and simply set
439up this information in the `cgitrc` file, if you want to do it that way.
440
441## Editing `cgit`
442
443In order to edit certain items within `cgit`, you need to edit the `cgitrc`
444file.
445
446```sh
447nano ~/cgit/cgitrc
448```
449
450Below is an example configuration for `cgitrc`. You can find all the
451configuration options within the [configuration manual]
452(<https://git.zx2c4.com/cgit/plain/cgitrc.5.txt>).
453
454```conf
455css=/cgit.css
456logo=/logo.png
457favicon=/favicon.png
458robots=noindex, nofollow
459
460enable-index-links=1
461enable-commit-graph=1
462enable-blame=1
463enable-log-filecount=1
464enable-log-linecount=1
465enable-git-config=1
466
467clone-url=git://git.example.com/$CGIT_REPO_URL ssh://git@git.example.com:/git/$CGIT_REPO_URL
468
469root-title=My Git Website
470root-desc=My personal git repositories.
471
472# Allow download of tar.gz, tar.bz2 and zip-files
473snapshots=tar.gz tar.bz2 zip
474
475##
476## List of common mimetypes
477##
478mimetype.gif=image/gif
479mimetype.html=text/html
480mimetype.jpg=image/jpeg
481mimetype.jpeg=image/jpeg
482mimetype.pdf=application/pdf
483mimetype.png=image/png
484mimetype.svg=image/svg+xml
485
486# Highlight source code
487# source-filter=/var/www/htdocs/cgit/filters/syntax-highlighting.sh
488source-filter=/var/www/htdocs/cgit/filters/syntax-highlighting.py
489
490# Format markdown, restructuredtext, manpages, text files, and html files
491# through the right converters
492about-filter=/var/www/htdocs/cgit/filters/about-formatting.sh
493
494##
495## Search for these files in the root of the default branch of repositories
496## for coming up with the about page:
497##
498readme=:README.md
499readme=:readme.md
500readme=:README.mkd
501readme=:readme.mkd
502readme=:README.rst
503readme=:readme.rst
504readme=:README.html
505readme=:readme.html
506readme=:README.htm
507readme=:readme.htm
508readme=:README.txt
509readme=:readme.txt
510readme=:README
511readme=:readme
512
513# Repositories
514
515# Uncomment the following line to scan a path instead of adding repositories manually
516# scan-path=/git
517
518## Test Section
519section=git/test-section
520
521repo.url=test.git
522repo.path=/git/test.git
523repo.readme=:README.md
524repo.owner=John Doe
525repo.desc=An example repository!
526```
527
528## Final Fixes: Syntax Highlighting & README Rendering
529
530After completing my initial install and playing around with it for a few days, I
531noticed two issues:
532
5331. Syntax highlighting did not work when viewing the source code within a file.
5342. The `about` tab within a repository was not rendered to HTML.
535
536The following process fixes these issues. To start, let's go to the `cgit`
537directory where we were editing our configuration file earlier.
538
539```sh
540cd ~/cgit
541```
542
543In here, create two folders that will hold our syntax files:
544
545```sh
546mkdir filters && mkdir filters/html-converters && cd filters
547```
548
549Next, download the default filters:
550
551```sh
552curl https://git.zx2c4.com/cgit/plain/filters/about-formatting.sh > about-formatting.sh
553chmod 755 about-formatting.sh
554curl https://git.zx2c4.com/cgit/plain/filters/syntax-highlighting.py > syntax-highlighting.py
555chmod 755 syntax-highlighting.py
556```
557
558Finally, download the HTML conversion files you need. The example below
559downloads the Markdown converter:
560
561```sh
562cd html-converters
563curl https://git.zx2c4.com/cgit/plain/filters/html-converters/md2html > md2html
564chmod 755 md2html
565```
566
567If you need other filters or html-converters found within [the cgit project
568files](https://git.zx2c4.com/cgit/tree/filters), repeat the `curl` and `chmod`
569process above for whichever files you need.
570
571However, formatting will not work quite yet since the Docker cgit container
572we're using doesn't have the formatting package installed. You can install this
573easily by install Python 3+ and the `pygments` package:
574
575```sh
576# Enter the container's command line
577sudo docker exec -it cgit bash
578```
579
580```sh
581# Install the necessary packages and then exit
582yum update -y && \
583yum upgrade -y && \
584yum install python3 python3-pip -y && \
585pip3 install markdown pygments && \
586exit
587```
588
589**You will need to enter the cgit docker container and re-run these `yum`
590commands every time you kill and restart the container!**
591
592If not done already, we need to add the following variables to our `cgitrc` file
593in order for `cgit` to know where our filtering files are:
594
595```conf
596# Highlight source code with python pygments-based highlighter
597source-filter=/var/www/htdocs/cgit/filters/syntax-highlighting.py
598
599# Format markdown, restructuredtext, manpages, text files, and html files
600# through the right converters
601about-filter=/var/www/htdocs/cgit/filters/about-formatting.sh
602```
603
604Now you should see that syntax highlighting and README rendering to the `about`
605tab is fixed.
606
607## Theming
608
609I won't go into much detail in this section, but you can fully theme your
610installation of `cgit` since you have access to the `cgit.css` file in your web
611root. This is another file you can add as a volume to the `docker-compose.yml`
612file if you want to edit this without entering the container's command line.
613
614### :warning: Remember to Back Up Your Data!
615
616The last thing to note is that running services on your own equipment means that
617you're assuming a level of risk that exists regarding data loss, catastrophes,
618etc. In order to reduce the impact of any such occurrence, I suggest backing up
619your data regularly.
620
621Backups can be automated via `cron`, by hooking your base directory up to a
622cloud provider, or even setting up hooks to push all repository info to git
623mirrors on other git hosts. Whatever the method, make sure that your data
624doesn't vanish in the event that your drives or servers fail.
content/blog/2022-07-01-git-server.org added +638
@@ -0,0 +1,638 @@
1#+date: <2022-07-01>
2#+title: Self-Hosting a Personal Git Server
3#+description:
4
5
6* My Approach to Self-Hosting Git
7
8I have often tried to self-host my Git repositories, but have always
9fallen short when I tried to find a suitable web interface to show on
10the front-end.
11
12After a few years, I have finally found a combination of methods that
13allow me to easily self-host my projects, view them on the web, and
14access them from anywhere.
15
16Before I dive into the details, I want to state a high-level summary of
17my self-hosted Git approach:
18
19- This method uses the =ssh://= (read & write) and =git://= (read-only)
20 protocols for push and pull access.
21 - For the =git://= protocol, I create a =git-daemon-export-ok= file in
22 any repository that I want to be cloneable by anyone.
23 - The web interface I am using (=cgit=) allows simple HTTP cloning by
24 default. I do not disable this setting as I want beginners to be
25 able to clone one of my repositories even if they don't know the
26 proper method.
27- I am not enabling Smart HTTPS for any repositories. Updates to
28 repositories must be pushed via SSH.
29- Beyond the actual repository management, I am using =cgit= for the
30 front-end web interface.
31 - If you use the =scan-path=<path>= configuration in the =cgitrc=
32 configuration file to automatically find repositories, you can't
33 exclude a repository from =cgit= if it's stored within the path that
34 =cgit= reads. To host private repositories, you'd need to set up
35 another directory that =cgit= can't read.
36
37* Assumptions
38
39For the purposes of this walkthrough, I am assuming you have a URL
40(=git.example.com=) or IP address (=207.84.26.991=) addressed to the
41server that you will be using to host your git repositories.
42
43* Adding a Git User
44
45In order to use the SSH method associated with git, we will need to add
46a user named =git=. If you have used the SSH method for other git
47hosting sites, you are probably used to the following syntax:
48
49#+begin_src sh
50git clone [user@]server:project.git
51#+end_src
52
53The syntax above is an =scp=-like syntax for using SSH on the =git= user
54on the server to access your repository.
55
56Let's delete any remnants of an old =git= user, if any, and create the
57new user account:
58
59#+begin_src sh
60sudo deluser --remove-home git
61sudo adduser git
62#+end_src
63
64** Import Your SSH Keys to the Git User
65
66Once the =git= user is created, you will need to copy your public SSH
67key on your local development machine to the =git= user on the server.
68
69If you don't have an SSH key yet, create one with this command:
70
71#+begin_src sh
72ssh-keygen
73#+end_src
74
75Once you create the key pair, the public should be saved to
76=~/.ssh/id_rsa.pub=.
77
78If your server still has password-based authentication available, you
79can copy it over to your user's home directory like this:
80
81#+begin_src sh
82ssh-copy-id git@server
83#+end_src
84
85Otherwise, copy it over to any user that you can access.
86
87#+begin_src sh
88scp ~/.ssh/id_rsa.pub your_user@your_server:
89#+end_src
90
91Once on the server, you will need to copy the contents into the =git=
92user's =authorized_keys= file:
93
94#+begin_src sh
95cat id_rsa.pub > /home/git/.ssh/authorized_keys
96#+end_src
97
98** (Optional) Disable Password-Based SSH
99
100If you want to lock down your server and ensure that no one can
101authenticate in via SSH with a password, you will need to edit your SSH
102configuration.
103
104#+begin_src sh
105sudo nano /etc/ssh/sshd_config
106#+end_src
107
108Within this file, find the following settings and set them to the values
109I am showing below:
110
111#+begin_src conf
112PermitRootLogin no
113PasswordAuthentication no
114AuthenticationMethods publickey
115#+end_src
116
117You may have other Authentication Methods required in your personal
118set-up, so the key here is just to ensure that =AuthenticationMethods=
119does not allow passwords.
120
121*** Setting up the Base Directory
122
123Now that we have set up a =git= user to handle all transport methods, we
124need to set up the directory that we will be using as our base of all
125repositories.
126
127In my case, I am using =/git= as my source folder. To create this folder
128and assign it to the user we created, execute the following commands:
129
130#+begin_src sh
131sudo mkdir /git
132sudo chown -R git:git /git
133#+end_src
134
135*** Creating a Test Repository
136
137On your server, switch over to the =git= user in order to start managing
138git files.
139
140#+begin_src sh
141su git
142#+end_src
143
144Once logged-in as the =git= user, go to your base directory and create a
145test repository.
146
147#+begin_src sh
148cd /git
149mkdir test.git && cd test.git
150git init --bare
151#+end_src
152
153If you want to make this repo viewable/cloneable to the public via the
154=git://= protocol, you need to create a =git-daemon-export-ok= file
155inside the repository.
156
157#+begin_src sh
158touch git-daemon-export-ok
159#+end_src
160
161* Change the Login Shell for =git=
162
163To make sure that the =git= user is only used for git operations and
164nothing else, you need to change the user's login shell. To do this,
165simply use the =chsh= command:
166
167#+begin_src sh
168sudo chsh git
169#+end_src
170
171The interactive prompt will ask which shell you want the =git= user to
172use. You must use the following value:
173
174#+begin_src sh
175/usr/bin/git-shell
176#+end_src
177
178Once done, no one will be able to SSH to the =git= user or execute
179commands other than the standard git commands.
180
181* Opening the Firewall
182
183Don't forget to open up ports on the device firewall and network
184firewall if you want to access these repositories publicly. If you're
185using default ports, forward ports =22= (ssh) and =9418= (git) from your
186router to your server's IP address.
187
188If your server also has a firewall, ensure that the firewall allows the
189same ports that are forwarded from the router. For example, if you use
190=ufw=:
191
192#+begin_src sh
193sudo ufw allow 22
194sudo ufw allow 9418
195#+end_src
196
197** Non-Standard SSH Ports
198
199If you use a non-standard port for SSH, such as =9876=, you will need to
200create an SSH configuration file on your local development machine in
201order to connect to your server's git repositories.
202
203To do this, you'll need to define your custom port on your client
204machine in your =~/.ssh/config= file:
205
206#+begin_src sh
207nano ~/.ssh/config
208#+end_src
209
210#+begin_src conf
211Host git.example.com
212 # HostName can be a URL or an IP address
213 HostName git.example.com
214 Port 9876
215 User git
216#+end_src
217
218** Testing SSH
219
220There are two main syntaxes you can use to manage git over SSH:
221
222- =git clone [user@]server:project.git=
223- =git clone ssh://[user@]server/project.git=
224
225I prefer the first, which is an =scp=-like syntax. To test it, try to
226clone the test repository you set up on the server:
227
228#+begin_src sh
229git clone git@git.example.com:/git/test.git
230#+end_src
231
232* Enabling Read-Only Access
233
234If you want people to be able to clone any repository where you've
235placed a =git-daemon-export-ok= file, you will need to start the git
236daemon.
237
238To do this on a system with =systemd=, create a service file:
239
240#+begin_src sh
241sudo nano /etc/systemd/system/git-daemon.service
242#+end_src
243
244Inside the =git-daemon.service= file, paste the following:
245
246#+begin_src conf
247[Unit]
248Description=Start Git Daemon
249
250[Service]
251ExecStart=/usr/bin/git daemon --reuseaddr --base-path=/git/ /git/
252
253Restart=always
254RestartSec=500ms
255
256StandardOutput=syslog
257StandardError=syslog
258SyslogIdentifier=git-daemon
259
260User=git
261Group=git
262
263[Install]
264WantedBy=multi-user.target
265#+end_src
266
267Once created, enable and start the service:
268
269#+begin_src sh
270sudo systemctl enable git-daemon.service
271sudo systemctl start git-daemon.service
272#+end_src
273
274To clone read-only via the =git://= protocol, you can use the following
275syntax:
276
277#+begin_src sh
278git clone git://git.example.com/test.git
279#+end_src
280
281* Migrating Repositories
282
283At this point, we have a working git server that works with both SSH and
284read-only access.
285
286For each of the repositories I had hosted a different provider, I
287executed the following commands in order to place a copy on my server as
288my new source of truth:
289
290Server:
291
292#+begin_src sh
293su git
294mkdir /git/<REPOSITORY_NAME>.git && cd /git/<REPOSITORY_NAME>.git
295git init --bare
296
297# If you want to make this repo viewable/cloneable to the public
298touch git-daemon-export-ok
299#+end_src
300
301Client:
302
303#+begin_src sh
304git clone git@<PREVIOUS_HOST>:<REPOSITORY_NAME>
305git remote set-url origin git@git.EXAMPLE.COM:/git/<REPOSITORY_NAME>.git
306git push
307#+end_src
308
309* Optional Web View: =cgit=
310
311If you want a web viewer for your repositories, you can use various
312tools, such as =gitweb=, =cgit=, or =klaus=. I chose =cgit= due to its
313simple interface and fairly easy set-up (compared to others). Not to
314mention that the [[https://git.kernel.org/][Linux kernel uses =cgit=]].
315
316** Docker Compose
317
318Instead of using my previous method of using a =docker run= command,
319I've updated this section to use =docker-compose= instead for an easier
320installation and simpler management and configuration.
321
322In order to use Docker Compose, you will set up a =docker-compose.yml=
323file to automatically connect resources like the repositories, =cgitrc=,
324and various files or folders to the =cgit= container you're creating:
325
326#+begin_src sh
327mkdir ~/cgit && cd ~/cgit
328nano docker-compose.yml
329#+end_src
330
331#+begin_src conf
332# docker-compose.yml
333version: '3'
334
335services:
336 cgit:
337 image: invokr/cgit
338 volumes:
339 - /git:/git
340 - ./cgitrc:/etc/cgitrc
341 - ./logo.png:/var/www/htdocs/cgit/logo.png
342 - ./favicon.png:/var/www/htdocs/cgit/favicon.png
343 - ./filters:/var/www/htdocs/cgit/filters
344 ports:
345 - "8763:80"
346 restart: always
347#+end_src
348
349Then, just start the container:
350
351#+begin_src sh
352sudo docker-compose up -d
353#+end_src
354
355Once it's finished installing, you can access the site at
356=<SERVER_IP>:8763= or use a reverse-proxy service to forward =cgit= to a
357URL, such as =git.example.com=. See the next section for more details on
358reverse proxying a URL to a local port.
359
360** Nginx Reverse Proxy
361
362I am using Nginx as my reverse proxy so that the =cgit= Docker container
363can use =git.example.com= as its URL. To do so, I simply created the
364following configuration file:
365
366#+begin_src sh
367sudo nano /etc/nginx/sites-available/git.example.com
368#+end_src
369
370#+begin_src conf
371server {
372 listen 80;
373 server_name git.example.com;
374
375 if ($host = git.example.com) {
376 return 301 https://$host$request_uri;
377 }
378
379 return 404;
380}
381
382server {
383 server_name git.example.com;
384 listen 443 ssl http2;
385
386 location / {
387 # The final `/` is important.
388 proxy_pass http://localhost:8763/;
389 add_header X-Frame-Options SAMEORIGIN;
390 add_header X-XSS-Protection "1; mode=block";
391 proxy_redirect off;
392 proxy_buffering off;
393 proxy_set_header Host $host;
394 proxy_set_header X-Real-IP $remote_addr;
395 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
396 proxy_set_header X-Forwarded-Proto $scheme;
397 proxy_set_header X-Forwarded-Port $server_port;
398 }
399
400 # INCLUDE ANY SSL CERTS HERE
401 include /etc/letsencrypt/options-ssl-nginx.conf;
402 ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
403}
404#+end_src
405
406Once created, symlink it and restart the web server.
407
408#+begin_src sh
409sudo ln -s /etc/nginx/sites-available/git.example.com /etc/nginx/sites-enabled/
410sudo systemctl restart nginx.service
411#+end_src
412
413As we can see below, my site at =git.example.com= is available and
414running:
415
416** Settings Up Git Details
417
418Once you have =cgit= running, you can add some small details, such as
419repository owners and descriptions by editing the following files within
420each repository.
421
422Alternatively, you can use the =cgitrc= file to edit these details if
423you only care to edit them for the purpose of seeing them on your
424website.
425
426The =description= file within the repository on your server will display
427the description online.
428
429#+begin_src sh
430cd /git/example.git
431nano description
432#+end_src
433
434You can add a =[gitweb]= block to the =config= file in order to display
435the owner of the repository.
436
437#+begin_src sh
438cd /git/example.git
439nano config
440#+end_src
441
442#+begin_src conf
443[gitweb]
444 owner = "YourName"
445#+end_src
446
447Note that you can ignore the configuration within each repository and
448simply set up this information in the =cgitrc= file, if you want to do
449it that way.
450
451** Editing =cgit=
452
453In order to edit certain items within =cgit=, you need to edit the
454=cgitrc= file.
455
456#+begin_src sh
457nano ~/cgit/cgitrc
458#+end_src
459
460Below is an example configuration for =cgitrc=. You can find all the
461configuration options within the [configuration manual]
462([[https://git.zx2c4.com/cgit/plain/cgitrc.5.txt]]).
463
464#+begin_src conf
465css=/cgit.css
466logo=/logo.png
467favicon=/favicon.png
468robots=noindex, nofollow
469
470enable-index-links=1
471enable-commit-graph=1
472enable-blame=1
473enable-log-filecount=1
474enable-log-linecount=1
475enable-git-config=1
476
477clone-url=git://git.example.com/$CGIT_REPO_URL ssh://git@git.example.com:/git/$CGIT_REPO_URL
478
479root-title=My Git Website
480root-desc=My personal git repositories.
481
482# Allow download of tar.gz, tar.bz2 and zip-files
483snapshots=tar.gz tar.bz2 zip
484
485##
486## List of common mimetypes
487##
488mimetype.gif=image/gif
489mimetype.html=text/html
490mimetype.jpg=image/jpeg
491mimetype.jpeg=image/jpeg
492mimetype.pdf=application/pdf
493mimetype.png=image/png
494mimetype.svg=image/svg+xml
495
496# Highlight source code
497# source-filter=/var/www/htdocs/cgit/filters/syntax-highlighting.sh
498source-filter=/var/www/htdocs/cgit/filters/syntax-highlighting.py
499
500# Format markdown, restructuredtext, manpages, text files, and html files
501# through the right converters
502about-filter=/var/www/htdocs/cgit/filters/about-formatting.sh
503
504##
505## Search for these files in the root of the default branch of repositories
506## for coming up with the about page:
507##
508readme=:README.md
509readme=:readme.md
510readme=:README.mkd
511readme=:readme.mkd
512readme=:README.rst
513readme=:readme.rst
514readme=:README.html
515readme=:readme.html
516readme=:README.htm
517readme=:readme.htm
518readme=:README.txt
519readme=:readme.txt
520readme=:README
521readme=:readme
522
523# Repositories
524
525# Uncomment the following line to scan a path instead of adding repositories manually
526# scan-path=/git
527
528## Test Section
529section=git/test-section
530
531repo.url=test.git
532repo.path=/git/test.git
533repo.readme=:README.md
534repo.owner=John Doe
535repo.desc=An example repository!
536#+end_src
537
538** Final Fixes: Syntax Highlighting & README Rendering
539
540After completing my initial install and playing around with it for a few
541days, I noticed two issues:
542
5431. Syntax highlighting did not work when viewing the source code within
544 a file.
5452. The =about= tab within a repository was not rendered to HTML.
546
547The following process fixes these issues. To start, let's go to the
548=cgit= directory where we were editing our configuration file earlier.
549
550#+begin_src sh
551cd ~/cgit
552#+end_src
553
554In here, create two folders that will hold our syntax files:
555
556#+begin_src sh
557mkdir filters && mkdir filters/html-converters && cd filters
558#+end_src
559
560Next, download the default filters:
561
562#+begin_src sh
563curl https://git.zx2c4.com/cgit/plain/filters/about-formatting.sh > about-formatting.sh
564chmod 755 about-formatting.sh
565curl https://git.zx2c4.com/cgit/plain/filters/syntax-highlighting.py > syntax-highlighting.py
566chmod 755 syntax-highlighting.py
567#+end_src
568
569Finally, download the HTML conversion files you need. The example below
570downloads the Markdown converter:
571
572#+begin_src sh
573cd html-converters
574curl https://git.zx2c4.com/cgit/plain/filters/html-converters/md2html > md2html
575chmod 755 md2html
576#+end_src
577
578If you need other filters or html-converters found within
579[[https://git.zx2c4.com/cgit/tree/filters][the cgit project files]],
580repeat the =curl= and =chmod= process above for whichever files you
581need.
582
583However, formatting will not work quite yet since the Docker cgit
584container we're using doesn't have the formatting package installed. You
585can install this easily by install Python 3+ and the =pygments= package:
586
587#+begin_src sh
588# Enter the container's command line
589sudo docker exec -it cgit bash
590#+end_src
591
592#+begin_src sh
593# Install the necessary packages and then exit
594yum update -y && \
595yum upgrade -y && \
596yum install python3 python3-pip -y && \
597pip3 install markdown pygments && \
598exit
599#+end_src
600
601*You will need to enter the cgit docker container and re-run these =yum=
602commands every time you kill and restart the container!*
603
604If not done already, we need to add the following variables to our
605=cgitrc= file in order for =cgit= to know where our filtering files are:
606
607#+begin_src conf
608# Highlight source code with python pygments-based highlighter
609source-filter=/var/www/htdocs/cgit/filters/syntax-highlighting.py
610
611# Format markdown, restructuredtext, manpages, text files, and html files
612# through the right converters
613about-filter=/var/www/htdocs/cgit/filters/about-formatting.sh
614#+end_src
615
616Now you should see that syntax highlighting and README rendering to the
617=about= tab is fixed.
618
619** Theming
620
621I won't go into much detail in this section, but you can fully theme
622your installation of =cgit= since you have access to the =cgit.css= file
623in your web root. This is another file you can add as a volume to the
624=docker-compose.yml= file if you want to edit this without entering the
625container's command line.
626
627*** :warning: Remember to Back Up Your Data!
628
629The last thing to note is that running services on your own equipment
630means that you're assuming a level of risk that exists regarding data
631loss, catastrophes, etc. In order to reduce the impact of any such
632occurrence, I suggest backing up your data regularly.
633
634Backups can be automated via =cron=, by hooking your base directory up
635to a cloud provider, or even setting up hooks to push all repository
636info to git mirrors on other git hosts. Whatever the method, make sure
637that your data doesn't vanish in the event that your drives or servers
638fail.
content/blog/2022-07-14-gnupg.md deleted −300
@@ -1,300 +0,0 @@
1+++
2date = 2022-07-14
3title = "GNU Privacy Guard (GPG)"
4description = ""
5draft = false
6+++
7
8# The History of GPG
9
10[GNU Privacy Guard](https://gnupg.org/), also known as GnuPG and GPG, is a free
11("free" as in both speech and beer) software that fully implements the OpenPGP
12Message Format documented in [RFC 4880](https://www.rfc-editor.org/rfc/rfc4880).
13
14I won't go in-depth on the full history of the software in this post, but it is
15important to understand that GPG is not the same as PGP (Pretty Good Privacy),
16which is a different implementation of RFC 4880. However, GPG was designed to
17interoperate with PGP.
18
19GPG was originally developed in the late 1990s by [Werner
20Koch](https://en.wikipedia.org/wiki/Werner_Koch) and has historically been
21funded generously by the German government.
22
23Now that we have all the high-level info out of the way, let's dive into the
24different aspects of GPG and its uses.
25
26# Encryption Algorithms
27
28GPG supports a wide range of different encryption algorithms, including
29public-key, cipher, hash, and compression algorithms. The support for these
30algorithms has grown since the adoption of the Libgcrypt library in the 2.x
31versions of GPG.
32
33As you will be able to see below in an example of a full key generation with the
34GPG command line tool, GPG recommends the following algorithms to new users:
35
36```sh
37Please select what kind of key you want:
38 (1) RSA and RSA
39 (2) DSA and Elgamal
40 (3) DSA (sign only)
41 (4) RSA (sign only)
42 (9) ECC (sign and encrypt) *default*
43 (10) ECC (sign only)
44```
45
46I am not doing an in-depth explanation here in order to keep the focus on GPG
47and not encryption algorithms. If you want a deep dive into cryptography or
48encryption algorithms, please read my other posts:
49
50- [AES Encryption](../aes-encryption/) (2018)
51- [Cryptography Basics](../cryptography-basics/) (2020)
52
53## Vulnerabilities
54
55As of 2022-07-14, there are a few different vulnerabilities associated with GPG
56or the libraries it uses:
57
58- GPG versions 1.0.2--1.2.3 contains a bug where "as soon as one
59 (GPG-generated) ElGamal signature of an arbitrary message is released, one
60 can recover the signer's private key in less than a second on a PC."
61 ([Source](https://www.di.ens.fr/~pnguyen/pub_Ng04.htm))
62- GPG versions prior to 1.4.2.1 contain a false positive signature
63 verification bug.
64 ([Source](https://lists.gnupg.org/pipermail/gnupg-announce/2006q1/000211.html))
65- GPG versions prior to 1.4.2.2 cannot detect injection of unsigned data. (
66 [Source](https://lists.gnupg.org/pipermail/gnupg-announce/2006q1/000218.html))
67- Libgcrypt, a library used by GPG, contained a bug which enabled full key
68 recovery for RSA-1024 and some RSA-2048 keys. This was resolved in a GPG
69 update in 2017. ([Source](https://lwn.net/Articles/727179/))
70- The [ROCA Vulnerability](https://en.wikipedia.org/wiki/ROCA_vulnerability)
71 affects RSA keys generated by YubiKey 4 tokens.
72 ([Source](https://crocs.fi.muni.cz/_media/public/papers/nemec_roca_ccs17_preprint.pdf))
73- The [SigSpoof Attack](https://en.wikipedia.org/wiki/SigSpoof) allows an
74 attacker to spoof digital signatures.
75 ([Source](https://arstechnica.com/information-technology/2018/06/decades-old-pgp-bug-allowed-hackers-to-spoof-just-about-anyones-signature/))
76- Libgcrypt 1.9.0 contains a severe flaw related to a heap buffer overflow,
77 fixed in Libgcrypt 1.9.1
78 ([Source](https://web.archive.org/web/20210221012505/https://www.theregister.com/2021/01/29/severe_libgcrypt_bug/))
79
80### Platforms
81
82Originally developed as a command-line program for \*nix systems, GPG now has a
83wealth of front-end applications and libraries available for end-users. However,
84the most recommended programs remain the same:
85
86- [GnuPG](https://gnupg.org) for Linux (depending on distro)
87- [Gpg4win](https://gpg4win.org) for Windows
88- [GPGTools](https://gpgtools.org) for macOS
89
90# Creating a Key Pair
91
92In order to create a GPG key pair, a user would first need to install GPG on
93their system. If we're assuming that the user is on Fedora Linux, they would
94execute the following:
95
96```sh
97sudo dnf install gpg
98```
99
100Once installed, a user can create a new key pair with the following command(s):
101
102```sh
103gpg --full-generate-key
104```
105
106GPG will walk the user through an interactive setup that asks for an algorithm
107preference, expiration date, name, and email to associate with this key.
108
109See the following example key set-up for a default key generation using the
110GnuPG command-line interface:
111
112```sh
113gpg (GnuPG) 2.3.6; Copyright (C) 2021 Free Software Foundation, Inc.
114This is free software: you are free to change and redistribute it.
115There is NO WARRANTY, to the extent permitted by law.
116
117Please select what kind of key you want:
118 (1) RSA and RSA
119 (2) DSA and Elgamal
120 (3) DSA (sign only)
121 (4) RSA (sign only)
122 (9) ECC (sign and encrypt) *default*
123 (10) ECC (sign only)
124 (14) Existing key from card
125Your selection? 9
126Please select which elliptic curve you want:
127 (1) Curve 25519 *default*
128 (4) NIST P-384
129Your selection? 1
130Please specify how long the key should be valid.
131 0 = key does not expire
132 <n> = key expires in n days
133 <n>w = key expires in n weeks
134 <n>m = key expires in n months
135 <n>y = key expires in n years
136Key is valid for? (0) 0
137Key does not expire at all
138Is this correct? (y/N) y
139
140GnuPG needs to construct a user ID to identify your key.
141
142Real name: John Doe
143Email address: johndoe@example.com
144Comment: test key
145You selected this USER-ID:
146 "John Doe (test key) <johndoe@example.com>"
147
148Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
149We need to generate a lot of random bytes. It is a good idea to perform
150some other action (type on the keyboard, move the mouse, utilize the
151disks) during the prime generation; this gives the random number
152generator a better chance to gain enough entropy.
153We need to generate a lot of random bytes. It is a good idea to perform
154some other action (type on the keyboard, move the mouse, utilize the
155disks) during the prime generation; this gives the random number
156generator a better chance to gain enough entropy.
157gpg: revocation certificate stored as 'example.rev'
158public and secret key created and signed.
159
160pub ed25519 2022-07-14 [SC]
161 E955B7700FFC11EF51C2BA1FE096AACDD4C32E9C
162uid John Doe (test key) <johndoe@example.com>
163sub cv25519 2022-07-14 [E]
164```
165
166Please note that GUI apps may differ slightly from the GPG command-line
167interface.
168
169# Common Usage
170
171As noted in RFC 4880, the general functions of OpenPGP are as follows:
172
173- digital signatures
174- encryption
175- compression
176- Radix-64 conversion
177- key management and certificate services
178
179From this, you can probably gather that the main use of GPG is for encrypting
180data and/or signing the data with a key. The purpose of encrypting data with GPG
181is to ensure that no one except the intended recipient(s) can access the data.
182
183Let's explore some specific GPG use-cases.
184
185## Email
186
187One of the more popular uses of GPG is to sign and/or encrypt emails. With the
188use of a GPG keypair, you can encrypt a message, its subject, and even the
189attachments within.
190
191The first process, regarding the signing of a message without any encryption, is
192generally used to provide assurance that an email is truly coming from the
193sender that the message claims. When I send an email, and it's signed with my
194public key, the recipient(s) of the message can verify that the message was
195signed with my personal key.
196
197The second process, regarding the actual encryption of the message and its
198contents, works by using a combination of the sender's keys and the recipient's
199keys. This process may vary slightly by implementation, but it most commonly
200uses asymmetric cryptography, also known as public-key cryptography. In this
201version of encryption, the sender's private key to sign the message and a
202combination of the sender's keys and the recipient's public key to encrypt the
203message.
204
205If two people each have their own private keys and exchange their public keys,
206they can send encrypted messages back and forth with GPG. This is also possible
207with symmetric cryptography, but the process differs since there are no key
208pairs.
209
210Implementation of email encryption varies greatly between email clients, so you
211will need to reference your email client's documentation to ensure you are
212setting it up correctly for that specific client.
213
214## File Encryption
215
216As noted in the section above regarding emails, GPG enables users to be able to
217send a message to each other if they are both set-up with GPG keys. In this
218example, I am going to show how a user could send a file called
219`example_file.txt` to another user via the recipient's email.
220
221The sender would find the file they want to send and execute the following
222command:
223
224```sh
225gpg --encrypt --output example_file.txt.gpg --recipient \
226recipient@example.com example_file.txt
227```
228
229Once received, the recipient can decrypt the file with the following command:
230
231```sh
232gpg --decrypt --output example_file.txt example_file.txt.gpg
233```
234
235## Ownership Signatures
236
237One important aspect of GPG, especially for developers, is the ability to sign
238data without encrypting it. For example, developers often sign code changes when
239they commit the changes back to a central repository, in order to display
240ownership of who made the changes. This allows other users to look at a code
241change and determine that the change was valid.
242
243In order to do this using [Git](https://git-scm.com), the developer simply needs
244to alter the `git commit` command to include the `-S` flag. Here's an example:
245
246```sh
247git commit -S -m "my commit message"
248```
249
250As an expansion of the example above, Git users can configure their environment
251with a default key to use by adding their GPG signature:
252
253```sh
254git config --global user.signingkey XXXXXXXXXXXXXXXX
255```
256
257If you're not sure what your signature is, you can find it titled `sig` in the
258output of this command:
259
260```sh
261gpg --list-signatures
262```
263
264## File Integrity
265
266When a person generates a signature for data, they are allowing users the
267ability to verify the signature on that data in the future to ensure the data
268has not been corrupted. This is most common with software applications hosted on
269the internet - developers provide signatures so that users can verify a website
270was not hijacked and download links replaced with dangerous software.
271
272In order to verify signed data, a user needs to have:
273
2741. The signed data
2752. A signature file
2763. The public GPG key of the signer
277
278Once the signer's public key is imported on the user's system, and they have the
279data and signature, they can verify the data with the following commands:
280
281```sh
282# If the signature is attached to the data
283gpg --verify [signature-file]
284
285# If the signature is detached as a separate file from the data
286gpg --verify [signature-file] [original-file]
287```
288
289### Finding Public Keys
290
291In order to use GPG with others, a user needs to know the other user(s) keys.
292This is easy to do if the user knows the other user(s) in person, but may be
293hard if the relationship is strictly digital. Luckily, there are a few options.
294The first option is to look at a user's web page or social pages if they have
295them.
296
297Otherwise, the best option is to use a keyserver, such as:
298
299- [pgp.mit.edu](https://pgp.mit.edu)
300- [keys.openpgp.org](https://keys.openpgp.org)
content/blog/2022-07-14-gnupg.org added +308
@@ -0,0 +1,308 @@
1#+date: <2022-07-14>
2#+title: GNU Privacy Guard (GPG)
3#+description:
4
5
6* The History of GPG
7
8[[https://gnupg.org/][GNU Privacy Guard]], also known as GnuPG and GPG,
9is a free ("free" as in both speech and beer) software that fully
10implements the OpenPGP Message Format documented in
11[[https://www.rfc-editor.org/rfc/rfc4880][RFC 4880]].
12
13I won't go in-depth on the full history of the software in this post,
14but it is important to understand that GPG is not the same as PGP
15(Pretty Good Privacy), which is a different implementation of RFC 4880.
16However, GPG was designed to interoperate with PGP.
17
18GPG was originally developed in the late 1990s by
19[[https://en.wikipedia.org/wiki/Werner_Koch][Werner Koch]] and has
20historically been funded generously by the German government.
21
22Now that we have all the high-level info out of the way, let's dive into
23the different aspects of GPG and its uses.
24
25* Encryption Algorithms
26
27GPG supports a wide range of different encryption algorithms, including
28public-key, cipher, hash, and compression algorithms. The support for
29these algorithms has grown since the adoption of the Libgcrypt library
30in the 2.x versions of GPG.
31
32As you will be able to see below in an example of a full key generation
33with the GPG command line tool, GPG recommends the following algorithms
34to new users:
35
36#+begin_src sh
37Please select what kind of key you want:
38 (1) RSA and RSA
39 (2) DSA and Elgamal
40 (3) DSA (sign only)
41 (4) RSA (sign only)
42 (9) ECC (sign and encrypt) *default*
43 (10) ECC (sign only)
44#+end_src
45
46I am not doing an in-depth explanation here in order to keep the focus
47on GPG and not encryption algorithms. If you want a deep dive into
48cryptography or encryption algorithms, please read my other posts:
49
50- [[../aes-encryption/][AES Encryption]] (2018)
51- [[../cryptography-basics/][Cryptography Basics]] (2020)
52
53** Vulnerabilities
54
55As of 2022-07-14, there are a few different vulnerabilities associated
56with GPG or the libraries it uses:
57
58- GPG versions 1.0.2--1.2.3 contains a bug where "as soon as one
59 (GPG-generated) ElGamal signature of an arbitrary message is released,
60 one can recover the signer's private key in less than a second on a
61 PC." ([[https://www.di.ens.fr/~pnguyen/pub_Ng04.htm][Source]])
62- GPG versions prior to 1.4.2.1 contain a false positive signature
63 verification bug.
64 ([[https://lists.gnupg.org/pipermail/gnupg-announce/2006q1/000211.html][Source]])
65- GPG versions prior to 1.4.2.2 cannot detect injection of unsigned
66 data. (
67 [[https://lists.gnupg.org/pipermail/gnupg-announce/2006q1/000218.html][Source]])
68- Libgcrypt, a library used by GPG, contained a bug which enabled full
69 key recovery for RSA-1024 and some RSA-2048 keys. This was resolved in
70 a GPG update in 2017. ([[https://lwn.net/Articles/727179/][Source]])
71- The [[https://en.wikipedia.org/wiki/ROCA_vulnerability][ROCA
72 Vulnerability]] affects RSA keys generated by YubiKey 4 tokens.
73 ([[https://crocs.fi.muni.cz/_media/public/papers/nemec_roca_ccs17_preprint.pdf][Source]])
74- The [[https://en.wikipedia.org/wiki/SigSpoof][SigSpoof Attack]] allows
75 an attacker to spoof digital signatures.
76 ([[https://arstechnica.com/information-technology/2018/06/decades-old-pgp-bug-allowed-hackers-to-spoof-just-about-anyones-signature/][Source]])
77- Libgcrypt 1.9.0 contains a severe flaw related to a heap buffer
78 overflow, fixed in Libgcrypt 1.9.1
79 ([[https://web.archive.org/web/20210221012505/https://www.theregister.com/2021/01/29/severe_libgcrypt_bug/][Source]])
80
81*** Platforms
82
83Originally developed as a command-line program for *nix systems, GPG now
84has a wealth of front-end applications and libraries available for
85end-users. However, the most recommended programs remain the same:
86
87- [[https://gnupg.org][GnuPG]] for Linux (depending on distro)
88- [[https://gpg4win.org][Gpg4win]] for Windows
89- [[https://gpgtools.org][GPGTools]] for macOS
90
91* Creating a Key Pair
92
93In order to create a GPG key pair, a user would first need to install
94GPG on their system. If we're assuming that the user is on Fedora Linux,
95they would execute the following:
96
97#+begin_src sh
98sudo dnf install gpg
99#+end_src
100
101Once installed, a user can create a new key pair with the following
102command(s):
103
104#+begin_src sh
105gpg --full-generate-key
106#+end_src
107
108GPG will walk the user through an interactive setup that asks for an
109algorithm preference, expiration date, name, and email to associate with
110this key.
111
112See the following example key set-up for a default key generation using
113the GnuPG command-line interface:
114
115#+begin_src sh
116gpg (GnuPG) 2.3.6; Copyright (C) 2021 Free Software Foundation, Inc.
117This is free software: you are free to change and redistribute it.
118There is NO WARRANTY, to the extent permitted by law.
119
120Please select what kind of key you want:
121 (1) RSA and RSA
122 (2) DSA and Elgamal
123 (3) DSA (sign only)
124 (4) RSA (sign only)
125 (9) ECC (sign and encrypt) *default*
126 (10) ECC (sign only)
127 (14) Existing key from card
128Your selection? 9
129Please select which elliptic curve you want:
130 (1) Curve 25519 *default*
131 (4) NIST P-384
132Your selection? 1
133Please specify how long the key should be valid.
134 0 = key does not expire
135 <n> = key expires in n days
136 <n>w = key expires in n weeks
137 <n>m = key expires in n months
138 <n>y = key expires in n years
139Key is valid for? (0) 0
140Key does not expire at all
141Is this correct? (y/N) y
142
143GnuPG needs to construct a user ID to identify your key.
144
145Real name: John Doe
146Email address: johndoe@example.com
147Comment: test key
148You selected this USER-ID:
149 "John Doe (test key) <johndoe@example.com>"
150
151Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
152We need to generate a lot of random bytes. It is a good idea to perform
153some other action (type on the keyboard, move the mouse, utilize the
154disks) during the prime generation; this gives the random number
155generator a better chance to gain enough entropy.
156We need to generate a lot of random bytes. It is a good idea to perform
157some other action (type on the keyboard, move the mouse, utilize the
158disks) during the prime generation; this gives the random number
159generator a better chance to gain enough entropy.
160gpg: revocation certificate stored as 'example.rev'
161public and secret key created and signed.
162
163pub ed25519 2022-07-14 [SC]
164 E955B7700FFC11EF51C2BA1FE096AACDD4C32E9C
165uid John Doe (test key) <johndoe@example.com>
166sub cv25519 2022-07-14 [E]
167#+end_src
168
169Please note that GUI apps may differ slightly from the GPG command-line
170interface.
171
172* Common Usage
173
174As noted in RFC 4880, the general functions of OpenPGP are as follows:
175
176- digital signatures
177- encryption
178- compression
179- Radix-64 conversion
180- key management and certificate services
181
182From this, you can probably gather that the main use of GPG is for
183encrypting data and/or signing the data with a key. The purpose of
184encrypting data with GPG is to ensure that no one except the intended
185recipient(s) can access the data.
186
187Let's explore some specific GPG use-cases.
188
189** Email
190
191One of the more popular uses of GPG is to sign and/or encrypt emails.
192With the use of a GPG keypair, you can encrypt a message, its subject,
193and even the attachments within.
194
195The first process, regarding the signing of a message without any
196encryption, is generally used to provide assurance that an email is
197truly coming from the sender that the message claims. When I send an
198email, and it's signed with my public key, the recipient(s) of the
199message can verify that the message was signed with my personal key.
200
201The second process, regarding the actual encryption of the message and
202its contents, works by using a combination of the sender's keys and the
203recipient's keys. This process may vary slightly by implementation, but
204it most commonly uses asymmetric cryptography, also known as public-key
205cryptography. In this version of encryption, the sender's private key to
206sign the message and a combination of the sender's keys and the
207recipient's public key to encrypt the message.
208
209If two people each have their own private keys and exchange their public
210keys, they can send encrypted messages back and forth with GPG. This is
211also possible with symmetric cryptography, but the process differs since
212there are no key pairs.
213
214Implementation of email encryption varies greatly between email clients,
215so you will need to reference your email client's documentation to
216ensure you are setting it up correctly for that specific client.
217
218** File Encryption
219
220As noted in the section above regarding emails, GPG enables users to be
221able to send a message to each other if they are both set-up with GPG
222keys. In this example, I am going to show how a user could send a file
223called =example_file.txt= to another user via the recipient's email.
224
225The sender would find the file they want to send and execute the
226following command:
227
228#+begin_src sh
229gpg --encrypt --output example_file.txt.gpg --recipient \
230recipient@example.com example_file.txt
231#+end_src
232
233Once received, the recipient can decrypt the file with the following
234command:
235
236#+begin_src sh
237gpg --decrypt --output example_file.txt example_file.txt.gpg
238#+end_src
239
240** Ownership Signatures
241
242One important aspect of GPG, especially for developers, is the ability
243to sign data without encrypting it. For example, developers often sign
244code changes when they commit the changes back to a central repository,
245in order to display ownership of who made the changes. This allows other
246users to look at a code change and determine that the change was valid.
247
248In order to do this using [[https://git-scm.com][Git]], the developer
249simply needs to alter the =git commit= command to include the =-S= flag.
250Here's an example:
251
252#+begin_src sh
253git commit -S -m "my commit message"
254#+end_src
255
256As an expansion of the example above, Git users can configure their
257environment with a default key to use by adding their GPG signature:
258
259#+begin_src sh
260git config --global user.signingkey XXXXXXXXXXXXXXXX
261#+end_src
262
263If you're not sure what your signature is, you can find it titled =sig=
264in the output of this command:
265
266#+begin_src sh
267gpg --list-signatures
268#+end_src
269
270** File Integrity
271
272When a person generates a signature for data, they are allowing users
273the ability to verify the signature on that data in the future to ensure
274the data has not been corrupted. This is most common with software
275applications hosted on the internet - developers provide signatures so
276that users can verify a website was not hijacked and download links
277replaced with dangerous software.
278
279In order to verify signed data, a user needs to have:
280
2811. The signed data
2822. A signature file
2833. The public GPG key of the signer
284
285Once the signer's public key is imported on the user's system, and they
286have the data and signature, they can verify the data with the following
287commands:
288
289#+begin_src sh
290# If the signature is attached to the data
291gpg --verify [signature-file]
292
293# If the signature is detached as a separate file from the data
294gpg --verify [signature-file] [original-file]
295#+end_src
296
297*** Finding Public Keys
298
299In order to use GPG with others, a user needs to know the other user(s)
300keys. This is easy to do if the user knows the other user(s) in person,
301but may be hard if the relationship is strictly digital. Luckily, there
302are a few options. The first option is to look at a user's web page or
303social pages if they have them.
304
305Otherwise, the best option is to use a keyserver, such as:
306
307- [[https://pgp.mit.edu][pgp.mit.edu]]
308- [[https://keys.openpgp.org][keys.openpgp.org]]
content/blog/2022-07-25-curseradio.md deleted −98
@@ -1,98 +0,0 @@
1+++
2date = 2022-07-25
3title = "CurseRadio: Listening to the Radio on the Command Line"
4description = ""
5draft = false
6+++
7
8# Overview
9
10While exploring some interesting Linux applications, I stumbled across
11[curseradio](https://github.com/chronitis/curseradio), a command-line radio
12player based on Python.
13
14This application is fantastic and incredibly easy to install, so I wanted to
15dedicate a post today to this app. Let's look at the features within the app and
16then walk through the installation process I took to get `curseradio` working.
17
18# Features
19
20The radio player itself is quite minimal. As you can see in the screenshot
21above, it contains a simple plaintext list of all available categories, which
22can be broken down further and further. In addition, radio shows are available
23for listening, alongside regular radio stations.
24
25For example, the `Sports` > `Pro Basketball` > `Shows` category contains a
26number of specific shows related to Professional Basketball.
27
28Aside from being able to play any of the listed stations/shows, you can make a
29channel your favorite by pressing `f`. It will now show up at the top of the
30radio player in the `Favourites` category.
31
32## Commands/Shortcuts
33
34| Key(s) | Command |
35| ---------- | ------------------------------- |
36| ↑, ↓ | navigate |
37| PgUp, PgDn | navigate quickly |
38| Home, End | to top/bottom |
39| Enter | open/close folders, play stream |
40| k | stop playing stream |
41| q | quit |
42| f | toggle favourite |
43
44# Installation
45
46## Dependencies
47
48Before installing `curseradio`, a handful of system and Python packages are
49required. To get started, install `python3`, `pip3`, and `mpv` on your system.
50In this example, I'm using Fedora Linux, which uses the `dnf` package manager.
51You may need to adjust this if you're using a different system.
52
53```sh
54sudo dnf install python3 pip3 mpv
55```
56
57Next, use `pip3` to install `requests`, `xdg`, and `lxml`:
58
59```sh
60pip3 install requests xdg lxml
61```
62
63## Repository Source Installation
64
65Once all the dependencies are installed, we can clone the source code and enter
66that directory:
67
68```sh
69git clone https://github.com/chronitis/curseradio && cd curseradio
70```
71
72Once you're within the `curseradio` directory, you can install the application
73with the provided `setup.py` script.
74
75```sh
76sudo python3 setup.py install
77```
78
79In my case, I ran into a few errors and needed to create the folders that
80curseradio wanted to use for its installation. If you don't get any errors, you
81can skip this and run the app.
82
83```sh
84sudo mkdir /usr/local/lib/python3.10/
85sudo mkdir /usr/local/lib/python3.10/site-packages/
86```
87
88```sh
89sudo chown -R $USER:$USER /usr/local/lib/python3.10/
90```
91
92# Run the Application
93
94Once fully installed without errors, you can run the application!
95
96```sh
97python3 /usr/local/bin/curseradio
98```
content/blog/2022-07-25-curseradio.org added +99
@@ -0,0 +1,99 @@
1#+date: <2022-07-25>
2#+title: CurseRadio: Listening to the Radio on the Command Line
3#+description:
4
5
6* Overview
7
8While exploring some interesting Linux applications, I stumbled across
9[[https://github.com/chronitis/curseradio][curseradio]], a command-line
10radio player based on Python.
11
12This application is fantastic and incredibly easy to install, so I
13wanted to dedicate a post today to this app. Let's look at the features
14within the app and then walk through the installation process I took to
15get =curseradio= working.
16
17* Features
18
19The radio player itself is quite minimal. As you can see in the
20screenshot above, it contains a simple plaintext list of all available
21categories, which can be broken down further and further. In addition,
22radio shows are available for listening, alongside regular radio
23stations.
24
25For example, the =Sports= > =Pro Basketball= > =Shows= category contains
26a number of specific shows related to Professional Basketball.
27
28Aside from being able to play any of the listed stations/shows, you can
29make a channel your favorite by pressing =f=. It will now show up at the
30top of the radio player in the =Favourites= category.
31
32** Commands/Shortcuts
33
34| Key(s) | Command |
35|------------+---------------------------------|
36| ↑, ↓ | navigate |
37| PgUp, PgDn | navigate quickly |
38| Home, End | to top/bottom |
39| Enter | open/close folders, play stream |
40| k | stop playing stream |
41| q | quit |
42| f | toggle favourite |
43
44* Installation
45
46** Dependencies
47
48Before installing =curseradio=, a handful of system and Python packages
49are required. To get started, install =python3=, =pip3=, and =mpv= on
50your system. In this example, I'm using Fedora Linux, which uses the
51=dnf= package manager. You may need to adjust this if you're using a
52different system.
53
54#+begin_src sh
55sudo dnf install python3 pip3 mpv
56#+end_src
57
58Next, use =pip3= to install =requests=, =xdg=, and =lxml=:
59
60#+begin_src sh
61pip3 install requests xdg lxml
62#+end_src
63
64** Repository Source Installation
65
66Once all the dependencies are installed, we can clone the source code
67and enter that directory:
68
69#+begin_src sh
70git clone https://github.com/chronitis/curseradio && cd curseradio
71#+end_src
72
73Once you're within the =curseradio= directory, you can install the
74application with the provided =setup.py= script.
75
76#+begin_src sh
77sudo python3 setup.py install
78#+end_src
79
80In my case, I ran into a few errors and needed to create the folders
81that curseradio wanted to use for its installation. If you don't get any
82errors, you can skip this and run the app.
83
84#+begin_src sh
85sudo mkdir /usr/local/lib/python3.10/
86sudo mkdir /usr/local/lib/python3.10/site-packages/
87#+end_src
88
89#+begin_src sh
90sudo chown -R $USER:$USER /usr/local/lib/python3.10/
91#+end_src
92
93* Run the Application
94
95Once fully installed without errors, you can run the application!
96
97#+begin_src sh
98python3 /usr/local/bin/curseradio
99#+end_src
content/blog/2022-07-30-flac-to-opus.md → content/blog/2022-07-30-flac-to-opus.org renamed +68 −68
@@ -1,68 +1,68 @@
1+++
2date = 2022-07-30
3title = "Recursive Command-Line FLAC to Opus Conversion"
4description = ""
5draft = false
6+++
1#+date: <2022-07-30>
2#+title: Recursive Command-Line FLAC to Opus Conversion
3#+description:
74
8# Converting FLAC to OPUS
5
6* Converting FLAC to OPUS
97
108I am currently rebuilding my music library from scratch so that I can
11effectively archive all the music I own in the [FLAC file
12format](https://en.wikipedia.org/wiki/FLAC), a lossless audio codec.
9effectively archive all the music I own in the
10[[https://en.wikipedia.org/wiki/FLAC][FLAC file format]], a lossless
11audio codec.
1312
14However, streaming FLAC files outside the home can be difficult due to the size
15of the files, especially if you're using a weak connection.
13However, streaming FLAC files outside the home can be difficult due to
14the size of the files, especially if you're using a weak connection.
1615
17So, in order to archive the music in a lossless format and still be able to
18stream it easily, I opted to create a copy of my FLAC files in the [Opus audio
19codec](<https://en.wikipedia.org/wiki/Opus_(audio_format)>). This allows me to
20archive a quality, lossless version of the music and then point my streaming
21service to the smaller, stream-ready version.
16So, in order to archive the music in a lossless format and still be able
17to stream it easily, I opted to create a copy of my FLAC files in the
18[[https://en.wikipedia.org/wiki/Opus_(audio_format)][Opus audio codec]].
19This allows me to archive a quality, lossless version of the music and
20then point my streaming service to the smaller, stream-ready version.
2221
23## Dependencies
22** Dependencies
2423
25The process I follow utilizes the `opus-tools` package in Ubuntu. Before
24The process I follow utilizes the =opus-tools= package in Ubuntu. Before
2625proceeding, install the package:
2726
28```sh
27#+begin_src sh
2928sudo apt install opus-tools
30```
29#+end_src
3130
32If you want to use a different conversion method, such as `ffmpeg` or `avconv`,
33simply install that package instead.
31If you want to use a different conversion method, such as =ffmpeg= or
32=avconv=, simply install that package instead.
3433
35## Conversion Process
34** Conversion Process
3635
37The script I'm using is stored in my home directory, but feel free to create it
38wherever you want. It does not need to be in the same directory as your music
39files.
36The script I'm using is stored in my home directory, but feel free to
37create it wherever you want. It does not need to be in the same
38directory as your music files.
4039
41```sh
40#+begin_src sh
4241cd ~ && nano transform.sh
43```
42#+end_src
4443
45Once you have your new bash script opened in an editor, go ahead and paste the
46following logic into the script.
44Once you have your new bash script opened in an editor, go ahead and
45paste the following logic into the script.
4746
48You **MUST** edit the following variables in order for it to work:
47You *MUST* edit the following variables in order for it to work:
4948
50- `source`: The source directory where your FLAC files are stored.
51- `dest`: The destination directory where you want the resulting Opus files to
52 be stored.
49- =source=: The source directory where your FLAC files are stored.
50- =dest=: The destination directory where you want the resulting Opus
51 files to be stored.
5352
54You **MAY** want to edit the following variables to suit your needs:
53You *MAY* want to edit the following variables to suit your needs:
5554
56- `filename`: If you are converting to a file format other than Opus, you'll
57 need to edit this so that your resulting files have the correct filename
58 extension.
59- `reldir`: This variable can be edited to strip out more leading directories
60 in the file path. As you'll see later, I ignore this for now and simply
61 clean it up afterward.
62- `opusenc`: This is the actual conversion process. You may want to edit the
63 bitrate to suit your needs. I set mine at 128 but some prefer 160 or higher.
55- =filename=: If you are converting to a file format other than Opus,
56 you'll need to edit this so that your resulting files have the correct
57 filename extension.
58- =reldir=: This variable can be edited to strip out more leading
59 directories in the file path. As you'll see later, I ignore this for
60 now and simply clean it up afterward.
61- =opusenc=: This is the actual conversion process. You may want to edit
62 the bitrate to suit your needs. I set mine at 128 but some prefer 160
63 or higher.
6464
65```sh
65#+begin_src sh
6666#!/bin/bash
6767## - The IFS takes care of spaces in file and dirnames
6868## - your folders may vary
@@ -124,51 +124,51 @@ opusenc --vbr --bitrate 128 --date "$DATE" \
124124## just for testing
125125# sleep 1
126126done
127```
127#+end_src
128128
129Once you're done, simply save the file and exit your editor. Don't forget to
130enable execution of the script:
129Once you're done, simply save the file and exit your editor. Don't
130forget to enable execution of the script:
131131
132```sh
132#+begin_src sh
133133chmod +x transform.sh
134```
134#+end_src
135135
136136Finally, you may now run the script:
137137
138```sh
138#+begin_src sh
139139./transform.sh
140```
140#+end_src
141141
142If you used `opusenc`, you'll see the conversions happen within the terminal as
143it progresses. You will also see variables printed if you uncommented any of the
144bash script's comments.
142If you used =opusenc=, you'll see the conversions happen within the
143terminal as it progresses. You will also see variables printed if you
144uncommented any of the bash script's comments.
145145
146## Cleanup
146** Cleanup
147147
148As I noted above, I didn't customize my `reldir` variable in the script, which
149caused my output directory to be `/mnt/music/library/archives` instead of
150`/mnt/music/library`. So, I moved the output up one level and deleted the
151accidental directory.
148As I noted above, I didn't customize my =reldir= variable in the script,
149which caused my output directory to be =/mnt/music/library/archives=
150instead of =/mnt/music/library=. So, I moved the output up one level and
151deleted the accidental directory.
152152
153```sh
153#+begin_src sh
154154cd /mnt/music/library
155155mv archives/** .
156156rm -rf archives
157```
157#+end_src
158158
159## Check the Resulting Size
159** Check the Resulting Size
160160
161If you want to see what kind of file size savings you've gained, you can always
162use the `du` command to check:
161If you want to see what kind of file size savings you've gained, you can
162always use the =du= command to check:
163163
164```sh
164#+begin_src sh
165165cd /mnt/music
166166du -h --max-depth=1 .
167```
167#+end_src
168168
169169In my case, my small library went from 78GB to 6.3GB!
170170
171```txt
171#+begin_src txt
17217278G ./archives
1731736.3G ./library
174```
174#+end_src
content/blog/2022-07-31-bash-it.md deleted −237
@@ -1,237 +0,0 @@
1+++
2date = 2022-07-31
3title = "Upgrade Bash with Bash-It and Ble.sh"
4description = ""
5draft = false
6+++
7
8# Bash
9
10For those who are not familiar,
11[Bash](<https://en.wikipedia.org/wiki/Bash_(Unix_shell)>) is a Unix shell that
12is used as the default login shell for most Linux distributions. This shell and
13command processor should be familiar if you've used Linux (or older version of
14macOS) before.
15
16However, bash is not the only option. There are numerous other shells that
17exist. Here are some popular examples:
18
19- [zsh](https://en.wikipedia.org/wiki/Z_shell)
20- [fish](<https://en.wikipedia.org/wiki/Fish_(Unix_shell)>)
21- [oksh](https://github.com/ibara/oksh)
22- [mksh](https://wiki.gentoo.org/wiki/Mksh)
23- [dash](https://en.wikipedia.org/wiki/Debian_Almquist_shell)
24
25While each shell has its differences, bash is POSIX compliant and the default
26for many Linux users. Because of this, I am going to explore a program called
27`bash-it` below that helps bash users increase the utility of their shell
28without installing a completely new shell.
29
30## Installation
31
32First, if bash is not already installed on your system, you can [download bash
33from GNU](https://www.gnu.org/software/bash/) or use your package manager to
34install it.
35
36For example, this is how you can install bash on Fedora Linux:
37
38```sh
39sudo dnf install bash
40```
41
42If you are not using bash as your default shell, use the `chsh` command to
43change your shell:
44
45```sh
46chsh
47```
48
49You should see a prompt like the one below. If the brackets (`[]`) contain
50`bash` already, you're done, and you can simply continue by hitting the Enter
51key.
52
53If the brackets contain another shell path (e.g. `/usr/bin/zsh`), enter the path
54to the bash program on your system (it's most likely located at
55`/usr/bin/bash`).
56
57```sh
58Changing shell for <user>.
59New shell [/usr/bin/bash]:
60```
61
62You must log out or restart the machine in order for the login shell to be
63refreshed. You can do it now or wait until you're finished customizing the
64shell.
65
66```sh
67sudo reboot now
68```
69
70# Bash-it
71
72As noted on the [Bash-it](https://github.com/Bash-it/bash-it) repository:
73
74> Bash-it is a collection of community Bash commands and scripts for Bash 3.2+.
75> (And a shameless ripoff of oh-my-zsh 😃)
76
77Bash-it makes it easy to install plugins, set up aliases for common commands,
78and easily change the visual theme of your shell.
79
80## Installation
81
82To install the framework, simply copy the repository files and use the
83`install.sh` script provided. If you want, you can (and should!) inspect the
84contents of the installation script before you run it.
85
86```sh
87git clone --depth=1 https://github.com/Bash-it/bash-it.git ~/.bash_it
88~/.bash_it/install.sh
89```
90
91If you didn't restart your session after making bash the default, and are
92currently working within another shell, be sure to enter a bash session before
93using `bash-it`:
94
95```sh
96bash
97```
98
99## Aliases
100
101Bash-it contains a number of aliases for common commands to help improve
102efficiency in the terminal. To list all available options, use the following
103command:
104
105```sh
106bash-it show aliases
107```
108
109This will provide you a list that looks like the following text block. Within
110this screen, you will be able to see all available options and which ones are
111currently enabled.
112
113```txt
114Alias Enabled? Description
115ag [ ] the silver searcher (ag) aliases
116ansible [ ] ansible abbreviations
117apt [ ] Apt and dpkg aliases for Ubuntu and Debian distros.
118atom [ ] Atom.io editor abbreviations
119bash-it [ ] Aliases for the bash-it command (these aliases are automatically included with the "general" aliases)
120bolt [ ] puppet bolt aliases
121bundler [ ] ruby bundler
122clipboard [ ] xclip shortcuts
123composer [ ] common composer abbreviations
124curl [x] Curl aliases for convenience.
125...
126```
127
128To enable an alias, do:
129
130```sh
131bash-it enable alias <alias name> [alias name]... -or- $ bash-it enable alias all
132```
133
134To disable an alias, do:
135
136```sh
137bash-it disable alias <alias name> [alias name]... -or- $ bash-it disable alias all
138```
139
140## Plugins
141
142Similar to aliases, plugins are available with bash-it. You can find a complete
143list of plugins in the same way as aliases. Simply execute the following:
144
145```sh
146bash-it show plugins
147```
148
149You will see the following output showing enabled and disabled plugins:
150
151```txt
152Plugin Enabled? Description
153alias-completion [ ]
154autojump [ ] Autojump configuration, see https://github.com/wting/autojump for more details
155aws [ ] AWS helper functions
156base [x] miscellaneous tools
157basher [ ] initializes basher, the shell package manager
158battery [x] display info about your battery charge level
159blesh [ ] load ble.sh, the Bash line editor!
160boot2docker [ ] Helpers to get Docker setup correctly for boot2docker
161browser [ ] render commandline output in your browser
162```
163
164To enable a plugin, do:
165
166```sh
167bash-it enable plugin <plugin name> [plugin name]... -or- $ bash-it enable plugin all
168```
169
170To disable a plugin, do:
171
172```sh
173bash-it disable plugin <plugin name> [plugin name]... -or- $ bash-it disable plugin all
174```
175
176## Themes
177
178There are quite a few pre-defined
179[themes](https://bash-it.readthedocs.io/en/latest/themes-list/#list-of-themes)
180available with bash-it.
181
182To list all themes:
183
184```sh
185ls ~/.bash_it/themes/
186```
187
188To use a new theme, you'll need to edit `.bashrc` and alter the `BASH_IT_THEME`
189variable to your desired theme. For example, I am using the `zork` theme.
190
191```sh
192nano ~/.bashrc
193```
194
195```sh
196export BASH_IT_THEME='zork'
197```
198
199Once you save your changes, you just need to exit your terminal and create a new
200one in order to see your changes to the `.bashrc` file. You can also `source`
201the file to see changes, but I recommend starting a completely new shell
202instead.
203
204### ble.sh
205
206One big feature I was missing in Bash that both `zsh` and `fish` have is an
207autosuggestion feature. To explain: as you type, an autosuggestion feature in
208the shell will offer suggestions in a lighter font color beyond the characters
209already typed. Once you see the command you want, you can click the right arrow
210and have the shell auto-complete that line for you.
211
212Luckily, the [Bash Line Editor](https://github.com/akinomyoga/ble.sh) (ble.sh)
213exists! This program provides a wonderful autosuggestions feature perfectly,
214among other features that I haven't tested yet.
215
216In order to install ble.sh, execute the following:
217
218```sh
219git clone --recursive https://github.com/akinomyoga/ble.sh.git
220make -C ble.sh install PREFIX=~/.local
221echo 'source ~/.local/share/blesh/ble.sh' >> ~/.bashrc
222```
223
224Again, exit the terminal and open a new one in order to see the newly-configured
225shell.
226
227# Restart the Session
228
229Finally, as mentioned above, you'll need to restart the session to ensure that
230your user is using bash by default.
231
232You will also need to exit and re-open a shell (e.g., terminal or terminal tab)
233any time you make changes to the `.bashrc` file.
234
235```sh
236sudo reboot now
237```
content/blog/2022-07-31-bash-it.org added +241
@@ -0,0 +1,241 @@
1#+date: <2022-07-31>
2#+title: Upgrade Bash with Bash-It and Ble.sh
3#+description:
4
5
6* Bash
7
8For those who are not familiar,
9[[https://en.wikipedia.org/wiki/Bash_(Unix_shell)][Bash]] is a Unix
10shell that is used as the default login shell for most Linux
11distributions. This shell and command processor should be familiar if
12you've used Linux (or older version of macOS) before.
13
14However, bash is not the only option. There are numerous other shells
15that exist. Here are some popular examples:
16
17- [[https://en.wikipedia.org/wiki/Z_shell][zsh]]
18- [[https://en.wikipedia.org/wiki/Fish_(Unix_shell)][fish]]
19- [[https://github.com/ibara/oksh][oksh]]
20- [[https://wiki.gentoo.org/wiki/Mksh][mksh]]
21- [[https://en.wikipedia.org/wiki/Debian_Almquist_shell][dash]]
22
23While each shell has its differences, bash is POSIX compliant and the
24default for many Linux users. Because of this, I am going to explore a
25program called =bash-it= below that helps bash users increase the
26utility of their shell without installing a completely new shell.
27
28** Installation
29
30First, if bash is not already installed on your system, you can
31[[https://www.gnu.org/software/bash/][download bash from GNU]] or use
32your package manager to install it.
33
34For example, this is how you can install bash on Fedora Linux:
35
36#+begin_src sh
37sudo dnf install bash
38#+end_src
39
40If you are not using bash as your default shell, use the =chsh= command
41to change your shell:
42
43#+begin_src sh
44chsh
45#+end_src
46
47You should see a prompt like the one below. If the brackets (=[]=)
48contain =bash= already, you're done, and you can simply continue by
49hitting the Enter key.
50
51If the brackets contain another shell path (e.g. =/usr/bin/zsh=), enter
52the path to the bash program on your system (it's most likely located at
53=/usr/bin/bash=).
54
55#+begin_src sh
56Changing shell for <user>.
57New shell [/usr/bin/bash]:
58#+end_src
59
60You must log out or restart the machine in order for the login shell to
61be refreshed. You can do it now or wait until you're finished
62customizing the shell.
63
64#+begin_src sh
65sudo reboot now
66#+end_src
67
68* Bash-it
69
70As noted on the [[https://github.com/Bash-it/bash-it][Bash-it]]
71repository:
72
73#+begin_quote
74Bash-it is a collection of community Bash commands and scripts for Bash
753.2+. (And a shameless ripoff of oh-my-zsh 😃)
76#+end_quote
77
78Bash-it makes it easy to install plugins, set up aliases for common
79commands, and easily change the visual theme of your shell.
80
81** Installation
82
83To install the framework, simply copy the repository files and use the
84=install.sh= script provided. If you want, you can (and should!) inspect
85the contents of the installation script before you run it.
86
87#+begin_src sh
88git clone --depth=1 https://github.com/Bash-it/bash-it.git ~/.bash_it
89~/.bash_it/install.sh
90#+end_src
91
92If you didn't restart your session after making bash the default, and
93are currently working within another shell, be sure to enter a bash
94session before using =bash-it=:
95
96#+begin_src sh
97bash
98#+end_src
99
100** Aliases
101
102Bash-it contains a number of aliases for common commands to help improve
103efficiency in the terminal. To list all available options, use the
104following command:
105
106#+begin_src sh
107bash-it show aliases
108#+end_src
109
110This will provide you a list that looks like the following text block.
111Within this screen, you will be able to see all available options and
112which ones are currently enabled.
113
114#+begin_src txt
115Alias Enabled? Description
116ag [ ] the silver searcher (ag) aliases
117ansible [ ] ansible abbreviations
118apt [ ] Apt and dpkg aliases for Ubuntu and Debian distros.
119atom [ ] Atom.io editor abbreviations
120bash-it [ ] Aliases for the bash-it command (these aliases are automatically included with the "general" aliases)
121bolt [ ] puppet bolt aliases
122bundler [ ] ruby bundler
123clipboard [ ] xclip shortcuts
124composer [ ] common composer abbreviations
125curl [x] Curl aliases for convenience.
126...
127#+end_src
128
129To enable an alias, do:
130
131#+begin_src sh
132bash-it enable alias <alias name> [alias name]... -or- $ bash-it enable alias all
133#+end_src
134
135To disable an alias, do:
136
137#+begin_src sh
138bash-it disable alias <alias name> [alias name]... -or- $ bash-it disable alias all
139#+end_src
140
141** Plugins
142
143Similar to aliases, plugins are available with bash-it. You can find a
144complete list of plugins in the same way as aliases. Simply execute the
145following:
146
147#+begin_src sh
148bash-it show plugins
149#+end_src
150
151You will see the following output showing enabled and disabled plugins:
152
153#+begin_src txt
154Plugin Enabled? Description
155alias-completion [ ]
156autojump [ ] Autojump configuration, see https://github.com/wting/autojump for more details
157aws [ ] AWS helper functions
158base [x] miscellaneous tools
159basher [ ] initializes basher, the shell package manager
160battery [x] display info about your battery charge level
161blesh [ ] load ble.sh, the Bash line editor!
162boot2docker [ ] Helpers to get Docker setup correctly for boot2docker
163browser [ ] render commandline output in your browser
164#+end_src
165
166To enable a plugin, do:
167
168#+begin_src sh
169bash-it enable plugin <plugin name> [plugin name]... -or- $ bash-it enable plugin all
170#+end_src
171
172To disable a plugin, do:
173
174#+begin_src sh
175bash-it disable plugin <plugin name> [plugin name]... -or- $ bash-it disable plugin all
176#+end_src
177
178** Themes
179
180There are quite a few pre-defined
181[[https://bash-it.readthedocs.io/en/latest/themes-list/#list-of-themes][themes]]
182available with bash-it.
183
184To list all themes:
185
186#+begin_src sh
187ls ~/.bash_it/themes/
188#+end_src
189
190To use a new theme, you'll need to edit =.bashrc= and alter the
191=BASH_IT_THEME= variable to your desired theme. For example, I am using
192the =zork= theme.
193
194#+begin_src sh
195nano ~/.bashrc
196#+end_src
197
198#+begin_src sh
199export BASH_IT_THEME='zork'
200#+end_src
201
202Once you save your changes, you just need to exit your terminal and
203create a new one in order to see your changes to the =.bashrc= file. You
204can also =source= the file to see changes, but I recommend starting a
205completely new shell instead.
206
207*** ble.sh
208
209One big feature I was missing in Bash that both =zsh= and =fish= have is
210an autosuggestion feature. To explain: as you type, an autosuggestion
211feature in the shell will offer suggestions in a lighter font color
212beyond the characters already typed. Once you see the command you want,
213you can click the right arrow and have the shell auto-complete that line
214for you.
215
216Luckily, the [[https://github.com/akinomyoga/ble.sh][Bash Line Editor]]
217(ble.sh) exists! This program provides a wonderful autosuggestions
218feature perfectly, among other features that I haven't tested yet.
219
220In order to install ble.sh, execute the following:
221
222#+begin_src sh
223git clone --recursive https://github.com/akinomyoga/ble.sh.git
224make -C ble.sh install PREFIX=~/.local
225echo 'source ~/.local/share/blesh/ble.sh' >> ~/.bashrc
226#+end_src
227
228Again, exit the terminal and open a new one in order to see the
229newly-configured shell.
230
231* Restart the Session
232
233Finally, as mentioned above, you'll need to restart the session to
234ensure that your user is using bash by default.
235
236You will also need to exit and re-open a shell (e.g., terminal or
237terminal tab) any time you make changes to the =.bashrc= file.
238
239#+begin_src sh
240sudo reboot now
241#+end_src
content/blog/2022-08-31-privacy-com-changes.md deleted −92
@@ -1,92 +0,0 @@
1+++
2date = 2022-08-31
3title = "Concerning Changes on Privacy.com"
4description = ""
5draft = false
6+++
7
8# Privacy.com Changes Their Terms
9
10Recently, Privacy.com reached out to their customers regarding a change in their
11terms of use. Further, all customers are required to agree to the changes in
12order to continue using their accounts.
13
14[You can view the new cardholder agreement
15here](https://privacy.com/commercial-cardholder-agreement).
16
17When you log in, you'll be greeted with a pop-up window asking you to review and
18agree to the new terms of use. You will also not be able to open any new cards
19until the terms are agreed to.
20
21## Changing from a "Prepaid Debit" Model to a "Charge Card" Model
22
23The actual content of the changes is interesting. While the historical model of
24using Privacy.com was akin to prepaid debit cards, the new model is very similar
25to a credit card (they use the term "charge card").
26
27I have used Privacy.com for 1-2 years, and the process was always that you would
28create a single-use or merchant-locked card. This card could be used for any
29dollar limit you set and would immediately draw the funds from whatever funding
30source you connected, e.g. PayPal account or a bank account.
31
32The benefit this service provides with a premium account is masking the merchant
33names from your funding source. If you have a concern that your bank account
34uses merchant data from your account, you could direct all charges through
35Privacy.com and set the merchant as one of their pre-set options, such as
36"Smiley's Corner Store" or "NSA Gift Shop."
37
38The new model still works with a bank account as a funding source, but the model
39is changed so that you get a "line of credit" set according to a 14-day billing
40cycle. It seems that Privacy.com will now allow charges to be incurred without
41being immediately paid.
42
43## Daily Payments and Available Credit
44
45Instead of paying as charges are incurred, you must make a "Daily Payment" and
46your "Available Credit" will be locked until you make that payment. There are
47also "End of Billing Cycle Payments" that are assigned a due date.
48
49Further, Privacy.com will decline charges that would cause you to exceed your
50Available Credit or Credit Limit.
51
52One particular interesting section states the following:
53
54> YOUR OBLIGATION TO PAY US BACK FOR ALL CARD TRANSACTIONS AND OTHER OBLIGATIONS
55> YOU INCUR IS SECURED BY THE SECURED ACCOUNT. IF YOU DO NOT PAY US BACK FOR ANY
56> AMOUNT YOU OWE US WHEN YOUR PAYMENTS ARE DUE, WE WILL EXERCISE OUR INTEREST
57> AND DEBIT THE SECURED ACCOUNT, AND YOU WILL LOSE THE MONEY IN THE SECURED
58> ACCOUNT. SEE SECTION 8: SECURITY AGREEMENT FOR MORE INFORMATION.
59
60## Personal Information
61
62Now that Privacy.com is more of a financial institution, they are obligated to
63comply with the [know your
64customer](https://en.wikipedia.org/wiki/Know_your_customer) guidelines/laws.
65
66I did not proceed with the change to my Privacy.com account, but I have heard
67from some peers that the changes require more personal information to be
68submitted, such as SSN. I am not aware of all new personal information required
69or if the funding source is now required to only be a bank account.
70
71## Fees
72
73Luckily, the fees section did not change much. The subscription fees for a
74premium account are still the only fees.
75
76# My Thoughts
77
78Personally, I wiped my personal information from my account and then permanently
79deleted it when I heard about these changes. I have no interest in yet another
80method of credit lending offered by private companies. While I accepted that
81they would have access to my bank account information for the purposes of paying
82off my prepaid debit payments, I have no interest in incurring charges that will
83need to be paid back at a later date. I also have no interest in submitting
84personal information to Privacy.com.
85
86This type of change toward a "buy it now, pay us later" model is concerning, and
87I will be watching Privacy.com to see if they further their interests in the
88credit model as time goes on.
89
90Could we see them start charging interest, fees, etc.? I'm not sure, but this
91change does not inspire confidence in their mission as a privacy-focused
92company.
content/blog/2022-08-31-privacy-com-changes.org added +99
@@ -0,0 +1,99 @@
1#+date: <2022-08-31>
2#+title: Concerning Changes on Privacy.com
3#+description:
4
5
6* Privacy.com Changes Their Terms
7
8Recently, Privacy.com reached out to their customers regarding a change
9in their terms of use. Further, all customers are required to agree to
10the changes in order to continue using their accounts.
11
12[[https://privacy.com/commercial-cardholder-agreement][You can view the
13new cardholder agreement here]].
14
15When you log in, you'll be greeted with a pop-up window asking you to
16review and agree to the new terms of use. You will also not be able to
17open any new cards until the terms are agreed to.
18
19** Changing from a "Prepaid Debit" Model to a "Charge Card" Model
20
21The actual content of the changes is interesting. While the historical
22model of using Privacy.com was akin to prepaid debit cards, the new
23model is very similar to a credit card (they use the term "charge
24card").
25
26I have used Privacy.com for 1-2 years, and the process was always that
27you would create a single-use or merchant-locked card. This card could
28be used for any dollar limit you set and would immediately draw the
29funds from whatever funding source you connected, e.g. PayPal account or
30a bank account.
31
32The benefit this service provides with a premium account is masking the
33merchant names from your funding source. If you have a concern that your
34bank account uses merchant data from your account, you could direct all
35charges through Privacy.com and set the merchant as one of their pre-set
36options, such as "Smiley's Corner Store" or "NSA Gift Shop."
37
38The new model still works with a bank account as a funding source, but
39the model is changed so that you get a "line of credit" set according to
40a 14-day billing cycle. It seems that Privacy.com will now allow charges
41to be incurred without being immediately paid.
42
43** Daily Payments and Available Credit
44
45Instead of paying as charges are incurred, you must make a "Daily
46Payment" and your "Available Credit" will be locked until you make that
47payment. There are also "End of Billing Cycle Payments" that are
48assigned a due date.
49
50Further, Privacy.com will decline charges that would cause you to exceed
51your Available Credit or Credit Limit.
52
53One particular interesting section states the following:
54
55#+begin_quote
56YOUR OBLIGATION TO PAY US BACK FOR ALL CARD TRANSACTIONS AND OTHER
57OBLIGATIONS YOU INCUR IS SECURED BY THE SECURED ACCOUNT. IF YOU DO NOT
58PAY US BACK FOR ANY AMOUNT YOU OWE US WHEN YOUR PAYMENTS ARE DUE, WE
59WILL EXERCISE OUR INTEREST AND DEBIT THE SECURED ACCOUNT, AND YOU WILL
60LOSE THE MONEY IN THE SECURED ACCOUNT. SEE SECTION 8: SECURITY AGREEMENT
61FOR MORE INFORMATION.
62#+end_quote
63
64** Personal Information
65
66Now that Privacy.com is more of a financial institution, they are
67obligated to comply with the
68[[https://en.wikipedia.org/wiki/Know_your_customer][know your customer]]
69guidelines/laws.
70
71I did not proceed with the change to my Privacy.com account, but I have
72heard from some peers that the changes require more personal information
73to be submitted, such as SSN. I am not aware of all new personal
74information required or if the funding source is now required to only be
75a bank account.
76
77** Fees
78
79Luckily, the fees section did not change much. The subscription fees for
80a premium account are still the only fees.
81
82* My Thoughts
83
84Personally, I wiped my personal information from my account and then
85permanently deleted it when I heard about these changes. I have no
86interest in yet another method of credit lending offered by private
87companies. While I accepted that they would have access to my bank
88account information for the purposes of paying off my prepaid debit
89payments, I have no interest in incurring charges that will need to be
90paid back at a later date. I also have no interest in submitting
91personal information to Privacy.com.
92
93This type of change toward a "buy it now, pay us later" model is
94concerning, and I will be watching Privacy.com to see if they further
95their interests in the credit model as time goes on.
96
97Could we see them start charging interest, fees, etc.? I'm not sure, but
98this change does not inspire confidence in their mission as a
99privacy-focused company.
content/blog/2022-09-17-serenity-os.md deleted −104
@@ -1,104 +0,0 @@
1+++
2date = 2022-09-17
3title = "Serenity OS: Testing Out a Unique System"
4description = ""
5draft = false
6+++
7
8# Overview
9
10[SerenityOS](https://serenityos.org) is a unique operating system (OS) that I
11have seen pop up in my news feed a few times over the last few years, but I have
12never had time to test it out until now.
13
14Testing out this system brought back fond memories of yellowed,
15modem-screeching, 100-pound computers that brought so many fond memories to my
16youth.
17
18Per their website:
19
20> A graphical Unix-like operating system for desktop computers!
21>
22> SerenityOS is a love letter to '90s user interfaces with a custom Unix-like
23> core. It flatters with sincerity by stealing beautiful ideas from various
24> other systems.
25>
26> Roughly speaking, the goal is a marriage between the aesthetic of late-1990s
27> productivity software and the power-user accessibility of late-2000s \*nix.
28>
29> This is a system by us, for us, based on the things we like.
30
31# Building
32
33Your first question may be "Where's the iso?" and the answer is... there are
34none. SerenityOS does not provide pre-built images for testing. You must build
35the images yourself. This seems intentionally to limit participation to only
36those who are truly interested enough to learn how to build the OS.
37
38## Clone
39
40In order to get started, you'll need to clone the source repository:
41
42```sh
43git clone https://github.com/SerenityOS/serenity && cd serenity
44```
45
46## Build
47
48Note that I followed the [Build
49Instructions](https://github.com/SerenityOS/serenity/blob/master/Documentation/BuildInstructions.md)
50in the SerenityOS repository as of commit
51`660d2b53b1206e868d5470eee80b5e62d7e30da7`. Things may have changed since my
52installation, and you should double-check the instructions first.
53
54Regardless, I want to repeat my steps here to illustrate any errors or differing
55commands I needed to run in order to build and run SerenityOS.
56
57Since I am running Fedora, I needed to install these packages in order to build
58the OS images:
59
60```sh
61sudo dnf install texinfo binutils-devel curl cmake mpfr-devel libmpc-devel gmp-devel e2fsprogs ninja-build patch ccache rsync @"C Development Tools and Libraries" @Virtualization
62```
63
64Next, make sure you're inside the `serenity` directory created earlier during
65the git cloning process and process to build the toolchain:
66
67```sh
68Meta/serenity.sh rebuild-toolchain
69```
70
71Once the toolchain is built, you can Build and run the OS!
72
73```sh
74Meta/serenity.sh run
75```
76
77After this process is completed, the image should run automatically and launch.
78
79# Issues
80
81I played around in SerenityOS for an hour or two in order to see what I could do
82and had a lot of fun with it. The only issue I ran into was a lack of working
83internet. I didn't try very hard, but I could tell that the main network link
84wasn't connecting to my Fedora host properly.
85
86# Screenshots
87
88The initial launch of the image displays the SerenityOS desktop, with a simple
89terminal already launched.
90
91There is also a "Fire" application (literally just shows fire burning), a
92browser with the local Serenity Browser page loaded, and a text editor.
93
94I also poked around the system utilities and found most tools you'd expect to
95find within a standard desktop, such as a system monitoring tool.
96
97Lastly, I noted that the default desktop contains numerous pre-defined themes to
98choose from. This is a small piece, but it's actually wonderful to see desktop
99developers consider theming directly out of the box rather than using an
100addon-based mentality.
101
102I didn't take a screenshot of the other pre-installed games, but I did spend
103nearly 30 minutes playing Solitaire before remembering that I was supposed to be
104writing a post about the OS.
content/blog/2022-09-17-serenity-os.org added +110
@@ -0,0 +1,110 @@
1#+date: <2022-09-17>
2#+title: Serenity OS: Testing Out a Unique System
3#+description:
4
5
6* Overview
7
8[[https://serenityos.org][SerenityOS]] is a unique operating system (OS)
9that I have seen pop up in my news feed a few times over the last few
10years, but I have never had time to test it out until now.
11
12Testing out this system brought back fond memories of yellowed,
13modem-screeching, 100-pound computers that brought so many fond memories
14to my youth.
15
16Per their website:
17
18#+begin_quote
19A graphical Unix-like operating system for desktop computers!
20
21SerenityOS is a love letter to '90s user interfaces with a custom
22Unix-like core. It flatters with sincerity by stealing beautiful ideas
23from various other systems.
24
25Roughly speaking, the goal is a marriage between the aesthetic of
26late-1990s productivity software and the power-user accessibility of
27late-2000s *nix.
28
29This is a system by us, for us, based on the things we like.
30#+end_quote
31
32* Building
33
34Your first question may be "Where's the iso?" and the answer is... there
35are none. SerenityOS does not provide pre-built images for testing. You
36must build the images yourself. This seems intentionally to limit
37participation to only those who are truly interested enough to learn how
38to build the OS.
39
40** Clone
41
42In order to get started, you'll need to clone the source repository:
43
44#+begin_src sh
45git clone https://github.com/SerenityOS/serenity && cd serenity
46#+end_src
47
48** Build
49
50Note that I followed the
51[[https://github.com/SerenityOS/serenity/blob/master/Documentation/BuildInstructions.md][Build
52Instructions]] in the SerenityOS repository as of commit
53=660d2b53b1206e868d5470eee80b5e62d7e30da7=. Things may have changed
54since my installation, and you should double-check the instructions
55first.
56
57Regardless, I want to repeat my steps here to illustrate any errors or
58differing commands I needed to run in order to build and run SerenityOS.
59
60Since I am running Fedora, I needed to install these packages in order
61to build the OS images:
62
63#+begin_src sh
64sudo dnf install texinfo binutils-devel curl cmake mpfr-devel libmpc-devel gmp-devel e2fsprogs ninja-build patch ccache rsync @"C Development Tools and Libraries" @Virtualization
65#+end_src
66
67Next, make sure you're inside the =serenity= directory created earlier
68during the git cloning process and process to build the toolchain:
69
70#+begin_src sh
71Meta/serenity.sh rebuild-toolchain
72#+end_src
73
74Once the toolchain is built, you can Build and run the OS!
75
76#+begin_src sh
77Meta/serenity.sh run
78#+end_src
79
80After this process is completed, the image should run automatically and
81launch.
82
83* Issues
84
85I played around in SerenityOS for an hour or two in order to see what I
86could do and had a lot of fun with it. The only issue I ran into was a
87lack of working internet. I didn't try very hard, but I could tell that
88the main network link wasn't connecting to my Fedora host properly.
89
90* Screenshots
91
92The initial launch of the image displays the SerenityOS desktop, with a
93simple terminal already launched.
94
95There is also a "Fire" application (literally just shows fire burning),
96a browser with the local Serenity Browser page loaded, and a text
97editor.
98
99I also poked around the system utilities and found most tools you'd
100expect to find within a standard desktop, such as a system monitoring
101tool.
102
103Lastly, I noted that the default desktop contains numerous pre-defined
104themes to choose from. This is a small piece, but it's actually
105wonderful to see desktop developers consider theming directly out of the
106box rather than using an addon-based mentality.
107
108I didn't take a screenshot of the other pre-installed games, but I did
109spend nearly 30 minutes playing Solitaire before remembering that I was
110supposed to be writing a post about the OS.
content/blog/2022-09-21-graphene-os.md deleted −159
@@ -1,159 +0,0 @@
1+++
2date = 2022-09-21
3title = "Installing Graphene OS on the Pixel 6 Pro"
4description = ""
5draft = false
6+++
7
8# Introduction
9
10After using iOS for a couple of years, I finally took the plunge and purchased a
11Pixel 6 Pro in order to test and use [GrapheneOS] (<https://grapheneos.org>).
12
13The installation process was rather quick once you have the tools and files you
14need. Overall, it can be done in just a few minutes.
15
16# Gathering Tools & Files
17
18## Android Tools
19
20First, in order to interact with the device, we will need the [Android platform
21tools](https://developer.android.com/studio/releases/platform-tools.html). Find
22the Linux download and save the ZIP folder to your preferred location.
23
24Once we've downloaded the files, we will need to unzip them, enter the
25directory, and move the necessary executables to a central location, such as
26`/usr/bin/`. For this installation, we only need the `fastboot` and `adb`
27executables.
28
29```sh
30cd ~/Downloads
31```
32
33```sh
34unzip platform-tools_r33.0.3-linux.zip
35cd platform-tools
36sudo mv fastboot /usr/bin/
37sudo mv adb /usr/bin
38```
39
40## GrapheneOS Files
41
42Next, we need the [GrapheneOS files](https://grapheneos.org/releases) for our
43device and model. For example, the Pixel 6 Pro is codenamed `raven` on the
44release page.
45
46Once we have the links, let's download them to our working directory:
47
48```sh
49curl -O https://releases.grapheneos.org/factory.pub
50curl -0 https://releases.grapheneos.org/raven-factory-2022091400.zip
51curl -0 https://releases.grapheneos.org/raven-factory-2022091400.zip.sig
52```
53
541. Validate Integrity
55
56 In order to validate the integrity of the downloaded files, we will need the
57 `signify` package and Graphene's `factory.pub` file.
58
59 ```sh
60 sudo dnf install signify
61 ```
62
63 ```sh
64 curl -O https://releases.grapheneos.org/factory.pub
65 ```
66
67 Then we can validate the files and ensure that no data was corrupted or
68 modified before it was saved to our device.
69
70 ```sh
71 signify -Cqp factory.pub -x raven-factory-2022091400.zip.sig && echo verified
72 ```
73
742. Unzip Files
75
76 Once the files are verified, we can unzip the Graphene image and enter the
77 directory:
78
79 ```sh
80 unzip raven-factory-2022091400.zip && cd raven-factory-2022091400
81 ```
82
83# Installation Process
84
85## Enable Developer Debugging & OEM Unlock
86
87Before we can actually flash anything to the phone, we will need to enable OEM
88Unlocking, as well as either USB Debugging or Wireless Debugging, depending on
89which method we will be using.
90
91To start, enable developer mode by going to `Settings` > `About` and tapping
92`Build Number` seven (7) times. You may need to enter your PIN to enable this
93mode.
94
95Once developer mode is enabled, go to `Settings` > `System` > `Devloper Options`
96and enable OEM Unlocking, as well as USB or Wireless Debugging. In my case, I
97chose USB Debugging and performed all actions via USB cable.
98
99Once these options are enabled, plug the phone into the computer and execute the
100following command:
101
102```sh
103adb devices
104```
105
106If an unauthorized error occurs, make sure the USB mode on the phone is changed
107from charging to something like "File Transfer" or "PTP." You can find the USB
108mode in the notification tray.
109
110## Reboot Device
111
112Once we have found the device via `adb`, we can either boot into the bootloader
113interface by holding the volume down button while the phone reboots or by
114executing the following command:
115
116```sh
117adb reboot bootloader
118```
119
120## Unlock the Bootloader
121
122The phone will reboot and load the bootloader screen upon startup. At this
123point, we are ready to start the actual flashing of GrapheneOS onto the device.
124
125**NOTE**: In my situation, I needed to use `sudo` with every `fastboot` command,
126but not with `adb` commands. I am not sure if this is standard or a Fedora
127quirk, but I'm documenting my commands verbatim in this post.
128
129First, we start by unlocking the bootloader so that we can load other ROMs:
130
131```sh
132sudo fastboot flashing unlock
133```
134
135## Flashing Factory Images
136
137Once the phone is unlocked, we can flash it with the `flash-all.sh` script found
138inside the `raven-factory-2022091400` folder we entered earlier:
139
140```sh
141sudo ./flash-all.sh
142```
143
144This process should take a few minutes and will print informational messages as
145things progress. Avoid doing anything on the phone while this process is
146operating.
147
148## Lock the Bootloader
149
150If everything was successful, the phone should reboot a few times and finally
151land back on the bootloader screen. At this point, we can re-lock the bootloader
152to enable full verified boot and protect the device from unwanted flashing or
153erasure of data.
154
155```sh
156sudo fastboot flashing lock
157```
158
159Once done, the device will be wiped and ready for a fresh set-up!
content/blog/2022-09-21-graphene-os.org added +164
@@ -0,0 +1,164 @@
1#+date: <2022-09-21>
2#+title: Installing Graphene OS on the Pixel 6 Pro
3#+description:
4
5
6* Introduction
7
8After using iOS for a couple of years, I finally took the plunge and
9purchased a Pixel 6 Pro in order to test and use [GrapheneOS]
10([[https://grapheneos.org]]).
11
12The installation process was rather quick once you have the tools and
13files you need. Overall, it can be done in just a few minutes.
14
15* Gathering Tools & Files
16
17** Android Tools
18
19First, in order to interact with the device, we will need the
20[[https://developer.android.com/studio/releases/platform-tools.html][Android
21platform tools]]. Find the Linux download and save the ZIP folder to
22your preferred location.
23
24Once we've downloaded the files, we will need to unzip them, enter the
25directory, and move the necessary executables to a central location,
26such as =/usr/bin/=. For this installation, we only need the =fastboot=
27and =adb= executables.
28
29#+begin_src sh
30cd ~/Downloads
31#+end_src
32
33#+begin_src sh
34unzip platform-tools_r33.0.3-linux.zip
35cd platform-tools
36sudo mv fastboot /usr/bin/
37sudo mv adb /usr/bin
38#+end_src
39
40** GrapheneOS Files
41
42Next, we need the [[https://grapheneos.org/releases][GrapheneOS files]]
43for our device and model. For example, the Pixel 6 Pro is codenamed
44=raven= on the release page.
45
46Once we have the links, let's download them to our working directory:
47
48#+begin_src sh
49curl -O https://releases.grapheneos.org/factory.pub
50curl -0 https://releases.grapheneos.org/raven-factory-2022091400.zip
51curl -0 https://releases.grapheneos.org/raven-factory-2022091400.zip.sig
52#+end_src
53
541. Validate Integrity
55
56 In order to validate the integrity of the downloaded files, we will
57 need the =signify= package and Graphene's =factory.pub= file.
58
59 #+begin_src sh
60 sudo dnf install signify
61 #+end_src
62
63 #+begin_src sh
64 curl -O https://releases.grapheneos.org/factory.pub
65 #+end_src
66
67 Then we can validate the files and ensure that no data was corrupted
68 or modified before it was saved to our device.
69
70 #+begin_src sh
71 signify -Cqp factory.pub -x raven-factory-2022091400.zip.sig && echo verified
72 #+end_src
73
742. Unzip Files
75
76 Once the files are verified, we can unzip the Graphene image and
77 enter the directory:
78
79 #+begin_src sh
80 unzip raven-factory-2022091400.zip && cd raven-factory-2022091400
81 #+end_src
82
83* Installation Process
84
85** Enable Developer Debugging & OEM Unlock
86
87Before we can actually flash anything to the phone, we will need to
88enable OEM Unlocking, as well as either USB Debugging or Wireless
89Debugging, depending on which method we will be using.
90
91To start, enable developer mode by going to =Settings= > =About= and
92tapping =Build Number= seven (7) times. You may need to enter your PIN
93to enable this mode.
94
95Once developer mode is enabled, go to =Settings= > =System= >
96=Devloper Options= and enable OEM Unlocking, as well as USB or Wireless
97Debugging. In my case, I chose USB Debugging and performed all actions
98via USB cable.
99
100Once these options are enabled, plug the phone into the computer and
101execute the following command:
102
103#+begin_src sh
104adb devices
105#+end_src
106
107If an unauthorized error occurs, make sure the USB mode on the phone is
108changed from charging to something like "File Transfer" or "PTP." You
109can find the USB mode in the notification tray.
110
111** Reboot Device
112
113Once we have found the device via =adb=, we can either boot into the
114bootloader interface by holding the volume down button while the phone
115reboots or by executing the following command:
116
117#+begin_src sh
118adb reboot bootloader
119#+end_src
120
121** Unlock the Bootloader
122
123The phone will reboot and load the bootloader screen upon startup. At
124this point, we are ready to start the actual flashing of GrapheneOS onto
125the device.
126
127*NOTE*: In my situation, I needed to use =sudo= with every =fastboot=
128command, but not with =adb= commands. I am not sure if this is standard
129or a Fedora quirk, but I'm documenting my commands verbatim in this
130post.
131
132First, we start by unlocking the bootloader so that we can load other
133ROMs:
134
135#+begin_src sh
136sudo fastboot flashing unlock
137#+end_src
138
139** Flashing Factory Images
140
141Once the phone is unlocked, we can flash it with the =flash-all.sh=
142script found inside the =raven-factory-2022091400= folder we entered
143earlier:
144
145#+begin_src sh
146sudo ./flash-all.sh
147#+end_src
148
149This process should take a few minutes and will print informational
150messages as things progress. Avoid doing anything on the phone while
151this process is operating.
152
153** Lock the Bootloader
154
155If everything was successful, the phone should reboot a few times and
156finally land back on the bootloader screen. At this point, we can
157re-lock the bootloader to enable full verified boot and protect the
158device from unwanted flashing or erasure of data.
159
160#+begin_src sh
161sudo fastboot flashing lock
162#+end_src
163
164Once done, the device will be wiped and ready for a fresh set-up!
content/blog/2022-10-04-mtp-linux.md deleted −78
@@ -1,78 +0,0 @@
1+++
2date = 2022-10-04
3title = "How to Mount an MTP Mobile Device on Fedora Linux"
4description = ""
5draft = false
6+++
7
8I recently ran into trouble attempting to mount my GrapheneOS phone to my laptop
9running Fedora Linux via the [Media Transfer
10Protocol](https://en.wikipedia.org/wiki/Media_transfer_protocol) (MTP) and
11discovered a simple and effective solution.
12
13# Use a USB 3.0 Port
14
15First, ensure that the device was plugged in to the laptop through a USB 3.0
16port, if possible. From a brief glance online, it seems that USB 2.0 ports may
17cause issues with dropped connections over MTP. This is purely anecdotal since I
18don't have any evidence to link showing that USB 2.0 causes issues, but I can
19confirm that switching to a USB 3.0 port seemed to cut out most of my issues.
20
21# Switch USB Preferences to MTP
22
23Secondly, you need to ensure that the phone's USB preferences/mode is changed to
24MTP or File Transfer once the phone is plugged in. Other modes will not allow
25you to access the phone's file system.
26
27# Install `jmtpfs`
28
29Next, I used the `jmtpfs` package to mount my phone to my laptop. There are
30other packages that exist, but this one worked perfectly for me. On Fedora
31Linux, you can install it like this:
32
33```sh
34sudo dnf install jmtpfs -y
35```
36
37# Create a Mount Point
38
39Once you have the package installed, you just need to create a folder for the
40device to use as a mount point. In my case, I used `/mnt/pixel`:
41
42```sh
43sudo mkdir /mnt/pixel
44sudo chown -R $USER:$USER /mnt/pixel
45```
46
47# Mount & Access the Phone's File System
48
49Finally, plug-in and mount the device, and you should be able to see all storage
50(internal and external) inside your new folder!
51
52```sh
53jmtpfs /mnt/pixel
54```
55
56The output should look something like this:
57
58```sh
59Device 0 (VID=18d1 and PID=4ee1) is a Google Inc Nexus/Pixel (MTP).
60Android device detected, assigning default bug flags
61```
62
63Now you are mounted and can do anything you'd like with the device's files:
64
65```sh
66cd /mnt/pixel
67ls -lha
68```
69
70From here, you will be able to see any internal or external storage available on
71the device:
72
73```sh
74total 0
75drwxr-xr-x. 3 user user 0 Jan 1 1970 .
76drwxr-xr-x. 1 root root 10 Oct 4 13:29 ..
77drwxr-xr-x. 16 user user 0 Apr 21 4426383 'Internal shared storage'
78```
content/blog/2022-10-04-mtp-linux.org added +78
@@ -0,0 +1,78 @@
1#+date: <2022-10-04>
2#+title: How to Mount an MTP Mobile Device on Fedora Linux
3#+description:
4
5
6I recently ran into trouble attempting to mount my GrapheneOS phone to
7my laptop running Fedora Linux via the
8[[https://en.wikipedia.org/wiki/Media_transfer_protocol][Media Transfer
9Protocol]] (MTP) and discovered a simple and effective solution.
10
11* Use a USB 3.0 Port
12
13First, ensure that the device was plugged in to the laptop through a USB
143.0 port, if possible. From a brief glance online, it seems that USB 2.0
15ports may cause issues with dropped connections over MTP. This is purely
16anecdotal since I don't have any evidence to link showing that USB 2.0
17causes issues, but I can confirm that switching to a USB 3.0 port seemed
18to cut out most of my issues.
19
20* Switch USB Preferences to MTP
21
22Secondly, you need to ensure that the phone's USB preferences/mode is
23changed to MTP or File Transfer once the phone is plugged in. Other
24modes will not allow you to access the phone's file system.
25
26* Install =jmtpfs=
27
28Next, I used the =jmtpfs= package to mount my phone to my laptop. There
29are other packages that exist, but this one worked perfectly for me. On
30Fedora Linux, you can install it like this:
31
32#+begin_src sh
33sudo dnf install jmtpfs -y
34#+end_src
35
36* Create a Mount Point
37
38Once you have the package installed, you just need to create a folder
39for the device to use as a mount point. In my case, I used =/mnt/pixel=:
40
41#+begin_src sh
42sudo mkdir /mnt/pixel
43sudo chown -R $USER:$USER /mnt/pixel
44#+end_src
45
46* Mount & Access the Phone's File System
47
48Finally, plug-in and mount the device, and you should be able to see all
49storage (internal and external) inside your new folder!
50
51#+begin_src sh
52jmtpfs /mnt/pixel
53#+end_src
54
55The output should look something like this:
56
57#+begin_src sh
58Device 0 (VID=18d1 and PID=4ee1) is a Google Inc Nexus/Pixel (MTP).
59Android device detected, assigning default bug flags
60#+end_src
61
62Now you are mounted and can do anything you'd like with the device's
63files:
64
65#+begin_src sh
66cd /mnt/pixel
67ls -lha
68#+end_src
69
70From here, you will be able to see any internal or external storage
71available on the device:
72
73#+begin_src sh
74total 0
75drwxr-xr-x. 3 user user 0 Jan 1 1970 .
76drwxr-xr-x. 1 root root 10 Oct 4 13:29 ..
77drwxr-xr-x. 16 user user 0 Apr 21 4426383 'Internal shared storage'
78#+end_src
content/blog/2022-10-04-syncthing.md deleted −169
@@ -1,169 +0,0 @@
1+++
2date = 2022-10-04
3title = "Syncthing: A Minimal Self-Hosted Cloud Storage Solution"
4description = ""
5draft = false
6+++
7
8# An Overview of Syncthing
9
10If you've been looking around the self-hosted cloud storage space for a while,
11you've undoubtedly run into someone suggesting
12[Syncthing](https://syncthing.net) as an option. However, it is an unusual
13alternative for those users out there who are used to having a centralized cloud
14server that serves as the "controller" of the data and interacts with clients on
15devices to fetch files.
16
17This post is a walkthrough of the Syncthing software, how I set up my personal
18storage, and some pros and cons of using the software.
19
20# Installing Syncthing
21
22To install Syncthing, visit the [Downloads](https://syncthing.net/downloads/)
23page or install via your device's package manager.
24
25## Server & Desktop
26
27You can install Syncthing on servers and desktops via the Downloads page linked
28above or via the command-line.
29
30For Debian-based distros:
31
32```sh
33sudo apt install syncthing
34```
35
36For Fedora-based distros:
37
38```sh
39sudo dnf install syncthing
40```
41
42## Mobile
43
44Syncthing for Android is available on
45[F-Droid](https://f-droid.org/packages/com.nutomic.syncthingandroid/) and
46[Google
47Play](https://play.google.com/store/apps/details?id=com.nutomic.syncthingandroid).
48Syncthing does not have an official iOS client, but there is a third-party
49client called [Möbius
50Sync](https://apps.apple.com/us/app/m%C3%B6bius-sync/id1539203216).
51
52# How Does Syncthing Work?
53
54To start, I wanted to include the main marketing blurb from their website:
55
56> Syncthing is a continuous file synchronization program. It synchronizes files
57> between two or more computers in real time, safely protected from prying eyes.
58> Your data is your data alone, and you deserve to choose where it is stored,
59> whether it is shared with some third party, and how it's transmitted over the
60> internet.
61
62Let's break this apart and add in some other details to help explain what
63exactly Syncthing does in order to sync files between devices.
64
65## Local Syncthing Server(s)
66
67Syncthing syncs files between multiple devices by creating a local server on
68each device. These local servers handle a few different things, such as watching
69files and directories for changes, hosting an administrative GUI website, and
70authenticating with connected devices.
71
72You can also start, stop, and restart the Syncthing server via the command-line
73or web dashboard. If you're running Syncthing on a device with `systemd`, you
74can use the following commands:
75
76```sh
77sudo systemctl start syncthing@username.service
78sudo systemctl restart syncthing@username.service
79sudo systemctl stop syncthing@username.service
80```
81
82## Syncthing Dashboard
83
84This biggest part of Syncthing is the admin GUI website that runs on each device
85(note that mobile devices will use the Syncthing app rather than the web GUI).
86The admin GUI is available through the web browser on the local device that is
87running Syncthing - simply go to `http://localhost:8384` or
88`http://127.0.0.1:8384`. This web page is the place where you will change
89settings, add/modify synced files, and add/modify connected devices.
90
91Here's an example web GUI dashboard:
92
93![Syncthing
94Dashboard](https://img.cleberg.net/blog/20221020-syncthing/syncthing_gui.png)
95
96## Remote Devices
97
98A cloud storage solution wouldn't be very useful if you aren't able to share
99data among various devices. Syncthing does this by sharing Device IDs to connect
100servers, and then by manually sharing Folders with devices that have been
101connected.
102
103For instance, if you have a laptop running Syncthing and then install the
104Syncthing mobile app on a phone, you could scan the laptop's QR code for Device
105ID and then accept the authentication on the laptop's dashboard. Next, you can
106use either device to select a folder for sharing and dictating which device
107should send, receive, or both.
108
109When you connect devices, you can set one device as an "Introducer," which can
110add devices from the introducer to the device list, for mutually shared folders.
111You can also configure Auto Accept, compression, rate limits, and more settings
112per device.
113
114# My Personal Cloud Storage Set-up
115
116Personally, I use a model similar to a traditional cloud storage service. I have
117a "centralized" server running 24/7 that acts as an Introducer for my Syncthing
118network. I think of this as my main storage and all other devices as tertiary
119client devices. I will likely add additional servers as backups as time goes on
120so that I don't have to rely on my laptop or phone as the only backups.
121
122Currently, I have one desktop and one mobile device connected to the network,
123both running intermittently as they are not powered-on 24/7.
124
125The initial set-up of the software was easy enough, but data transfer rates were
126incredibly slow for me due to the Wi-Fi. Instead, I plugged my laptop into the
127ethernet network that my server is on and manually copied my folders over to the
128server with `scp`. Once complete, Syncthing validated that all files were there
129and not missing, and it did not need to transfer any data through the WAN.
130
131As slow as the transfer was going, this probably saved me a few days of waiting
132for my ~100GB sync.
133
134# Pros & Cons
135
136I've put together a short list of pros and cons for Syncthing. I thought about
137my experiences with Nextcloud, WebDAV, proprietary services (Google Drive,
138iCloud, etc.), and privacy-focused cloud solutions (pCloud, Tresorit, etc.).
139
140**Pros:**
141
142- I've faced no data loss at all through my two-month trial run.
143- No third-parties store your data on their servers.
144- You have full control over your data and can take your data and leave at any
145 time.
146- It's possible to encrypt client-side easily with software like Cryptomator.
147- No proprietary clients or mounted volumes, just plain files and folders.
148
149**Cons:**
150
151- The learning curve is steeper than traditional cloud services and is focused
152 on a technical audience.
153- If a device needs to modify files in a Folder, the devices will need to sync
154 ALL files from the folder, which may be large. To avoid size restraints,
155 split large folders into smaller folders for syncing.
156- Syncing can be slow due to the clients/servers initially connecting or
157 re-connecting after sleeping.
158- Multiple personal devices are required and require the user to own or rent
159 them as no third-party servers are involved in the storage of data.
160
161Overall, I've had a great experience with Syncthing so far. I've had no data
162loss, syncing has been quick and easy when changes are made to files, device
163connections are reliable, and I love the freedom of controlling the clients and
164servers as I choose.
165
166Not to mention that I appreciate that I - or someone else - could pull the
167Syncthing [source code](https://github.com/syncthing) and continue
168development/support if the Syncthing Foundation decides to stop developing the
169software or sells the business.
content/blog/2022-10-04-syncthing.org added +179
@@ -0,0 +1,179 @@
1#+date: <2022-10-04>
2#+title: Syncthing: A Minimal Self-Hosted Cloud Storage Solution
3#+description:
4
5
6* An Overview of Syncthing
7
8If you've been looking around the self-hosted cloud storage space for a
9while, you've undoubtedly run into someone suggesting
10[[https://syncthing.net][Syncthing]] as an option. However, it is an
11unusual alternative for those users out there who are used to having a
12centralized cloud server that serves as the "controller" of the data and
13interacts with clients on devices to fetch files.
14
15This post is a walkthrough of the Syncthing software, how I set up my
16personal storage, and some pros and cons of using the software.
17
18* Installing Syncthing
19
20To install Syncthing, visit the
21[[https://syncthing.net/downloads/][Downloads]] page or install via your
22device's package manager.
23
24** Server & Desktop
25
26You can install Syncthing on servers and desktops via the Downloads page
27linked above or via the command-line.
28
29For Debian-based distros:
30
31#+begin_src sh
32sudo apt install syncthing
33#+end_src
34
35For Fedora-based distros:
36
37#+begin_src sh
38sudo dnf install syncthing
39#+end_src
40
41** Mobile
42
43Syncthing for Android is available on
44[[https://f-droid.org/packages/com.nutomic.syncthingandroid/][F-Droid]]
45and
46[[https://play.google.com/store/apps/details?id=com.nutomic.syncthingandroid][Google
47Play]]. Syncthing does not have an official iOS client, but there is a
48third-party client called
49[[https://apps.apple.com/us/app/m%C3%B6bius-sync/id1539203216][Möbius
50Sync]].
51
52* How Does Syncthing Work?
53
54To start, I wanted to include the main marketing blurb from their
55website:
56
57#+begin_quote
58Syncthing is a continuous file synchronization program. It synchronizes
59files between two or more computers in real time, safely protected from
60prying eyes. Your data is your data alone, and you deserve to choose
61where it is stored, whether it is shared with some third party, and how
62it's transmitted over the internet.
63#+end_quote
64
65Let's break this apart and add in some other details to help explain
66what exactly Syncthing does in order to sync files between devices.
67
68** Local Syncthing Server(s)
69
70Syncthing syncs files between multiple devices by creating a local
71server on each device. These local servers handle a few different
72things, such as watching files and directories for changes, hosting an
73administrative GUI website, and authenticating with connected devices.
74
75You can also start, stop, and restart the Syncthing server via the
76command-line or web dashboard. If you're running Syncthing on a device
77with =systemd=, you can use the following commands:
78
79#+begin_src sh
80sudo systemctl start syncthing@username.service
81sudo systemctl restart syncthing@username.service
82sudo systemctl stop syncthing@username.service
83#+end_src
84
85** Syncthing Dashboard
86
87This biggest part of Syncthing is the admin GUI website that runs on
88each device (note that mobile devices will use the Syncthing app rather
89than the web GUI). The admin GUI is available through the web browser on
90the local device that is running Syncthing - simply go to
91=http://localhost:8384= or =http://127.0.0.1:8384=. This web page is the
92place where you will change settings, add/modify synced files, and
93add/modify connected devices.
94
95Here's an example web GUI dashboard:
96
97#+caption: Syncthing Dashboard
98[[https://img.cleberg.net/blog/20221020-syncthing/syncthing_gui.png]]
99
100** Remote Devices
101
102A cloud storage solution wouldn't be very useful if you aren't able to
103share data among various devices. Syncthing does this by sharing Device
104IDs to connect servers, and then by manually sharing Folders with
105devices that have been connected.
106
107For instance, if you have a laptop running Syncthing and then install
108the Syncthing mobile app on a phone, you could scan the laptop's QR code
109for Device ID and then accept the authentication on the laptop's
110dashboard. Next, you can use either device to select a folder for
111sharing and dictating which device should send, receive, or both.
112
113When you connect devices, you can set one device as an "Introducer,"
114which can add devices from the introducer to the device list, for
115mutually shared folders. You can also configure Auto Accept,
116compression, rate limits, and more settings per device.
117
118* My Personal Cloud Storage Set-up
119
120Personally, I use a model similar to a traditional cloud storage
121service. I have a "centralized" server running 24/7 that acts as an
122Introducer for my Syncthing network. I think of this as my main storage
123and all other devices as tertiary client devices. I will likely add
124additional servers as backups as time goes on so that I don't have to
125rely on my laptop or phone as the only backups.
126
127Currently, I have one desktop and one mobile device connected to the
128network, both running intermittently as they are not powered-on 24/7.
129
130The initial set-up of the software was easy enough, but data transfer
131rates were incredibly slow for me due to the Wi-Fi. Instead, I plugged
132my laptop into the ethernet network that my server is on and manually
133copied my folders over to the server with =scp=. Once complete,
134Syncthing validated that all files were there and not missing, and it
135did not need to transfer any data through the WAN.
136
137As slow as the transfer was going, this probably saved me a few days of
138waiting for my ~100GB sync.
139
140* Pros & Cons
141
142I've put together a short list of pros and cons for Syncthing. I thought
143about my experiences with Nextcloud, WebDAV, proprietary services
144(Google Drive, iCloud, etc.), and privacy-focused cloud solutions
145(pCloud, Tresorit, etc.).
146
147*Pros:*
148
149- I've faced no data loss at all through my two-month trial run.
150- No third-parties store your data on their servers.
151- You have full control over your data and can take your data and leave
152 at any time.
153- It's possible to encrypt client-side easily with software like
154 Cryptomator.
155- No proprietary clients or mounted volumes, just plain files and
156 folders.
157
158*Cons:*
159
160- The learning curve is steeper than traditional cloud services and is
161 focused on a technical audience.
162- If a device needs to modify files in a Folder, the devices will need
163 to sync ALL files from the folder, which may be large. To avoid size
164 restraints, split large folders into smaller folders for syncing.
165- Syncing can be slow due to the clients/servers initially connecting or
166 re-connecting after sleeping.
167- Multiple personal devices are required and require the user to own or
168 rent them as no third-party servers are involved in the storage of
169 data.
170
171Overall, I've had a great experience with Syncthing so far. I've had no
172data loss, syncing has been quick and easy when changes are made to
173files, device connections are reliable, and I love the freedom of
174controlling the clients and servers as I choose.
175
176Not to mention that I appreciate that I - or someone else - could pull
177the Syncthing [[https://github.com/syncthing][source code]] and continue
178development/support if the Syncthing Foundation decides to stop
179developing the software or sells the business.
content/blog/2022-10-22-alpine-linux.md deleted −266
@@ -1,266 +0,0 @@
1+++
2date = 2022-10-22
3title = "Alpine Linux: My New Server OS"
4description = ""
5draft = false
6+++
7
8# Alpine Linux
9
10[Alpine Linux](https://alpinelinux.org) is a very small distro, built on musl
11libc and busybox. It uses ash as the default shell, OpenRC as the init system,
12and apk as the package manager. According to their website, an Alpine container
13"requires no more than 8 MB and a minimal installation to disk requires around
14130 MB of storage." An actual bare metal machine is recommended to have 100 MB
15of RAM and 0-700 MB of storage space.
16
17Historically, I've used Ubuntu's minimal installation image as my server OS for
18the last five years. Ubuntu worked well and helped as my original server
19contained an nVidia GPU and no onboard graphics, so quite a few distros won't
20boot or install without a lot of tinkering.
21
22Alpine has given me a huge increase in performance across my Docker apps and
23Nginx websites. CPU load for the new server I'm using to test Alpine hovers
24around 0-5% on average with an Intel(R) Core(TM) i3-6100 CPU @ 3.70GHz.
25
26The only services I haven't moved over to Alpine are Plex Media Server and
27Syncthing, which may increase CPU load quite a bit depending on how many streams
28are running.
29
30## Installation
31
32In terms of installation, Alpine has an incredibly useful
33[wiki](https://wiki.alpinelinux.org/wiki/Installation) that will guide a user
34throughout the installation and post-installation processes, as well as various
35other articles and guides.
36
37To install Alpine, find an appropriate [image to
38download](https://alpinelinux.org/downloads/) and flash it to a USB using
39software such as Rufus or Etcher. I opted to use the Standard image for my
40x86~64~ architecture.
41
42Once the USB is ready, plug it into the machine and reboot. Note that you may
43have to use a key such as `Esc` or `F1-12` to access the boot menu. The Alpine
44Linux terminal will load quickly and for a login.
45
46To log in to the installation image, use the `root` account; there is no
47password. Once logged-in, execute the setup command:
48
49```sh
50setup-alpine
51```
52
53The setup script will ask a series of questions to configure the system. Be sure
54to answer carefully or else you may have to re-configure the system after boot.
55
56- Keyboard Layout (Local keyboard language and usage mode, e.g., us and
57 variant of us-nodeadkeys.)
58- Hostname (The name for the computer.)
59- Network (For example, automatic IP address discovery with the "DHCP"
60 protocol.)
61- DNS Servers (Domain Name Servers to query. For privacy reasons, it is NOT
62 recommended to route every local request to servers like Google's 8.8.8.8.)
63- Timezone
64- Proxy (Proxy server to use for accessing the web. Use "none" for direct
65 connections to the internet.)
66- Mirror (From where to download packages. Choose the organization you trust
67 giving your usage patterns to.)
68- SSH (Secure SHell remote access server. "Openssh" is part of the default
69 install image. Use "none" to disable remote login, e.g. on laptops.)
70- NTP (Network Time Protocol client used for keeping the system clock in sync
71 with a time-server. Package "chrony" is part of the default install image.)
72- Disk Mode (Select between diskless (disk="none"), "data" or "sys", as
73 described above.)
74
75Once the setup script is finished, be sure to reboot the machine and remove the
76USB device.
77
78```sh
79reboot
80```
81
82## Post-Installation
83
84There are many things you can do once your Alpine Linux system is up and
85running, and it largely depends on what you'll use the machine for. I'm going to
86walk through my personal post-installation setup for my web server.
87
881. Upgrade the System
89
90 First, login as `root` in order to update and upgrade the system:
91
92 ```sh
93 apk -U upgrade
94 ```
95
962. Adding a User
97
98 I needed to add a user so that I don't need to log in as root. Note that if
99 you're used to using the `sudo` command, you will now need to use the `doas`
100 command on Alpine Linux.
101
102 ```sh
103 apk add doas
104 adduser <username>
105 adduser <username> wheel
106 ```
107
108 You can now log out and log back in using the newly-created user:
109
110 ```sh
111 exit
112 ```
113
1143. Enable Community Packages
115
116 In order to install more common packages that aren't found in the `main`
117 repository, you will need to enable the `community` repository:
118
119 ```sh
120 doas nano /etc/apk/repositories
121 ```
122
123 Uncomment the community line for whichever version of Alpine you're running:
124
125 ```sh
126 /media/usb/apks
127 http://dl-cdn.alpinelinux.org/alpine/v3.16/main
128 http://dl-cdn.alpinelinux.org/alpine/v3.16/community
129 #http://dl-cdn.alpinelinux.org/alpine/edge/main
130 #http://dl-cdn.alpinelinux.org/alpine/edge/community
131 #http://dl-cdn.alpinelinux.org/alpine/edge/testing
132 ```
133
1344. Install Required Packages
135
136 Now that the community packages are available, you can install any packages
137 you need. In my case, I installed the web server packages I need for my
138 services:
139
140 ```sh
141 doas apk add nano nginx docker docker-compose ufw
142 ```
143
1445. SSH
145
146 If you didn't install OpenSSH as part of the installation, you can do so
147 now:
148
149 ```sh
150 doas apk add openssh
151 ```
152
153 Next, either create a new key or copy your SSH key to the server from your
154 current machines:
155
156 ```sh
157 # Create a new key
158 ssh-keygen
159 ```
160
161 If you need to copy an existing SSH key from a current machine:
162
163 ```sh
164 # Copy key from existing machines
165 ssh-copy-id <username>@<ip_address>
166 ```
167
1686. Firewall
169
170 Lastly, I installed `ufw` above as my firewall. To set up, default to deny
171 incoming and allow outgoing connections. Then selectively allow other ports
172 or apps as needed.
173
174 ```sh
175 doas ufw default deny incoming
176 doas ufw default allow outgoing
177 doas ufw allow SSH
178 doas ufw allow "WWW Full"
179 doas ufw allow 9418 # Git server port
180 ```
181
1827. Change Hostname
183
184 If you don't like the hostname set during installation, you just need to
185 edit two files. First, edit the simple hostname file:
186
187 ```sh
188 doas nano /etc/hostname
189 ```
190
191 ```sh
192 <hostname>
193 ```
194
195 Next, edit the `hosts` file:
196
197 ```sh
198 doas nano /etc/hosts
199 ```
200
201 ```sh
202 127.0.0.1 <hostname>.local <hostname> localhost.local localhost
203 ::1 <hostname> <hostname>.local
204 ```
205
206# Nginx Web Server
207
208To set up my web server, I simply created the `www` user and created the
209necessary files.
210
211```sh
212doas adduser -D -g 'www' www
213mkdir /www
214doas mkdir /www
215doas chown -R www:www /var/lib/nginx/
216doas chown -R www:www /www
217```
218
219If you're running a simple webroot, you can alter the main `nginx.conf` file.
220Otherwise, you can drop configuration files in the following directory. You
221don't need to enable or symlink the configuration file like you do in other
222systems.
223
224```sh
225doas nano /etc/nginx/http.d/example_website.conf
226```
227
228Once the configuration is set and pointed at the `/www` directory to serve
229files, enable the Nginx service:
230
231```sh
232# Note that 'default' must be included or Nginx will not start on boot
233doas rc-update add nginx default
234```
235
236# Docker Containers
237
238Docker works exactly the same as other systems. Either execute a `docker run`
239command or create a `docker-compose.yml` file and do `docker-compose up -d`.
240
241# Git Server
242
243I went in-depth on how to self-host a git server in another post: [Self-Hosting
244a Personal Git Server](../git-server/).
245
246However, there are a few differences with Alpine. First note that in order to
247change the `git` user's shell, you must do a few things a little different:
248
249```sh
250doas apk add libuser
251doas touch /etc/login.defs
252doas mkdir /etc/default
253doas touch /etc/default/useradd
254doas lchsh git
255```
256
257# Thoughts on Alpine
258
259So far, I love Alpine Linux. I have no complaints about anything at this point,
260but I'm not completely finished with the migration yet. Once I'm able to upgrade
261my hardware to a rack-mounted server, I will migrate Plex and Syncthing over to
262Alpine as well - possibly putting Plex into a container or VM.
263
264The performance is stellar, the `apk` package manager is seamless, and system
265administration tasks are effortless. My only regret is that I didn't install
266Alpine sooner.
content/blog/2022-10-22-alpine-linux.org added +276
@@ -0,0 +1,276 @@
1#+date: <2022-10-22>
2#+title: Alpine Linux: My New Server OS
3#+description:
4
5
6* Alpine Linux
7
8[[https://alpinelinux.org][Alpine Linux]] is a very small distro, built
9on musl libc and busybox. It uses ash as the default shell, OpenRC as
10the init system, and apk as the package manager. According to their
11website, an Alpine container "requires no more than 8 MB and a minimal
12installation to disk requires around 130 MB of storage." An actual bare
13metal machine is recommended to have 100 MB of RAM and 0-700 MB of
14storage space.
15
16Historically, I've used Ubuntu's minimal installation image as my server
17OS for the last five years. Ubuntu worked well and helped as my original
18server contained an nVidia GPU and no onboard graphics, so quite a few
19distros won't boot or install without a lot of tinkering.
20
21Alpine has given me a huge increase in performance across my Docker apps
22and Nginx websites. CPU load for the new server I'm using to test Alpine
23hovers around 0-5% on average with an Intel(R) Core(TM) i3-6100 CPU @
243.70GHz.
25
26The only services I haven't moved over to Alpine are Plex Media Server
27and Syncthing, which may increase CPU load quite a bit depending on how
28many streams are running.
29
30** Installation
31
32In terms of installation, Alpine has an incredibly useful
33[[https://wiki.alpinelinux.org/wiki/Installation][wiki]] that will guide
34a user throughout the installation and post-installation processes, as
35well as various other articles and guides.
36
37To install Alpine, find an appropriate
38[[https://alpinelinux.org/downloads/][image to download]] and flash it
39to a USB using software such as Rufus or Etcher. I opted to use the
40Standard image for my x86_{64} architecture.
41
42Once the USB is ready, plug it into the machine and reboot. Note that
43you may have to use a key such as =Esc= or =F1-12= to access the boot
44menu. The Alpine Linux terminal will load quickly and for a login.
45
46To log in to the installation image, use the =root= account; there is no
47password. Once logged-in, execute the setup command:
48
49#+begin_src sh
50setup-alpine
51#+end_src
52
53The setup script will ask a series of questions to configure the system.
54Be sure to answer carefully or else you may have to re-configure the
55system after boot.
56
57- Keyboard Layout (Local keyboard language and usage mode, e.g., us and
58 variant of us-nodeadkeys.)
59- Hostname (The name for the computer.)
60- Network (For example, automatic IP address discovery with the "DHCP"
61 protocol.)
62- DNS Servers (Domain Name Servers to query. For privacy reasons, it is
63 NOT recommended to route every local request to servers like Google's
64 8.8.8.8.)
65- Timezone
66- Proxy (Proxy server to use for accessing the web. Use "none" for
67 direct connections to the internet.)
68- Mirror (From where to download packages. Choose the organization you
69 trust giving your usage patterns to.)
70- SSH (Secure SHell remote access server. "Openssh" is part of the
71 default install image. Use "none" to disable remote login, e.g. on
72 laptops.)
73- NTP (Network Time Protocol client used for keeping the system clock in
74 sync with a time-server. Package "chrony" is part of the default
75 install image.)
76- Disk Mode (Select between diskless (disk="none"), "data" or "sys", as
77 described above.)
78
79Once the setup script is finished, be sure to reboot the machine and
80remove the USB device.
81
82#+begin_src sh
83reboot
84#+end_src
85
86** Post-Installation
87
88There are many things you can do once your Alpine Linux system is up and
89running, and it largely depends on what you'll use the machine for. I'm
90going to walk through my personal post-installation setup for my web
91server.
92
931. Upgrade the System
94
95 First, login as =root= in order to update and upgrade the system:
96
97 #+begin_src sh
98 apk -U upgrade
99 #+end_src
100
1012. Adding a User
102
103 I needed to add a user so that I don't need to log in as root. Note
104 that if you're used to using the =sudo= command, you will now need to
105 use the =doas= command on Alpine Linux.
106
107 #+begin_src sh
108 apk add doas
109 adduser <username>
110 adduser <username> wheel
111 #+end_src
112
113 You can now log out and log back in using the newly-created user:
114
115 #+begin_src sh
116 exit
117 #+end_src
118
1193. Enable Community Packages
120
121 In order to install more common packages that aren't found in the
122 =main= repository, you will need to enable the =community=
123 repository:
124
125 #+begin_src sh
126 doas nano /etc/apk/repositories
127 #+end_src
128
129 Uncomment the community line for whichever version of Alpine you're
130 running:
131
132 #+begin_src sh
133 /media/usb/apks
134 http://dl-cdn.alpinelinux.org/alpine/v3.16/main
135 http://dl-cdn.alpinelinux.org/alpine/v3.16/community
136 #http://dl-cdn.alpinelinux.org/alpine/edge/main
137 #http://dl-cdn.alpinelinux.org/alpine/edge/community
138 #http://dl-cdn.alpinelinux.org/alpine/edge/testing
139 #+end_src
140
1414. Install Required Packages
142
143 Now that the community packages are available, you can install any
144 packages you need. In my case, I installed the web server packages I
145 need for my services:
146
147 #+begin_src sh
148 doas apk add nano nginx docker docker-compose ufw
149 #+end_src
150
1515. SSH
152
153 If you didn't install OpenSSH as part of the installation, you can do
154 so now:
155
156 #+begin_src sh
157 doas apk add openssh
158 #+end_src
159
160 Next, either create a new key or copy your SSH key to the server from
161 your current machines:
162
163 #+begin_src sh
164 # Create a new key
165 ssh-keygen
166 #+end_src
167
168 If you need to copy an existing SSH key from a current machine:
169
170 #+begin_src sh
171 # Copy key from existing machines
172 ssh-copy-id <username>@<ip_address>
173 #+end_src
174
1756. Firewall
176
177 Lastly, I installed =ufw= above as my firewall. To set up, default to
178 deny incoming and allow outgoing connections. Then selectively allow
179 other ports or apps as needed.
180
181 #+begin_src sh
182 doas ufw default deny incoming
183 doas ufw default allow outgoing
184 doas ufw allow SSH
185 doas ufw allow "WWW Full"
186 doas ufw allow 9418 # Git server port
187 #+end_src
188
1897. Change Hostname
190
191 If you don't like the hostname set during installation, you just need
192 to edit two files. First, edit the simple hostname file:
193
194 #+begin_src sh
195 doas nano /etc/hostname
196 #+end_src
197
198 #+begin_src sh
199 <hostname>
200 #+end_src
201
202 Next, edit the =hosts= file:
203
204 #+begin_src sh
205 doas nano /etc/hosts
206 #+end_src
207
208 #+begin_src sh
209 127.0.0.1 <hostname>.local <hostname> localhost.local localhost
210 ::1 <hostname> <hostname>.local
211 #+end_src
212
213* Nginx Web Server
214
215To set up my web server, I simply created the =www= user and created the
216necessary files.
217
218#+begin_src sh
219doas adduser -D -g 'www' www
220mkdir /www
221doas mkdir /www
222doas chown -R www:www /var/lib/nginx/
223doas chown -R www:www /www
224#+end_src
225
226If you're running a simple webroot, you can alter the main =nginx.conf=
227file. Otherwise, you can drop configuration files in the following
228directory. You don't need to enable or symlink the configuration file
229like you do in other systems.
230
231#+begin_src sh
232doas nano /etc/nginx/http.d/example_website.conf
233#+end_src
234
235Once the configuration is set and pointed at the =/www= directory to
236serve files, enable the Nginx service:
237
238#+begin_src sh
239# Note that 'default' must be included or Nginx will not start on boot
240doas rc-update add nginx default
241#+end_src
242
243* Docker Containers
244
245Docker works exactly the same as other systems. Either execute a
246=docker run= command or create a =docker-compose.yml= file and do
247=docker-compose up -d=.
248
249* Git Server
250
251I went in-depth on how to self-host a git server in another post:
252[[../git-server/][Self-Hosting a Personal Git Server]].
253
254However, there are a few differences with Alpine. First note that in
255order to change the =git= user's shell, you must do a few things a
256little different:
257
258#+begin_src sh
259doas apk add libuser
260doas touch /etc/login.defs
261doas mkdir /etc/default
262doas touch /etc/default/useradd
263doas lchsh git
264#+end_src
265
266* Thoughts on Alpine
267
268So far, I love Alpine Linux. I have no complaints about anything at this
269point, but I'm not completely finished with the migration yet. Once I'm
270able to upgrade my hardware to a rack-mounted server, I will migrate
271Plex and Syncthing over to Alpine as well - possibly putting Plex into a
272container or VM.
273
274The performance is stellar, the =apk= package manager is seamless, and
275system administration tasks are effortless. My only regret is that I
276didn't install Alpine sooner.
content/blog/2022-10-30-linux-display-manager.md deleted −77
@@ -1,77 +0,0 @@
1+++
2date = 2022-10-30
3title = "How to Disable or Change the Display Manager on Void Linux"
4description = ""
5draft = false
6+++
7
8# Display Manager Services
9
10In order to change the [display
11manager](https://en.wikipedia.org/wiki/Display_manager) on Void Linux - or any
12other Linux distro - you need to identify the currently enabled display manager.
13
14## Disabling the Current Display Manager
15
16Void Linux only has one ISO available for download with a pre-built display
17manager at the time of this post: the XFCE ISO. If you've installed this
18version, the pre-assigned display manager is `lxdm`. If you installed another
19display manager, replace `lxdm` in the following command with the display
20manager you have installed.
21
22To disable `lxdm`, simply remove the service symlink:
23
24```sh
25sudo rm /var/service/lxdm
26```
27
28## Enabling a New Display Manager
29
30If you want to enable a new display manager, you can do so after `lxdm` is
31disabled. Make sure to replace `<new_display_manager>` with your new DM, such as
32`gdm`, `xdm`, etc.
33
34```sh
35sudo ln -s /etc/sv/<new_display_manager> /var/service
36```
37
38# Set Up `.xinitrc`
39
40Depending on your setup, you may need to create a few X files, such as
41`~/.xinitrc`. For my personal set-up, I created this file to launch the i3wm as
42my desktop.
43
44```sh
45nano ~/.xinitrc
46```
47
48```sh
49#!/bin/sh
50
51exec i3
52```
53
54If you run a desktop other than i3, simply replace `i3` with the shell command
55that launches that desktop.
56
57# Set Up Your Shell Profile
58
59Finally, in order to automatically launch an X session upon login, you will need
60to edit the `.bash_profile` (bash) or `.zprofile` (zsh) files for your shell:
61
62```sh
63nano ~/.zprofile
64```
65
66Add the following snippet to the end of the shell profile file. This will
67execute the `startx` command upon login.
68
69```sh
70if [ -z "${DISPLAY}" ] && [ "${XDG_VTNR}" -eq 1 ]; then
71 exec startx
72fi
73```
74
75Alternatively, you can ignore this step and simply choose to manually execute
76`startx` upon login. This can be useful if you have issues with your desktop or
77like to manually launch different desktops by choice.
content/blog/2022-10-30-linux-display-manager.org added +77
@@ -0,0 +1,77 @@
1#+date: <2022-10-30>
2#+title: How to Disable or Change the Display Manager on Void Linux
3#+description:
4
5
6* Display Manager Services
7
8In order to change the
9[[https://en.wikipedia.org/wiki/Display_manager][display manager]] on
10Void Linux - or any other Linux distro - you need to identify the
11currently enabled display manager.
12
13** Disabling the Current Display Manager
14
15Void Linux only has one ISO available for download with a pre-built
16display manager at the time of this post: the XFCE ISO. If you've
17installed this version, the pre-assigned display manager is =lxdm=. If
18you installed another display manager, replace =lxdm= in the following
19command with the display manager you have installed.
20
21To disable =lxdm=, simply remove the service symlink:
22
23#+begin_src sh
24sudo rm /var/service/lxdm
25#+end_src
26
27** Enabling a New Display Manager
28
29If you want to enable a new display manager, you can do so after =lxdm=
30is disabled. Make sure to replace =<new_display_manager>= with your new
31DM, such as =gdm=, =xdm=, etc.
32
33#+begin_src sh
34sudo ln -s /etc/sv/<new_display_manager> /var/service
35#+end_src
36
37* Set Up =.xinitrc=
38
39Depending on your setup, you may need to create a few X files, such as
40=~/.xinitrc=. For my personal set-up, I created this file to launch the
41i3wm as my desktop.
42
43#+begin_src sh
44nano ~/.xinitrc
45#+end_src
46
47#+begin_src sh
48#!/bin/sh
49
50exec i3
51#+end_src
52
53If you run a desktop other than i3, simply replace =i3= with the shell
54command that launches that desktop.
55
56* Set Up Your Shell Profile
57
58Finally, in order to automatically launch an X session upon login, you
59will need to edit the =.bash_profile= (bash) or =.zprofile= (zsh) files
60for your shell:
61
62#+begin_src sh
63nano ~/.zprofile
64#+end_src
65
66Add the following snippet to the end of the shell profile file. This
67will execute the =startx= command upon login.
68
69#+begin_src sh
70if [ -z "${DISPLAY}" ] && [ "${XDG_VTNR}" -eq 1 ]; then
71 exec startx
72fi
73#+end_src
74
75Alternatively, you can ignore this step and simply choose to manually
76execute =startx= upon login. This can be useful if you have issues with
77your desktop or like to manually launch different desktops by choice.
content/blog/2022-11-07-self-hosting-matrix.md → content/blog/2022-11-07-self-hosting-matrix.org renamed +88 −85
@@ -1,103 +1,104 @@
1+++
2date = 2022-11-07
3title = "Self-Hosting Matrix Synapse on Alpine Linux"
4description = ""
5draft = false
6+++
1#+date: <2022-11-07>
2#+title: Self-Hosting Matrix Synapse on Alpine Linux
3#+description:
74
8# Synapse
5
6* Synapse
97
108If you're reading this, you likely know that
11[Synapse](https://github.com/matrix-org/synapse/) is a popular
12[Matrix](https://matrix.org/) home server software that allows users to run
13their own Matrix home server.
9[[https://github.com/matrix-org/synapse/][Synapse]] is a popular
10[[https://matrix.org/][Matrix]] home server software that allows users
11to run their own Matrix home server.
1412
15This post is a short guide describing how I was able to get Synapse working in a
16minimally-usable state on Alpine Linux.
13This post is a short guide describing how I was able to get Synapse
14working in a minimally-usable state on Alpine Linux.
1715
18# Installation Process
16* Installation Process
1917
20## Dependencies
18** Dependencies
2119
22First, since there is no Alpine-specific package for Synapse, we need to ensure
23that Alpine has the required dependencies for the Python-based installation
24method.
20First, since there is no Alpine-specific package for Synapse, we need to
21ensure that Alpine has the required dependencies for the Python-based
22installation method.
2523
26```sh
24#+begin_src sh
2725doas apk -U update
2826doas apk add python3 py3-virtualenv
29```
27#+end_src
3028
3129Next, we need to set up a Python virtual environment for Synapse:
3230
33```sh
31#+begin_src sh
3432mkdir -p ~/synapse && cd ~/synapse
3533virtualenv -p python3 ~/synapse/env
3634source ~/synapse/env/bin/activate
3735pip install --upgrade pip
3836pip install --upgrade setuptools
3937pip install matrix-synapse
40```
38#+end_src
4139
42## Running Synapse
40** Running Synapse
4341
44Once installed, running Synapse is easy. Simply execute the following command,
45replacing `example.com` with the domain name that will be used with this home
46server. This will generate the configuration files needed to run the server.
42Once installed, running Synapse is easy. Simply execute the following
43command, replacing =example.com= with the domain name that will be used
44with this home server. This will generate the configuration files needed
45to run the server.
4746
48```sh
47#+begin_src sh
4948python -m synapse.app.homeserver \
5049 --server-name example.com \
5150 --config-path homeserver.yaml \
5251 --generate-config \
5352 --report-stats=no
54```
53#+end_src
5554
5655Once the configuration is generated, we can start up the Synapse server:
5756
58```sh
57#+begin_src sh
5958synctl start
60```
59#+end_src
6160
62## Configuring Synapse
61** Configuring Synapse
6362
64To make any change to Synapse, we need to edit the `YAML` configuration file:
63To make any change to Synapse, we need to edit the =YAML= configuration
64file:
6565
66```sh
66#+begin_src sh
6767nano ~/synapse/homeserver.yaml
68```
68#+end_src
6969
70For now, we just need to ensure the `server_name` is accurate. However, there
71are a lot of other configuration options found in the [Configuring
72Synapse](https://matrix-org.github.io/synapse/develop/usage/configuration/config_documentation.html)
73documentation that can be enabled/disabled at any point.
70For now, we just need to ensure the =server_name= is accurate. However,
71there are a lot of other configuration options found in the
72[[https://matrix-org.github.io/synapse/develop/usage/configuration/config_documentation.html][Configuring
73Synapse]] documentation that can be enabled/disabled at any point.
7474
75```yaml
75#+begin_src yaml
7676server_name: "example.com"
77```
77#+end_src
7878
7979Make sure to restart Synapse when you make changes to the configuration:
8080
81```sh
81#+begin_src sh
8282synctl restart
83```
83#+end_src
8484
85## Nginx Reverse-Proxy
85** Nginx Reverse-Proxy
8686
87To ensure that Synapse is reachable from the public, we need to connect our
88domain to the Synapse server. In my case, I use a Nginx reverse-proxy for this
89purpose.
87To ensure that Synapse is reachable from the public, we need to connect
88our domain to the Synapse server. In my case, I use a Nginx
89reverse-proxy for this purpose.
9090
9191To use Nginx, we need to create a reverse-proxy configuration file:
9292
93```sh
93#+begin_src sh
9494doas nano /etc/nginx/http.d/example.com.conf
95```
95#+end_src
9696
97If you already have TLS certificates for this domain (`example.com`), you can
98simply use the SSL configuration and point toward your TLS certificates.
97If you already have TLS certificates for this domain (=example.com=),
98you can simply use the SSL configuration and point toward your TLS
99certificates.
99100
100```conf
101#+begin_src conf
101102server {
102103 listen 443 ssl http2;
103104 listen [::]:443 ssl http2;
@@ -136,14 +137,15 @@ server {
136137 listen 80;
137138 return 404;
138139}
139```
140#+end_src
140141
141142If you need to generate TLS certificates (I recommend
142[Certbot](https://certbot.eff.org/)), you'll need a more minimal Nginx conf file
143before you can use the TLS-enabled example above. Instead, use this
144configuration file during the Certbot certificate generation process:
143[[https://certbot.eff.org/][Certbot]]), you'll need a more minimal Nginx
144conf file before you can use the TLS-enabled example above. Instead, use
145this configuration file during the Certbot certificate generation
146process:
145147
146```conf
148#+begin_src conf
147149server {
148150 server_name example.com;
149151 location / {
@@ -151,62 +153,63 @@ server {
151153 }
152154 listen 80;
153155}
154```
156#+end_src
155157
156158Once you're done editing the Nginx conf file, restart Nginx:
157159
158```sh
160#+begin_src sh
159161doas rc-service nginx restart
160```
162#+end_src
161163
162If you still need to generate TLS certificates, run `certbot` now and obtain the
163certificates. Certbot will ask if you want to use a webroot or spin up a
164temporary web server. I **highly** recommend using the temporary web server due
165to the many issues with using a webroot.
164If you still need to generate TLS certificates, run =certbot= now and
165obtain the certificates. Certbot will ask if you want to use a webroot
166or spin up a temporary web server. I *highly* recommend using the
167temporary web server due to the many issues with using a webroot.
166168
167You will need to stop Nginx in order to user the temporary web server option
168with Certbot:
169You will need to stop Nginx in order to user the temporary web server
170option with Certbot:
169171
170```sh
172#+begin_src sh
171173# Stop Nginx so certbot can spin up a temp webserver for cert generation
172174doas rc-service nginx stop
173175doas certbot certonly -v
174176doas rc-service nginx start
175```
177#+end_src
176178
177## Open Firewall & Router Ports
179** Open Firewall & Router Ports
178180
179If you use a firewall on the server, open the `8448` port for discovery and
180federation, as well as the normal web server ports if you're using a reverse
181proxy. If you want additional services, such as voice calls, you will need to
182read the Synapse documentation to see which ports need to be opened for those
183features.
181If you use a firewall on the server, open the =8448= port for discovery
182and federation, as well as the normal web server ports if you're using a
183reverse proxy. If you want additional services, such as voice calls, you
184will need to read the Synapse documentation to see which ports need to
185be opened for those features.
184186
185187Here's an example of the Universal Firewall (UFW) software:
186188
187```sh
189#+begin_src sh
188190# Matrix port
189191doas ufw allow 8448
190192# Standard web server ports
191193doas ufw allow "Nginx Full"
192```
194#+end_src
193195
194Remember to forward any Synapse ports, such as `8448`, `80`, and `443`, in your
195Router from the internet to your server's IP address.
196Remember to forward any Synapse ports, such as =8448=, =80=, and =443=,
197in your Router from the internet to your server's IP address.
196198
197## Adding Matrix Users
199** Adding Matrix Users
198200
199Finally, if you didn't enable public registration in the `homeserver.yaml` file,
200you can manually create users via the command-line:
201Finally, if you didn't enable public registration in the
202=homeserver.yaml= file, you can manually create users via the
203command-line:
201204
202```sh
205#+begin_src sh
203206cd ~/synapse
204207register_new_matrix_user -c homeserver.yaml
205```
208#+end_src
206209
207210Remember that the format for federated Matrix usernames is
208`@username:example.com` when logging in to client applications.
211=@username:example.com= when logging in to client applications.
209212
210Once Synapse is running, and you have a username, you are ready to log in to a
211Matrix client and start sending messages, joining rooms, and utilizing your very
212own Matrix server.
213Once Synapse is running, and you have a username, you are ready to log
214in to a Matrix client and start sending messages, joining rooms, and
215utilizing your very own Matrix server.
content/blog/2022-11-11-nginx-tmp-errors.md deleted −77
@@ -1,77 +0,0 @@
1+++
2date = 2022-11-11
3title = "Fixing Permission Errors in /var/lib/nginx"
4description = ""
5draft = false
6+++
7
8_This is a brief post so that I personally remember the solution as it has
9occurred multiple times for me._
10
11# The Problem
12
13After migrating to a new server OS, I started receiving quite a few permission
14errors like the one below. These popped up for various different websites I'm
15serving via Nginx on this server, but did not prevent the website from loading.
16
17I found the errors in the standard log file:
18
19```sh
20cat /var/log/nginx/error.log
21```
22
23```sh
242022/11/11 11:30:34 [crit] 8970#8970: *10 open() "/var/lib/nginx/tmp/proxy/3/00/0000000003" failed (13: Permission denied) while reading upstream, client: 169.150.203.10, server: cyberchef.example.com, request: "GET /assets/main.css HTTP/2.0", upstream: "http://127.0.0.1:8111/assets/main.css", host: "cyberchef.example.com", referrer: "https://cyberchef.example.com/"
25```
26
27You can see that the error is `13: Permission denied` and it occurs in the
28`/var/lib/nginx/tmp/` directory. In my case, I had thousands of errors where
29Nginx was denied permission to read/write files in this directory.
30
31So how do I fix it?
32
33# The Solution
34
35In order to resolve the issue, I had to ensure the `/var/lib/nginx` directory is
36owned by Nginx. Mine was owned by the `www` user and Nginx was not able to read
37or write files within that directory. This prevented Nginx from caching
38temporary files.
39
40```sh
41# Alpine Linux
42doas chown -R nginx:nginx /var/lib/nginx
43
44# Other Distros
45sudo chown -R nginx:nginx /var/lib/nginx
46```
47
48You _may_ also be able to change the `proxy_temp_path` in your Nginx config, but
49I did not try this. Here's a suggestion I found online that may work if the
50above solution does not:
51
52```sh
53nano /etc/nginx/http.d/example.com.conf
54```
55
56```conf
57server {
58 ...
59
60 # Set the proxy_temp_path to your preference, make sure it's owned by the
61 # `nginx` user
62 proxy_temp_path /tmp;
63
64 ...
65}
66```
67
68Finally, restart Nginx and your server should be able to cache temporary files
69again.
70
71```sh
72# Alpine Linux (OpenRC)
73doas rc-service nginx restart
74
75# Other Distros (systemd)
76sudo systemctl restart nginx
77```
content/blog/2022-11-11-nginx-tmp-errors.org added +77
@@ -0,0 +1,77 @@
1#+date: <2022-11-11>
2#+title: Fixing Permission Errors in /var/lib/nginx
3#+description:
4
5
6/This is a brief post so that I personally remember the solution as it
7has occurred multiple times for me./
8
9* The Problem
10
11After migrating to a new server OS, I started receiving quite a few
12permission errors like the one below. These popped up for various
13different websites I'm serving via Nginx on this server, but did not
14prevent the website from loading.
15
16I found the errors in the standard log file:
17
18#+begin_src sh
19cat /var/log/nginx/error.log
20#+end_src
21
22#+begin_src sh
232022/11/11 11:30:34 [crit] 8970#8970: *10 open() "/var/lib/nginx/tmp/proxy/3/00/0000000003" failed (13: Permission denied) while reading upstream, client: 169.150.203.10, server: cyberchef.example.com, request: "GET /assets/main.css HTTP/2.0", upstream: "http://127.0.0.1:8111/assets/main.css", host: "cyberchef.example.com", referrer: "https://cyberchef.example.com/"
24#+end_src
25
26You can see that the error is =13: Permission denied= and it occurs in
27the =/var/lib/nginx/tmp/= directory. In my case, I had thousands of
28errors where Nginx was denied permission to read/write files in this
29directory.
30
31So how do I fix it?
32
33* The Solution
34
35In order to resolve the issue, I had to ensure the =/var/lib/nginx=
36directory is owned by Nginx. Mine was owned by the =www= user and Nginx
37was not able to read or write files within that directory. This
38prevented Nginx from caching temporary files.
39
40#+begin_src sh
41# Alpine Linux
42doas chown -R nginx:nginx /var/lib/nginx
43
44# Other Distros
45sudo chown -R nginx:nginx /var/lib/nginx
46#+end_src
47
48You /may/ also be able to change the =proxy_temp_path= in your Nginx
49config, but I did not try this. Here's a suggestion I found online that
50may work if the above solution does not:
51
52#+begin_src sh
53nano /etc/nginx/http.d/example.com.conf
54#+end_src
55
56#+begin_src conf
57server {
58 ...
59
60 # Set the proxy_temp_path to your preference, make sure it's owned by the
61 # `nginx` user
62 proxy_temp_path /tmp;
63
64 ...
65}
66#+end_src
67
68Finally, restart Nginx and your server should be able to cache temporary
69files again.
70
71#+begin_src sh
72# Alpine Linux (OpenRC)
73doas rc-service nginx restart
74
75# Other Distros (systemd)
76sudo systemctl restart nginx
77#+end_src
content/blog/2022-11-27-server-build.md deleted −143
@@ -1,143 +0,0 @@
1+++
2date = 2022-11-27
3title = "Building a Custom Rack-Mounted Server"
4description = ""
5draft = false
6+++
7
8# The Dilemma
9
10For years, I have been using desktops and a Raspberry Pi as the backbone of my
11homelab. I have always wanted to move toward a single dedicated server that
12could handle all of my tasks, but was often put off by the complexity of the
13choices (and financial roadblocks at some times).
14
15However, after purchasing a small server rack this past year, I have been
16researching to see what kind of rack-mounted server I could buy. I initially
17bought a Dell R720XD loaded up with everything I could ever want in a server -
18but I did not care for it. It was far too loud, and the age of the CPU/RAM was
19something I wanted to improve upon.
20
21After returning the R720XD, I decided that I wanted to build my own server with
22modern, consumer-grade PC components. This time, I am very happy with the
23results of my server.
24
25# Components
26
27I'll start by listing all the components I used for this server build:
28
29- **Case**: [Rosewill RSV-R4100U 4U Server Chassis Rackmount
30 Case](https://www.rosewill.com/rosewill-rsv-r4100u-black/p/9SIA072GJ92825)
31- **Motherboard**: [NZXT B550](https://nzxt.com/product/n7-b550)
32- **CPU**: AMD Ryzen 7 5700G with Radeon Graphics
33- **GPU**: N/A - I specifically chose one of the few AMD CPUs that support
34 onboard graphics.
35- **RAM**: 64GB RAM (2x32GB) _Max of 128GB RAM on this motherboard_
36- **Boot Drive**: Western Digital 500GB M.2 NVME SSD
37- **HDD Bay**:
38 - 10TB WD White _(shucked, moved from previous server)_
39 - 8TB WD White _(shucked, moved from previous server)_
40 - 2 x 8TB WD Red Plus _(Black Friday lined up perfectly with this build,
41 so I grabbed two of these)_
42- **PSU**: Corsair RM850 PSU
43- **Extras**:
44 - Corsair TM3Q Thermal Paste
45 - Noctua 120mm fan _(replacement for front case fan)_
46 - 2 x Noctua 80mm fans _(replacement for rear case fans)_
47 - CableMatters 6Gbps SATA Cables
48
49# Building the Server
50
51This took quite a while for me to build (in my opinion of time), totaling around
523 hours from start to finish. The case has some peculiar construction, so you
53have to completely remove the ODD & HDD cages to install the motherboard and
54other components first.
55
56Now, I've never built a computer of any kind before, so I was quite nervous.
57Personally, the only challenging part was getting the CPU cooler to screw into
58the motherboard without sliding the thermal paste around too much underneath.
59I'm still not entirely sure if I did a great job of it, but nothing's broken
60yet.
61
62The main components were all fine and simple. However, installing the hard
63drives is slightly tedious as I need to power off the server and completely
64unscrew the HDD cage to install or remove any drives. Additionally, the drives
65are screwed directly into the metal cage with small screws, which are quite a
66bit different from the HDD trays I'm used to in other machines.
67
68Seeing that the cases with hot-swap bays were 3-4x the price, I'm okay dealing
69with the tedium of removing the cage to install new drives.
70
71# Software
72
73I'm not going to dive into the software as I have done so in other recent posts.
74However, I wanted to note that I am using Alpine Linux on this server and
75hosting most services inside Docker. No virtual machines (VMs) and very few
76bare-metal services.
77
78# The Results
79
80How did my build turn out? Well, after migrating my other servers and their
81services over, I found that my server is blazing fast. The heaviest of my
82applications, Plex, is handled with ease. Even 4k streaming seems to be
83effortless.
84
85I am very happy with the results and will likely continue to improve on this
86server as the years go by rather than buying another used server online.
87
88## Mistakes I Made
89
90This post wouldn't be complete unless I wrote about the mistakes I made while
91building. The only real mistake I made beyond a "whoops I dropped a screw"
92related to airflow and fan direction.
93
94While installing the two new hard drives that showed up on 2022-11-30 and
95getting ready to install the case in my rack, I noticed that the hard drive
96temperatures were quite high.
97
98I used the `smartctl` command for each of my drives (`/dev/sda` through
99`/dev/sdd`):
100
101```sh
102doas smartctl -a /dev/sda | grep Temperature_Celsius
103```
104
105The results were unusual - all four drives were idling at ~44-46 degrees
106Celsius. The only drive that was cooler was my 10TB drive, which was at 38
107degrees Celsius. I noted that this 10TB drive was also closest to the case fan.
108
109```sh
110ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE
111# /dev/sda
112194 Temperature_Celsius 0x0002 147 147 000 Old_age Always - 44 (Min/Max 22/46)
113# /dev/sdb
114194 Temperature_Celsius 0x0002 141 141 000 Old_age Always - 46 (Min/Max 21/48)
115# /dev/sdc
116194 Temperature_Celsius 0x0002 144 144 000 Old_age Always - 45 (Min/Max 19/61)
117# /dev/sdd
118194 Temperature_Celsius 0x0002 171 171 000 Old_age Always - 38 (Min/Max 14/56)
119```
120
121After looking to see if I could fit more fans into the case, I noticed that the
122120mm fan used for intake from the front of the case was actually pushing air
123out of the case by mistake. This fan sits right in front of the hard drive bay.
124
125Once I flipped the fan around to act as an intake fan, the temperatures dropped
126immediately! They are now idling at ~31-33 degrees Celsius. A single fan
127spinning the wrong way caused my drives to idle 10-15 degrees higher than they
128should have.
129
130```sh
131ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE
132# /dev/sda
133194 Temperature_Celsius 0x0002 209 209 000 Old_age Always - 31 (Min/Max 14/56)
134# /dev/sdb
135194 Temperature_Celsius 0x0002 196 196 000 Old_age Always - 33 (Min/Max 19/61)
136# /dev/sdc
137194 Temperature_Celsius 0x0002 203 203 000 Old_age Always - 32 (Min/Max 21/48)
138# /dev/sdd
139194 Temperature_Celsius 0x0002 196 196 000 Old_age Always - 33 (Min/Max 22/46)
140```
141
142This was a silly error to make, but I'm glad I found it today before I screwed
143the case into the rack and made things a lot more tedious to fix.
content/blog/2022-11-27-server-build.org added +147
@@ -0,0 +1,147 @@
1#+date: <2022-11-27>
2#+title: Building a Custom Rack-Mounted Server
3#+description:
4
5
6* The Dilemma
7
8For years, I have been using desktops and a Raspberry Pi as the backbone
9of my homelab. I have always wanted to move toward a single dedicated
10server that could handle all of my tasks, but was often put off by the
11complexity of the choices (and financial roadblocks at some times).
12
13However, after purchasing a small server rack this past year, I have
14been researching to see what kind of rack-mounted server I could buy. I
15initially bought a Dell R720XD loaded up with everything I could ever
16want in a server - but I did not care for it. It was far too loud, and
17the age of the CPU/RAM was something I wanted to improve upon.
18
19After returning the R720XD, I decided that I wanted to build my own
20server with modern, consumer-grade PC components. This time, I am very
21happy with the results of my server.
22
23* Components
24
25I'll start by listing all the components I used for this server build:
26
27- *Case*:
28 [[https://www.rosewill.com/rosewill-rsv-r4100u-black/p/9SIA072GJ92825][Rosewill
29 RSV-R4100U 4U Server Chassis Rackmount Case]]
30- *Motherboard*: [[https://nzxt.com/product/n7-b550][NZXT B550]]
31- *CPU*: AMD Ryzen 7 5700G with Radeon Graphics
32- *GPU*: N/A - I specifically chose one of the few AMD CPUs that support
33 onboard graphics.
34- *RAM*: 64GB RAM (2x32GB) /Max of 128GB RAM on this motherboard/
35- *Boot Drive*: Western Digital 500GB M.2 NVME SSD
36- *HDD Bay*:
37 - 10TB WD White /(shucked, moved from previous server)/
38 - 8TB WD White /(shucked, moved from previous server)/
39 - 2 x 8TB WD Red Plus /(Black Friday lined up perfectly with this
40 build, so I grabbed two of these)/
41- *PSU*: Corsair RM850 PSU
42- *Extras*:
43 - Corsair TM3Q Thermal Paste
44 - Noctua 120mm fan /(replacement for front case fan)/
45 - 2 x Noctua 80mm fans /(replacement for rear case fans)/
46 - CableMatters 6Gbps SATA Cables
47
48* Building the Server
49
50This took quite a while for me to build (in my opinion of time),
51totaling around 3 hours from start to finish. The case has some peculiar
52construction, so you have to completely remove the ODD & HDD cages to
53install the motherboard and other components first.
54
55Now, I've never built a computer of any kind before, so I was quite
56nervous. Personally, the only challenging part was getting the CPU
57cooler to screw into the motherboard without sliding the thermal paste
58around too much underneath. I'm still not entirely sure if I did a great
59job of it, but nothing's broken yet.
60
61The main components were all fine and simple. However, installing the
62hard drives is slightly tedious as I need to power off the server and
63completely unscrew the HDD cage to install or remove any drives.
64Additionally, the drives are screwed directly into the metal cage with
65small screws, which are quite a bit different from the HDD trays I'm
66used to in other machines.
67
68Seeing that the cases with hot-swap bays were 3-4x the price, I'm okay
69dealing with the tedium of removing the cage to install new drives.
70
71* Software
72
73I'm not going to dive into the software as I have done so in other
74recent posts. However, I wanted to note that I am using Alpine Linux on
75this server and hosting most services inside Docker. No virtual machines
76(VMs) and very few bare-metal services.
77
78* The Results
79
80How did my build turn out? Well, after migrating my other servers and
81their services over, I found that my server is blazing fast. The
82heaviest of my applications, Plex, is handled with ease. Even 4k
83streaming seems to be effortless.
84
85I am very happy with the results and will likely continue to improve on
86this server as the years go by rather than buying another used server
87online.
88
89** Mistakes I Made
90
91This post wouldn't be complete unless I wrote about the mistakes I made
92while building. The only real mistake I made beyond a "whoops I dropped
93a screw" related to airflow and fan direction.
94
95While installing the two new hard drives that showed up on 2022-11-30
96and getting ready to install the case in my rack, I noticed that the
97hard drive temperatures were quite high.
98
99I used the =smartctl= command for each of my drives (=/dev/sda= through
100=/dev/sdd=):
101
102#+begin_src sh
103doas smartctl -a /dev/sda | grep Temperature_Celsius
104#+end_src
105
106The results were unusual - all four drives were idling at ~44-46 degrees
107Celsius. The only drive that was cooler was my 10TB drive, which was at
10838 degrees Celsius. I noted that this 10TB drive was also closest to the
109case fan.
110
111#+begin_src sh
112ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE
113# /dev/sda
114194 Temperature_Celsius 0x0002 147 147 000 Old_age Always - 44 (Min/Max 22/46)
115# /dev/sdb
116194 Temperature_Celsius 0x0002 141 141 000 Old_age Always - 46 (Min/Max 21/48)
117# /dev/sdc
118194 Temperature_Celsius 0x0002 144 144 000 Old_age Always - 45 (Min/Max 19/61)
119# /dev/sdd
120194 Temperature_Celsius 0x0002 171 171 000 Old_age Always - 38 (Min/Max 14/56)
121#+end_src
122
123After looking to see if I could fit more fans into the case, I noticed
124that the 120mm fan used for intake from the front of the case was
125actually pushing air out of the case by mistake. This fan sits right in
126front of the hard drive bay.
127
128Once I flipped the fan around to act as an intake fan, the temperatures
129dropped immediately! They are now idling at ~31-33 degrees Celsius. A
130single fan spinning the wrong way caused my drives to idle 10-15 degrees
131higher than they should have.
132
133#+begin_src sh
134ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE
135# /dev/sda
136194 Temperature_Celsius 0x0002 209 209 000 Old_age Always - 31 (Min/Max 14/56)
137# /dev/sdb
138194 Temperature_Celsius 0x0002 196 196 000 Old_age Always - 33 (Min/Max 19/61)
139# /dev/sdc
140194 Temperature_Celsius 0x0002 203 203 000 Old_age Always - 32 (Min/Max 21/48)
141# /dev/sdd
142194 Temperature_Celsius 0x0002 196 196 000 Old_age Always - 33 (Min/Max 22/46)
143#+end_src
144
145This was a silly error to make, but I'm glad I found it today before I
146screwed the case into the rack and made things a lot more tedious to
147fix.
content/blog/2022-11-29-nginx-referrer-ban-list.md deleted −136
@@ -1,136 +0,0 @@
1+++
2date = 2022-11-29
3title = "Creating a Referrer Ban List in Nginx"
4description = ""
5draft = false
6+++
7
8# Creating the Ban List
9
10In order to ban list referral domains or websites with Nginx, you need to create
11a ban list file. The file below will accept regexes for different domains or
12websites you wish to block.
13
14First, create the file in your nginx directory:
15
16```sh
17doas nano /etc/nginx/banlist.conf
18```
19
20Next, paste the following contents in and fill out the regexes with whichever
21domains you're blocking.
22
23```conf
24# /etc/nginx/banlist.conf
25
26map $http_referer $bad_referer {
27 hostnames;
28
29 default 0;
30
31 # Put regexes for undesired referrers here
32 "~news.ycombinator.com" 1;
33}
34```
35
36# Configuring Nginx
37
38In order for the ban list to work, Nginx needs to know it exists and how to
39handle it. For this, edit the `nginx.conf` file.
40
41```sh
42doas nano /etc/nginx/nginx.conf
43```
44
45Within this file, find the `http` block and add your ban list file location to
46the end of the block.
47
48```conf
49# /etc/nginx/nginx.conf
50
51http {
52 ...
53
54 # Include ban list
55 include /etc/nginx/banlist.conf;
56}
57```
58
59# Enabling the Ban List
60
61Finally, we need to take action when a bad referral site is found. To do so,
62edit the configuration file for your website. For example, I have all website
63configuration files in the `http.d` directory. You may have them in the
64`sites-available` directory on some distributions.
65
66```sh
67doas nano /etc/nginx/http.d/example.com.conf
68```
69
70Within each website's configuration file, edit the `server` blocks that are
71listening to ports 80 and 443 and create a check for the `$bad_referrer`
72variable we created in the ban list file.
73
74If a matching site is found, you can return any [HTTP Status
75Code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) you want. Code
76403 (Forbidden) is logical in this case since you are preventing a client
77connection due to a banned domain.
78
79```conf
80server {
81 ...
82
83 # If a referral site is banned, return an error
84 if ($bad_referer) {
85 return 403;
86 }
87
88 ...
89}
90```
91
92# Restart Nginx
93
94Lastly, restart Nginx to enable all changes made.
95
96```sh
97doas rc-service nginx restart
98```
99
100# Testing Results
101
102In order to test the results, let's curl the contents of our site. To start,
103I'll curl the site normally:
104
105```sh
106curl https://cleberg.net
107```
108
109The HTML contents of the page come back successfully:
110
111```html
112<!doctype html>...</html>
113```
114
115Next, let's include a banned referrer:
116
117```sh
118curl --referer https://news.ycombinator.com https://cleberg.net
119```
120
121This time, I'm met with a 403 Forbidden response page. That means we are
122successful and any clients being referred from a banned domain will be met with
123this same response code.
124
125```html
126<html>
127 <head>
128 <title>403 Forbidden</title>
129 </head>
130 <body>
131 <center><h1>403 Forbidden</h1></center>
132 <hr />
133 <center>nginx</center>
134 </body>
135</html>
136```
content/blog/2022-11-29-nginx-referrer-ban-list.org added +134
@@ -0,0 +1,134 @@
1#+date: <2022-11-29>
2#+title: Creating a Referrer Ban List in Nginx
3#+description:
4
5
6* Creating the Ban List
7
8In order to ban list referral domains or websites with Nginx, you need
9to create a ban list file. The file below will accept regexes for
10different domains or websites you wish to block.
11
12First, create the file in your nginx directory:
13
14#+begin_src sh
15doas nano /etc/nginx/banlist.conf
16#+end_src
17
18Next, paste the following contents in and fill out the regexes with
19whichever domains you're blocking.
20
21#+begin_src conf
22# /etc/nginx/banlist.conf
23
24map $http_referer $bad_referer {
25 hostnames;
26
27 default 0;
28
29 # Put regexes for undesired referrers here
30 "~news.ycombinator.com" 1;
31}
32#+end_src
33
34* Configuring Nginx
35
36In order for the ban list to work, Nginx needs to know it exists and how
37to handle it. For this, edit the =nginx.conf= file.
38
39#+begin_src sh
40doas nano /etc/nginx/nginx.conf
41#+end_src
42
43Within this file, find the =http= block and add your ban list file
44location to the end of the block.
45
46#+begin_src conf
47# /etc/nginx/nginx.conf
48
49http {
50 ...
51
52 # Include ban list
53 include /etc/nginx/banlist.conf;
54}
55#+end_src
56
57* Enabling the Ban List
58
59Finally, we need to take action when a bad referral site is found. To do
60so, edit the configuration file for your website. For example, I have
61all website configuration files in the =http.d= directory. You may have
62them in the =sites-available= directory on some distributions.
63
64#+begin_src sh
65doas nano /etc/nginx/http.d/example.com.conf
66#+end_src
67
68Within each website's configuration file, edit the =server= blocks that
69are listening to ports 80 and 443 and create a check for the
70=$bad_referrer= variable we created in the ban list file.
71
72If a matching site is found, you can return any
73[[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes][HTTP Status
74Code]] you want. Code 403 (Forbidden) is logical in this case since you
75are preventing a client connection due to a banned domain.
76
77#+begin_src conf
78server {
79 ...
80
81 # If a referral site is banned, return an error
82 if ($bad_referer) {
83 return 403;
84 }
85
86 ...
87}
88#+end_src
89
90* Restart Nginx
91
92Lastly, restart Nginx to enable all changes made.
93
94#+begin_src sh
95doas rc-service nginx restart
96#+end_src
97
98* Testing Results
99
100In order to test the results, let's curl the contents of our site. To
101start, I'll curl the site normally:
102
103#+begin_src sh
104curl https://cleberg.net
105#+end_src
106
107The HTML contents of the page come back successfully:
108
109#+begin_src html
110<!doctype html>...</html>
111#+end_src
112
113Next, let's include a banned referrer:
114
115#+begin_src sh
116curl --referer https://news.ycombinator.com https://cleberg.net
117#+end_src
118
119This time, I'm met with a 403 Forbidden response page. That means we are
120successful and any clients being referred from a banned domain will be
121met with this same response code.
122
123#+begin_src html
124<html>
125 <head>
126 <title>403 Forbidden</title>
127 </head>
128 <body>
129 <center><h1>403 Forbidden</h1></center>
130 <hr />
131 <center>nginx</center>
132 </body>
133</html>
134#+end_src
content/blog/2022-12-01-nginx-compression.md deleted −76
@@ -1,76 +0,0 @@
1+++
2date = 2022-12-01
3title = "Enable GZIP Compression in Nginx"
4description = ""
5draft = false
6+++
7
8# Text Compression
9
10Text compression allows a web server to serve text-based resources faster than
11uncompressed data. This can speed up things like First Contentful Paint, Tie to
12Interactive, and Speed Index.
13
14# Enable Nginx Compression with gzip
15
16In order to enable text compression on Nginx, we need to enable it within the
17configuration file:
18
19```sh
20nano /etc/nginx/nginx.conf
21```
22
23Within the `http` block, find the section that shows something like the block
24below. This is the default gzip configuration I found in my `nginx.conf` file on
25Alpine Linux 3.17. Yours may look slightly different, just make sure that you're
26not creating any duplicate gzip options.
27
28```conf
29# Enable gzipping of responses.
30#gzip on;
31
32# Set the Vary HTTP header as defined in the RFC 2616. Default is 'off'.
33gzip_vary on;
34```
35
36Remove the default gzip lines and replace them with the following:
37
38```conf
39# Enable gzipping of responses.
40gzip on;
41gzip_vary on;
42gzip_min_length 10240;
43gzip_proxied expired no-cache no-store private auth;
44gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml;
45gzip_disable "MSIE [1-6]";
46```
47
48# Explanations of ngx~httpgzipmodule~ Options
49
50Each of the lines above enables a different aspect of the gzip response for
51Nginx. Here are the full explanations:
52
53- `gzip` -- Enables or disables gzipping of responses.
54- `gzip_vary` -- Enables or disables inserting the "Vary: Accept-Encoding"
55 response header field if the directives gzip, gzip~static~, or gunzip are
56 active.
57- `gzip_min_length` -- Sets the minimum length of a response that will be
58 gzipped. The length is determined only from the "Content-Length" response
59 header field.
60- `gzip_proxied` -- Enables or disables gzipping of responses for proxied
61 requests depending on the request and response. The fact that the request is
62 proxied is determined by the presence of the "Via" request header field.
63- `gzip_types` -- Enables gzipping of responses for the specified MIME types
64 in addition to "text/html". The special value "\*" matches any MIME type
65 (0.8.29). Responses with the "text/html" type are always compressed.
66- `gzip_disable` -- Disables gzipping of responses for requests with
67 "User-Agent" header fields matching any of the specified regular
68 expressions.
69 - The special mask "msie6" (0.7.12) corresponds to the regular expression
70 "MSIE [4-6].", but works faster. Starting from version 0.8.11, "MSIE
71 6.0; ... SV1" is excluded from this mask.
72
73More information on these directives and their options can be found on the
74[Module
75ngx~httpgzipmodule~](https://nginx.org/en/docs/http/ngx_http_gzip_module.html)
76page in Nginx's documentation.
content/blog/2022-12-01-nginx-compression.org added +76
@@ -0,0 +1,76 @@
1#+date: <2022-12-01>
2#+title: Enable GZIP Compression in Nginx
3#+description:
4
5
6* Text Compression
7
8Text compression allows a web server to serve text-based resources
9faster than uncompressed data. This can speed up things like First
10Contentful Paint, Tie to Interactive, and Speed Index.
11
12* Enable Nginx Compression with gzip
13
14In order to enable text compression on Nginx, we need to enable it
15within the configuration file:
16
17#+begin_src sh
18nano /etc/nginx/nginx.conf
19#+end_src
20
21Within the =http= block, find the section that shows something like the
22block below. This is the default gzip configuration I found in my
23=nginx.conf= file on Alpine Linux 3.17. Yours may look slightly
24different, just make sure that you're not creating any duplicate gzip
25options.
26
27#+begin_src conf
28# Enable gzipping of responses.
29#gzip on;
30
31# Set the Vary HTTP header as defined in the RFC 2616. Default is 'off'.
32gzip_vary on;
33#+end_src
34
35Remove the default gzip lines and replace them with the following:
36
37#+begin_src conf
38# Enable gzipping of responses.
39gzip on;
40gzip_vary on;
41gzip_min_length 10240;
42gzip_proxied expired no-cache no-store private auth;
43gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml;
44gzip_disable "MSIE [1-6]";
45#+end_src
46
47* Explanations of ngx_{httpgzipmodule} Options
48
49Each of the lines above enables a different aspect of the gzip response
50for Nginx. Here are the full explanations:
51
52- =gzip= -- Enables or disables gzipping of responses.
53- =gzip_vary= -- Enables or disables inserting the "Vary:
54 Accept-Encoding" response header field if the directives gzip,
55 gzip_{static}, or gunzip are active.
56- =gzip_min_length= -- Sets the minimum length of a response that will
57 be gzipped. The length is determined only from the "Content-Length"
58 response header field.
59- =gzip_proxied= -- Enables or disables gzipping of responses for
60 proxied requests depending on the request and response. The fact that
61 the request is proxied is determined by the presence of the "Via"
62 request header field.
63- =gzip_types= -- Enables gzipping of responses for the specified MIME
64 types in addition to "text/html". The special value "*" matches any
65 MIME type (0.8.29). Responses with the "text/html" type are always
66 compressed.
67- =gzip_disable= -- Disables gzipping of responses for requests with
68 "User-Agent" header fields matching any of the specified regular
69 expressions.
70 - The special mask "msie6" (0.7.12) corresponds to the regular
71 expression "MSIE [4-6].", but works faster. Starting from version
72 0.8.11, "MSIE 6.0; ... SV1" is excluded from this mask.
73
74More information on these directives and their options can be found on
75the [[https://nginx.org/en/docs/http/ngx_http_gzip_module.html][Module
76ngx_{httpgzipmodule}]] page in Nginx's documentation.
content/blog/2022-12-07-nginx-wildcard-redirect.md → content/blog/2022-12-07-nginx-wildcard-redirect.org renamed +49 −49
@@ -1,26 +1,24 @@
1+++
2date = 2022-12-07
3title = "Redirect Nginx Subdomains and Trailing Content with Regex"
4description = ""
5draft = false
6+++
1#+date: <2022-12-07>
2#+title: Redirect Nginx Subdomains and Trailing Content with Regex
3#+description:
74
8# Problem
95
10I recently migrated domains and replaced the old webpage with a simple info page
11with instructions to users on how to edit their bookmarks and URLs to get to the
12page they were seeking.
6* Problem
137
14This was not ideal as it left the work up to the user and may have caused
15friction for users who accessed my RSS feed.
8I recently migrated domains and replaced the old webpage with a simple
9info page with instructions to users on how to edit their bookmarks and
10URLs to get to the page they were seeking.
1611
17# Solution
12This was not ideal as it left the work up to the user and may have
13caused friction for users who accessed my RSS feed.
1814
19Instead, I finally found a solution that allows me to redirect both subdomains
20AND trailing content. For example, both of these URLs now redirect properly
21using the logic I'll explain below:
15* Solution
2216
23```txt
17Instead, I finally found a solution that allows me to redirect both
18subdomains AND trailing content. For example, both of these URLs now
19redirect properly using the logic I'll explain below:
20
21#+begin_src txt
2422# Example 1 - Simple base domain redirect with trailing content
2523https://domain1.com/blog/alpine-linux/ -> https://domain2.com/blog/alpine-linux/
2624
@@ -28,23 +26,23 @@ https://domain1.com/blog/alpine-linux/ -> https://domain2.com/blog/alpine-linux/
2826https://libreddit.domain1.com/r/history/comments/7z8cbg/new_discovery_mode_turns_video_game_assassins/
2927->
3028https://libreddit.domain2.com/r/history/comments/7z8cbg/new_discovery_mode_turns_video_game_assassins/
31```
29#+end_src
3230
3331Go ahead, try the URLs if you want to test them.
3432
35## Nginx Config
33** Nginx Config
3634
37To make this possible. I needed to configure a proper redirect scheme in my
38Nginx configuration.
35To make this possible. I needed to configure a proper redirect scheme in
36my Nginx configuration.
3937
40```sh
38#+begin_src sh
4139doas nano /etc/nginx/http.d/domain1.conf
42```
40#+end_src
4341
44Within this file, I had one block configured to redirect HTTP requests to HTTPS
45for the base domain and all subdomains.
42Within this file, I had one block configured to redirect HTTP requests
43to HTTPS for the base domain and all subdomains.
4644
47```conf
45#+begin_src conf
4846server {
4947 listen [::]:80;
5048 listen 80;
@@ -60,13 +58,14 @@ server {
6058
6159 return 404;
6260}
63```
61#+end_src
6462
65For the base domain, I have another `server` block dedicated to redirecting all
66base domain requests. You can see that the `rewrite` line is instructing Nginx
67to gather all trailing content and append it to the new `domain2.com` URL.
63For the base domain, I have another =server= block dedicated to
64redirecting all base domain requests. You can see that the =rewrite=
65line is instructing Nginx to gather all trailing content and append it
66to the new =domain2.com= URL.
6867
69```conf
68#+begin_src conf
7069server {
7170 listen [::]:443 ssl http2;
7271 listen 443 ssl http2;
@@ -78,20 +77,21 @@ server {
7877 ssl_certificate /etc/letsencrypt/live/domain1.com/fullchain.pem;
7978 ssl_certificate_key /etc/letsencrypt/live/domain1.com/privkey.pem;
8079}
81```
80#+end_src
8281
83Finally, the tricky part is figuring out how to tell Nginx to redirect while
84keeping both a subdomain and trailing content intact. I found that the easiest
85way to do this is to give it a `server` block of its own.
82Finally, the tricky part is figuring out how to tell Nginx to redirect
83while keeping both a subdomain and trailing content intact. I found that
84the easiest way to do this is to give it a =server= block of its own.
8685
87Within this block, we need to do some regex on the `server_name` line before we
88can rewrite anything. This creates a variable called `subdomain`.
86Within this block, we need to do some regex on the =server_name= line
87before we can rewrite anything. This creates a variable called
88=subdomain=.
8989
90Once the server gets to the `rewrite` line, it pulls the `subdomain` variable
91from above and uses it on the new `domain2.com` domain before appending the
92trailing content (`$request_uri`).
90Once the server gets to the =rewrite= line, it pulls the =subdomain=
91variable from above and uses it on the new =domain2.com= domain before
92appending the trailing content (=$request_uri=).
9393
94```conf
94#+begin_src conf
9595server {
9696 listen [::]:443 ssl http2;
9797 listen 443 ssl http2;
@@ -103,17 +103,17 @@ server {
103103 ssl_certificate /etc/letsencrypt/live/domain1.com/fullchain.pem;
104104 ssl_certificate_key /etc/letsencrypt/live/domain1.com/privkey.pem;
105105}
106```
106#+end_src
107107
108That's all there is to it. With this, I simply restarted Nginx and watched the
109redirections work in-action.
108That's all there is to it. With this, I simply restarted Nginx and
109watched the redirections work in-action.
110110
111```sh
111#+begin_src sh
112112doas rc-service nginx restart
113```
113#+end_src
114114
115Looking back on it, I wish I had done this sooner. Who knows how many people
116went looking for my sites or bookmarks and gave up when they saw the redirect
117instructions page.
115Looking back on it, I wish I had done this sooner. Who knows how many
116people went looking for my sites or bookmarks and gave up when they saw
117the redirect instructions page.
118118
119119Oh well, it's done now. Live and learn.
content/blog/2022-12-17-st.md deleted −91
@@ -1,91 +0,0 @@
1+++
2date = 2022-12-17
3title = "Simple Terminal"
4description = ""
5draft = false
6+++
7
8# st
9
10[st](https://st.suckless.org) standards for Simple Terminal, a simple terminal
11implementation for X made by the [suckless](https://suckless.org) team.
12
13This post walks through the dependencies needed and process to build and install
14`st` on Fedora Workstation.
15
16## Obtain Files
17
18To start, obtain the source files for `st` via `git clone`.
19
20```sh
21mkdir ~/suckless && cd ~/suckless
22git clone https://git.suckless.org/st && cd st
23```
24
25## Dependencies
26
27Once you have the files and are in the `st` directory, ensure the following
28packages are installed.
29
30```sh
31sudo dnf update && sudo dnf upgrade
32sudo dnf install gcc patch libX11-devel libXft-devel
33```
34
35## Building
36
37Before building, ensure that you read the README file.
38
39```sh
40cat README
41```
42
43Once you've read the instructions, open the `config.mk` file and ensure it
44matches your setup. If you're not sure, leave the default options within the
45file.
46
47Finally, you can build `st` with the following command. Ensure you run as root
48(e.g., `sudo`) or else you may not end up with a usable application file.
49
50```sh
51sudo make clean install
52```
53
54## Customization (Patches)
55
56Note that customizing `st` requires you to modify the source files or to
57download one of the [available patches](https://st.suckless.org/patches/) for
58suckless.org.
59
60If you've already installed `st` and want to customize or install a patch, start
61by uninstalling the current program.
62
63```sh
64cd ~/suckless/st
65sudo make uninstall
66```
67
68Next, grab the `<path>.diff` file from the page of the patch you chose. For
69example, I will be using the
70[defaultfontsize](https://st.suckless.org/patches/defaultfontsize/) patch in the
71below example.
72
73```sh
74wget https://st.suckless.org/patches/defaultfontsize/st-defaultfontsize-20210225-4ef0cbd.diff
75```
76
77Once the file is downloaded inside the `st` folder, apply the patch and
78re-install the program. You may need to install the `patch` command if you don't
79have it installed already (you should have installed it above).
80
81```sh
82patch -i st-defaultfontsize-20210225-4ef0cbd.diff
83sudo make clean install
84```
85
86Once installed, you can use the default font size patch to launch `st` with any
87font size you wish:
88
89```sh
90st -z 16
91```
content/blog/2022-12-17-st.org added +92
@@ -0,0 +1,92 @@
1#+date: <2022-12-17>
2#+title: Simple Terminal
3#+description:
4
5
6* st
7
8[[https://st.suckless.org][st]] standards for Simple Terminal, a simple
9terminal implementation for X made by the
10[[https://suckless.org][suckless]] team.
11
12This post walks through the dependencies needed and process to build and
13install =st= on Fedora Workstation.
14
15** Obtain Files
16
17To start, obtain the source files for =st= via =git clone=.
18
19#+begin_src sh
20mkdir ~/suckless && cd ~/suckless
21git clone https://git.suckless.org/st && cd st
22#+end_src
23
24** Dependencies
25
26Once you have the files and are in the =st= directory, ensure the
27following packages are installed.
28
29#+begin_src sh
30sudo dnf update && sudo dnf upgrade
31sudo dnf install gcc patch libX11-devel libXft-devel
32#+end_src
33
34** Building
35
36Before building, ensure that you read the README file.
37
38#+begin_src sh
39cat README
40#+end_src
41
42Once you've read the instructions, open the =config.mk= file and ensure
43it matches your setup. If you're not sure, leave the default options
44within the file.
45
46Finally, you can build =st= with the following command. Ensure you run
47as root (e.g., =sudo=) or else you may not end up with a usable
48application file.
49
50#+begin_src sh
51sudo make clean install
52#+end_src
53
54** Customization (Patches)
55
56Note that customizing =st= requires you to modify the source files or to
57download one of the [[https://st.suckless.org/patches/][available
58patches]] for suckless.org.
59
60If you've already installed =st= and want to customize or install a
61patch, start by uninstalling the current program.
62
63#+begin_src sh
64cd ~/suckless/st
65sudo make uninstall
66#+end_src
67
68Next, grab the =<path>.diff= file from the page of the patch you chose.
69For example, I will be using the
70[[https://st.suckless.org/patches/defaultfontsize/][defaultfontsize]]
71patch in the below example.
72
73#+begin_src sh
74wget https://st.suckless.org/patches/defaultfontsize/st-defaultfontsize-20210225-4ef0cbd.diff
75#+end_src
76
77Once the file is downloaded inside the =st= folder, apply the patch and
78re-install the program. You may need to install the =patch= command if
79you don't have it installed already (you should have installed it
80above).
81
82#+begin_src sh
83patch -i st-defaultfontsize-20210225-4ef0cbd.diff
84sudo make clean install
85#+end_src
86
87Once installed, you can use the default font size patch to launch =st=
88with any font size you wish:
89
90#+begin_src sh
91st -z 16
92#+end_src
content/blog/2022-12-23-alpine-desktop.md → content/blog/2022-12-23-alpine-desktop.org renamed +104 −102
@@ -1,90 +1,89 @@
1+++
2date = 2022-12-23
3title = "Alpine Linux as a Desktop OS"
4description = ""
5draft = false
6+++
1#+date: <2022-12-23>
2#+title: Alpine Linux as a Desktop OS
3#+description:
74
8# Isn't Alpine Linux for Servers?
95
10This is a question I see a lot when people are presented with an example of
11Alpine Linux running as a desktop OS.
6* Isn't Alpine Linux for Servers?
127
13While Alpine is small, fast, and minimal, that doesn't stop it from functioning
14at a productive level for desktop users.
8This is a question I see a lot when people are presented with an example
9of Alpine Linux running as a desktop OS.
1510
16This post is documentation of how I installed and modified Alpine Linux to
17become my daily desktop OS.
11While Alpine is small, fast, and minimal, that doesn't stop it from
12functioning at a productive level for desktop users.
1813
19# Installation
14This post is documentation of how I installed and modified Alpine Linux
15to become my daily desktop OS.
2016
21Note that I cover the installation of Alpine Linux in my other post, so I won't
22repeat it here: [Alpine Linux: My New Server OS](../alpine-linux/).
17* Installation
2318
24Basically, get a bootable USB or whatever you prefer with Alpine on it, boot the
25ISO, and run the setup script.
19Note that I cover the installation of Alpine Linux in my other post, so
20I won't repeat it here: [[../alpine-linux/][Alpine Linux: My New Server
21OS]].
2622
27```sh
23Basically, get a bootable USB or whatever you prefer with Alpine on it,
24boot the ISO, and run the setup script.
25
26#+begin_src sh
2827setup-alpine
29```
28#+end_src
3029
31Once you have gone through all the options and installer finishes without
32errors, reboot.
30Once you have gone through all the options and installer finishes
31without errors, reboot.
3332
34```sh
33#+begin_src sh
3534reboot
36```
35#+end_src
3736
38# Initial Setup
37* Initial Setup
3938
4039Once Alpine is installed and the machine has rebooted, login is as root
41initially or `su` to root once you log in as your user. From here, you should
42start by updating and upgrading the system in case the ISO was not fully
43up-to-date.
40initially or =su= to root once you log in as your user. From here, you
41should start by updating and upgrading the system in case the ISO was
42not fully up-to-date.
4443
45```sh
44#+begin_src sh
4645# Update and upgrade system
4746apk -U update && apk -U upgrade
4847
4948# Add an editor so we can enable the community repository
5049apk add nano
51```
50#+end_src
5251
53You need to uncomment the `community` repository for your version of Alpine
54Linux.
52You need to uncomment the =community= repository for your version of
53Alpine Linux.
5554
56For v3.17, the `repositories` file should look like this:
55For v3.17, the =repositories= file should look like this:
5756
58```sh
57#+begin_src sh
5958nano /etc/apk/repositories
60```
59#+end_src
6160
62```conf
61#+begin_src conf
6362#/media/sda/apks
6463http://mirrors.gigenet.com/alpinelinux/v3.17/main
6564http://mirrors.gigenet.com/alpinelinux/v3.17/community
6665#http://mirrors.gigenet.com/alpinelinux/edge/main
6766#http://mirrors.gigenet.com/alpinelinux/edge/community
6867#http://mirrors.gigenet.com/alpinelinux/edge/testing
69```
68#+end_src
7069
71```sh
70#+begin_src sh
7271# Add the rest of your packages
7372apk add linux-firmware iwd doas git curl wget
7473
7574# Add yourself to the wheel group so you can use the doas command
7675adduser $USER wheel
77```
76#+end_src
7877
79# Window Manager (Desktop)
78* Window Manager (Desktop)
8079
81The [Sway installation guide](https://wiki.alpinelinux.org/wiki/Sway) has
82everything you need to get Sway working on Alpine.
80The [[https://wiki.alpinelinux.org/wiki/Sway][Sway installation guide]]
81has everything you need to get Sway working on Alpine.
8382
84However, I'll include a brief list of the commands I ran and their purpose for
85posterity here.
83However, I'll include a brief list of the commands I ran and their
84purpose for posterity here.
8685
87```sh
86#+begin_src sh
8887# Add eudev and set it up
8988apk add eudev
9089setup-devd udev
@@ -121,21 +120,21 @@ apk add \ # Install optional dependencies:
121120 swaylock swaylockd \ # lockscreen tool
122121 swaybg \ # wallpaper daemon
123122 swayidle # idle management (DPMS) daemon
124```
123#+end_src
125124
126125Once you have the packages installed and set-up, you need to export the
127`XDG_RUNTIME_DIR` upon login. To do this, edit your `.profile` file.
126=XDG_RUNTIME_DIR= upon login. To do this, edit your =.profile= file.
128127
129If you use another shell, such as `zsh`, you need to edit that shell's profile
130(e.g., `~/.zprofile`)!
128If you use another shell, such as =zsh=, you need to edit that shell's
129profile (e.g., =~/.zprofile=)!
131130
132```sh
131#+begin_src sh
133132nano ~/.profile
134```
133#+end_src
135134
136135Within the file, paste this:
137136
138```sh
137#+begin_src sh
139138if test -z "${XDG_RUNTIME_DIR}"; then
140139 export XDG_RUNTIME_DIR=/tmp/$(id -u)-runtime-dir
141140 if ! test -d "${XDG_RUNTIME_DIR}"; then
@@ -143,19 +142,20 @@ if test -z "${XDG_RUNTIME_DIR}"; then
143142 chmod 0700 "${XDG_RUNTIME_DIR}"
144143 fi
145144fi
146```
145#+end_src
147146
148147Once that's complete, you can launch Sway manually.
149148
150```sh
149#+begin_src sh
151150dbus-run-session -- sway
152```
151#+end_src
153152
154## Personal Touches
153** Personal Touches
155154
156I also added the following packages, per my personal preferences and situation.
155I also added the following packages, per my personal preferences and
156situation.
157157
158```sh
158#+begin_src sh
159159doas apk add brightnessctl \ # Brightness controller
160160 zsh \ # Shell
161161 firefox \ # Browser
@@ -165,90 +165,92 @@ doas apk add brightnessctl \ # Brightness controller
165165 neomutt \ # CLI email client
166166 thunderbird \ # GUI email client
167167 gnupg # GPG key manager
168```
168#+end_src
169169
170From here, I use my Syncthing storage to pull all the configuration files I
171stored from prior desktops, such as [my
172dotfiles](https://git.sr.ht/~cyborg/dotfiles).
170From here, I use my Syncthing storage to pull all the configuration
171files I stored from prior desktops, such as
172[[https://git.sr.ht/~cyborg/dotfiles][my dotfiles]].
173173
174# Resolving Issues
174* Resolving Issues
175175
176## WiFi Issues
176** WiFi Issues
177177
178I initially tried to set up my Wi-Fi the standard way with `iwd`, but it didn't
179work.
178I initially tried to set up my Wi-Fi the standard way with =iwd=, but it
179didn't work.
180180
181Here is what I initially tried (I did all of this as `root`):
181Here is what I initially tried (I did all of this as =root=):
182182
183```sh
183#+begin_src sh
184184apk add iwd
185185rc-service iwd start
186186iwctl station wlan0 connect <SSID> # This will prompt for the password
187187rc-update add iwd boot && rc-update add dbus boot
188```
188#+end_src
189189
190Then, I added the Wi-Fi entry to the bottom of the networking interface file:
190Then, I added the Wi-Fi entry to the bottom of the networking interface
191file:
191192
192```sh
193#+begin_src sh
193194nano /etc/network/interfaces
194```
195#+end_src
195196
196```conf
197#+begin_src conf
197198auto wlan0
198199iface wlan0 inet dhcp
199```
200#+end_src
200201
201202Finally, restart the networking service:
202203
203```sh
204#+begin_src sh
204205rc-service networking restart
205```
206#+end_src
206207
207My Wi-Fi interface would receive an IP address from the router, but it could not
208ping anything in the network. To solve the Wi-Fi issues, I originally upgraded
209to Alpine's `edge` repositories, which was unnecessary.
208My Wi-Fi interface would receive an IP address from the router, but it
209could not ping anything in the network. To solve the Wi-Fi issues, I
210originally upgraded to Alpine's =edge= repositories, which was
211unnecessary.
210212
211Really, the solution was to enable the `NameResolvingService=resolvconf` in
212`/etc/iwd/main.conf`.
213Really, the solution was to enable the =NameResolvingService=resolvconf=
214in =/etc/iwd/main.conf=.
213215
214```sh
216#+begin_src sh
215217doas nano /etc/iwd/main.conf
216```
218#+end_src
217219
218```conf
220#+begin_src conf
219221[Network]
220222
221223NameResolvingService=resolvconf
222```
224#+end_src
223225
224226Once I finished this process, my Wi-Fi is working flawlessly.
225227
226## Sound Issues
228** Sound Issues
227229
228Same as with the Wi-Fi, I had no sound and could not control the mute/unmute or
229volume buttons on my laptop.
230Same as with the Wi-Fi, I had no sound and could not control the
231mute/unmute or volume buttons on my laptop.
230232
231233To resolve this, I installed
232[pipewire](https://wiki.alpinelinux.org/wiki/PipeWire).
234[[https://wiki.alpinelinux.org/wiki/PipeWire][pipewire]].
233235
234```sh
236#+begin_src sh
235237# Add your user to the following groups
236238addgroup $USER audio
237239addgroup $USER video
238240
239241# Install pipewire and other useful packages
240242apk add pipewire wireplumber pipewire-pulse pipewire-jack pipewire-alsa
241```
243#+end_src
242244
243Finally, I needed to add `/usr/libexec/pipewire-launcher` to my
244`.config/sway/config` file so that Pipewire would run every time I launched
245sway.
245Finally, I needed to add =/usr/libexec/pipewire-launcher= to my
246=.config/sway/config= file so that Pipewire would run every time I
247launched sway.
246248
247```sh
249#+begin_src sh
248250nano ~/.config/sway/config
249```
251#+end_src
250252
251```conf
253#+begin_src conf
252254# Run pipewire audio server
253255exec /usr/libexec/pipewire-launcher
254256
@@ -257,10 +259,10 @@ bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume @DEFAULT
257259bindsym XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ -5%
258260bindsym XF86AudioMute exec --no-startup-id pactl set-sink-mute @DEFAULT_SINK@ toggle
259261bindsym XF86AudioMicMute exec --no-startup-id pactl set-source-mute @DEFAULT_SOURCE@ toggle
260```
262#+end_src
261263
262Note that I do not use bluetooth or screen sharing, so I won't cover those
263options in this post.
264Note that I do not use bluetooth or screen sharing, so I won't cover
265those options in this post.
264266
265Other than these issues, I have a working Alpine desktop. No other complaints
266thus far!
267Other than these issues, I have a working Alpine desktop. No other
268complaints thus far!
content/blog/2023-01-03-recent-website-changes.md deleted −82
@@ -1,82 +0,0 @@
1+++
2date = 2023-01-03
3title = "Recent Website Changes"
4description = ""
5draft = false
6+++
7
8# The State of This Website
9
10Over the years, this website has changed URLs, styles, content focus, and much
11more. However, it seems that I am never done, as I am yet again changing this
12page to fit my current needs and wants.
13
14While this site was already minimal (~12kb), it contained a lot of disorganized
15content and some poorly chosen color schemes.
16
17The recent updates attempt to fix these items while focusing on what I truly
18care about here: the content within each page.
19
20# Recent Changes
21
22I've made quite a few changes to the style of this website today, both in the
23CSS and in the HTML.
24
25Here are some of the key takeaways from today's changes:
26
271. Reduce the width of the website from `60em` to `40em`.
282. Remove breadcrumb navigation and replaced with a simple "Return Home" link
29 when visiting anything other than the homepage.
303. Remove syntax highlighting from code blocks. CSS now loads from a single
31 file.
324. Move blog posts on the homepage to the top, pushing tertiary content down.
335. Update font-family from `monospace` to `sans-serif` for readability.
34
35# Future Focus
36
37## Accessibility
38
39My main focus amidst all the changes this past year was accessibility. I tried
40to improve the accessibility of all pages on this site as well as I know how.
41For example, I added aria labels to identify different navigation menus in
42November.
43
44I want to continue this in 2023 and learn more about the accessibility features
45I can utilize to help those who may need them.
46
47## Content First
48
49Beyond accessibility, I am making a concerted effort to bring the content of
50this site to the forefront and push the tertiary information (e.g., About Me,
51Services, etc.) down below the content.
52
53Further, I want to review and edit previous blog posts for grammar, accuracy,
54dead links, and more. Where necessary, I may even add a blurb at the top of old
55posts that are no longer accurate or helpful.
56
57## Style
58
59As always, I will be searching for ways to reduce distractions and bring a
60minimalistic approach to this site. While there are certainly more drastic
61measures I could take, such as making this a [Shinobi
62Website](https://shinobi.bt.ht) or a [No CSS Website](https://nocss.club), I
63prefer to keep some modern features here.
64
65Two ideas have been floating around in my head, but I am not sure how I feel
66about these approaches yet:
67
681. Going all-in with Emacs org-mode and creating a script to auto-publish all my
69 blog posts from org-mode to plain HTML files and then letting my CSS style it
70 after the fact.
712. Focus on publishing for Gemini or Gopher and then utilize a conversion
72 program to translate the pages to HTML.
73
74# Looking Back
75
76As I sit here and finalize the changes, as well as this post, I am happy with
77the current state of this website. It's certainly a long way from my first
78attempts (parallax background images, anyone?) and it's good to see the
79progress.
80
81I can only hope and work hard to ensure that I make more meaningful progress by
82this time next year.
content/blog/2023-01-03-recent-website-changes.org added +81
@@ -0,0 +1,81 @@
1#+date: <2023-01-03>
2#+title: Recent Website Changes
3#+description:
4
5
6* The State of This Website
7
8Over the years, this website has changed URLs, styles, content focus,
9and much more. However, it seems that I am never done, as I am yet again
10changing this page to fit my current needs and wants.
11
12While this site was already minimal (~12kb), it contained a lot of
13disorganized content and some poorly chosen color schemes.
14
15The recent updates attempt to fix these items while focusing on what I
16truly care about here: the content within each page.
17
18* Recent Changes
19
20I've made quite a few changes to the style of this website today, both
21in the CSS and in the HTML.
22
23Here are some of the key takeaways from today's changes:
24
251. Reduce the width of the website from =60em= to =40em=.
262. Remove breadcrumb navigation and replaced with a simple "Return Home"
27 link when visiting anything other than the homepage.
283. Remove syntax highlighting from code blocks. CSS now loads from a
29 single file.
304. Move blog posts on the homepage to the top, pushing tertiary content
31 down.
325. Update font-family from =monospace= to =sans-serif= for readability.
33
34* Future Focus
35
36** Accessibility
37
38My main focus amidst all the changes this past year was accessibility. I
39tried to improve the accessibility of all pages on this site as well as
40I know how. For example, I added aria labels to identify different
41navigation menus in November.
42
43I want to continue this in 2023 and learn more about the accessibility
44features I can utilize to help those who may need them.
45
46** Content First
47
48Beyond accessibility, I am making a concerted effort to bring the
49content of this site to the forefront and push the tertiary information
50(e.g., About Me, Services, etc.) down below the content.
51
52Further, I want to review and edit previous blog posts for grammar,
53accuracy, dead links, and more. Where necessary, I may even add a blurb
54at the top of old posts that are no longer accurate or helpful.
55
56** Style
57
58As always, I will be searching for ways to reduce distractions and bring
59a minimalistic approach to this site. While there are certainly more
60drastic measures I could take, such as making this a
61[[https://shinobi.bt.ht][Shinobi Website]] or a [[https://nocss.club][No
62CSS Website]], I prefer to keep some modern features here.
63
64Two ideas have been floating around in my head, but I am not sure how I
65feel about these approaches yet:
66
671. Going all-in with Emacs org-mode and creating a script to
68 auto-publish all my blog posts from org-mode to plain HTML files and
69 then letting my CSS style it after the fact.
702. Focus on publishing for Gemini or Gopher and then utilize a
71 conversion program to translate the pages to HTML.
72
73* Looking Back
74
75As I sit here and finalize the changes, as well as this post, I am happy
76with the current state of this website. It's certainly a long way from
77my first attempts (parallax background images, anyone?) and it's good to
78see the progress.
79
80I can only hope and work hard to ensure that I make more meaningful
81progress by this time next year.
content/blog/2023-01-05-mass-unlike-tumblr-posts.md deleted −92
@@ -1,92 +0,0 @@
1+++
2date = 2023-01-05
3title = " How to Easily Mass Unlike Tumblr Posts with Javascript"
4description = ""
5draft = false
6+++
7
8# The Dilemma
9
10The dilemma I had was pretty simple: I wanted to unlike all the posts I have
11liked on Tumblr so that I could follow a new focus on blogs and start fresh.
12Otherwise, Tumblr will keep recommending content based on your previous likes.
13
14# The Solution
15
16I searched the web for a while and noted that most solutions referenced Tumblr
17setting and dashboard pages that no longer exist. Additionally, I did not want
18to install a third party extension to do this, as some suggested.
19
20Luckily, I used Javascript for a while a few years ago and figured it would be
21easy enough to script a solution, as long as Tumblr had a system for the unlike
22buttons.
23
24## Identifying Unlike Buttons
25
26Tumblr's unlike buttons are structured as you can see in the following code
27block. All unlike buttons have an `aria-label` with a value of `Unlike`.
28
29```html
30<button class="TRX6J" aria-label="Unlike">
31 <span class="EvhBA B1Z5w ztpfZ" tabindex="-1">
32 <svg
33 xmlns="http://www.w3.org/2000/svg"
34 height="21"
35 width="23"
36 role="presentation"
37 >
38 <use href="#managed-icon__like-filled"></use>
39 </svg>
40 </span>
41</button>
42```
43
44## Running a Script to Unlike All Likes
45
46To run this script, you will need to load the [Likes \|
47Tumblr](https://www.tumblr.com/likes) page while logged in to your account.
48
49Further, be sure to scroll down to the bottom and force Tumblr to load more
50posts so that this script unlikes more posts at a time.
51
52Once you are logged in and the page is loaded, open the Developer Tools and be
53sure you're on the "Console" tab. It should look something like this (this is in
54Firefox, Chromium should be similar):
55
56![Firefox Dev
57!Tools](https:///img.cleberg.net/blog/20230105-mass-unlike-tumblr-posts/dev_console.png)
58
59All you need to do is paste the following snippet into the dev console. This
60code will collect all unlike buttons (`elements`) and then click each button to
61unlike it.
62
63Optionally, you can comment-out the line `elements[i].click();` and uncomment
64the `console.log()` lines to simply print out information without performing any
65actions. This can be useful to debug issues or confirm that the code below isn't
66doing anything you don't want it to.
67
68```javascript
69const elements = document.querySelectorAll('[aria-label="Unlike"]');
70// console.log(elements); // 👉 [button]
71
72for (let i = 0; i < elements.length; i++) {
73 // console.log(elements[i]);
74 elements[i].click();
75}
76```
77
78# Results
79
80The results were quick for my situation, as it unliked ~200 posts within 2-3
81seconds. I am not sure how this will perform on larger sets of likes (or if
82Tumblr has a limit to unliking posts).
83
84You can see the below screenshot showing that I pasted the snippet into the
85console, pressed Enter, and then the posts are automatically unliked.
86
87![Script
88!Results](https:///img.cleberg.net/blog/20230105-mass-unlike-tumblr-posts/script_results.png)
89
90Thinking about this further, I would bet that this would be fairly simple to
91package into a browser add-on so that users could install the add-on, go to
92their Likes page, and click a button to run the script. Food for thought.
content/blog/2023-01-05-mass-unlike-tumblr-posts.org added +96
@@ -0,0 +1,96 @@
1#+date: <2023-01-05>
2#+title: How to Easily Mass Unlike Tumblr Posts with Javascript
3#+description:
4
5
6* The Dilemma
7
8The dilemma I had was pretty simple: I wanted to unlike all the posts I
9have liked on Tumblr so that I could follow a new focus on blogs and
10start fresh. Otherwise, Tumblr will keep recommending content based on
11your previous likes.
12
13* The Solution
14
15I searched the web for a while and noted that most solutions referenced
16Tumblr setting and dashboard pages that no longer exist. Additionally, I
17did not want to install a third party extension to do this, as some
18suggested.
19
20Luckily, I used Javascript for a while a few years ago and figured it
21would be easy enough to script a solution, as long as Tumblr had a
22system for the unlike buttons.
23
24** Identifying Unlike Buttons
25
26Tumblr's unlike buttons are structured as you can see in the following
27code block. All unlike buttons have an =aria-label= with a value of
28=Unlike=.
29
30#+begin_src html
31<button class="TRX6J" aria-label="Unlike">
32 <span class="EvhBA B1Z5w ztpfZ" tabindex="-1">
33 <svg
34 xmlns="http://www.w3.org/2000/svg"
35 height="21"
36 width="23"
37 role="presentation"
38 >
39 <use href="#managed-icon__like-filled"></use>
40 </svg>
41 </span>
42</button>
43#+end_src
44
45** Running a Script to Unlike All Likes
46
47To run this script, you will need to load the
48[[https://www.tumblr.com/likes][Likes | Tumblr]] page while logged in to
49your account.
50
51Further, be sure to scroll down to the bottom and force Tumblr to load
52more posts so that this script unlikes more posts at a time.
53
54Once you are logged in and the page is loaded, open the Developer Tools
55and be sure you're on the "Console" tab. It should look something like
56this (this is in Firefox, Chromium should be similar):
57
58#+caption: Firefox Dev !Tools
59[[https:///img.cleberg.net/blog/20230105-mass-unlike-tumblr-posts/dev_console.png]]
60
61All you need to do is paste the following snippet into the dev console.
62This code will collect all unlike buttons (=elements=) and then click
63each button to unlike it.
64
65Optionally, you can comment-out the line =elements[i].click();= and
66uncomment the =console.log()= lines to simply print out information
67without performing any actions. This can be useful to debug issues or
68confirm that the code below isn't doing anything you don't want it to.
69
70#+begin_src javascript
71const elements = document.querySelectorAll('[aria-label="Unlike"]');
72// console.log(elements); // 👉 [button]
73
74for (let i = 0; i < elements.length; i++) {
75 // console.log(elements[i]);
76 elements[i].click();
77}
78#+end_src
79
80* Results
81
82The results were quick for my situation, as it unliked ~200 posts within
832-3 seconds. I am not sure how this will perform on larger sets of likes
84(or if Tumblr has a limit to unliking posts).
85
86You can see the below screenshot showing that I pasted the snippet into
87the console, pressed Enter, and then the posts are automatically
88unliked.
89
90#+caption: Script !Results
91[[https:///img.cleberg.net/blog/20230105-mass-unlike-tumblr-posts/script_results.png]]
92
93Thinking about this further, I would bet that this would be fairly
94simple to package into a browser add-on so that users could install the
95add-on, go to their Likes page, and click a button to run the script.
96Food for thought.
content/blog/2023-01-08-fedora-login-manager.md deleted −45
@@ -1,45 +0,0 @@
1+++
2date = 2023-01-08
3title = "How to Remove the Login Manager from Fedora i3"
4description = ""
5draft = false
6+++
7
8# Fedora i3's Login Manager
9
10Since I use the i3 spin of Fedora Workstation, I don't like to have a login
11manager installed by default. As of the current version of Fedora i3, the
12default login manager is LightDM.
13
14If this is no longer the case, you can search for currently-installed packages
15with the following command and see if you can identify a different login
16manager.
17
18```sh
19sudo dnf list installed
20```
21
22# Removing the Login Manager
23
24In order to remove the login manager, simply uninstall the package.
25
26```sh
27sudo dnf remove lightdm
28```
29
30# Launching i3 Manually
31
32In order to launch i3 manually, you need to set up your X session properly. To
33start, create or edit the `~/.xinitrc` file to include the following at the
34bottom.
35
36```config
37exec i3
38```
39
40Now, whenever you log in to the TTY, you can launch your desktop with the
41following command.
42
43```sh
44startx
45```
content/blog/2023-01-08-fedora-login-manager.org added +43
@@ -0,0 +1,43 @@
1#+date: <2023-01-08>
2#+title: How to Remove the Login Manager from Fedora i3
3#+description:
4
5
6* Fedora i3's Login Manager
7
8Since I use the i3 spin of Fedora Workstation, I don't like to have a
9login manager installed by default. As of the current version of Fedora
10i3, the default login manager is LightDM.
11
12If this is no longer the case, you can search for currently-installed
13packages with the following command and see if you can identify a
14different login manager.
15
16#+begin_src sh
17sudo dnf list installed
18#+end_src
19
20* Removing the Login Manager
21
22In order to remove the login manager, simply uninstall the package.
23
24#+begin_src sh
25sudo dnf remove lightdm
26#+end_src
27
28* Launching i3 Manually
29
30In order to launch i3 manually, you need to set up your X session
31properly. To start, create or edit the =~/.xinitrc= file to include the
32following at the bottom.
33
34#+begin_src config
35exec i3
36#+end_src
37
38Now, whenever you log in to the TTY, you can launch your desktop with
39the following command.
40
41#+begin_src sh
42startx
43#+end_src
content/blog/2023-01-21-flatpak-symlinks.md deleted −46
@@ -1,46 +0,0 @@
1+++
2date = 2023-01-21
3title = "Running Flatpak Apps with Symlinks"
4description = ""
5draft = false
6+++
7
8# Running Flatpak Apps Should Be Faster
9
10If you're like me and use Flatpak for those pesky apps that cannot run on your
11system for one reason or another, you likely get annoyed with opening a terminal
12and manually running the Flatpak app with the lengthy `flatpak run ...` command.
13
14In the past, I manually created aliases in my `.zshrc` file for certain apps.
15For example, an alias would look like the example below.
16
17This would allow me to run the command fast within the terminal, but it wouldn't
18allow me to run it in an application launcher.
19
20```sh
21# ~/.zshrc
22alias librewolf = "flatpak run io.gitlab.librewolf-community"
23```
24
25However, I now use a much faster and better method that integrates with the
26tiling WMs I use and their application launchers - `dmenu` and `bemenu`.
27
28# Creating Symlinks for Flatpak Apps
29
30Let's use the example of Librewolf below. I can install the application like so:
31
32```sh
33flatpak install flathub io.gitlab.librewolf-community
34```
35
36Once installed, I can create a symlink to link the flatpak app to my new symlink
37in a location commonly included in your PATH. In this case, I chose `/usr/bin`.
38You may need to choose a different location if `/usr/bin` isn't in your PATH.
39
40```sh
41ln -s /var/lib/flatpak/exports/bin/io.gitlab.librewolf-community /usr/bin/librewolf
42```
43
44Once complete, you should be able to launch the app using the command name you
45chose above in the symlink (`librewolf`) from a terminal or from your
46application launcher!
content/blog/2023-01-21-flatpak-symlinks.org added +48
@@ -0,0 +1,48 @@
1#+date: <2023-01-21>
2#+title: Running Flatpak Apps with Symlinks
3#+description:
4
5
6* Running Flatpak Apps Should Be Faster
7
8If you're like me and use Flatpak for those pesky apps that cannot run
9on your system for one reason or another, you likely get annoyed with
10opening a terminal and manually running the Flatpak app with the lengthy
11=flatpak run ...= command.
12
13In the past, I manually created aliases in my =.zshrc= file for certain
14apps. For example, an alias would look like the example below.
15
16This would allow me to run the command fast within the terminal, but it
17wouldn't allow me to run it in an application launcher.
18
19#+begin_src sh
20# ~/.zshrc
21alias librewolf = "flatpak run io.gitlab.librewolf-community"
22#+end_src
23
24However, I now use a much faster and better method that integrates with
25the tiling WMs I use and their application launchers - =dmenu= and
26=bemenu=.
27
28* Creating Symlinks for Flatpak Apps
29
30Let's use the example of Librewolf below. I can install the application
31like so:
32
33#+begin_src sh
34flatpak install flathub io.gitlab.librewolf-community
35#+end_src
36
37Once installed, I can create a symlink to link the flatpak app to my new
38symlink in a location commonly included in your PATH. In this case, I
39chose =/usr/bin=. You may need to choose a different location if
40=/usr/bin= isn't in your PATH.
41
42#+begin_src sh
43ln -s /var/lib/flatpak/exports/bin/io.gitlab.librewolf-community /usr/bin/librewolf
44#+end_src
45
46Once complete, you should be able to launch the app using the command
47name you chose above in the symlink (=librewolf=) from a terminal or
48from your application launcher!
content/blog/2023-01-23-random-wireguard.md → content/blog/2023-01-23-random-wireguard.org renamed +53 −53
@@ -1,47 +1,47 @@
1+++
2date = 2023-01-23
3title = "Connecting to a Random Mullvad Wireguard Host on Boot"
4description = ""
5draft = false
6+++
1#+date: <2023-01-23>
2#+title: Connecting to a Random Mullvad Wireguard Host on Boot
3#+description:
74
8# Mullvad Wireguard
95
10If you're using an OS that does not support one of Mullvad's apps, you're likely
11using the Wireguard configuration files instead.
6* Mullvad Wireguard
127
13If not, the first step is to visit Mullvad's [Wireguard configuration
14files](https://mullvad.net/en/account/#/wireguard-config) page and download a
15ZIP of the configuration files you want to use.
8If you're using an OS that does not support one of Mullvad's apps,
9you're likely using the Wireguard configuration files instead.
1610
17Personally, I downloaded all configuration files across the world and chose my
18connections using the script below.
11If not, the first step is to visit Mullvad's
12[[https://mullvad.net/en/account/#/wireguard-config][Wireguard
13configuration files]] page and download a ZIP of the configuration files
14you want to use.
1915
20Once the files are downloaded, unzip them and move them to your preferred
21location:
16Personally, I downloaded all configuration files across the world and
17chose my connections using the script below.
2218
23```sh
19Once the files are downloaded, unzip them and move them to your
20preferred location:
21
22#+begin_src sh
2423cd Downloads
2524unzip mullvad_wireguard_linux_all_all.zip
2625mkdir ~/mullvad && mv ~/Downloads/*.conf ~/mullvad/
27```
26#+end_src
2827
29### Creating a Script to Connect to a Random Host
28*** Creating a Script to Connect to a Random Host
3029
31Once you have a folder of Wireguard configuration files from Mullvad, you can
32create a script to randomly connect to any one of the locations.
30Once you have a folder of Wireguard configuration files from Mullvad,
31you can create a script to randomly connect to any one of the locations.
3332
34Start by creating a shell script - mine is called `vpn.sh`.
33Start by creating a shell script - mine is called =vpn.sh=.
3534
36```sh
35#+begin_src sh
3736nano ~/vpn.sh
38```
37#+end_src
3938
40Within this script, you can paste the following info. Note that I specify `us-*`
41in my script, which means that it will only consider US-based VPN locations. You
42can alter this or simply change it `*` to consider all locations.
39Within this script, you can paste the following info. Note that I
40specify =us-*= in my script, which means that it will only consider
41US-based VPN locations. You can alter this or simply change it =*= to
42consider all locations.
4343
44```sh
44#+begin_src sh
4545#!/bin/sh
4646
4747ls /home/$USER/mullvad/us-** |sort -R |tail -n 1 |while read file; do
@@ -52,19 +52,19 @@ ls /home/$USER/mullvad/us-** |sort -R |tail -n 1 |while read file; do
5252 printf "\n\nPrinting new IP info:\n"
5353 curl https://am.i.mullvad.net/connected
5454done
55```
55#+end_src
5656
57Once you've modified the script to your liking, add executable permissions and
58run the script:
57Once you've modified the script to your liking, add executable
58permissions and run the script:
5959
60```sh
60#+begin_src sh
6161chmod +x ~/vpn.sh
6262~/vpn.sh
63```
63#+end_src
6464
6565The output should look like the following:
6666
67```txt
67#+begin_src txt
6868doas (user@host) password:
6969
7070# ... The script will process all of the iptables and wg commands here
@@ -73,40 +73,40 @@ Created Mullvad wireguard connection with file: /home/user/mullvad/us-nyc-wg-210
7373
7474Printing new IP info:
7575You are connected to Mullvad (server country-city-wg-num). Your IP address is 12.345.678.99
76```
76#+end_src
7777
78That's all there is to it. You can see your new location and IP via the `printf`
79and `curl` commands included in the script.
78That's all there is to it. You can see your new location and IP via the
79=printf= and =curl= commands included in the script.
8080
81You can also go to the [Connection Check ​\|
82Mullvad](https://mullvad.net/en/check/) page to see if you are fully connected
83to Mullvad and if any leaks exist.
81You can also go to the [[https://mullvad.net/en/check/][Connection Check
82​| Mullvad]] page to see if you are fully connected to Mullvad and if any
83leaks exist.
8484
85# Disconnecting from the Wireguard Connection
85* Disconnecting from the Wireguard Connection
8686
87If you forget which connection you're using, you can execute the following
88command to see where Wireguard is currently connected:
87If you forget which connection you're using, you can execute the
88following command to see where Wireguard is currently connected:
8989
90```sh
90#+begin_src sh
9191wg show
92```
92#+end_src
9393
9494This command will show you the Wireguard interfaces and should output a
95connection like so: `interface: us-lax-wg-104`.
95connection like so: =interface: us-lax-wg-104=.
9696
9797Once you have this, just disconnect using that files' full path:
9898
99```sh
99#+begin_src sh
100100wg-quick down /home/user/mullvad/us-lax-wg-104.conf
101```
101#+end_src
102102
103I have a TODO item on figuring out how to easily export an environment variable
104that contains the configuration file's full name, so that I can just execute the
105following:
103I have a TODO item on figuring out how to easily export an environment
104variable that contains the configuration file's full name, so that I can
105just execute the following:
106106
107```sh
107#+begin_src sh
108108# Ideal situation if I can export the $file variable to the environment
109109wg-quick down $file
110```
110#+end_src
111111
112112If you have an idea on how to do this, email me!
content/blog/2023-01-28-self-hosting-wger.md deleted −142
@@ -1,142 +0,0 @@
1+++
2date = 2023-01-28
3title = "Self-Hosting Wger Workout Manager"
4description = ""
5draft = false
6+++
7
8# Wger: The Self-Hosted Workout Manager
9
10[Wger Workout Manager](https://wger.de) is a fitness tracking tool for those who
11enjoy self-hosting their data. You can also register an account on their main
12website if you'd prefer to try without self-hosting.
13
14## Features
15
16I didn't see a full listing of features anywhere, so I compiled this list of my
17own after installing wger:
18
191. Dashboard
20
21 - Dashboard view of Workout Schedule, Nutrition Plan, Weight Graph, & last
22 5 Weight Logs
23
242. Training
25
26 - Workout Log
27 - Workout Schedule
28 - Calendar (shows weight logs and Bad/Neutral/Good days)
29 - Gallery (shows images you upload)
30 - Workout templates
31 - Public templates
32 - Exercises
33
343. Nutrition
35
36 - Nutrition plans
37 - BMI calculator
38 - Daily calories calculator
39 - Ingredient overview
40
414. Body Weight
42
43 - Weight overview
44
45## Documentation
46
47In order to self-host wger, I opted to use the Docker version of the
48application. You can read the README within the
49[wger-project/docker](https://github.com/wger-project/docker) project on GitHub
50for information and installation instructions.
51
52## Installation
53
54To start the installation, I created a folder for wger and started creating the
55three necessary files:
56
57```sh
58mkdir ~/wger && mkdir ~/wger/config
59touch ~/wger/docker-compose.yml && \
60touch ~/wger/config/prod.env && \
61touch ~/wger/config/nginx.conf
62```
63
64Once you have the folders and files created, you will need to copy the contents
65of the `docker-compose.yml`, `prod.env`, and `nginx.conf` from the GitHub link
66above.
67
68A few notes to explain the changes I made to the default files:
69
70- I updated the `ALLOW_REGISTRAION` variable in `prod.env` to `False` after I
71 created an account via my LAN connection, **before** I connected this app to
72 a publicly-available domain.
73- I uncommented and updated `CSRF_TRUSTED_ORIGINS` to be equal to the public
74 version of this app: `https://wger.example.com`.
75- I updated the port within `docker-compose.yml`, within the `nginx` block.
76 The port I updated this to will be reflected in my nginx configuration file
77 on the server (NOT the wger nginx.conf file).
78
79## Deploy
80
81Once all files are created and modified to your needs, simply start the
82container.
83
84```sh
85docker-compose up -d
86```
87
88You can now visit the website on your LAN by going to `localhost:YOUR_PORT` or
89by the server's IP, if you're not on the same machine that is running the
90container.
91
92If you wish to connect this app to a public domain name, you'll need to point an
93`A` DNS record from the domain to your server's public IP. You'll then need to
94create a configuration file for whichever web server or reverse proxy you're
95using.
96
97Wger's README suggests the following reverse proxy configuration for Nginx:
98
99```conf
100upstream wger {
101 # This port should match the port in the `nginx` block of docker-compose.yml
102 # If the container is running on this same machine, replace this with
103 # server 127.0.0.1:8080
104 server 123.456.789.0:8080;
105}
106
107server {
108 listen 80;
109 listen [::]:443 ssl;
110 listen 443 ssl;
111
112 location / {
113 proxy_pass http://wger;
114 proxy_set_header Host $http_host;
115 proxy_set_header X-Real-IP $remote_addr;
116 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
117 proxy_set_header X-Forwarded-Proto $scheme;
118 proxy_redirect off;
119 }
120
121 server_name my.domain.example.com;
122
123 ssl_certificate /path/to/https/certificate.crt;
124 ssl_certificate_key /path/to/https/certificate.key;
125}
126```
127
128# Thoughts on Wger
129
130I'm still playing around with the app itself, but it seems to be a solid
131all-around workout manager, weight log, and food log.
132
133I like that the weight log graph is fluid and updates quickly. You can also
134import or export data in CSV format if you'd like to move your data elsewhere.
135
136The workout manager is slightly odd, as it requires you to enter sets and reps
137for each exercise when you enter it into the plan. Then, when you add a log
138entry for performing a workout, you then add what you actually performed, in
139terms of reps and weight.
140
141I haven't tried the food log yet and I likely will not, at least for a while. I
142have no need for a food log or calorie tracker at the moment.
content/blog/2023-01-28-self-hosting-wger.org added +143
@@ -0,0 +1,143 @@
1#+date: <2023-01-28>
2#+title: Self-Hosting Wger Workout Manager
3#+description:
4
5
6* Wger: The Self-Hosted Workout Manager
7
8[[https://wger.de][Wger Workout Manager]] is a fitness tracking tool for
9those who enjoy self-hosting their data. You can also register an
10account on their main website if you'd prefer to try without
11self-hosting.
12
13** Features
14
15I didn't see a full listing of features anywhere, so I compiled this
16list of my own after installing wger:
17
181. Dashboard
19
20 - Dashboard view of Workout Schedule, Nutrition Plan, Weight Graph, &
21 last 5 Weight Logs
22
232. Training
24
25 - Workout Log
26 - Workout Schedule
27 - Calendar (shows weight logs and Bad/Neutral/Good days)
28 - Gallery (shows images you upload)
29 - Workout templates
30 - Public templates
31 - Exercises
32
333. Nutrition
34
35 - Nutrition plans
36 - BMI calculator
37 - Daily calories calculator
38 - Ingredient overview
39
404. Body Weight
41
42 - Weight overview
43
44** Documentation
45
46In order to self-host wger, I opted to use the Docker version of the
47application. You can read the README within the
48[[https://github.com/wger-project/docker][wger-project/docker]] project
49on GitHub for information and installation instructions.
50
51** Installation
52
53To start the installation, I created a folder for wger and started
54creating the three necessary files:
55
56#+begin_src sh
57mkdir ~/wger && mkdir ~/wger/config
58touch ~/wger/docker-compose.yml && \
59touch ~/wger/config/prod.env && \
60touch ~/wger/config/nginx.conf
61#+end_src
62
63Once you have the folders and files created, you will need to copy the
64contents of the =docker-compose.yml=, =prod.env=, and =nginx.conf= from
65the GitHub link above.
66
67A few notes to explain the changes I made to the default files:
68
69- I updated the =ALLOW_REGISTRAION= variable in =prod.env= to =False=
70 after I created an account via my LAN connection, *before* I connected
71 this app to a publicly-available domain.
72- I uncommented and updated =CSRF_TRUSTED_ORIGINS= to be equal to the
73 public version of this app: =https://wger.example.com=.
74- I updated the port within =docker-compose.yml=, within the =nginx=
75 block. The port I updated this to will be reflected in my nginx
76 configuration file on the server (NOT the wger nginx.conf file).
77
78** Deploy
79
80Once all files are created and modified to your needs, simply start the
81container.
82
83#+begin_src sh
84docker-compose up -d
85#+end_src
86
87You can now visit the website on your LAN by going to
88=localhost:YOUR_PORT= or by the server's IP, if you're not on the same
89machine that is running the container.
90
91If you wish to connect this app to a public domain name, you'll need to
92point an =A= DNS record from the domain to your server's public IP.
93You'll then need to create a configuration file for whichever web server
94or reverse proxy you're using.
95
96Wger's README suggests the following reverse proxy configuration for
97Nginx:
98
99#+begin_src conf
100upstream wger {
101 # This port should match the port in the `nginx` block of docker-compose.yml
102 # If the container is running on this same machine, replace this with
103 # server 127.0.0.1:8080
104 server 123.456.789.0:8080;
105}
106
107server {
108 listen 80;
109 listen [::]:443 ssl;
110 listen 443 ssl;
111
112 location / {
113 proxy_pass http://wger;
114 proxy_set_header Host $http_host;
115 proxy_set_header X-Real-IP $remote_addr;
116 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
117 proxy_set_header X-Forwarded-Proto $scheme;
118 proxy_redirect off;
119 }
120
121 server_name my.domain.example.com;
122
123 ssl_certificate /path/to/https/certificate.crt;
124 ssl_certificate_key /path/to/https/certificate.key;
125}
126#+end_src
127
128* Thoughts on Wger
129
130I'm still playing around with the app itself, but it seems to be a solid
131all-around workout manager, weight log, and food log.
132
133I like that the weight log graph is fluid and updates quickly. You can
134also import or export data in CSV format if you'd like to move your data
135elsewhere.
136
137The workout manager is slightly odd, as it requires you to enter sets
138and reps for each exercise when you enter it into the plan. Then, when
139you add a log entry for performing a workout, you then add what you
140actually performed, in terms of reps and weight.
141
142I haven't tried the food log yet and I likely will not, at least for a
143while. I have no need for a food log or calorie tracker at the moment.
content/blog/2023-02-02-exploring-hare.md deleted −161
@@ -1,161 +0,0 @@
1+++
2date = 2023-02-02
3title = "Exploring the Hare Programming Language"
4description = ""
5draft = false
6+++
7
8# A Quick Note
9
10By no means am I a professional developer, so this post will be rather short. I
11won't be going into depth on the specification or anything that technical.
12
13Instead, I will simply be talking about how I (a relatively basic hobbyist
14programmer) have been playing with Hare and what intrigues me about the
15language.
16
17# Hare
18
19The [Hare](https://harelang.org) programming language is a straightforward
20language that should look familiar if you've ever programmed with C, Rust, or
21other languages that aim to build software at the system-level.
22
23The Hare homepage states the following:
24
25> Hare is a systems programming language designed to be simple, stable, and
26> robust. Hare uses a static type system, manual memory management, and minimal
27> runtime. It is well-suited to writing operating systems, system tools,
28> compilers, networking software, and other low-level, high performance tasks.
29
30I have found this all to be true while playing with it for the first time today.
31In the next few sections, I'm going to walk through my installation and first
32program.
33
34## Installation
35
36I'm currently running Alpine Linux on my Thinkpad, so the installation was quite
37easy as there is a package for Hare in the `apk` repositories.
38
39```sh
40doas apk add hare hare-doc
41```
42
43However, I was able to install Hare from scratch on Fedora Linux a short while
44ago, which was also very easy to do. If you need further instructions and Hare
45doesn't have a package on your system, take a look at the [Hare
46Installation](https://harelang.org/installation/) page.
47
48## Creating a Test Project
49
50In order to play with the language, I created
51[hare-test](https://git.sr.ht/~cyborg/learning-hare) and will be putting any of my
52Hare-related adventures in here.
53
54> **Update:** I also created a simple Hare program for creating a file from user
55> input:
56> [files.ha](hhttps://git.sr.ht/~cyborg/learning-hare/blob/main/files/files.ha)
57
58Luckily, Hare doesn't require any complex set-up tools or build environment.
59Once you have Hare installed, you simply need to create a file ending with `.ha`
60and you can run a Hare program.
61
62I created a file called `rgb.ha` in order to test out the random number
63generation and passing parameters between functions.
64
65```sh
66nano rgb.ha
67```
68
69Within this file, I was able to easily import a few of the [standard library
70modules](https://harelang.org/tutorials/stdlib/): `fmt`, `math::random`, and
71`datetime`.
72
73With these modules, I created two functions:
74
751. `main`: This function calls the `generate_rgb` function and then prints out
76 the returned values.
772. `generate_rgb`: This function uses the current Unix epoch time to generate a
78 pseudo-random value and uses this value to create three more random values
79 between 0 and 255. These three numbers represent a color in RGB format.
80
81> **Note**: Some syntax coloring may look odd, as Zola currently doesn't have a
82> syntax highlighting theme for Hare. Instead, I'm using the C theme, which may
83> not be exactly accurate when coloring the code below.
84
85```c
86use datetime;
87use fmt;
88use math::random;
89
90export fn main() void = {
91 const rgb = generate_rgb();
92 fmt::printfln("RGB: ({}, {}, {})", rgb[0], rgb[1], rgb[2])!;
93};
94
95fn generate_rgb() []u64 = {
96 // Use the current Unix epoch time as the seed value
97 let datetime = datetime::epochunix(&datetime::now());
98
99 // Generate initial pseudo-random value
100 // You must cast the datetime from int to u64
101 let x = random::init(datetime: u64);
102
103 // Generate RGB values between (0, 255) using pseudo-random init value
104 let r = random::u64n(&x, 255);
105 let g = random::u64n(&x, 255);
106 let b = random::u64n(&x, 255);
107
108 // Structure data as array and return
109 let rgb_array: [3]u64 = [r, g, b];
110 return rgb_array;
111};
112```
113
114## Running a Program
115
116Once you have a Hare file written and ready to run, you simply need to run it:
117
118```sh
119hare run file.ha
120```
121
122You can also compile the program into an executable:
123
124```sh
125hare build -o example file.ha
126./example
127```
128
129## Initial Thoughts
130
1311. Documentation Improvements Would Help
132
133 While I was able to piece everything together eventually, the biggest
134 downfall right now in Hare's documentation. For such a new project, the
135 documentation is in a great spot. However, bare specifications don't help as
136 much as a brief examples section would.
137
138 For example, it took me a while to figure out what the `u64n` function was
139 looking for. I could tell that it took two parameters and the second was my
140 max value (255), but couldn't figure out what the first value should be.
141 Eventually, I inspected the `random.ha` file in the [Hare source
142 code](https://git.sr.ht/~sircmpwn/hare/tree/master/item/math/random/random.ha)
143 and found the test suite that helped me discover that it needed an `init()`
144 value in the form of `&var`.
145
1462. More Basic Modules
147
148 This is another point that comes from Hare being new and awaiting more
149 contributions, but there are some basic functions that I would personally
150 enjoy seeing in Hare, such as one to convert decimal (base 10) values to
151 hexadecimal (base 16).
152
153 If I'm feeling comfortable with my math, I may work on the list of functions
154 I want and see if any can make it into the Hare source code.
155
1563. Overall Thoughts
157
158 Overall, I actually really enjoy Hare. It's not as tedious to get a project
159 up and running as Rust, but it's also simpler and more user-friendly than
160 learning C. I am going to continue playing with it and see if I can make
161 anything of particular value.
content/blog/2023-02-02-exploring-hare.org added +172
@@ -0,0 +1,172 @@
1#+date: <2023-02-02>
2#+title: Exploring the Hare Programming Language
3#+description:
4
5
6* A Quick Note
7
8By no means am I a professional developer, so this post will be rather
9short. I won't be going into depth on the specification or anything that
10technical.
11
12Instead, I will simply be talking about how I (a relatively basic
13hobbyist programmer) have been playing with Hare and what intrigues me
14about the language.
15
16* Hare
17
18The [[https://harelang.org][Hare]] programming language is a
19straightforward language that should look familiar if you've ever
20programmed with C, Rust, or other languages that aim to build software
21at the system-level.
22
23The Hare homepage states the following:
24
25#+begin_quote
26Hare is a systems programming language designed to be simple, stable,
27and robust. Hare uses a static type system, manual memory management,
28and minimal runtime. It is well-suited to writing operating systems,
29system tools, compilers, networking software, and other low-level, high
30performance tasks.
31#+end_quote
32
33I have found this all to be true while playing with it for the first
34time today. In the next few sections, I'm going to walk through my
35installation and first program.
36
37** Installation
38
39I'm currently running Alpine Linux on my Thinkpad, so the installation
40was quite easy as there is a package for Hare in the =apk= repositories.
41
42#+begin_src sh
43doas apk add hare hare-doc
44#+end_src
45
46However, I was able to install Hare from scratch on Fedora Linux a short
47while ago, which was also very easy to do. If you need further
48instructions and Hare doesn't have a package on your system, take a look
49at the [[https://harelang.org/installation/][Hare Installation]] page.
50
51** Creating a Test Project
52
53In order to play with the language, I created
54[[https://git.sr.ht/~cyborg/learning-hare][hare-test]] and will be
55putting any of my Hare-related adventures in here.
56
57#+begin_quote
58*Update:* I also created a simple Hare program for creating a file from
59user input:
60[[https://git.sr.ht/~cyborg/learning-hare/blob/main/files/files.ha][files.ha]]
61#+end_quote
62
63Luckily, Hare doesn't require any complex set-up tools or build
64environment. Once you have Hare installed, you simply need to create a
65file ending with =.ha= and you can run a Hare program.
66
67I created a file called =rgb.ha= in order to test out the random number
68generation and passing parameters between functions.
69
70#+begin_src sh
71nano rgb.ha
72#+end_src
73
74Within this file, I was able to easily import a few of the
75[[https://harelang.org/tutorials/stdlib/][standard library modules]]:
76=fmt=, =math::random=, and =datetime=.
77
78With these modules, I created two functions:
79
801. =main=: This function calls the =generate_rgb= function and then
81 prints out the returned values.
822. =generate_rgb=: This function uses the current Unix epoch time to
83 generate a pseudo-random value and uses this value to create three
84 more random values between 0 and 255. These three numbers represent a
85 color in RGB format.
86
87#+begin_quote
88*Note*: Some syntax coloring may look odd, as Zola currently doesn't
89have a syntax highlighting theme for Hare. Instead, I'm using the C
90theme, which may not be exactly accurate when coloring the code below.
91#+end_quote
92
93#+begin_src C
94use datetime;
95use fmt;
96use math::random;
97
98export fn main() void = {
99 const rgb = generate_rgb();
100 fmt::printfln("RGB: ({}, {}, {})", rgb[0], rgb[1], rgb[2])!;
101};
102
103fn generate_rgb() []u64 = {
104 // Use the current Unix epoch time as the seed value
105 let datetime = datetime::epochunix(&datetime::now());
106
107 // Generate initial pseudo-random value
108 // You must cast the datetime from int to u64
109 let x = random::init(datetime: u64);
110
111 // Generate RGB values between (0, 255) using pseudo-random init value
112 let r = random::u64n(&x, 255);
113 let g = random::u64n(&x, 255);
114 let b = random::u64n(&x, 255);
115
116 // Structure data as array and return
117 let rgb_array: [3]u64 = [r, g, b];
118 return rgb_array;
119};
120#+end_src
121
122** Running a Program
123
124Once you have a Hare file written and ready to run, you simply need to
125run it:
126
127#+begin_src sh
128hare run file.ha
129#+end_src
130
131You can also compile the program into an executable:
132
133#+begin_src sh
134hare build -o example file.ha
135./example
136#+end_src
137
138** Initial Thoughts
139
1401. Documentation Improvements Would Help
141
142 While I was able to piece everything together eventually, the biggest
143 downfall right now in Hare's documentation. For such a new project,
144 the documentation is in a great spot. However, bare specifications
145 don't help as much as a brief examples section would.
146
147 For example, it took me a while to figure out what the =u64n=
148 function was looking for. I could tell that it took two parameters
149 and the second was my max value (255), but couldn't figure out what
150 the first value should be. Eventually, I inspected the =random.ha=
151 file in the
152 [[https://git.sr.ht/~sircmpwn/hare/tree/master/item/math/random/random.ha][Hare
153 source code]] and found the test suite that helped me discover that
154 it needed an =init()= value in the form of =&var=.
155
1562. More Basic Modules
157
158 This is another point that comes from Hare being new and awaiting
159 more contributions, but there are some basic functions that I would
160 personally enjoy seeing in Hare, such as one to convert decimal
161 (base 10) values to hexadecimal (base 16).
162
163 If I'm feeling comfortable with my math, I may work on the list of
164 functions I want and see if any can make it into the Hare source
165 code.
166
1673. Overall Thoughts
168
169 Overall, I actually really enjoy Hare. It's not as tedious to get a
170 project up and running as Rust, but it's also simpler and more
171 user-friendly than learning C. I am going to continue playing with it
172 and see if I can make anything of particular value.
content/blog/2023-05-22-burnout.md deleted −45
@@ -1,45 +0,0 @@
1+++
2date = 2023-05-22
3title = "RE: Burnout"
4description = ""
5draft = false
6+++
7
8# RE: Burnout
9
10I recently read [Drew DeVault's post on
11burnout](https://drewdevault.com/2023/05/01/2023-05-01-Burnout.html) around the
12same time I was pulling out of a burnout rut myself earlier this month. Finally,
13seeing the light at the end of my burnout tunnel made me want to write my first
14post back on this topic.
15
16# Busy Seasons on Busy Seasons
17
18My career deals with busy seasons, generally driven by client demand. This last
19year, I dealt with a harsh busy season from Aug to Oct 2022 to issue a few SOC
20reports for the period ending 2022-09-30. Immediately following that, I had to
21pivot and found another busy season from Oct to Jan for financial statement
22audits ending on 2022-12-31. Then again, supporting other clients from Jan to
23Mar 2023, followed by my current client workload aiming for SOC reports due on
242023-06-30.
25
26The result? A busy season that has lasted from August 2022 through today. I will
27likely be rushing throughout the next month or two before I have a brief break
28and need to focus on the 2023-09-30 SOC reports again. While auditing and
29consulting always involve a busy season, this is the first time I've had one
30last 9+ months without a break.
31
32While it's been tough, I have a handful of breaks pre-planned throughout this
33next cycle and should be able to moderate the level of commitment required for
34each client.
35
36# Refocusing
37
38Outside of work, I finally have time to work on hobbies such as this website,
39programming, athletics, games, etc.
40
41You may have noticed my absence if you're in the same channels, forums, and
42rooms that I am, but I should finally be active again.
43
44I'm hoping to break an item out of my backlog soon and start working on building
45a new project or hack around with a stale one.
content/blog/2023-05-22-burnout.org added +44
@@ -0,0 +1,44 @@
1#+date: <2023-05-22>
2#+title: RE: Burnout
3#+description:
4
5
6* RE: Burnout
7
8I recently read
9[[https://drewdevault.com/2023/05/01/2023-05-01-Burnout.html][Drew
10DeVault's post on burnout]] around the same time I was pulling out of a
11burnout rut myself earlier this month. Finally, seeing the light at the
12end of my burnout tunnel made me want to write my first post back on
13this topic.
14
15* Busy Seasons on Busy Seasons
16
17My career deals with busy seasons, generally driven by client demand.
18This last year, I dealt with a harsh busy season from Aug to Oct 2022 to
19issue a few SOC reports for the period ending 2022-09-30. Immediately
20following that, I had to pivot and found another busy season from Oct to
21Jan for financial statement audits ending on 2022-12-31. Then again,
22supporting other clients from Jan to Mar 2023, followed by my current
23client workload aiming for SOC reports due on 2023-06-30.
24
25The result? A busy season that has lasted from August 2022 through
26today. I will likely be rushing throughout the next month or two before
27I have a brief break and need to focus on the 2023-09-30 SOC reports
28again. While auditing and consulting always involve a busy season, this
29is the first time I've had one last 9+ months without a break.
30
31While it's been tough, I have a handful of breaks pre-planned throughout
32this next cycle and should be able to moderate the level of commitment
33required for each client.
34
35* Refocusing
36
37Outside of work, I finally have time to work on hobbies such as this
38website, programming, athletics, games, etc.
39
40You may have noticed my absence if you're in the same channels, forums,
41and rooms that I am, but I should finally be active again.
42
43I'm hoping to break an item out of my backlog soon and start working on
44building a new project or hack around with a stale one.
content/blog/2023-06-08-goaccess-geoip.md deleted −52
@@ -1,52 +0,0 @@
1+++
2date = 2023-06-08
3title = "Inspecting Nginx Logs with GoAccess and MaxMind GeoIP Data"
4description = ""
5draft = false
6+++
7
8# Overview
9
10[GoAccess](https://goaccess.io/) is an open source real-time web log analyzer
11and interactive viewer that runs in a terminal in \*nix systems or through your
12browser.
13
14# Installation
15
16To start, you'll need to install GoAccess for your OS. Here's an example for
17Debian-based distros:
18
19```sh
20sudo apt install goaccess
21```
22
23Next, find any number of the MaxMind GeoIP database files on GitHub or another
24file hosting website. We're going to use P3TERX's version in this example:
25
26```sh
27wget https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-City.mmdb
28```
29
30Be sure to save this file in an easy to remember location!
31
32# Usage
33
34In order to utilize the full capabilities of GoAccess and MMDB, start with the
35command template below and customize as necessary. This will export an HTML view
36of the GoAccess dashboard, showing all relevant information related to that
37site's access log. You can also omit the `-o output.html` parameter if you
38prefer to view the data within the CLI instead of creating an HTML file.
39
40With the addition of the GeoIP Database parameter, section `16 - Geo Location`
41will be added with the various countries that are associated with the collected
42IP addresses.
43
44```sh
45zcat /var/log/nginx/example.access.log.*.gz | goaccess \
46--geoip-database=/home/user/GeoLite2-City.mmdb \
47--date-format=%d/%b/%Y \
48--time-format=%H:%M:%S \
49--log-format=COMBINED \
50-o output.html \
51/var/log/nginx/example.access.log -
52```
\ No newline at end of file
content/blog/2023-06-08-goaccess-geoip.org added +52
@@ -0,0 +1,52 @@
1#+date: <2023-06-08>
2#+title: Inspecting Nginx Logs with GoAccess and MaxMind GeoIP Data
3#+description:
4
5
6* Overview
7
8[[https://goaccess.io/][GoAccess]] is an open source real-time web log
9analyzer and interactive viewer that runs in a terminal in *nix systems
10or through your browser.
11
12* Installation
13
14To start, you'll need to install GoAccess for your OS. Here's an example
15for Debian-based distros:
16
17#+begin_src sh
18sudo apt install goaccess
19#+end_src
20
21Next, find any number of the MaxMind GeoIP database files on GitHub or
22another file hosting website. We're going to use P3TERX's version in
23this example:
24
25#+begin_src sh
26wget https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-City.mmdb
27#+end_src
28
29Be sure to save this file in an easy to remember location!
30
31* Usage
32
33In order to utilize the full capabilities of GoAccess and MMDB, start
34with the command template below and customize as necessary. This will
35export an HTML view of the GoAccess dashboard, showing all relevant
36information related to that site's access log. You can also omit the
37=-o output.html= parameter if you prefer to view the data within the CLI
38instead of creating an HTML file.
39
40With the addition of the GeoIP Database parameter, section
41=16 - Geo Location= will be added with the various countries that are
42associated with the collected IP addresses.
43
44#+begin_src sh
45zcat /var/log/nginx/example.access.log.*.gz | goaccess \
46--geoip-database=/home/user/GeoLite2-City.mmdb \
47--date-format=%d/%b/%Y \
48--time-format=%H:%M:%S \
49--log-format=COMBINED \
50-o output.html \
51/var/log/nginx/example.access.log -
52#+end_src
content/blog/2023-06-08-self-hosting-baikal.md → content/blog/2023-06-08-self-hosting-baikal.org renamed +71 −71
@@ -1,34 +1,32 @@
1+++
2date = 2023-06-08
3title = "Self-Hosting Baikal Server (CalDAV & CardDAV)"
4description = ""
5draft = false
6+++
1#+date: <2023-06-08>
2#+title: Self-Hosting Baikal Server (CalDAV & CardDAV)
3#+description:
74
8# What is Baikal?
95
10[Baikal](https://sabre.io/baikal/) is a lightweight CalDAV + CardDAV server that
11you can self-host on your own machine. While I have tried (& failed) to get this
12CalDAV + CardDAV server running before, it was quite easy this time. Not really
13sure what I did differently this time, but I'm documenting my process here to
14ensure I don't forget.
6* What is Baikal?
157
16# Installation
8[[https://sabre.io/baikal/][Baikal]] is a lightweight CalDAV + CardDAV
9server that you can self-host on your own machine. While I have tried (&
10failed) to get this CalDAV + CardDAV server running before, it was quite
11easy this time. Not really sure what I did differently this time, but
12I'm documenting my process here to ensure I don't forget.
1713
18First, create a folder on your server and open a `docker-compose.yml` file for
19editing:
14* Installation
2015
21```sh
16First, create a folder on your server and open a =docker-compose.yml=
17file for editing:
18
19#+begin_src sh
2220mkdir baikal && cd baikal
2321nano docker-compose.yml
24```
22#+end_src
2523
26Within this file, you'll need to paste the information below. You can customize
27the `ports` section to use any port on your server to pass through to port 80 in
28the container. You can also edit the `volumes` section to use docker volumes
29instead of local folders.
24Within this file, you'll need to paste the information below. You can
25customize the =ports= section to use any port on your server to pass
26through to port 80 in the container. You can also edit the =volumes=
27section to use docker volumes instead of local folders.
3028
31```conf
29#+begin_src conf
3230version: "2"
3331services:
3432 baikal:
@@ -39,58 +37,59 @@ services:
3937 volumes:
4038 - ./config:/var/www/baikal/config
4139 - ./data:/var/www/baikal/Specific
42```
40#+end_src
4341
44Once finished with editing, save and close the file. Then, launch the docker
45container:
42Once finished with editing, save and close the file. Then, launch the
43docker container:
4644
47```sh
45#+begin_src sh
4846sudo docker-compose up -d
49```
47#+end_src
5048
51# Intial Setup
49* Intial Setup
5250
53As long as no issues came up when starting the container, you should be able to
54visit the server's set-up page at `http://<server_ip>:<port>`. The application
55will ask you to create an administrator account and choose the database type for
56your storage. Personally, I opted to use SQLite.
51As long as no issues came up when starting the container, you should be
52able to visit the server's set-up page at =http://<server_ip>:<port>=.
53The application will ask you to create an administrator account and
54choose the database type for your storage. Personally, I opted to use
55SQLite.
5756
58Make sure the administrator credentials are adequate to protect against common
59attacks.
57Make sure the administrator credentials are adequate to protect against
58common attacks.
6059
61# Creating Users
60* Creating Users
6261
63Once you've set up the application, you will be greeted by the Dashboard page,
64which will show the version of the app, status of the admin/CalDAV/CardDAV
65services, and the number of users, calendars, events, address books, and
66contacts.
62Once you've set up the application, you will be greeted by the Dashboard
63page, which will show the version of the app, status of the
64admin/CalDAV/CardDAV services, and the number of users, calendars,
65events, address books, and contacts.
6766
68To create a new user, navigate to the `Users and resources` page. This process
69is as simple as entering a username, password, and email.
67To create a new user, navigate to the =Users and resources= page. This
68process is as simple as entering a username, password, and email.
7069
71Once a user has been created, you can create any number of calendars and address
72books for user, as well as inspect their information.
70Once a user has been created, you can create any number of calendars and
71address books for user, as well as inspect their information.
7372
74# Setting Up a Public URL
73* Setting Up a Public URL
7574
76Once your application is working locally, you can open access remotely via a URL
77by using a reverse-proxy like Nginx.
75Once your application is working locally, you can open access remotely
76via a URL by using a reverse-proxy like Nginx.
7877
79As a prerequisite, you must own a domain name and set up DNS records to point a
80domain name to the server hosting Baikal.
78As a prerequisite, you must own a domain name and set up DNS records to
79point a domain name to the server hosting Baikal.
8180
82Start by navigating to your web server's configuration directory and create a
83new file for this application.
81Start by navigating to your web server's configuration directory and
82create a new file for this application.
8483
85```sh
84#+begin_src sh
8685cd /etc/nginx/sites-available/
8786nano dav
88```
87#+end_src
8988
9089Within this file, paste in the configuration from below and change
91`dav.example.com` to match the URL you'll be using.
90=dav.example.com= to match the URL you'll be using.
9291
93```conf
92#+begin_src conf
9493server {
9594 if ($host ~ ^[^.]+\.example\.com$) {
9695 return 301 https://$host$request_uri;
@@ -120,34 +119,35 @@ server {
120119 include /etc/letsencrypt/options-ssl-nginx.conf;
121120 ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
122121}
123```
122#+end_src
124123
125For Nginx on Ubuntu, you'll need to symlink the configuration file to the
126`sites-enabled` directory and then restart Nginx.
124For Nginx on Ubuntu, you'll need to symlink the configuration file to
125the =sites-enabled= directory and then restart Nginx.
127126
128```sh
127#+begin_src sh
129128sudo ln -s /etc/nginx/sites-available/dav /etc/nginx/sites-enabled/dav
130129sudo systemctl restart nginx.service
131```
130#+end_src
132131
133At this point, the Baikal server should be available over the internet at the
134URL configured above!
132At this point, the Baikal server should be available over the internet
133at the URL configured above!
135134
136# Configuring Clients
135* Configuring Clients
137136
138Lastly, configuring clients and applications is essential to making sure the DAV
139server is being fully utilized.
137Lastly, configuring clients and applications is essential to making sure
138the DAV server is being fully utilized.
140139
141You can also use the `Users and resources` page to inspect a user's personal
142link to their calendars and address books by clicking the info (i) button. It
143will show a URI like `/dav.php/calendars/your-user/default/`.
140You can also use the =Users and resources= page to inspect a user's
141personal link to their calendars and address books by clicking the info
142(i) button. It will show a URI like
143=/dav.php/calendars/your-user/default/=.
144144
145145However, I found that the following URL works for most applications:
146`/dav.php/principals/your-user/`.
146=/dav.php/principals/your-user/=.
147147
148I used the `principals` URL above for Thunderbird (calendar, tasks, and
149contacts), as well as iOS (calendar, tasks, and contacts) and everything works
150flawlessly so far.
148I used the =principals= URL above for Thunderbird (calendar, tasks, and
149contacts), as well as iOS (calendar, tasks, and contacts) and everything
150works flawlessly so far.
151151
152152Syncing is quick between the server and clients, and I haven't seen any
153153disruptions in the service or data integrity.
content/blog/2023-06-18-unifi-ip-blocklist.md deleted −70
@@ -1,70 +0,0 @@
1+++
2date = 2023-06-18
3title = "Block IP Addresses and Subnets with Unifi Network Firewall"
4description = ""
5draft = false
6+++
7
8# Identifying Abusive IPs
9
10If you're like me and use Unifi network equipment at the edge of the network you
11manage, you may know that Unifi is only somewhat decent at identifying and
12blocking IPs that represent abusive or threat actors.
13
14While Unifi has a [threat
15management](https://help.ui.com/hc/en-us/articles/360006893234-UniFi-Gateway-Threat-Management)
16tool inside their Network application, it can be lacking in functionality and
17identification. For example, I have my UDM Pro set to identify and block almost
18all categories of threats available within the Unifi settings. However, I
19regularly identify abusive actors on my web server via the server logs.
20
21In addition, I have identified IP addresses and subnets directly within Unifi's
22logs that the UDM did not block for whatever reason.
23
24This guide is meant to be another step in the process to manually block abusive
25IP addresses or subnets that you have identified but are not being automatically
26blocked yet.
27
28# Create an IP Group Profile
29
30To start, login to the Unifi machine's web GUI and navigate to the Network app > Settings > Profiles.
31
32Within this page, choose the `IP Groups` tab and click `Create New`.
33
34Each IP Group profile can be used as one of three options:
35
361. Port Group
372. IPv4 Address/Subnet
383. IPv6 Address/Subnet
39
40In this example, I'm creating an IPv4 Address/Subnet group and adding a few
41different IP addresses and a subnet. Once you've added all IP addresses and
42subnets, click the `Apply` button that should appear at the bottom.
43
44At this point, the IPv4 Address/Subnet has been created but not yet used.
45
46# Drop IP Group Profile via the Unifi Firewall
47
48To instruct the Unifi machine to block the profile we just created, we need to
49navigate to the Network app > Settings > Firewall & Security.
50
51Within this screen, find the Firewall Rules table and click `Create Entry`. This
52entry should contain the following settings:
53
54- Type: `Internet In`
55- Description: `<Your Custom Rule>`
56- Rule Applied: `Before Predefined Rules`
57- Action: `Drop`
58- Source Type: `Port/IP Group`
59- IPv4 Address Group: `<Name of the Group Profile You Created Above>`
60
61Customize the remaining configurations to your liking, and then save and enable
62the firewall rule.
63
64Once enabled, the Unifi machine will be able to drop all incoming connections
65from the defined IP addresses and subnets within the created profile.
66
67> As a personal aside to this topic, I'm looking for a convenient way to update
68> the firewall rules or profiles remotely (within the LAN) from the web server
69> to accelerate this process. If you have an idea on how to automatically update
70> Unifi IP groups or firewall rules, let me know!
content/blog/2023-06-18-unifi-ip-blocklist.org added +75
@@ -0,0 +1,75 @@
1#+date: <2023-06-18>
2#+title: Block IP Addresses and Subnets with Unifi Network Firewall
3#+description:
4
5
6* Identifying Abusive IPs
7
8If you're like me and use Unifi network equipment at the edge of the
9network you manage, you may know that Unifi is only somewhat decent at
10identifying and blocking IPs that represent abusive or threat actors.
11
12While Unifi has a
13[[https://help.ui.com/hc/en-us/articles/360006893234-UniFi-Gateway-Threat-Management][threat
14management]] tool inside their Network application, it can be lacking in
15functionality and identification. For example, I have my UDM Pro set to
16identify and block almost all categories of threats available within the
17Unifi settings. However, I regularly identify abusive actors on my web
18server via the server logs.
19
20In addition, I have identified IP addresses and subnets directly within
21Unifi's logs that the UDM did not block for whatever reason.
22
23This guide is meant to be another step in the process to manually block
24abusive IP addresses or subnets that you have identified but are not
25being automatically blocked yet.
26
27* Create an IP Group Profile
28
29To start, login to the Unifi machine's web GUI and navigate to the
30Network app > Settings > Profiles.
31
32Within this page, choose the =IP Groups= tab and click =Create New=.
33
34Each IP Group profile can be used as one of three options:
35
361. Port Group
372. IPv4 Address/Subnet
383. IPv6 Address/Subnet
39
40In this example, I'm creating an IPv4 Address/Subnet group and adding a
41few different IP addresses and a subnet. Once you've added all IP
42addresses and subnets, click the =Apply= button that should appear at
43the bottom.
44
45At this point, the IPv4 Address/Subnet has been created but not yet
46used.
47
48* Drop IP Group Profile via the Unifi Firewall
49
50To instruct the Unifi machine to block the profile we just created, we
51need to navigate to the Network app > Settings > Firewall & Security.
52
53Within this screen, find the Firewall Rules table and click
54=Create Entry=. This entry should contain the following settings:
55
56- Type: =Internet In=
57- Description: =<Your Custom Rule>=
58- Rule Applied: =Before Predefined Rules=
59- Action: =Drop=
60- Source Type: =Port/IP Group=
61- IPv4 Address Group: =<Name of the Group Profile You Created Above>=
62
63Customize the remaining configurations to your liking, and then save and
64enable the firewall rule.
65
66Once enabled, the Unifi machine will be able to drop all incoming
67connections from the defined IP addresses and subnets within the created
68profile.
69
70#+begin_quote
71As a personal aside to this topic, I'm looking for a convenient way to
72update the firewall rules or profiles remotely (within the LAN) from the
73web server to accelerate this process. If you have an idea on how to
74automatically update Unifi IP groups or firewall rules, let me know!
75#+end_quote
content/blog/2023-06-20-audit-review-template.md deleted −77
@@ -1,77 +0,0 @@
1+++
2date = 2023-06-20
3title = "Audit Testing Review Template"
4description = ""
5draft = false
6+++
7
8# Overview
9
10This post is a _very_ brief overview on the basic process to review audit test
11results, focusing on work done as part of a financial statement audit (FSA) or
12service organization controls (SOC) report.
13
14While there are numerous different things to review and look for - all varying
15wildly depending on the report, client, and tester - this list serves as a solid
16base foundation for a reviewer.
17
18I have used this throughout my career as a starting point to my reviews, and it
19has worked wonders for creating a consistent and objective template to my
20reviews. The goal is to keep this base high-level enough to be used on a wide
21variety of engagements, while still ensuring that all key areas are covered.
22
23# Review Template
24
251. [ ] Check all documents for spelling and grammar.
262. [ ] Ensure all acronyms are fully explained upon first use.
273. [ ] For all people referenced, use their full names and job titles upon first
28 use.
294. [ ] All supporting documents must cross-reference to the lead sheet and
30 vice-versa.
315. [ ] Verify that the control has been adequately tested:
32 - [ ] **Test of Design**: Did the tester obtain information regarding how
33 the control should perform normally and abnormally (e.g., emergency
34 scenarios)?
35 - [ ] **Test of Operating Effectiveness**: Did the tester inquire, observe,
36 inspect, or re-perform sufficient evidence to support their conclusion
37 over the control? Inquiry alone is not adequate!
386. [ ] For any information used in the control, whether by the control operator
39 or by the tester, did the tester appropriately document the source
40 (system or person), extraction method, parameters, and completeness and
41 accuracy (C&A)?
42 - [ ] For any reports, queries, etc. used in the extraction, did the tester
43 include a copy and notate C&A considerations?
447. [ ] Did the tester document the specific criteria that the control is being
45 tested against?
468. [ ] Did the tester notate in the supporting documents where each criterion
47 was satisfied?
489. [ ] If testing specific policies or procedures, are the documents adequate?
49 - [ ] e.g., a test to validate that a review of policy XYZ occurs
50 periodically should also evaluate the sufficiency of the policy
51 itself, if meant to cover the risk that such a policy does not exist
52 and is not reviewed.
5310. [ ] Does the test cover the appropriate period under review?
54 - [ ] If the test is meant to cover only a portion of the audit period, do
55 other controls exist to mitigate the risks that exist for the
56 remainder of the period?
5711. [ ] For any computer-aided audit tools (CAATs) or other automation
58 techniques used in the test, is the use of such tools explained and
59 appropriately documented?
6012. [ ] If prior-period documentation exists, are there any missing pieces of
61 evidence that would further enhance the quality of the test?
6213. [ ] Was any information discovered during the walkthrough or inquiry phase
63 that was not incorporated into the test?
6414. [ ] Are there new rules or expectations from your company's internal
65 guidance or your regulatory bodies that would affect the audit approach
66 for this control?
6715. [ ] Was an exception, finding, or deficiency identified as a result of this
68 test?
69 - [ ] Was the control deficient in design, operation, or both?
70 - [ ] What was the root cause of the finding?
71 - [ ] Does the finding indicate other findings or potential fraud?
72 - [ ] What's the severity and scope of the finding?
73 - [ ] Do other controls exist as a form of compensation against the
74 finding's severity, and do they mitigate the risk within the control
75 objective?
76 - [ ] Does the finding exist at the end of the period, or was it resolved
77 within the audit period?
content/blog/2023-06-20-audit-review-template.org added +78
@@ -0,0 +1,78 @@
1#+date: <2023-06-20>
2#+title: Audit Testing Review Template
3#+description:
4
5
6* Overview
7
8This post is a /very/ brief overview on the basic process to review
9audit test results, focusing on work done as part of a financial
10statement audit (FSA) or service organization controls (SOC) report.
11
12While there are numerous different things to review and look for - all
13varying wildly depending on the report, client, and tester - this list
14serves as a solid base foundation for a reviewer.
15
16I have used this throughout my career as a starting point to my reviews,
17and it has worked wonders for creating a consistent and objective
18template to my reviews. The goal is to keep this base high-level enough
19to be used on a wide variety of engagements, while still ensuring that
20all key areas are covered.
21
22* Review Template
23
241. [ ] Check all documents for spelling and grammar.
252. [ ] Ensure all acronyms are fully explained upon first use.
263. [ ] For all people referenced, use their full names and job titles
27 upon first use.
284. [ ] All supporting documents must cross-reference to the lead sheet
29 and vice-versa.
305. [ ] Verify that the control has been adequately tested:
31 - [ ] *Test of Design*: Did the tester obtain information regarding
32 how the control should perform normally and abnormally (e.g.,
33 emergency scenarios)?
34 - [ ] *Test of Operating Effectiveness*: Did the tester inquire,
35 observe, inspect, or re-perform sufficient evidence to support
36 their conclusion over the control? Inquiry alone is not adequate!
376. [ ] For any information used in the control, whether by the control
38 operator or by the tester, did the tester appropriately document the
39 source (system or person), extraction method, parameters, and
40 completeness and accuracy (C&A)?
41 - [ ] For any reports, queries, etc. used in the extraction, did the
42 tester include a copy and notate C&A considerations?
437. [ ] Did the tester document the specific criteria that the control is
44 being tested against?
458. [ ] Did the tester notate in the supporting documents where each
46 criterion was satisfied?
479. [ ] If testing specific policies or procedures, are the documents
48 adequate?
49 - [ ] e.g., a test to validate that a review of policy XYZ occurs
50 periodically should also evaluate the sufficiency of the policy
51 itself, if meant to cover the risk that such a policy does not
52 exist and is not reviewed.
5310. [ ] Does the test cover the appropriate period under review?
54 - [ ] If the test is meant to cover only a portion of the audit
55 period, do other controls exist to mitigate the risks that exist
56 for the remainder of the period?
5711. [ ] For any computer-aided audit tools (CAATs) or other automation
58 techniques used in the test, is the use of such tools explained and
59 appropriately documented?
6012. [ ] If prior-period documentation exists, are there any missing
61 pieces of evidence that would further enhance the quality of the
62 test?
6313. [ ] Was any information discovered during the walkthrough or inquiry
64 phase that was not incorporated into the test?
6514. [ ] Are there new rules or expectations from your company's internal
66 guidance or your regulatory bodies that would affect the audit
67 approach for this control?
6815. [ ] Was an exception, finding, or deficiency identified as a result
69 of this test?
70 - [ ] Was the control deficient in design, operation, or both?
71 - [ ] What was the root cause of the finding?
72 - [ ] Does the finding indicate other findings or potential fraud?
73 - [ ] What's the severity and scope of the finding?
74 - [ ] Do other controls exist as a form of compensation against the
75 finding's severity, and do they mitigate the risk within the
76 control objective?
77 - [ ] Does the finding exist at the end of the period, or was it
78 resolved within the audit period?
content/blog/2023-06-23-byobu.md → content/blog/2023-06-23-byobu.org renamed +26 −26
@@ -1,44 +1,44 @@
1+++
2date = 2023-06-23
3title = "Byobu"
4description = ""
5draft = false
6+++
1#+date: <2023-06-23>
2#+title: Byobu
3#+description:
74
8# Byobu
95
10[byobu](https://www.byobu.org) is a command-line tool that allows you to use
11numerous screens within a single terminal emulator instance. More specifically,
12it's a text based window manager, using either `screen` or `tmux`.
6* Byobu
137
14This post is mostly just a self-reference as I explore byobu, so I may come back
15later and update this post with more content.
8[[https://www.byobu.org][byobu]] is a command-line tool that allows you
9to use numerous screens within a single terminal emulator instance. More
10specifically, it's a text based window manager, using either =screen= or
11=tmux=.
1612
17## Screenshot
13This post is mostly just a self-reference as I explore byobu, so I may
14come back later and update this post with more content.
1815
19Take a look below at my current multi-window set-up in byobu while I write this
20blog post:
16** Screenshot
2117
22![byobu](https://img.cleberg.net/blog/20230623-byobu/byobu.png)
18Take a look below at my current multi-window set-up in byobu while I
19write this blog post:
2320
24### Keybindings
21#+caption: byobu
22[[https://img.cleberg.net/blog/20230623-byobu/byobu.png]]
2523
26You can open the help menu with either of the following commands; they will both
27open the same manpage:
24*** Keybindings
2825
29```sh
26You can open the help menu with either of the following commands; they
27will both open the same manpage:
28
29#+begin_src sh
3030byobu --help
3131# or
3232man byobu
33```
33#+end_src
3434
35While the manpage contains a ton of information about the functionality of byobu
36(such as status notifications, sessions, and windows), the first location to
37explore should be the keybindings section.
35While the manpage contains a ton of information about the functionality
36of byobu (such as status notifications, sessions, and windows), the
37first location to explore should be the keybindings section.
3838
3939The keybindings are configured as follows:
4040
41```txt
41#+begin_src txt
4242byobu keybindings can be user defined in /usr/share/byobu/keybindings/ (or
4343within .screenrc if byobu-export was used). The common key bindings are:
4444
@@ -66,4 +66,4 @@ Ctrl-a R - Reload profile
6666Ctrl-a ! - Toggle key bindings on and off
6767Ctrl-a k - Kill the current window
6868Ctrl-a ~ - Save the current window's scrollback buffer
69```
69#+end_src
content/blog/2023-06-23-self-hosting-convos.md deleted −161
@@ -1,161 +0,0 @@
1+++
2date = 2023-06-23
3title = "Self-Hosting Convos IRC Web Client"
4description = ""
5draft = false
6+++
7
8# Convos
9
10[Convos](https://convos.chat/) is an always-online web client for IRC. It has a
11few features that made it attractive to me as a self-hosted option:
12
13- Extremely simple Docker Compose installation method.
14- Runs in the background and monitors chats even while you're not logged in.
15- Neatly organized sidebar for conversation and client settings.
16- Ability to connect to different hosts and create profiles for hosts.
17- By default, registration is closed to the public. You can enable public
18 registration on the Settings page or generate invitation links on the Users
19 page.
20- Customization of the client theme, organization name and URL, admin email,
21 and video service.
22
23# Docker Installation
24
25To install Convos, simply create a directory and a `docker-compose.yml` file:
26
27```sh
28mkdir ~/convos && cd ~/convos
29nano docker-compose.yml
30```
31
32With the `docker-compose.yml` file open, paste the configuration below into the
33file. You can customize the host port to be something unique, such as
34`21897:3000`. You can also change the `data` folder to be a docker volume
35instead, if you prefer.
36
37```config
38version: '3'
39
40services:
41 convos:
42 image: 'convos/convos:stable'
43 ports:
44 - '3000:3000'
45 volumes:
46 - './data:/data'
47 environment:
48 - CONVOS_REVERSE_PROXY=1
49 restart: always
50```
51
52Save the `docker-compose.yml` file and bring the container up:
53
54```sh
55sudo docker-compose up -d
56```
57
58At this point, Convos is available at `<server_ip>:3000` but not available to
59the public.
60
61# Nginx Reverse Proxy
62
63If you're using Nginx, you can create a configuration file for the convos
64application. Start by opening the file:
65
66```sh
67nano /etc/nginx/sites-available/convos
68```
69
70Within the nginx configuration file, paste the following content and be sure to
71update `convos.example.com` to match your domain and `127.0.0.1:3000` to match
72the port you opened in the `docker-compose.yml` file.
73
74```config
75# Host and port where convos is running
76upstream convos_upstream { server 127.0.0.1:3000; }
77
78server {
79 if ($host ~ ^[^.]+\.example\.com$) {
80 return 301 https://$host$request_uri;
81 }
82
83 listen [::]:80;
84 listen 80;
85 server_name convos.example.com;
86 return 404;
87}
88
89server {
90 listen [::]:443 ssl http2;
91 listen 443 ssl http2;
92 server_name convos.example.com;
93 access_log /var/log/nginx/convos.access.log;
94 error_log /var/log/nginx/convos.error.log;
95
96 location / {
97 proxy_pass http://convos_upstream;
98 proxy_http_version 1.1;
99 proxy_set_header Upgrade $http_upgrade;
100 proxy_set_header Connection "upgrade";
101 client_max_body_size 0;
102 proxy_set_header Host $host;
103 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
104 proxy_set_header X-Request-Base "$scheme://$host/";
105 }
106
107 ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
108 ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
109 include /etc/letsencrypt/options-ssl-nginx.conf;
110 ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
111}
112```
113
114Once the file is saved, link it to the `sites-enabled` directory and restart
115Nginx.
116
117```sh
118sudo ln -s /etc/nginx/sites-available/convos /etc/nginx/sites-enabled/convos
119sudo systemctl restart nginx.service
120```
121
122# Screenshots
123
124See the screenshots below for an example of the Convos homepage and an excerpt
125of the chat screen.
126
127There are numerous themes to choose from; the theme shown in the images below is
128Dracula.
129
130![Convos Home](https://img.cleberg.net/blog/20230623-convos/convos_home.png)
131
132![Convos Chat](https://img.cleberg.net/blog/20230623-convos/convos_chat.png)
133
134# Registering a Nickname
135
136Hop into the server channel so that you can message NickServ. In the case of
137Convos, the default sever is libera.chat. Simply click the `libera` conversation
138at the top of the sidebar to open it. Once the chat is open, you can claim a
139nickname by typing:
140
141```txt
142/nick <nick>
143```
144
145If the nickname is available, and you'd like to register the nickname to
146yourself, you'll need to type another command:
147
148```txt
149/msg NickServ REGISTER
150<password> <email>
151```
152
153On libera.chat, the server will send a confirmation email with a command that
154you must message in IRC to verify registration of the nickname:
155
156```txt
157/msg NickServ VERIFY REGISTER <nick> <verification_code>
158```
159
160Once entered, the server should confirm registration of the nickname to the
161supplied email with the password specified.
content/blog/2023-06-23-self-hosting-convos.org added +165
@@ -0,0 +1,165 @@
1#+date: <2023-06-23>
2#+title: Self-Hosting Convos IRC Web Client
3#+description:
4
5
6* Convos
7
8[[https://convos.chat/][Convos]] is an always-online web client for IRC.
9It has a few features that made it attractive to me as a self-hosted
10option:
11
12- Extremely simple Docker Compose installation method.
13- Runs in the background and monitors chats even while you're not logged
14 in.
15- Neatly organized sidebar for conversation and client settings.
16- Ability to connect to different hosts and create profiles for hosts.
17- By default, registration is closed to the public. You can enable
18 public registration on the Settings page or generate invitation links
19 on the Users page.
20- Customization of the client theme, organization name and URL, admin
21 email, and video service.
22
23* Docker Installation
24
25To install Convos, simply create a directory and a =docker-compose.yml=
26file:
27
28#+begin_src sh
29mkdir ~/convos && cd ~/convos
30nano docker-compose.yml
31#+end_src
32
33With the =docker-compose.yml= file open, paste the configuration below
34into the file. You can customize the host port to be something unique,
35such as =21897:3000=. You can also change the =data= folder to be a
36docker volume instead, if you prefer.
37
38#+begin_src config
39version: '3'
40
41services:
42 convos:
43 image: 'convos/convos:stable'
44 ports:
45 - '3000:3000'
46 volumes:
47 - './data:/data'
48 environment:
49 - CONVOS_REVERSE_PROXY=1
50 restart: always
51#+end_src
52
53Save the =docker-compose.yml= file and bring the container up:
54
55#+begin_src sh
56sudo docker-compose up -d
57#+end_src
58
59At this point, Convos is available at =<server_ip>:3000= but not
60available to the public.
61
62* Nginx Reverse Proxy
63
64If you're using Nginx, you can create a configuration file for the
65convos application. Start by opening the file:
66
67#+begin_src sh
68nano /etc/nginx/sites-available/convos
69#+end_src
70
71Within the nginx configuration file, paste the following content and be
72sure to update =convos.example.com= to match your domain and
73=127.0.0.1:3000= to match the port you opened in the
74=docker-compose.yml= file.
75
76#+begin_src config
77# Host and port where convos is running
78upstream convos_upstream { server 127.0.0.1:3000; }
79
80server {
81 if ($host ~ ^[^.]+\.example\.com$) {
82 return 301 https://$host$request_uri;
83 }
84
85 listen [::]:80;
86 listen 80;
87 server_name convos.example.com;
88 return 404;
89}
90
91server {
92 listen [::]:443 ssl http2;
93 listen 443 ssl http2;
94 server_name convos.example.com;
95 access_log /var/log/nginx/convos.access.log;
96 error_log /var/log/nginx/convos.error.log;
97
98 location / {
99 proxy_pass http://convos_upstream;
100 proxy_http_version 1.1;
101 proxy_set_header Upgrade $http_upgrade;
102 proxy_set_header Connection "upgrade";
103 client_max_body_size 0;
104 proxy_set_header Host $host;
105 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
106 proxy_set_header X-Request-Base "$scheme://$host/";
107 }
108
109 ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
110 ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
111 include /etc/letsencrypt/options-ssl-nginx.conf;
112 ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
113}
114#+end_src
115
116Once the file is saved, link it to the =sites-enabled= directory and
117restart Nginx.
118
119#+begin_src sh
120sudo ln -s /etc/nginx/sites-available/convos /etc/nginx/sites-enabled/convos
121sudo systemctl restart nginx.service
122#+end_src
123
124* Screenshots
125
126See the screenshots below for an example of the Convos homepage and an
127excerpt of the chat screen.
128
129There are numerous themes to choose from; the theme shown in the images
130below is Dracula.
131
132#+caption: Convos Home
133[[https://img.cleberg.net/blog/20230623-convos/convos_home.png]]
134
135#+caption: Convos Chat
136[[https://img.cleberg.net/blog/20230623-convos/convos_chat.png]]
137
138* Registering a Nickname
139
140Hop into the server channel so that you can message NickServ. In the
141case of Convos, the default sever is libera.chat. Simply click the
142=libera= conversation at the top of the sidebar to open it. Once the
143chat is open, you can claim a nickname by typing:
144
145#+begin_src txt
146/nick <nick>
147#+end_src
148
149If the nickname is available, and you'd like to register the nickname to
150yourself, you'll need to type another command:
151
152#+begin_src txt
153/msg NickServ REGISTER
154<password> <email>
155#+end_src
156
157On libera.chat, the server will send a confirmation email with a command
158that you must message in IRC to verify registration of the nickname:
159
160#+begin_src txt
161/msg NickServ VERIFY REGISTER <nick> <verification_code>
162#+end_src
163
164Once entered, the server should confirm registration of the nickname to
165the supplied email with the password specified.
content/blog/2023-06-28-backblaze-b2.md deleted −181
@@ -1,181 +0,0 @@
1+++
2date = 2023-06-28
3title = "Getting Started with Backblaze B2 Cloud Storage"
4description = ""
5draft = false
6+++
7
8# Overview
9
10Backblaze [B2 Cloud Storage](https://www.backblaze.com/b2/cloud-storage.html) is
11an inexpensive and reliable on-demand cloud storage and backup solution.
12
13The service starts at $5/TB/month ($0.005/GB/month) with a download rate of
14$0.01/GB/month.
15
16However, there are free tiers:
17
18- The first 10 GB of storage is free.
19- The first 1 GB of data downloaded each day is free.
20- Class A transactions are free.
21- The first 2500 Class B transactions each day are free.
22- The first 2500 Class C transactions each day are free.
23
24You can see which API calls fall into categories A, B, or C here: [Pricing
25Organized by API
26Calls](https://www.backblaze.com/b2/b2-transactions-price.html).
27
28For someone like me, who wants an offsite backup of their server's `/home/`
29directory and various other server configs that fall under 10 GB total,
30Backblaze is a great solution from a financial perspective.
31
32# Create An Account
33
34To start with Backblaze, you'll need to [create a free
35account](https://www.backblaze.com/b2/sign-up.html) - no payment method is
36required to sign up.
37
38Once you have an account, you can test out the service with their web GUI, their
39mobile app, or their CLI tool. I'm going to use the CLI tool below to test a
40file upload and then sync an entire directory to my Backblaze bucket.
41
42# Create a Bucket
43
44Before you can start uploading, you need to create a bucket. If you're familiar
45with other object storage services, this will feel familiar. If not, it's pretty
46simple to create one.
47
48As their webpage says:
49
50> A bucket is a container that holds files that are uploaded into B2 Cloud
51> Storage. The bucket name must be globally unique and must have a minimum of 6
52> characters. A limit of 100 buckets may be created per account. An unlimited
53> number of files may be uploaded into a bucket.
54
55Once you click the `Create a Bucket` button on their webpage or mobile app, you
56need to provide the following:
57
58- Bucket Unique Name
59- Files in Bucket are: `Private` or `Public`
60- Default Encryption: `Disable` or `Enable`
61- Object Lock: `Disable` or `Enable`
62
63For my bucket, I created a private bucket with encryption enabled and object
64lock disabled.
65
66Once your bucket is created, you can test the upload/download feature on their
67web GUI or mobile app! At this point, you have a fully functional bucket and
68account.
69
70# Linux CLI Tool
71
72## Installation
73
74To install the `b2` CLI tool, you'll need to download it from the [CLI
75Tools](https://www.backblaze.com/docs/cloud-storage-command-line-tools) page. I
76recommend copying the URL from the link that says `Linux` and using wget to
77download it, as shown below.
78
79Once downloaded, make the file executable and move it to a location on your
80`$PATH`, so that you can execute that command from anywhere on the machine.
81
82```sh
83wget <b2_cli_url>
84chmod +x b2_linux
85mv b2_linux /usr/bin/b2
86```
87
88## Log In
89
90The first step after installation is to log in. To do this, execute the
91following command and provide your `<applicationKeyId>` and `<applicationKey>`.
92
93If you don't want to provide these values in the command itself, you can simply
94execute the base command and it will request them in an interactive prompt.
95
96```sh
97# if you want to provide the keys directly:
98b2 authorize-account [<applicationKeyId>] [<applicationKey>]
99
100# or, if you don't want your keys in your shell history:
101b2 authorize-account
102```
103
104## Upload a Test File
105
106In order to test the functionality of the CLI tool, I'll start by uploading a
107single test file to the bucket I created above. We can do this with the
108`upload_file` function.
109
110The command is issued as follows:
111
112```sh
113b2 upload_file <bucket_name> <local_file> <remote_file>
114```
115
116In my situation, I executed the following command with my username.
117
118```sh
119b2 upload_file my_unique_bucket /home/<user>/test.md test.md
120```
121
122To confirm that the file was uploaded successfully, list the files in your
123bucket:
124
125```sh
126b2 ls <bucket_name>
127```
128
129```txt
130test.md
131```
132
133## Sync a Directory
134
135If you have numerous files, you can use the `sync` function to perform
136functionality similar to `rsync`, where you can check what's in your bucket and
137sync anything that is new or modified.
138
139The command is issued as follows:
140
141```sh
142b2 sync <source file location> <B2 bucket destination>
143```
144
145In my case, I can sync my user's entire home directory to my bucket without
146specifying any of the files directly:
147
148```sh
149b2 sync /home/<user>/ "b2://<bucketName>/home/<user>"
150```
151
152# Caveats
153
154## Timing of Updates to the Web GUI
155
156When performing actions over a bucket, there is a slight delay in the web GUI
157when inspecting a bucket or its file. Note that simple actions such as uploading
158or deleting files may have a delay of a few minutes up to 24 hours. In my
159experience (\<10 GB and ~20,000 files), any actions took only a few minutes to
160update across clients.
161
162## Symlinks
163
164Note that symlinks are resolved by b2, so if you have a link from
165`/home/<user>/nas-storage` that symlinks out to a `/mnt/nas-storage` folder that
166has 10TB of data, `b2` will resolve that link and start uploading all 10TB of
167data linked within the folder.
168
169If you're not sure if you have any symlinks, a symlink will look like this (note
170the `->` symbol):
171
172```sh
173> ls -lha
174lrwxrwxrwx 1 root root 20 Jun 28 13:32 nas -> /mnt/nas-storage/
175```
176
177You can recursively find symlink in a path with the following command:
178
179```sh
180ls -lR /path/to/search | grep '^l'
181```
content/blog/2023-06-28-backblaze-b2.org added +186
@@ -0,0 +1,186 @@
1#+date: <2023-06-28>
2#+title: Getting Started with Backblaze B2 Cloud Storage
3#+description:
4
5
6* Overview
7
8Backblaze [[https://www.backblaze.com/b2/cloud-storage.html][B2 Cloud
9Storage]] is an inexpensive and reliable on-demand cloud storage and
10backup solution.
11
12The service starts at $5/TB/month ($0.005/GB/month) with a download rate
13of $0.01/GB/month.
14
15However, there are free tiers:
16
17- The first 10 GB of storage is free.
18- The first 1 GB of data downloaded each day is free.
19- Class A transactions are free.
20- The first 2500 Class B transactions each day are free.
21- The first 2500 Class C transactions each day are free.
22
23You can see which API calls fall into categories A, B, or C here:
24[[https://www.backblaze.com/b2/b2-transactions-price.html][Pricing
25Organized by API Calls]].
26
27For someone like me, who wants an offsite backup of their server's
28=/home/= directory and various other server configs that fall under 10
29GB total, Backblaze is a great solution from a financial perspective.
30
31* Create An Account
32
33To start with Backblaze, you'll need to
34[[https://www.backblaze.com/b2/sign-up.html][create a free account]] -
35no payment method is required to sign up.
36
37Once you have an account, you can test out the service with their web
38GUI, their mobile app, or their CLI tool. I'm going to use the CLI tool
39below to test a file upload and then sync an entire directory to my
40Backblaze bucket.
41
42* Create a Bucket
43
44Before you can start uploading, you need to create a bucket. If you're
45familiar with other object storage services, this will feel familiar. If
46not, it's pretty simple to create one.
47
48As their webpage says:
49
50#+begin_quote
51A bucket is a container that holds files that are uploaded into B2 Cloud
52Storage. The bucket name must be globally unique and must have a minimum
53of 6 characters. A limit of 100 buckets may be created per account. An
54unlimited number of files may be uploaded into a bucket.
55#+end_quote
56
57Once you click the =Create a Bucket= button on their webpage or mobile
58app, you need to provide the following:
59
60- Bucket Unique Name
61- Files in Bucket are: =Private= or =Public=
62- Default Encryption: =Disable= or =Enable=
63- Object Lock: =Disable= or =Enable=
64
65For my bucket, I created a private bucket with encryption enabled and
66object lock disabled.
67
68Once your bucket is created, you can test the upload/download feature on
69their web GUI or mobile app! At this point, you have a fully functional
70bucket and account.
71
72* Linux CLI Tool
73
74** Installation
75
76To install the =b2= CLI tool, you'll need to download it from the
77[[https://www.backblaze.com/docs/cloud-storage-command-line-tools][CLI
78Tools]] page. I recommend copying the URL from the link that says
79=Linux= and using wget to download it, as shown below.
80
81Once downloaded, make the file executable and move it to a location on
82your =$PATH=, so that you can execute that command from anywhere on the
83machine.
84
85#+begin_src sh
86wget <b2_cli_url>
87chmod +x b2_linux
88mv b2_linux /usr/bin/b2
89#+end_src
90
91** Log In
92
93The first step after installation is to log in. To do this, execute the
94following command and provide your =<applicationKeyId>= and
95=<applicationKey>=.
96
97If you don't want to provide these values in the command itself, you can
98simply execute the base command and it will request them in an
99interactive prompt.
100
101#+begin_src sh
102# if you want to provide the keys directly:
103b2 authorize-account [<applicationKeyId>] [<applicationKey>]
104
105# or, if you don't want your keys in your shell history:
106b2 authorize-account
107#+end_src
108
109** Upload a Test File
110
111In order to test the functionality of the CLI tool, I'll start by
112uploading a single test file to the bucket I created above. We can do
113this with the =upload_file= function.
114
115The command is issued as follows:
116
117#+begin_src sh
118b2 upload_file <bucket_name> <local_file> <remote_file>
119#+end_src
120
121In my situation, I executed the following command with my username.
122
123#+begin_src sh
124b2 upload_file my_unique_bucket /home/<user>/test.md test.md
125#+end_src
126
127To confirm that the file was uploaded successfully, list the files in
128your bucket:
129
130#+begin_src sh
131b2 ls <bucket_name>
132#+end_src
133
134#+begin_src txt
135test.md
136#+end_src
137
138** Sync a Directory
139
140If you have numerous files, you can use the =sync= function to perform
141functionality similar to =rsync=, where you can check what's in your
142bucket and sync anything that is new or modified.
143
144The command is issued as follows:
145
146#+begin_src sh
147b2 sync <source file location> <B2 bucket destination>
148#+end_src
149
150In my case, I can sync my user's entire home directory to my bucket
151without specifying any of the files directly:
152
153#+begin_src sh
154b2 sync /home/<user>/ "b2://<bucketName>/home/<user>"
155#+end_src
156
157* Caveats
158
159** Timing of Updates to the Web GUI
160
161When performing actions over a bucket, there is a slight delay in the
162web GUI when inspecting a bucket or its file. Note that simple actions
163such as uploading or deleting files may have a delay of a few minutes up
164to 24 hours. In my experience (<10 GB and ~20,000 files), any actions
165took only a few minutes to update across clients.
166
167** Symlinks
168
169Note that symlinks are resolved by b2, so if you have a link from
170=/home/<user>/nas-storage= that symlinks out to a =/mnt/nas-storage=
171folder that has 10TB of data, =b2= will resolve that link and start
172uploading all 10TB of data linked within the folder.
173
174If you're not sure if you have any symlinks, a symlink will look like
175this (note the =->= symbol):
176
177#+begin_src sh
178> ls -lha
179lrwxrwxrwx 1 root root 20 Jun 28 13:32 nas -> /mnt/nas-storage/
180#+end_src
181
182You can recursively find symlink in a path with the following command:
183
184#+begin_src sh
185ls -lR /path/to/search | grep '^l'
186#+end_src
content/blog/2023-06-30-self-hosting-voyager.md → content/blog/2023-06-30-self-hosting-voyager.org renamed +50 −50
@@ -1,54 +1,53 @@
1+++
2date = 2023-06-30
3title = "Self-Hosting Voyager - A Lemmy Web Client"
4description = ""
5draft = false
6+++
1#+date: <2023-06-30>
2#+title: Self-Hosting Voyager - A Lemmy Web Client
3#+description:
74
8# Installation Guide
95
10[Voyager](https://github.com/aeharding/voyager) is a mobile-first Lemmy web
11client, based on iOS design standards. It follows very closely to Apollo's
12design.
6* Installation Guide
137
14This post is a guide showing how I was able to build and launch my own instance
15of Voyager via Docker Compose.
8[[https://github.com/aeharding/voyager][Voyager]] is a mobile-first
9Lemmy web client, based on iOS design standards. It follows very closely
10to Apollo's design.
1611
17## Clone the Repository
12This post is a guide showing how I was able to build and launch my own
13instance of Voyager via Docker Compose.
14
15** Clone the Repository
1816
1917Start by cloning the repository and entering it:
2018
21```sh
19#+begin_src sh
2220git clone https://github.com/aeharding/voyager
2321cd voyager
24```
22#+end_src
2523
26## Build the Image
24** Build the Image
2725
28With this repository, you can build the image yourself without any further
29configuration. When complete, it'll give you the image ID for you to run.
26With this repository, you can build the image yourself without any
27further configuration. When complete, it'll give you the image ID for
28you to run.
3029
31```sh
30#+begin_src sh
3231sudo docker build .
3332# Successfully built 5f00723cb5be
34```
33#+end_src
3534
36With the image ID above, you can run the container and pass the requested port
37`5314` through or use a custom port, if you wish.
35With the image ID above, you can run the container and pass the
36requested port =5314= through or use a custom port, if you wish.
3837
39You can also set the `CUSTOM_LEMMY_SERVERS` environment variable if you want to
40add to the default suggested login servers. This must be set with a comma
41separated list of suggested servers. The first instance in the list will be the
42default view for logged-out users.
38You can also set the =CUSTOM_LEMMY_SERVERS= environment variable if you
39want to add to the default suggested login servers. This must be set
40with a comma separated list of suggested servers. The first instance in
41the list will be the default view for logged-out users.
4342
44I will be using a `docker-compose.yml` file to run this container, instead of a
45`docker run` command.
43I will be using a =docker-compose.yml= file to run this container,
44instead of a =docker run= command.
4645
47```sh
46#+begin_src sh
4847nano docker-compose.yml
49```
48#+end_src
5049
51```conf
50#+begin_src conf
5251version: "2"
5352services:
5453 voyager:
@@ -58,29 +57,30 @@ services:
5857 - "<custom_port>:5314"
5958 environment:
6059 - CUSTOM_LEMMY_SERVERS=lemmy.dbzer0.com,lemmy.world,lemmy.ml,beehaw.org
61```
60#+end_src
6261
63```sh
62#+begin_src sh
6463sudo docker-compose up -d
65```
64#+end_src
6665
6766The web app will now be available at the following address:
68`<machine_ip>:<custom_port>`. If you are running it on your local device, try
69`localhost:<custom_port>`.
67=<machine_ip>:<custom_port>=. If you are running it on your local
68device, try =localhost:<custom_port>=.
7069
71## Reverse Proxy
70** Reverse Proxy
7271
73If you want to visit this app via an external URL or domain name, you'll need to
74set up a reverse proxy. The example below uses Nginx as a reverse proxy.
72If you want to visit this app via an external URL or domain name, you'll
73need to set up a reverse proxy. The example below uses Nginx as a
74reverse proxy.
7575
76Simply create the configuration file, paste the contents below, save the file,
77symlink the file, and restart Nginx.
76Simply create the configuration file, paste the contents below, save the
77file, symlink the file, and restart Nginx.
7878
79```sh
79#+begin_src sh
8080sudo nano /etc/nginx/sites-available/voyager
81```
81#+end_src
8282
83```conf
83#+begin_src conf
8484server {
8585 if ($host ~ ^[^.]+\.example\.com$) {
8686 return 301 https://$host$request_uri;
@@ -110,14 +110,14 @@ server {
110110 include /etc/letsencrypt/options-ssl-nginx.conf;
111111 ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
112112}
113```
113#+end_src
114114
115```sh
115#+begin_src sh
116116sudo ln sudo ln -s /etc/nginx/sites-available/voyager /etc/nginx/sites-enabled/voyager
117117sudo systemctl restart nginx.service
118```
118#+end_src
119119
120The site will now be available at the `server_name` you specified above!
120The site will now be available at the =server_name= you specified above!
121121
122You can visit my instance at [voyager.cleberg.net](https://voyager.cleberg.net)
123for an example.
122You can visit my instance at
123[[https://voyager.cleberg.net][voyager.cleberg.net]] for an example.
content/blog/2023-07-12-wireguard-lan.md → content/blog/2023-07-12-wireguard-lan.org renamed +62 −58
@@ -1,31 +1,30 @@
1+++
2date = 2023-07-12
3title = "Enable LAN Access in Mullvad Wireguard Configuration Files"
4description = ""
5draft = false
6+++
1#+date: <2023-07-12>
2#+title: Enable LAN Access in Mullvad Wireguard Configuration Files
3#+description:
74
8# Download Configuration Files from Mullvad
95
10To begin, you'll need [Wireguard configuration files from
11Mullvad](https://mullvad.net/account/wireguard-config). You can choose any of
12the options as you download them. For example, I enabled the kill switch,
13selected all countries, and selected a few content filters.
6* Download Configuration Files from Mullvad
147
15Once downloaded, unzip the files and move them to the Wireguard folder on your
16system.
8To begin, you'll need
9[[https://mullvad.net/account/wireguard-config][Wireguard configuration
10files from Mullvad]]. You can choose any of the options as you download
11them. For example, I enabled the kill switch, selected all countries,
12and selected a few content filters.
1713
18```sh
14Once downloaded, unzip the files and move them to the Wireguard folder
15on your system.
16
17#+begin_src sh
1918cd ~/Downloads
2019unzip mullvad_wireguard_linux_all_all.zip
2120doas mv *.conf /etc/wireguard/
22```
21#+end_src
2322
24## Configuration File Layout
23** Configuration File Layout
2524
2625The default configuration files will look something like this:
2726
28```conf
27#+begin_src conf
2928[Interface]
3029# Device: <redacted>
3130PrivateKey = <redacted>
@@ -38,42 +37,46 @@ PreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m ad
3837PublicKey = <redacted>
3938AllowedIPs = <redacted>
4039Endpoint = <redacted>
41```
40#+end_src
4241
43> Note: If you didn't select the kill switch option, you won't see the `PostUp`
44> and `PreDown` lines. In this case, you'll need to modify the script below to
45> simply append those lines to the `[Interface]` block.
42#+begin_quote
43Note: If you didn't select the kill switch option, you won't see the
44=PostUp= and =PreDown= lines. In this case, you'll need to modify the
45script below to simply append those lines to the =[Interface]= block.
46#+end_quote
4647
47# Editing the Configuration Files
48* Editing the Configuration Files
4849
49Once you have the files, you'll need to edit them and replace the `PostUp` and
50`PreDown` lines to enable LAN access.
50Once you have the files, you'll need to edit them and replace the
51=PostUp= and =PreDown= lines to enable LAN access.
5152
52I recommend that you do this process as root, since you'll need to be able to
53access files in `/etc/wireguard`, which are generally owned by root. You can
54also try using `sudo` or `doas`, but I didn't test that scenario so you may need
55to adjust, as necessary.
53I recommend that you do this process as root, since you'll need to be
54able to access files in =/etc/wireguard=, which are generally owned by
55root. You can also try using =sudo= or =doas=, but I didn't test that
56scenario so you may need to adjust, as necessary.
5657
57```sh
58#+begin_src sh
5859su
59```
60#+end_src
6061
61Create the Python file that we'll be using to update the Wireguard configuration
62files.
62Create the Python file that we'll be using to update the Wireguard
63configuration files.
6364
64```sh
65#+begin_src sh
6566nano replace.py
66```
67#+end_src
6768
68Within the Python file, copy and paste the logic below. This script will open a
69directory, loop through every configuration file within the directory, and
70replace the `PostUp` and `PreDown` lines with the new LAN-enabled iptables
71commands.
69Within the Python file, copy and paste the logic below. This script will
70open a directory, loop through every configuration file within the
71directory, and replace the =PostUp= and =PreDown= lines with the new
72LAN-enabled iptables commands.
7273
73> Note: If your LAN is on a subnet other than `192.168.1.0/24`, you'll need to
74> update the Python script below appropriately.
74#+begin_quote
75Note: If your LAN is on a subnet other than =192.168.1.0/24=, you'll
76need to update the Python script below appropriately.
77#+end_quote
7578
76```python
79#+begin_src python
7780import os
7881import fileinput
7982
@@ -92,25 +95,25 @@ for file in os.listdir(dir):
9295 print(line, end="")
9396
9497print("--- done ---")
95```
98#+end_src
9699
97Once you're done, save and close the file. You can now run the Python script and
98watch as each file is updated.
100Once you're done, save and close the file. You can now run the Python
101script and watch as each file is updated.
99102
100```sh
103#+begin_src sh
101104python3 replace.py
102```
105#+end_src
103106
104To confirm it worked, you can `cat` one of the configuration files to inspect
105the new logic and connect to one to test it out.
107To confirm it worked, you can =cat= one of the configuration files to
108inspect the new logic and connect to one to test it out.
106109
107```sh
110#+begin_src sh
108111cat /etc/wireguard/us-chi-wg-001.conf
109```
112#+end_src
110113
111114The configuration files should now look like this:
112115
113```conf
116#+begin_src conf
114117[Interface]
115118# Device: <redacted>
116119PrivateKey = <redacted>
@@ -123,19 +126,20 @@ PreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m ad
123126PublicKey = <redacted>
124127AllowedIPs = <redacted>
125128Endpoint = <redacted>
126```
129#+end_src
127130
128If you connect to a Wireguard interface, such as `us-chi-wg-001`, you can test
129your SSH functionality and see that it works even while on the VPN.
131If you connect to a Wireguard interface, such as =us-chi-wg-001=, you
132can test your SSH functionality and see that it works even while on the
133VPN.
130134
131```sh
135#+begin_src sh
132136wg-quick up us-chi-wg-001
133137ssh user@lan-host
134```
138#+end_src
135139
136140To confirm your VPN connection, you can curl Mullvad's connection API:
137141
138```sh
142#+begin_src sh
139143curl https://am.i.mullvad.net/connected
140144# You are connected to Mullvad (server us-chi-wg-001). Your IP address is <redacted>
141```
145#+end_src
content/blog/2023-07-19-plex-transcoder-errors.md → content/blog/2023-07-19-plex-transcoder-errors.org renamed +38 −37
@@ -1,61 +1,62 @@
1+++
2date = 2023-07-19
3title = "How to Avoid Plex Error: 'Conversion failed. The transcoder failed to start up.'"
4description = ""
5draft = false
6+++
1#+date: <2023-07-19>
2#+title: How to Avoid Plex Error: 'Conversion failed. The transcoder failed to start up.'
3#+description:
74
8# Plex Transcoder Error
95
10Occasionally, you may see an error in your Plex client that references a failure
11with the transcoder conversion process. The specific error wording is:
6* Plex Transcoder Error
127
13```txt
8Occasionally, you may see an error in your Plex client that references a
9failure with the transcoder conversion process. The specific error
10wording is:
11
12#+begin_src txt
1413Conversion failed. The transcoder failed to start up.
15```
14#+end_src
1615
17# Debugging the Cause
16* Debugging the Cause
1817
19In order to get a better look at what is causing the error, I'm going to observe
20the Plex console while the error occurs. To do this, open the Plex web client,
21go to `Settings` > `Manage` > `Console`. Now, try to play the title again and
22watch to see which errors occur.
18In order to get a better look at what is causing the error, I'm going to
19observe the Plex console while the error occurs. To do this, open the
20Plex web client, go to =Settings= > =Manage= > =Console=. Now, try to
21play the title again and watch to see which errors occur.
2322
24In my case, you can see the errors below are related to a subtitle file (`.srt`)
25causing the transcoder to crash.
23In my case, you can see the errors below are related to a subtitle file
24(=.srt=) causing the transcoder to crash.
2625
27```txt
26#+begin_src txt
2827Jul 19, 2023 16:49:34.945 [140184571120440] Error — Couldn't find the file to stream: /movies/Movie Title (2021)/Movie Title (2021).srt
2928Jul 19, 2023 16:49:34.947 [140184532732728] Error — [Req#7611/Transcode/42935159-67C1-4192-9336-DDC6F7BC9330] Error configuring transcoder: TPU: Failed to download sub-stream to temporary file
3029Jul 19, 2023 16:49:35.225 [140184532732728] Warning — [Req#760d/Transcode] Got a request to stop a transcode session without a valid session GUID.
3130Jul 19, 2023 16:49:45.561 [140184532732728] Info — [Req#7648] AutoUpdate: no updates available
3231Jul 19, 2023 16:51:23.402 [140184510081848] Info — Library section 1 (Movies) will be updated because of a change in "/movies/Movie Title (2021)/Movie Title (2021).srt"
33```
32#+end_src
3433
35# Solving the Error
34* Solving the Error
3635
37In my case, I simply removed the subtitle file because it was not critical to
38keep. You may also avoid this by turning off subtitles if you don't want to
39delete the file.
36In my case, I simply removed the subtitle file because it was not
37critical to keep. You may also avoid this by turning off subtitles if
38you don't want to delete the file.
4039
41```sh
40#+begin_src sh
4241rm -rf "/movies/Movie Title (2021)/Movie Title (2021).srt"
43```
42#+end_src
4443
45Once the subtitle is removed from the directory or subtitles are turned off, try
46to play the title again. At this point, it should play without error. If not,
47reopen or refresh your Plex client and the Plex server:
44Once the subtitle is removed from the directory or subtitles are turned
45off, try to play the title again. At this point, it should play without
46error. If not, reopen or refresh your Plex client and the Plex server:
4847
49```sh
48#+begin_src sh
5049sudo systemctl restart plexmediaserver.service
51```
50#+end_src
5251
53# Related Discussion
52* Related Discussion
5453
55Looking at [a related Plex forum
56post](https://forums.plex.tv/t/subtitles-crashing-plex-transcoder-samsung-q80-tv-with-or-without-hardware-transcode/741441/2),
57it seems that `.srt` subtitles are the core issue here. However, Plex does not
58seem to have a resolution that fixes these errors.
54Looking at
55[[https://forums.plex.tv/t/subtitles-crashing-plex-transcoder-samsung-q80-tv-with-or-without-hardware-transcode/741441/2][a
56related Plex forum post]], it seems that =.srt= subtitles are the core
57issue here. However, Plex does not seem to have a resolution that fixes
58these errors.
5959
6060Unfortunately, I would suggest converting subtitle formats, burning the
61subtitles into the title, or simply removing subtitles when they cause crashes.
61subtitles into the title, or simply removing subtitles when they cause
62crashes.
content/blog/2023-08-18-agile-auditing.md deleted −150
@@ -1,150 +0,0 @@
1+++
2date = 2023-08-18
3title = "Agile Auditing: An Introduction"
4description = ""
5draft = false
6+++
7
8# What is Agile Auditing?
9
10[Agile](https://en.wikipedia.org/wiki/Agile_software_development), the
11collaborative philosophy behind many software development methods, has been
12picking up steam as a beneficial tool to use in the external and internal
13auditing world.
14
15This blog post will walk through commonly used terms within Agile, Scrum, and
16Kanban in order to translate these terms and roles into audit-specific terms.
17
18Whether your team is in charge of a financial statement audit, an attestation
19(SOC 1, SOC 2, etc.), or a unique internal audit, the terms used throughout this
20post should still apply.
21
22# Agile
23
24To start, I'll take a look at Agile.
25
26> The Agile methodology is a project management approach that involves breaking
27> the project into phases and emphasizes continuous collaboration and
28> improvement. Teams follow a cycle of planning, executing, and evaluating.
29
30While this approach may seem familiar to what audit teams have historically
31done, an audit team must make distinct changes in their mentality and how they
32approach and manage a project.
33
34## Agile Values
35
36The Agile Manifesto, written in 2001 at a summit in Utah, contain a set of four
37main values that comprise the Agile approach:
38
391. Individuals and interactions over processes and tools.
402. Working software over comprehensive documentation.
413. Customer collaboration over contract negotiation.
424. Responding to change over following a plan.
43
44Beyond the four values, [twelve
45principles](https://agilemanifesto.org/principles.html) were also written as
46part of the summit.
47
48In order to relate these values to an audit or attestation engagement, we need
49to shift the focus from software development to the main goal of an engagement:
50completing sufficient audit testing to address to relevant risks over the
51processes and controls at hand.
52
53Audit Examples:
54
55- Engagement teams must value the team members, client contacts, and their
56 interactions over the historical processes and tools that have been used.
57- Engagement teams must value a final report that contains sufficient audit
58 documentation over excessive documentation or scope creep.
59- Engagement teams must collaborate with the audit clients as much as feasible
60 to ensure that both sides are constantly updated with current knowledge of
61 the engagement's status and any potential findings, rather than waiting for
62 pre-set meetings or the end of the engagement to communicate.
63- Engagement teams must be able to respond to change in an engagement's
64 schedule, scope, or environment to ensure that the project is completed in a
65 timely manner and that all relevant areas are tested.
66 - In terms of an audit department's portfolio, they must be able to
67 respond to changes in their company's or client's environment and be
68 able to dynamically change their audit plan accordingly.
69
70# Scrum
71
72The above section discusses the high-level details of the Agile philosophy and
73how an audit team can potentially mold that mindset into the audit world, but
74how does a team implement these ideas?
75
76There are many methods that use an Agile mindset, but I prefer
77[Scrum](<https://en.wikipedia.org/wiki/Scrum_(software_development)>). Scrum is
78a framework based on Agile that enables a team to work through a project through
79a series of roles, ceremonies, artifacts, and values.
80
81Let's dive into each of these individually.
82
83## Scrum Team
84
85A scrum project is only as good as the team running the project. Standard scrum
86teams are separated into three distinct areas:
87
881. **Product Owner (Client Contact)**: The client contact is the audit
89 equivalent of the product owner in Scrum. They are responsible for partnering
90 with the engagement or audit team to ensure progress is being made,
91 priorities are established, and clear guidance is given when questions or
92 findings arise within each sprint.
932. **Scrum Master (Engagement Lead)**: The engagement or audit team lead is
94 responsible for coaching the team and the client contact on the scrum
95 process, tracking team progress against plan, scheduling necessary resources,
96 and helping remove obstacles.
973. **Scrum Developers (Engagement Members)**: The engagement or audit team is
98 the set of team members responsible for getting the work done. These team
99 members will work on each task, report progress, resolve obstacles, and
100 collaborate with other team members and the client contact to ensure goals
101 are being met.
102
103## Scrum Ceremonies
104
105Scrum ceremonies are events that are performed on a regular basis.
106
1071. **Sprint Planning**: The team works together to plan the upcoming sprint goal
108 and which user stories (tasks) will be added to the sprint to achieve that
109 goal.
1102. **Sprint**: The time period, typically at least one week and no more than one
111 month in length, where the team works on the stories and anything in the
112 backlog.
1133. **Daily Scrum**: A very short meeting held each day, typically 15 minutes, to
114 quickly emphasize alignment on the sprint goal and plan the next 24 hours.
115 Each team member may share what they did the day before, what they'll do
116 today, and any obstacles to their work.
1174. **Sprint Review**: At the end of each sprint, the team will gather and
118 discuss the progress, obstacles, and backlog from the previous sprint.
1195. **Sprint Retrospective**: More specific than the sprint review, the
120 retrospective is meant to discuss what worked and what did not work during
121 the sprint. This may be processes, tools, people, or even things related to
122 the Scrum ceremonies.
123
124One additional ceremony that may be applicable is organizing the backlog. This
125is typically the responsibility of the engagement leader and is meant to
126prioritize and clarify what needs to be done to complete items in the backlog.
127
128## Artifacts
129
130While artifacts are generally not customizable in the audit world (i.e., each
131control test must include some kind of working paper with evidence supporting
132the test results), I wanted to include some quick notes on associating scrum
133artifact terms with an audit.
134
1351. **Product Backlog**: This is the overall backlog of unfinished audit tasks
136 from all prior sprints.
1372. **Sprint Backlog**: This is the backlog of unfinished audit tasks from one
138 individual sprint.
1393. **Increment**: This is the output of each sprint - generally this is best
140 thought of as any documentation prepared during the sprint, such as risk
141 assessments, control working papers, deficiency analysis, etc.
142
143# Kanban
144
145Last but not least, Kanban is a methodology that relies on boards to categorize
146work into distinct, descriptive categories that allow an agile or scrum team to
147effectively plan the work of a sprint or project.
148
149See Atlassian's [Kanban](https://www.atlassian.com/agile/kanban) page for more
150information.
content/blog/2023-08-18-agile-auditing.org added +157
@@ -0,0 +1,157 @@
1#+date: <2023-08-18>
2#+title: Agile Auditing: An Introduction
3#+description:
4
5
6* What is Agile Auditing?
7
8[[https://en.wikipedia.org/wiki/Agile_software_development][Agile]], the
9collaborative philosophy behind many software development methods, has
10been picking up steam as a beneficial tool to use in the external and
11internal auditing world.
12
13This blog post will walk through commonly used terms within Agile,
14Scrum, and Kanban in order to translate these terms and roles into
15audit-specific terms.
16
17Whether your team is in charge of a financial statement audit, an
18attestation (SOC 1, SOC 2, etc.), or a unique internal audit, the terms
19used throughout this post should still apply.
20
21* Agile
22
23To start, I'll take a look at Agile.
24
25#+begin_quote
26The Agile methodology is a project management approach that involves
27breaking the project into phases and emphasizes continuous collaboration
28and improvement. Teams follow a cycle of planning, executing, and
29evaluating.
30#+end_quote
31
32While this approach may seem familiar to what audit teams have
33historically done, an audit team must make distinct changes in their
34mentality and how they approach and manage a project.
35
36** Agile Values
37
38The Agile Manifesto, written in 2001 at a summit in Utah, contain a set
39of four main values that comprise the Agile approach:
40
411. Individuals and interactions over processes and tools.
422. Working software over comprehensive documentation.
433. Customer collaboration over contract negotiation.
444. Responding to change over following a plan.
45
46Beyond the four values,
47[[https://agilemanifesto.org/principles.html][twelve principles]] were
48also written as part of the summit.
49
50In order to relate these values to an audit or attestation engagement,
51we need to shift the focus from software development to the main goal of
52an engagement: completing sufficient audit testing to address to
53relevant risks over the processes and controls at hand.
54
55Audit Examples:
56
57- Engagement teams must value the team members, client contacts, and
58 their interactions over the historical processes and tools that have
59 been used.
60- Engagement teams must value a final report that contains sufficient
61 audit documentation over excessive documentation or scope creep.
62- Engagement teams must collaborate with the audit clients as much as
63 feasible to ensure that both sides are constantly updated with current
64 knowledge of the engagement's status and any potential findings,
65 rather than waiting for pre-set meetings or the end of the engagement
66 to communicate.
67- Engagement teams must be able to respond to change in an engagement's
68 schedule, scope, or environment to ensure that the project is
69 completed in a timely manner and that all relevant areas are tested.
70 - In terms of an audit department's portfolio, they must be able to
71 respond to changes in their company's or client's environment and be
72 able to dynamically change their audit plan accordingly.
73
74* Scrum
75
76The above section discusses the high-level details of the Agile
77philosophy and how an audit team can potentially mold that mindset into
78the audit world, but how does a team implement these ideas?
79
80There are many methods that use an Agile mindset, but I prefer
81[[https://en.wikipedia.org/wiki/Scrum_(software_development)][Scrum]].
82Scrum is a framework based on Agile that enables a team to work through
83a project through a series of roles, ceremonies, artifacts, and values.
84
85Let's dive into each of these individually.
86
87** Scrum Team
88
89A scrum project is only as good as the team running the project.
90Standard scrum teams are separated into three distinct areas:
91
921. *Product Owner (Client Contact)*: The client contact is the audit
93 equivalent of the product owner in Scrum. They are responsible for
94 partnering with the engagement or audit team to ensure progress is
95 being made, priorities are established, and clear guidance is given
96 when questions or findings arise within each sprint.
972. *Scrum Master (Engagement Lead)*: The engagement or audit team lead
98 is responsible for coaching the team and the client contact on the
99 scrum process, tracking team progress against plan, scheduling
100 necessary resources, and helping remove obstacles.
1013. *Scrum Developers (Engagement Members)*: The engagement or audit team
102 is the set of team members responsible for getting the work done.
103 These team members will work on each task, report progress, resolve
104 obstacles, and collaborate with other team members and the client
105 contact to ensure goals are being met.
106
107** Scrum Ceremonies
108
109Scrum ceremonies are events that are performed on a regular basis.
110
1111. *Sprint Planning*: The team works together to plan the upcoming
112 sprint goal and which user stories (tasks) will be added to the
113 sprint to achieve that goal.
1142. *Sprint*: The time period, typically at least one week and no more
115 than one month in length, where the team works on the stories and
116 anything in the backlog.
1173. *Daily Scrum*: A very short meeting held each day, typically 15
118 minutes, to quickly emphasize alignment on the sprint goal and plan
119 the next 24 hours. Each team member may share what they did the day
120 before, what they'll do today, and any obstacles to their work.
1214. *Sprint Review*: At the end of each sprint, the team will gather and
122 discuss the progress, obstacles, and backlog from the previous
123 sprint.
1245. *Sprint Retrospective*: More specific than the sprint review, the
125 retrospective is meant to discuss what worked and what did not work
126 during the sprint. This may be processes, tools, people, or even
127 things related to the Scrum ceremonies.
128
129One additional ceremony that may be applicable is organizing the
130backlog. This is typically the responsibility of the engagement leader
131and is meant to prioritize and clarify what needs to be done to complete
132items in the backlog.
133
134** Artifacts
135
136While artifacts are generally not customizable in the audit world (i.e.,
137each control test must include some kind of working paper with evidence
138supporting the test results), I wanted to include some quick notes on
139associating scrum artifact terms with an audit.
140
1411. *Product Backlog*: This is the overall backlog of unfinished audit
142 tasks from all prior sprints.
1432. *Sprint Backlog*: This is the backlog of unfinished audit tasks from
144 one individual sprint.
1453. *Increment*: This is the output of each sprint - generally this is
146 best thought of as any documentation prepared during the sprint, such
147 as risk assessments, control working papers, deficiency analysis,
148 etc.
149
150* Kanban
151
152Last but not least, Kanban is a methodology that relies on boards to
153categorize work into distinct, descriptive categories that allow an
154agile or scrum team to effectively plan the work of a sprint or project.
155
156See Atlassian's [[https://www.atlassian.com/agile/kanban][Kanban]] page
157for more information.
content/blog/2023-09-15-self-hosting-gitweb.md → content/blog/2023-09-15-self-hosting-gitweb.org renamed +30 −30
@@ -1,36 +1,35 @@
1+++
2date = 2023-09-15
3title = "Self-Hosting Gitweb via Nginx"
4description = ""
5draft = false
6+++
1#+date: <2023-09-15>
2#+title: Self-Hosting Gitweb via Nginx
3#+description:
74
8# Overview
95
10[GitWeb](https://git-scm.com/book/en/v2/Git-on-the-Server-GitWeb) is a simple
11web-based visualizer for git repositories. By default, GitWeb will only run with
12the `lighttpd` or `webrick` web servers.
6* Overview
137
14However, this guide will show you how to keep GitWeb running in the background
15and display information for all repositories in a chosen directory.
8[[https://git-scm.com/book/en/v2/Git-on-the-Server-GitWeb][GitWeb]] is a
9simple web-based visualizer for git repositories. By default, GitWeb
10will only run with the =lighttpd= or =webrick= web servers.
1611
17# Install Dependencies
12However, this guide will show you how to keep GitWeb running in the
13background and display information for all repositories in a chosen
14directory.
15
16* Install Dependencies
1817
1918To start, you'll need install the following packages:
2019
21```sh
20#+begin_src sh
2221sudo apt install git gitweb fcgiwrap nginx
23```
22#+end_src
2423
25# Configure Nginx
24* Configure Nginx
2625
2726Once installed, create an Nginx configuration file.
2827
29```sh
28#+begin_src sh
3029sudo nano /etc/nginx/sites-available/git.example.com
31```
30#+end_src
3231
33```conf
32#+begin_src conf
3433server {
3534 listen 80;
3635 server_name example.com;
@@ -49,23 +48,24 @@ server {
4948 index index.cgi;
5049 }
5150}
52```
51#+end_src
5352
54To make the configuration active, you need to symlink it and then restart Nginx.
53To make the configuration active, you need to symlink it and then
54restart Nginx.
5555
56```sh
56#+begin_src sh
5757sudo ln -s /etc/nginx/sites-available/git.example.com /etc/nginx/sites-enabled/git.example.com
5858sudo systemctl restart nginx.service
59```
59#+end_src
6060
61The GitWeb application should now be available via the URL you set in the Nginx
62configuration above.
61The GitWeb application should now be available via the URL you set in
62the Nginx configuration above.
6363
64# Customize GitWeb
64* Customize GitWeb
6565
66If you need to, you can customize many things about Gitweb by editing the
67[gitweb.conf](https://git-scm.com/docs/gitweb.conf) file.
66If you need to, you can customize many things about Gitweb by editing
67the [[https://git-scm.com/docs/gitweb.conf][gitweb.conf]] file.
6868
69```sh
69#+begin_src sh
7070sudo nano /etc/gitweb.conf
71```
71#+end_src
content/blog/2023-09-19-audit-sql-scripts.md → content/blog/2023-09-19-audit-sql-scripts.org renamed +62 −59
@@ -1,24 +1,23 @@
1+++
2date = 2023-09-19
3title = "Useful SQL Scripts for Auditing Logical Access"
4description = ""
5draft = false
6+++
1#+date: <2023-09-19>
2#+title: Useful SQL Scripts for Auditing Logical Access
3#+description:
74
8# Overview
95
10When you have to scope a database into your engagement, you may be curious how
11to best extract the information from the database. While there are numerous
12different methods to extract this type of information, I'm going to show an
13example of how to gather all users and privileges from three main database
14types: Oracle, Microsoft SQL, and MySQL.
6* Overview
157
16# Oracle
8When you have to scope a database into your engagement, you may be
9curious how to best extract the information from the database. While
10there are numerous different methods to extract this type of
11information, I'm going to show an example of how to gather all users and
12privileges from three main database types: Oracle, Microsoft SQL, and
13MySQL.
1714
18You can use the following SQL script to see all users and their privileges in an
19Oracle database:
15* Oracle
2016
21```sql
17You can use the following SQL script to see all users and their
18privileges in an Oracle database:
19
20#+begin_src sql
2221SELECT
2322 grantee AS "User",
2423 privilege AS "Privilege"
@@ -34,37 +33,39 @@ FROM
3433 dba_tab_privs
3534WHERE
3635 grantee IN (SELECT DISTINCT grantee FROM dba_tab_privs);
37```
36#+end_src
3837
39This script queries the `dba_sys_privs` and `dba_tab_privs` views to retrieve
40system and table-level privileges respectively. It then combines the results
41using `UNION ALL` to show all users and their associated privileges. Please note
42that this method does not extract information from the `dba_role_privs` table -
43use the method below for that data.
38This script queries the =dba_sys_privs= and =dba_tab_privs= views to
39retrieve system and table-level privileges respectively. It then
40combines the results using =UNION ALL= to show all users and their
41associated privileges. Please note that this method does not extract
42information from the =dba_role_privs= table - use the method below for
43that data.
4444
45Please note that you might need appropriate privileges (e.g., DBA privileges) to
46access these views, and you should exercise caution when querying system tables
47in a production Oracle database.
45Please note that you might need appropriate privileges (e.g., DBA
46privileges) to access these views, and you should exercise caution when
47querying system tables in a production Oracle database.
4848
49## Alternative Oracle Query
49** Alternative Oracle Query
5050
51You can also extract each table's information separately and perform processing
52outside the database to explore and determine the information necessary for the
53audit:
51You can also extract each table's information separately and perform
52processing outside the database to explore and determine the information
53necessary for the audit:
5454
55```sql
55#+begin_src sql
5656SELECT ** FROM sys.dba_role_privs;
5757SELECT ** FROM sys.dba_sys_privs;
5858SELECT ** FROM sys.dba_tab_privs;
5959SELECT ** FROM sys.dba_users;
60```
60#+end_src
6161
62# Microsoft SQL
62* Microsoft SQL
6363
64You can use the following SQL script to see all users and their privileges in a
65Microsoft SQL Server database ([source](https://stackoverflow.com/a/30040784)):
64You can use the following SQL script to see all users and their
65privileges in a Microsoft SQL Server database
66([[https://stackoverflow.com/a/30040784][source]]):
6667
67```sql
68#+begin_src sql
6869/*
6970Security Audit Report
70711) List all access provisioned to a sql user or windows user/group directly
@@ -95,7 +96,7 @@ ObjectName : Name of the object that the user/role is assigned permissions
9596 definitions.
9697ColumnName : Name of the column of the object that the user/role is assigned permissions on. This value
9798 is only populated if the object is a table, view or a table value function.
98*/
99,*/
99100
100101--List all access provisioned to a sql user or windows user/group directly
101102SELECT
@@ -209,41 +210,43 @@ ORDER BY
209210 perm.[permission_name],
210211 perm.[state_desc],
211212 obj.type_desc--perm.[class_desc]
212```
213#+end_src
213214
214# MySQL
215* MySQL
215216
216You can use the following SQL script to see all users and their privileges in a
217MySQL database:
217You can use the following SQL script to see all users and their
218privileges in a MySQL database:
218219
219```sh
220#+begin_src sh
220221mysql -u root -p
221```
222#+end_src
222223
223224Find all users and hosts with access to the database:
224225
225```sql
226#+begin_src sql
226227SELECT ** FROM information_schema.user_privileges;
227```
228#+end_src
228229
229This script retrieves user information and their associated database-level
230privileges from the `information_schema.user_privileges` table in MySQL. It
231lists various privileges such as SELECT, INSERT, UPDATE, DELETE, CREATE, and
232more for each user and database combination.
230This script retrieves user information and their associated
231database-level privileges from the =information_schema.user_privileges=
232table in MySQL. It lists various privileges such as SELECT, INSERT,
233UPDATE, DELETE, CREATE, and more for each user and database combination.
233234
234Please note that you may need appropriate privileges (e.g., `SELECT` privileges
235on `information_schema.user_privileges`) to access this information in a MySQL
236database. Additionally, some privileges like GRANT OPTION, EXECUTE, EVENT, and
237TRIGGER may not be relevant for all users and databases.
235Please note that you may need appropriate privileges (e.g., =SELECT=
236privileges on =information_schema.user_privileges=) to access this
237information in a MySQL database. Additionally, some privileges like
238GRANT OPTION, EXECUTE, EVENT, and TRIGGER may not be relevant for all
239users and databases.
238240
239## Alternative MySQL Query
241** Alternative MySQL Query
240242
241You can also grab individual sets of data from MySQL if you prefer to join them
242after extraction. I have marked the queries below with `SELECT ...` and excluded
243most `WHERE` clauses for brevity. You should determine the relevant privileges
244in-scope and query for those privileges to reduce the length of time to query.
243You can also grab individual sets of data from MySQL if you prefer to
244join them after extraction. I have marked the queries below with
245=SELECT ...= and excluded most =WHERE= clauses for brevity. You should
246determine the relevant privileges in-scope and query for those
247privileges to reduce the length of time to query.
245248
246```sql
249#+begin_src sql
247250-- Global Permissions
248251SELECT ... FROM mysql.user;
249252
@@ -262,4 +265,4 @@ WHERE db = @db_name;
262265-- Password Configuration
263266SHOW GLOBAL VARIABLES LIKE 'validate_password%';
264267SHOW VARIABLES LIKE 'validate_password%';
265```
268#+end_src
content/blog/2023-10-04-digital-minimalism.md deleted −99
@@ -1,99 +0,0 @@
1+++
2date = 2023-10-04
3title = "Digital Minimalism"
4description = ""
5draft = false
6+++
7
8I've written [a note about minimalism](file:///wiki/#digital-garden) before, but
9I wanted to dedicate some time to reflect on digital minimalism and how I've
10been able to minimize the impact of digital devices in my life.
11
12> These changes crept up on us and happened fast, before we had a chance to step
13> back and ask what we really wanted out of the rapid advances of the past
14> decade. We added new technologies to the periphery of our experience for minor
15> reasons, then woke one morning to discover that they had colonized the core of
16> our daily life. We didn't, in other words, sign up for the digital world in
17> which we're currently entrenched; we seem to have stumbled backward into it.
18>
19> _(Digital Minimalism, 2019)_
20
21# The Principles of Digital Minimalism
22
23As noted in Cal Newport's book, _Digital Minimalism_, there are three main
24principles to digital minimalism that I tend to agree with:
25
261. Clutter is costly.
27 - Digital minimalists recognize that cluttering their time and attention
28 with too many devices, apps, and services creates an overall negative
29 cost that can swamp the small benefits that each individual item
30 provides in isolation.
312. Optimization is important.
32 - Digital minimalists believe that deciding a particular technology
33 supports something they value is only the first step. To truly extract
34 its full potential benefit, it's necessary to think carefully about how
35 they'll use the technology.
363. Intentionality is satisfying.
37 - Digital minimalists derive significant satisfaction from their general
38 commitment to being more intentional about how they engage with new
39 technologies. This source of satisfaction is independent of the specific
40 decisions they make and is one of the biggest reasons that minimalism
41 tends to be immensely meaningful to its practitioners.
42
43# Taking Action
44
45In order to put the logic into practice, I've created a few new habits and
46continued performing old habits that are working well:
47
48## Using Devices With Intention
49
50- I already rarely use "social media", mostly limited to forums such as Hacker
51 News and Tildes, so I've just tweaked my behavior to stop looking for
52 content in those places when I'm bored.
53- Use devices with intention. Each time I pick up a digital device, there
54 should be an intention to use the device to improve my current situation. No
55 more endless scrolling or searching for something to interest me.
56
57## Prevent Distractions
58
59- Disable (most) notifications on all devices. I spent 15-30 minutes going
60 through the notifications on my phone, watch, and computer to ensure that
61 only a select few apps have the ability to interrupt me: Calendar, Messages,
62 Phone, Reminders, & Signal.
63- Disable badges for any apps except the ones mentioned in the bullet above.
64- Set-up focus profiles across devices so that I can enable different modes,
65 such as Personal when I only want to see notifications from people I care
66 about or Do Not Disturb, where absolutely nothing can interrupt me.
67- Clean up my home screens. This one was quite easy as I already maintain a
68 minimalist set-up, but I went extreme by limiting my phone to just eight
69 apps on the home screen and four in the dock. If I need another app, I'll
70 have to search or use the app library.
71- Remove the work profile from my phone. This was a tough decision as having
72 my work profile on my device definitely makes my life easier at times, but
73 it also has quite a negative effect when I'm "always online" and can see the
74 notifications and team activity 24/7. I believe creating a distinct barrier
75 between my work and personal devices will be beneficial in the end.
76
77## Creating Alternative Activities
78
79This is the most difficult piece, as most of my hobbies and interests lie in the
80digital world. However, I'm making a concerted effort to put devices down unless
81necessary and force myself to perform other activities in the physical world
82instead.
83
84I've started with a few basics that are always readily available to me:
85
86- Do a chore, such as organizing or cleaning.
87- Read a book, study a piece of art, etc.
88- Exercise or get outdoors.
89- Participate in a hobby, such as photography, birding, disc golf, etc.
90- Let yourself be bored and wander into creativity.
91
92# Making Progress
93
94I'll be taking notes as I continue down this journey and hope to see positive
95trends. I've always been a minimalist in the physical world and it feels
96refreshing to filter out the clutter that has come to dominate my digital life
97over the years.
98
99I'm excited to see where this journey leads.
content/blog/2023-10-04-digital-minimalism.org added +106
@@ -0,0 +1,106 @@
1#+date: <2023-10-04>
2#+title: Digital Minimalism
3#+description:
4
5
6I've written [[file:///wiki/#digital-garden][a note about minimalism]]
7before, but I wanted to dedicate some time to reflect on digital
8minimalism and how I've been able to minimize the impact of digital
9devices in my life.
10
11#+begin_quote
12These changes crept up on us and happened fast, before we had a chance
13to step back and ask what we really wanted out of the rapid advances of
14the past decade. We added new technologies to the periphery of our
15experience for minor reasons, then woke one morning to discover that
16they had colonized the core of our daily life. We didn't, in other
17words, sign up for the digital world in which we're currently
18entrenched; we seem to have stumbled backward into it.
19
20/(Digital Minimalism, 2019)/
21#+end_quote
22
23* The Principles of Digital Minimalism
24
25As noted in Cal Newport's book, /Digital Minimalism/, there are three
26main principles to digital minimalism that I tend to agree with:
27
281. Clutter is costly.
29 - Digital minimalists recognize that cluttering their time and
30 attention with too many devices, apps, and services creates an
31 overall negative cost that can swamp the small benefits that each
32 individual item provides in isolation.
332. Optimization is important.
34 - Digital minimalists believe that deciding a particular technology
35 supports something they value is only the first step. To truly
36 extract its full potential benefit, it's necessary to think
37 carefully about how they'll use the technology.
383. Intentionality is satisfying.
39 - Digital minimalists derive significant satisfaction from their
40 general commitment to being more intentional about how they engage
41 with new technologies. This source of satisfaction is independent
42 of the specific decisions they make and is one of the biggest
43 reasons that minimalism tends to be immensely meaningful to its
44 practitioners.
45
46* Taking Action
47
48In order to put the logic into practice, I've created a few new habits
49and continued performing old habits that are working well:
50
51** Using Devices With Intention
52
53- I already rarely use "social media", mostly limited to forums such as
54 Hacker News and Tildes, so I've just tweaked my behavior to stop
55 looking for content in those places when I'm bored.
56- Use devices with intention. Each time I pick up a digital device,
57 there should be an intention to use the device to improve my current
58 situation. No more endless scrolling or searching for something to
59 interest me.
60
61** Prevent Distractions
62
63- Disable (most) notifications on all devices. I spent 15-30 minutes
64 going through the notifications on my phone, watch, and computer to
65 ensure that only a select few apps have the ability to interrupt me:
66 Calendar, Messages, Phone, Reminders, & Signal.
67- Disable badges for any apps except the ones mentioned in the bullet
68 above.
69- Set-up focus profiles across devices so that I can enable different
70 modes, such as Personal when I only want to see notifications from
71 people I care about or Do Not Disturb, where absolutely nothing can
72 interrupt me.
73- Clean up my home screens. This one was quite easy as I already
74 maintain a minimalist set-up, but I went extreme by limiting my phone
75 to just eight apps on the home screen and four in the dock. If I need
76 another app, I'll have to search or use the app library.
77- Remove the work profile from my phone. This was a tough decision as
78 having my work profile on my device definitely makes my life easier at
79 times, but it also has quite a negative effect when I'm "always
80 online" and can see the notifications and team activity 24/7. I
81 believe creating a distinct barrier between my work and personal
82 devices will be beneficial in the end.
83
84** Creating Alternative Activities
85
86This is the most difficult piece, as most of my hobbies and interests
87lie in the digital world. However, I'm making a concerted effort to put
88devices down unless necessary and force myself to perform other
89activities in the physical world instead.
90
91I've started with a few basics that are always readily available to me:
92
93- Do a chore, such as organizing or cleaning.
94- Read a book, study a piece of art, etc.
95- Exercise or get outdoors.
96- Participate in a hobby, such as photography, birding, disc golf, etc.
97- Let yourself be bored and wander into creativity.
98
99* Making Progress
100
101I'll be taking notes as I continue down this journey and hope to see
102positive trends. I've always been a minimalist in the physical world and
103it feels refreshing to filter out the clutter that has come to dominate
104my digital life over the years.
105
106I'm excited to see where this journey leads.
content/blog/2023-10-11-self-hosting-authelia.md → content/blog/2023-10-11-self-hosting-authelia.org renamed +111 −109
@@ -1,55 +1,54 @@
1+++
2date = 2023-10-11
3title = "Self-Hosting Authelia"
4description = ""
5draft = false
6+++
1#+date: <2023-10-11>
2#+title: Self-Hosting Authelia
3#+description:
74
8# Overview
95
10[Authelia](https://www.authelia.com/) is an open-source authentication service
11that allows you to place a portal between end users on the internet and
12self-hosted services on your server.
6* Overview
137
14You can require one factor (username+password) or two factor authentication for
15any such user before allowing them to access a specific service on your domain.
8[[https://www.authelia.com/][Authelia]] is an open-source authentication
9service that allows you to place a portal between end users on the
10internet and self-hosted services on your server.
11
12You can require one factor (username+password) or two factor
13authentication for any such user before allowing them to access a
14specific service on your domain.
1615
1716This guide will walk through a standard installation of Authelia for
18`example.com`, using `auth.example.com` as Authelia's authentication domain and
19`teddit.example.com` as the website we want to protect behind the authentication
20portal.
17=example.com=, using =auth.example.com= as Authelia's authentication
18domain and =teddit.example.com= as the website we want to protect behind
19the authentication portal.
2120
22# Prerequisites
21* Prerequisites
2322
2423This guide assumes you have the following already set-up:
2524
26- A registered domain with DNS pointing to your server.
27- A subdomain for Authelia (`auth.example.com`) and a subdomain to protect via
28 Authelia (`app.example.com`).
29- A working Nginx web server.
30- Docker and docker-compose installed.
25- A registered domain with DNS pointing to your server.
26- A subdomain for Authelia (=auth.example.com=) and a subdomain to
27 protect via Authelia (=app.example.com=).
28- A working Nginx web server.
29- Docker and docker-compose installed.
3130
32# Installation
31* Installation
3332
34This guide will walk through each installation step one-by-one, starting with
35the container and finishing by cleaning up external access via an Nginx reverse
36proxy.
33This guide will walk through each installation step one-by-one, starting
34with the container and finishing by cleaning up external access via an
35Nginx reverse proxy.
3736
38## Docker-Compose
37** Docker-Compose
3938
40To start, create a directory for Authelia and create a `docker-compose.yml`
41file.
39To start, create a directory for Authelia and create a
40=docker-compose.yml= file.
4241
43```sh
42#+begin_src sh
4443mkdir ~/authelia
4544nano ~/authelia/docker-compose.yml
46```
45#+end_src
4746
48Within this file, paste the following content. If you prefer a different local
49port, modify the port on the left side of the colon on the `9091:9091` line. Be
50sure to modify the `TZ` variable to your timezone.
47Within this file, paste the following content. If you prefer a different
48local port, modify the port on the left side of the colon on the
49=9091:9091= line. Be sure to modify the =TZ= variable to your timezone.
5150
52```yml
51#+begin_src yml
5352version: "3.3"
5453
5554services:
@@ -62,51 +61,53 @@ services:
6261 - 9091:9091
6362 environment:
6463 - TZ=America/Chicago
65```
64#+end_src
6665
6766Start the container with docker-compose:
6867
69```sh
68#+begin_src sh
7069sudo docker-compose up -d
71```
70#+end_src
7271
73After the first start, the container will automatically exit and require you to
74modify the app's configuration files before continuing. Read on to learn more.
72After the first start, the container will automatically exit and require
73you to modify the app's configuration files before continuing. Read on
74to learn more.
7575
76## Authelia Configuration
76** Authelia Configuration
7777
78To configure Authelia before we restart the container, we need to open the
79`config` directory and modify the files. Start by editing the
80`configuration.yml` file, where all of Authelia's settings are stored.
78To configure Authelia before we restart the container, we need to open
79the =config= directory and modify the files. Start by editing the
80=configuration.yml= file, where all of Authelia's settings are stored.
8181
82My personal preference is to copy the original configuration file to a backup
83file and edit a fresh copy.
82My personal preference is to copy the original configuration file to a
83backup file and edit a fresh copy.
8484
85```sh
85#+begin_src sh
8686sudo cp ~/authelia/config/configuration.yml ~/authelia/config/configuration.yml.bk
8787sudo nano ~/authelia/config/configuration.yml
88```
88#+end_src
8989
90Within the blank `configuration.yml` file, paste the following information. You
91will need to make quite a few updates, so be sure to read each line carefully
92and modify as necessary.
90Within the blank =configuration.yml= file, paste the following
91information. You will need to make quite a few updates, so be sure to
92read each line carefully and modify as necessary.
9393
9494The major required changes are:
9595
96- Any instances of `example.com` should be replaced by your domain.
97- `jwt_secret` - Use the `pwgen 40 1` command to generate a secret for
98 yourself.
99- `access_control` - Set the Authelia domain to bypass here, as well as any
100 subdomains you want to protect.
101- `session` > `secret` - Use the `pwgen 40 1` command to generate a secret for
102 yourself.
103- `regulation` - Set the variables here to restrict login attempts and bans.
104- `storage` > `encryption_key` - Use the `pwgen 40 1` command to generate a
105 secret for yourself.
106- `smtp` - If you have access to an SMTP service, set up the information here
107 to active outgoing emails.
108
109```yml
96- Any instances of =example.com= should be replaced by your domain.
97- =jwt_secret= - Use the =pwgen 40 1= command to generate a secret for
98 yourself.
99- =access_control= - Set the Authelia domain to bypass here, as well as
100 any subdomains you want to protect.
101- =session= > =secret= - Use the =pwgen 40 1= command to generate a
102 secret for yourself.
103- =regulation= - Set the variables here to restrict login attempts and
104 bans.
105- =storage= > =encryption_key= - Use the =pwgen 40 1= command to
106 generate a secret for yourself.
107- =smtp= - If you have access to an SMTP service, set up the information
108 here to active outgoing emails.
109
110#+begin_src yml
110111# yamllint disable rule:comments-indentation
111112---
112113###############################################################################
@@ -194,25 +195,26 @@ notifier:
194195 tls:
195196 skip_verify: false
196197 minimum_version: TLS1.2
197```
198#+end_src
198199
199## Authelia Users
200** Authelia Users
200201
201202Next, create the users file for authentication.
202203
203```sh
204#+begin_src sh
204205sudo nano ~/authelia/config/users_database.yml
205```
206#+end_src
206207
207Within the file, you will need to create an entry for each user that needs
208access to Authelia. The `my_username` entry will be the username used on the
209login page.
208Within the file, you will need to create an entry for each user that
209needs access to Authelia. The =my_username= entry will be the username
210used on the login page.
210211
211To generate the password, go to [Argon2 Hash Generator](https://argon2.online),
212generate a random salt, and make sure the rest of the settings match the
213`authentication_backend` section of `configuration.yml` file.
212To generate the password, go to [[https://argon2.online][Argon2 Hash
213Generator]], generate a random salt, and make sure the rest of the
214settings match the =authentication_backend= section of
215=configuration.yml= file.
214216
215```yaml
217#+begin_src yaml
216218users:
217219 my_username:
218220 displayname: "My User"
@@ -223,31 +225,31 @@ users:
223225 groups:
224226 - admins
225227 - dev
226```
228#+end_src
227229
228230Once the app is configured, restart the container from scratch.
229231
230```sh
232#+begin_src sh
231233cd ~/authelia
232234sudo docker-compose down && sudo docker-compose up -d
233```
235#+end_src
234236
235## Nginx: Authelia Domain
237** Nginx: Authelia Domain
236238
237Once the container is running and configured, the final step is to configure
238external access to the server via Nginx reverse proxy.
239Once the container is running and configured, the final step is to
240configure external access to the server via Nginx reverse proxy.
239241
240242Start by creating the Authelia domain.
241243
242```sh
244#+begin_src sh
243245sudo nano /etc/nginx/sites-available/auth
244```
246#+end_src
245247
246248Within this file, paste the following information and be sure to update
247`example.com` to your domain. Make sure the `$upstream_authelia` variable
248matches the location of your Authelia container.
249=example.com= to your domain. Make sure the =$upstream_authelia=
250variable matches the location of your Authelia container.
249251
250```conf
252#+begin_src conf
251253server {
252254 if ($host ~ ^[^.]+\.example\.com$) {
253255 return 301 https://$host$request_uri;
@@ -299,30 +301,30 @@ server {
299301 }
300302
301303}
302```
304#+end_src
303305
304Next, symlink the file and restart Nginx. If there are errors, be sure to
305resolve those before moving on.
306Next, symlink the file and restart Nginx. If there are errors, be sure
307to resolve those before moving on.
306308
307```sh
309#+begin_src sh
308310sudo ln -s /etc/nginx/sites-available/auth /etc/nginx/sites-enabled/auth
309311sudo systemctl restart nginx.service
310```
312#+end_src
311313
312## Nginx: Protected Domain(s)
314** Nginx: Protected Domain(s)
313315
314Now that Authelia is accessible externally, you need to configure the domain you
315intend to protect with Authelia. In this example, I'm protecting
316`teddit.example.com`.
316Now that Authelia is accessible externally, you need to configure the
317domain you intend to protect with Authelia. In this example, I'm
318protecting =teddit.example.com=.
317319
318320Similar to the process above, paste the content and update the relevant
319321variables.
320322
321```sh
323#+begin_src sh
322324sudo nano /etc/nginx/sites-available/teddit
323```
325#+end_src
324326
325```conf
327#+begin_src conf
326328server {
327329 if ($host ~ ^[^.]+\.example\.com$) {
328330 return 301 https://$host$request_uri;
@@ -424,26 +426,26 @@ server {
424426 proxy_buffers 64 256k;
425427 }
426428}
427```
429#+end_src
428430
429431Same as before, symlink the file and restart Nginx.
430432
431```sh
433#+begin_src sh
432434sudo ln -s /etc/nginx/sites-available/teddit /etc/nginx/sites-enabled/teddit
433435sudo systemctl restart nginx.service
434```
436#+end_src
435437
436# Results
438* Results
437439
438440When visiting the protected domain, you will now be redirected to your
439441authentication domain and presented with the Authelia login portal.
440442
441![Authelia
442Portal](https://img.cleberg.net/blog/20231010-authelia/authelia_portal.png)
443#+caption: Authelia Portal
444[[https://img.cleberg.net/blog/20231010-authelia/authelia_portal.png]]
443445
444Once you've successfully authenticated, you can visit your authentication domain
445directly and see that you're currently authenticated to any domain protected by
446Authelia.
446Once you've successfully authenticated, you can visit your
447authentication domain directly and see that you're currently
448authenticated to any domain protected by Authelia.
447449
448![Authelia
449Success](https://img.cleberg.net/blog/20231010-authelia/authelia_success.png)
450#+caption: Authelia Success
451[[https://img.cleberg.net/blog/20231010-authelia/authelia_success.png]]
content/blog/2023-10-15-alpine-ssh-hardening.md → content/blog/2023-10-15-alpine-ssh-hardening.org renamed +35 −37
@@ -1,69 +1,67 @@
1+++
2date = 2023-10-15
3title = "SSH Hardening for Alpine Linux"
4description = ""
5draft = false
6+++
1#+date: <2023-10-15>
2#+title: SSH Hardening for Alpine Linux
3#+description:
74
8# Overview
5
6* Overview
97
108This guide follows the standard
11[ssh-audit](https://www.ssh-audit.com/hardening_guides.html) hardening guide,
12tweaked for Alpine Linux.
9[[https://www.ssh-audit.com/hardening_guides.html][ssh-audit]] hardening
10guide, tweaked for Alpine Linux.
1311
14# Hardening Guide
12* Hardening Guide
1513
16These steps must be performed as root. You can try to use `doas` or `sudo`, but
17there may be issues.
14These steps must be performed as root. You can try to use =doas= or
15=sudo=, but there may be issues.
1816
191. Re-generate the RSA and ED25519 keys
171. Re-generate the RSA and ED25519 keys
2018
21```sh
19#+begin_src sh
2220rm /etc/ssh/ssh_host_*
2321ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ""
2422ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""
25```
23#+end_src
2624
272. Remove small Diffie-Hellman moduli
252. [@2] Remove small Diffie-Hellman moduli
2826
29```sh
27#+begin_src sh
3028awk '$5 >= 3071' /etc/ssh/moduli > /etc/ssh/moduli.safe
3129mv /etc/ssh/moduli.safe /etc/ssh/moduli
32```
30#+end_src
3331
343. Enable the RSA and ED25519 HostKey directives in the /etc/ssh/sshd~config~
35 file
323. [@3] Enable the RSA and ED25519 HostKey directives in the
33 /etc/ssh/sshd_{config} file
3634
37```sh
35#+begin_src sh
3836sed -i 's/^\#HostKey \/etc\/ssh\/ssh_host_\(rsa\|ed25519\)_key$/HostKey \/etc\/ssh\/ssh_host_\1_key/g' /etc/ssh/sshd_config
39```
37#+end_src
4038
414. Restrict supported key exchange, cipher, and MAC algorithms
394. [@4] Restrict supported key exchange, cipher, and MAC algorithms
4240
43```sh
41#+begin_src sh
4442echo -e "\n# Restrict key exchange, cipher, and MAC algorithms, as per sshaudit.com\n# hardening guide.\nKexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256\nCiphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr\nMACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com\nHostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com" > /etc/ssh/sshd_config.d/ssh-audit_hardening.conf
45```
43#+end_src
4644
475. Include the /etc/ssh/sshd~config~.d directory
455. [@5] Include the /etc/ssh/sshd_{config}.d directory
4846
49```sh
47#+begin_src sh
5048echo -e "Include /etc/ssh/sshd_config.d/*.conf" > /etc/ssh/sshd_config
51```
49#+end_src
5250
536. Restart OpenSSH server
516. [@6] Restart OpenSSH server
5452
55```sh
53#+begin_src sh
5654rc-service sshd restart
57```
55#+end_src
5856
59# Testing SSH
57* Testing SSH
6058
61You can test the results with the `ssh-audit` python script.
59You can test the results with the =ssh-audit= python script.
6260
63```sh
61#+begin_src sh
6462pip3 install ssh-audit
6563ssh-audit localhost
66```
64#+end_src
6765
68If everything succeeded, the results will show as all green. If anything is
69yellow, orange, or red, you may need to tweak additional settings.
66If everything succeeded, the results will show as all green. If anything
67is yellow, orange, or red, you may need to tweak additional settings.
content/blog/2023-10-17-self-hosting-anonymousoverflow.md → content/blog/2023-10-17-self-hosting-anonymousoverflow.org renamed +39 −40
@@ -1,34 +1,33 @@
1+++
2date = 2023-10-17
3title = "Self-Hosting AnonymousOverflow"
4description = ""
5draft = false
6+++
1#+date: <2023-10-17>
2#+title: Self-Hosting AnonymousOverflow
3#+description:
74
8# Overview
5
6* Overview
97
108I recently launched an instance of AnonymousOverflow at
11[ao.cleberg.net](https://ao.cleberg.net) and wanted to write a brief post on how
12easy it is to install with Docker Compose and Nginx.
9[[https://ao.cleberg.net][ao.cleberg.net]] and wanted to write a brief
10post on how easy it is to install with Docker Compose and Nginx.
1311
14This guide uses Ubuntu server, Docker Compose, and Nginx as a reverse proxy.
12This guide uses Ubuntu server, Docker Compose, and Nginx as a reverse
13proxy.
1514
16# Installation
15* Installation
1716
18## Docker Compose
17** Docker Compose
1918
20To install AnonymousOverflow, start by creating a directory for the application
21and create its `docker-compose.yml` file.
19To install AnonymousOverflow, start by creating a directory for the
20application and create its =docker-compose.yml= file.
2221
23```sh
22#+begin_src sh
2423mkdir ~/anonymousoverflow && cd ~/anonymousoverflow
2524nano docker-compose.yml
26```
25#+end_src
2726
2827Within this file, paste the following information. Be sure to change the
29`APP_URL`, `JWT_SIGNING_SECRET`, and `ports` to match your needs.
28=APP_URL=, =JWT_SIGNING_SECRET=, and =ports= to match your needs.
3029
31```yaml
30#+begin_src yaml
3231version: "3"
3332
3433services:
@@ -41,31 +40,31 @@ services:
4140 ports:
4241 - "9380:8080"
4342 restart: "always"
44```
43#+end_src
4544
46Save and exit the file when complete. You can now launch the container and
47access it via your local network.
45Save and exit the file when complete. You can now launch the container
46and access it via your local network.
4847
49```sh
48#+begin_src sh
5049sudo docker-compose up -d
51```
50#+end_src
5251
53## Nginx Reverse Proxy
52** Nginx Reverse Proxy
5453
55If you want to access this service outside the local network, I recommend using
56Nginx as a reverse proxy.
54If you want to access this service outside the local network, I
55recommend using Nginx as a reverse proxy.
5756
5857Let's start by creating a configuration file.
5958
60```sh
59#+begin_src sh
6160sudo nano /etc/nginx/sites-available/ao
62```
61#+end_src
6362
64Within this file, paste the following content and repace `ao.example.com` with
65your URL. You may need to update the SSL certificate statements if your
66certificates are in a different location.
63Within this file, paste the following content and repace
64=ao.example.com= with your URL. You may need to update the SSL
65certificate statements if your certificates are in a different location.
6766
68```conf
67#+begin_src conf
6968server {
7069 if ($host ~ ^[^.]+\.cleberg\.net$) {
7170 return 301 https://$host$request_uri;
@@ -117,16 +116,16 @@ server {
117116 proxy_buffers 64 256k;
118117 }
119118}
120```
119#+end_src
121120
122Save and exit the file when complete. On Ubuntu, you will need to symlink the
123configuration file before it will be recognized by Nginx. Once complete, simply
124restart the web server.
121Save and exit the file when complete. On Ubuntu, you will need to
122symlink the configuration file before it will be recognized by Nginx.
123Once complete, simply restart the web server.
125124
126```sh
125#+begin_src sh
127126sudo ln -s /etc/nginx/sites-available/ao /etc/nginx/sites-enabled/ao
128127sudo systemctl restart nginx.service
129```
128#+end_src
130129
131The website will now be available publicly. Visit [my
132instance](https://ao.cleberg.net) for an example.
130The website will now be available publicly. Visit
131[[https://ao.cleberg.net][my instance]] for an example.
content/blog/2023-11-08-scli.md deleted −147
@@ -1,147 +0,0 @@
1+++
2date = 2023-11-08
3title = "Installing scli on Alpine Linux (musl)"
4description = ""
5draft = false
6+++
7
8[scli](https://github.com/isamert/scli) is a command-line tool that allows you
9to connect to your Signal messenger account. This program utilizes a two-pane
10display that shows you chats on the left and the focused conversation on the
11right.
12
13This guide will show you how to install `scli` and its dependencies on Alpine
14Linux, which requires some extra work due to musl.
15
16If you're using a non-musl system, you can likely following the `scli` README
17and download the packaged binaries for an easier installation process.
18
19# Dependencies
20
21In order to use `scli`, you need a few dependencies:
22
23- `openjdk17-jre` - Used as a dependency for the `signal-cli` tool. Version
24 may vary.
25- `signal-cli` - Used as the backbone of the `scli` tool.
26- `findutils` - Replaces the standard Busybox version of `xargs`.
27- `urwid` - A console user interface library for Python.
28- `urwid-readline` - For GNU emacs-like keybinds on the input line.
29- `qrencode` - Displays a QR code in the terminal to link the device using
30 your phone. Not necessary if you're only linking on desktop and can
31 copy/paste the connection URL.
32
33Let's start by installing the packages available via Alpine's repositories. Be
34sure to install the latest version of `openjdk`. If you run into Java-related
35issues, uninstall `openjdk` and install an older version.
36
37```sh
38doas apk add openjdk17-jre findutils qrencode
39```
40
41Next, let's install `signal-cli`. Be sure to export the version of `signal-cli`
42that you want. I use version `0.12.4` below, but that may be outdated by the
43time you're reading this.
44
45```sh
46export VERSION="0.12.4"
47wget https://github.com/AsamK/signal-cli/releases/download/v"${VERSION}"/signal-cli-"${VERSION}".tar.gz
48doas tar xf signal-cli-"${VERSION}".tar.gz -C /opt
49doas ln -sf /opt/signal-cli-${VERSION}"/bin/signal/cli /usr/local/bin
50```
51
52Finally, install the `urwid` packages using the Python packaging utility.
53
54```sh
55pip3 install urwid urwid-readline
56```
57
58# Installation
59
60Now that we have all of the dependencies we need, we can install `scli`. Start
61by simply cloning the repository.
62
63```sh
64git clone https://github.com/isamert/scli
65```
66
67When I cloned this repository on 2023-11-08, I found a bug in the logic that
68required a fix. You must edit the `scli` file and replace the one instance of
69`RLIMIT_OFILE` with `RLIMIT_NOFILE`.
70
71```sh
72cd scli
73nano scli
74```
75
76Once complete, you can move this program to anywhere on your `$PATH`. I chose
77the following directory.
78
79```sh
80doas mv scli /usr/local/bin/scli
81```
82
83# Initial Setup
84
85Now that everything is installed, we can login and configure the client. Start
86by generating a connection link.
87
88```sh
89signal-cli link -n "YOUR-DEVICE-NICKNAME" | tee >(xargs -L 1 qrencode -t utf8)
90```
91
92This will generate a connection link and related QR code for you to use to link
93the devices together. Once complete, **wait patiently** for the connection
94process to finish.
95
96Once it completes, it will exit and return you to the prompt. From here, you
97need to perform an initial `receive` command to start things off. The `USERNAME`
98variable should be your phone number, such as `+15551237890`.
99
100```sh
101signal-cli -u USERNAME receive
102```
103
104Also be sure to test the daemon to ensure it works properly. If no errors occur,
105it's working. If you run into errors because you're not running a DBUS session,
106see my notes below.
107
108```sh
109signal-cli -u USERNAME daemon
110```
111
112Once the initial reception is complete, you are ready to use `scli`.
113
114This process will differ depending on your desktop environment (DE). If you are
115running a DE, you likely have a DBUS session running already and can simply
116launch the program.
117
118However, if you're like me and running your computer straight on the TTY without
119a DE, you'll need to start a DBUS session for this program.
120
121```sh
122# If you're not running a DBUS session yet, you need to start one for scli
123dbus-run-session -- scli
124
125# OR - If you're already running a DBUS session, simply run scli
126scli
127```
128
129# Configuration
130
131Lastly, there are a number of configuration options that you can pass via the
132command or in the `~/.config/sclirc` file. See the Github README for more
133information on configuration options.
134
135```sh
136nano ~/.config/sclirc
137```
138
139```conf
140# ~/.config/sclirc
141
142wrap-at = 80
143enable-notifications = true
144```
145
146That's it! Following this guide, I have a functional `scli` program that
147successfully sends messages to my contacts and myself!
content/blog/2023-11-08-scli.org added +149
@@ -0,0 +1,149 @@
1#+date: <2023-11-08>
2#+title: Installing scli on Alpine Linux (musl)
3#+description:
4
5
6[[https://github.com/isamert/scli][scli]] is a command-line tool that
7allows you to connect to your Signal messenger account. This program
8utilizes a two-pane display that shows you chats on the left and the
9focused conversation on the right.
10
11This guide will show you how to install =scli= and its dependencies on
12Alpine Linux, which requires some extra work due to musl.
13
14If you're using a non-musl system, you can likely following the =scli=
15README and download the packaged binaries for an easier installation
16process.
17
18* Dependencies
19
20In order to use =scli=, you need a few dependencies:
21
22- =openjdk17-jre= - Used as a dependency for the =signal-cli= tool.
23 Version may vary.
24- =signal-cli= - Used as the backbone of the =scli= tool.
25- =findutils= - Replaces the standard Busybox version of =xargs=.
26- =urwid= - A console user interface library for Python.
27- =urwid-readline= - For GNU emacs-like keybinds on the input line.
28- =qrencode= - Displays a QR code in the terminal to link the device
29 using your phone. Not necessary if you're only linking on desktop and
30 can copy/paste the connection URL.
31
32Let's start by installing the packages available via Alpine's
33repositories. Be sure to install the latest version of =openjdk=. If you
34run into Java-related issues, uninstall =openjdk= and install an older
35version.
36
37#+begin_src sh
38doas apk add openjdk17-jre findutils qrencode
39#+end_src
40
41Next, let's install =signal-cli=. Be sure to export the version of
42=signal-cli= that you want. I use version =0.12.4= below, but that may
43be outdated by the time you're reading this.
44
45#+begin_src sh
46export VERSION="0.12.4"
47wget https://github.com/AsamK/signal-cli/releases/download/v"${VERSION}"/signal-cli-"${VERSION}".tar.gz
48doas tar xf signal-cli-"${VERSION}".tar.gz -C /opt
49doas ln -sf /opt/signal-cli-${VERSION}"/bin/signal/cli /usr/local/bin
50#+end_src
51
52Finally, install the =urwid= packages using the Python packaging
53utility.
54
55#+begin_src sh
56pip3 install urwid urwid-readline
57#+end_src
58
59* Installation
60
61Now that we have all of the dependencies we need, we can install =scli=.
62Start by simply cloning the repository.
63
64#+begin_src sh
65git clone https://github.com/isamert/scli
66#+end_src
67
68When I cloned this repository on 2023-11-08, I found a bug in the logic
69that required a fix. You must edit the =scli= file and replace the one
70instance of =RLIMIT_OFILE= with =RLIMIT_NOFILE=.
71
72#+begin_src sh
73cd scli
74nano scli
75#+end_src
76
77Once complete, you can move this program to anywhere on your =$PATH=. I
78chose the following directory.
79
80#+begin_src sh
81doas mv scli /usr/local/bin/scli
82#+end_src
83
84* Initial Setup
85
86Now that everything is installed, we can login and configure the client.
87Start by generating a connection link.
88
89#+begin_src sh
90signal-cli link -n "YOUR-DEVICE-NICKNAME" | tee >(xargs -L 1 qrencode -t utf8)
91#+end_src
92
93This will generate a connection link and related QR code for you to use
94to link the devices together. Once complete, *wait patiently* for the
95connection process to finish.
96
97Once it completes, it will exit and return you to the prompt. From here,
98you need to perform an initial =receive= command to start things off.
99The =USERNAME= variable should be your phone number, such as
100=+15551237890=.
101
102#+begin_src sh
103signal-cli -u USERNAME receive
104#+end_src
105
106Also be sure to test the daemon to ensure it works properly. If no
107errors occur, it's working. If you run into errors because you're not
108running a DBUS session, see my notes below.
109
110#+begin_src sh
111signal-cli -u USERNAME daemon
112#+end_src
113
114Once the initial reception is complete, you are ready to use =scli=.
115
116This process will differ depending on your desktop environment (DE). If
117you are running a DE, you likely have a DBUS session running already and
118can simply launch the program.
119
120However, if you're like me and running your computer straight on the TTY
121without a DE, you'll need to start a DBUS session for this program.
122
123#+begin_src sh
124# If you're not running a DBUS session yet, you need to start one for scli
125dbus-run-session -- scli
126
127# OR - If you're already running a DBUS session, simply run scli
128scli
129#+end_src
130
131* Configuration
132
133Lastly, there are a number of configuration options that you can pass
134via the command or in the =~/.config/sclirc= file. See the Github README
135for more information on configuration options.
136
137#+begin_src sh
138nano ~/.config/sclirc
139#+end_src
140
141#+begin_src conf
142# ~/.config/sclirc
143
144wrap-at = 80
145enable-notifications = true
146#+end_src
147
148That's it! Following this guide, I have a functional =scli= program that
149successfully sends messages to my contacts and myself!
content/blog/2023-12-03-unifi-nextdns.md → content/blog/2023-12-03-unifi-nextdns.org renamed +80 −79
@@ -1,102 +1,103 @@
1+++
2date = 2023-12-03
3title = "How to Install NextDNS on the Unifi Dream Machine"
4description = ""
5draft = false
6+++
1#+date: <2023-12-03>
2#+title: How to Install NextDNS on the Unifi Dream Machine
3#+description:
74
8# Overview
5
6* Overview
97
108I recently installed NextDNS on my Unifi Dream Machine router using the
11[UnifiOS](https://github.com/nextdns/nextdns/wiki/UnifiOS) wiki page on
12NextDNS's GitHub repository.
9[[https://github.com/nextdns/nextdns/wiki/UnifiOS][UnifiOS]] wiki page
10on NextDNS's GitHub repository.
1311
14As a result of this, I wanted to write down the process in case the wiki or
15installer ever gets lost.
12As a result of this, I wanted to write down the process in case the wiki
13or installer ever gets lost.
1614
17# Wiki
15* Wiki
1816
19The following is copied from the wiki page linked above, with one difference in
20the `ssh` command.
17The following is copied from the wiki page linked above, with one
18difference in the =ssh= command.
2119
22Install instructions for Unifi Dream Machine (UDM) standard and pro routers.
20Install instructions for Unifi Dream Machine (UDM) standard and pro
21routers.
2322
24## Install
23** Install
2524
2625Enable SSH:
2726
28- Go to your unifi admin interface and select your device (not the controller
29 settings, but the Dream Machine settings)
30- Click on "Settings" at the bottom of the page
31- Go to the "Advanced" section on the left pan
32- Enable SSH
33- Set a SSH password
27- Go to your unifi admin interface and select your device (not the
28 controller settings, but the Dream Machine settings)
29- Click on "Settings" at the bottom of the page
30- Go to the "Advanced" section on the left pan
31- Enable SSH
32- Set a SSH password
3433
35Connect to your router using `ssh root@xxx.xxx.xxx.xxx` with the password you
36configured.
34Connect to your router using =ssh root@xxx.xxx.xxx.xxx= with the
35password you configured.
3736
3837Run the following command and follow the instructions:
3938
40```sh
39#+begin_src sh
4140sh -c 'sh -c "$(curl -sL https://nextdns.io/install)"'
42```
41#+end_src
4342
44Note: Queries from the UDM itself won't be routed to NextDNS nor encrypted due
45to current system limitation. All traffic from other devices on then network
46will.
43Note: Queries from the UDM itself won't be routed to NextDNS nor
44encrypted due to current system limitation. All traffic from other
45devices on then network will.
4746
48## Upgrade
47** Upgrade
4948
50To upgrade to the last version, simply re-run the installer above. If a new
51version is available, the upgrade action will added to the list of possible
52actions.
49To upgrade to the last version, simply re-run the installer above. If a
50new version is available, the upgrade action will added to the list of
51possible actions.
5352
54## Uninstall
53** Uninstall
5554
56To uninstall, re-run the installer above and select "Remove" in the menu.
55To uninstall, re-run the installer above and select "Remove" in the
56menu.
5757
58## Troubleshooting
58** Troubleshooting
5959
60If the installation fail, please the installer in debug mode and contact us at
61team@nextdns.io with the transcript of the installation:
60If the installation fail, please the installer in debug mode and contact
61us at team@nextdns.io with the transcript of the installation:
6262
63```sh
63#+begin_src sh
6464sh -c 'DEBUG=1 sh -c "$(curl -sL https://nextdns.io/install)"'
65```
65#+end_src
6666
67### Content Filtering Conflict
67*** Content Filtering Conflict
6868
69NextDNS CLI and the UDM Content Filtering or the Ad Blocking features are
70incompatible. If you want to use NextDNS CLI, please make sure they are
71disabled.
69NextDNS CLI and the UDM Content Filtering or the Ad Blocking features
70are incompatible. If you want to use NextDNS CLI, please make sure they
71are disabled.
7272
73To disable Content Filtering, go to Settings > Network, then for each network,
74set the Content Filtering feature to None
73To disable Content Filtering, go to Settings > Network, then for each
74network, set the Content Filtering feature to None
7575
76To disable Ad Blocking, go to Settings > Application Firewall. In the General
77tab, uncheck the Ad Blocking checkbox.
76To disable Ad Blocking, go to Settings > Application Firewall. In the
77General tab, uncheck the Ad Blocking checkbox.
7878
79### APT Error
79*** APT Error
8080
8181If you get an apt error as follow:
8282
83```sh
83#+begin_src sh
8484E: Failed to fetch http://security.debian.org/dists/stretch/updates/main/binary-arm64/Packages 404 Not Found [IP: 151.101.70.132 80]
85```
85#+end_src
8686
8787You may try to following:
8888
89```sh
89#+begin_src sh
9090sed -i -e 's/deb.debian.org/archive.debian.org/g' \
9191 -e 's|security.debian.org|archive.debian.org/|g' \
9292 -e '/stretch-updates/d' /etc/apt/sources.list
93```
93#+end_src
9494
95## install.sh
95** install.sh
9696
97Here are the contents of the `install.sh` file used above, as of 2023-12-03:
97Here are the contents of the =install.sh= file used above, as of
982023-12-03:
9899
99```sh
100#+begin_src sh
100101#!/bin/sh
101102
102103main() {
@@ -321,13 +322,13 @@ install_bin() {
321322 fi
322323 log_debug "Installing $INSTALL_RELEASE binary for $GOOS/$GOARCH to $bin_path"
323324 case "$INSTALL_RELEASE" in
324 */*)
325 ,*/*)
325326 # Snapshot
326327 branch=${INSTALL_RELEASE%/*}
327328 hash=${INSTALL_RELEASE#*/}
328329 url="https://snapshot.nextdns.io/${branch}/nextdns-${hash}_${GOOS}_${GOARCH}.tar.gz"
329330 ;;
330 *)
331 ,*)
331332 url="https://github.com/nextdns/nextdns/releases/download/v${INSTALL_RELEASE}/nextdns_${INSTALL_RELEASE}_${GOOS}_${GOARCH}.tar.gz"
332333 ;;
333334 esac
@@ -629,12 +630,12 @@ install_type() {
629630 echo "$FORCE_INSTALL_TYPE"; return 0
630631 fi
631632 case "$INSTALL_RELEASE" in
632 */*)
633 ,*/*)
633634 case $OS in
634635 ubios)
635636 echo "ubios_snapshot"; return 0
636637 ;;
637 *)
638 ,*)
638639 # Snapshot mode always use binary install
639640 echo "bin"; return 0
640641 ;;
@@ -662,7 +663,7 @@ install_type() {
662663 . /etc/os-release
663664 major=$(echo "$VERSION_ID" | cut -d. -f1)
664665 case $major in
665 *[!0-9]*)
666 ,*[!0-9]*)
666667 if [ "$VERSION_ID" = "19.07.0-rc1" ]; then
667668 # No opkg support before 19.07.0-rc2
668669 echo "bin"
@@ -671,7 +672,7 @@ install_type() {
671672 echo "openwrt"
672673 fi
673674 ;;
674 *)
675 ,*)
675676 if [ "$major" -lt 19 ]; then
676677 # No opkg support before 19.07.0-rc2
677678 echo "bin"
@@ -717,7 +718,7 @@ install_type() {
717718 # TODO: pkg for xbps
718719 echo "bin"
719720 ;;
720 *)
721 ,*)
721722 log_error "Unsupported installation for $(detect_os)"
722723 return 1
723724 ;;
@@ -869,7 +870,7 @@ ask_bool() {
869870 false)
870871 msg="$msg [y|N]: "
871872 ;;
872 *)
873 ,*)
873874 msg="$msg (y/n): "
874875 esac
875876 while true; do
@@ -887,7 +888,7 @@ ask_bool() {
887888 echo "false"
888889 return 0
889890 ;;
890 *)
891 ,*)
891892 echo "Invalid input, use yes or no"
892893 ;;
893894 esac
@@ -899,12 +900,12 @@ detect_endiannes() {
899900 # Some firmwares do not contain hexdump, for those, try to detect endianness
900901 # differently.
901902 case $(cat /proc/cpuinfo) in
902 *BCM5300*)
903 ,*BCM5300*)
903904 # RT-AC66U does not support Merlin version over 380.70 which
904905 # lacks hexdump command.
905906 echo "le"
906907 ;;
907 *)
908 ,*)
908909 log_error "Cannot determine endianness"
909910 return 1
910911 ;;
@@ -935,10 +936,10 @@ detect_goarch() {
935936 arm)
936937 # FreeBSD does not include arm version
937938 case "$(sysctl -b hw.model 2>/dev/null)" in
938 *A9*)
939 ,*A9*)
939940 echo "armv7"
940941 ;;
941 *)
942 ,*)
942943 # Unknown version, fallback to the lowest
943944 echo "armv5"
944945 ;;
@@ -962,7 +963,7 @@ detect_goarch() {
962963 # "out of memory allocating heap arena metadata".
963964 echo "armv7"
964965 ;;
965 *)
966 ,*)
966967 echo "arm64"
967968 ;;
968969 esac
@@ -974,7 +975,7 @@ detect_goarch() {
974975 # TODO: detect hardfloat
975976 echo "$(uname -m)$(detect_endiannes)_softfloat"
976977 ;;
977 *)
978 ,*)
978979 log_error "Unsupported GOARCH: $(uname -m)"
979980 return 1
980981 ;;
@@ -1001,7 +1002,7 @@ detect_goos() {
10011002 OpenBSD)
10021003 echo "openbsd"
10031004 ;;
1004 *)
1005 ,*)
10051006 log_error "Unsupported GOOS: $(uname -s)"
10061007 return 1
10071008 esac
@@ -1078,7 +1079,7 @@ detect_os() {
10781079 OpenBSD)
10791080 echo "openbsd"; return 0
10801081 ;;
1081 *)
1082 ,*)
10821083 esac
10831084 log_error "Unsupported OS: $(uname -o) $(grep ID "/etc/os-release" 2>/dev/null | xargs)"
10841085 return 1
@@ -1097,7 +1098,7 @@ guess_host_type() {
10971098 darwin|steamos)
10981099 echo "workstation"
10991100 ;;
1100 *)
1101 ,*)
11011102 echo "unsure"
11021103 ;;
11031104 esac
@@ -1156,7 +1157,7 @@ bin_location() {
11561157 steamos)
11571158 echo "$HOME/.local/bin/nextdns"
11581159 ;;
1159 *)
1160 ,*)
11601161 log_error "Unknown bin location for $OS"
11611162 ;;
11621163 esac
@@ -1164,12 +1165,12 @@ bin_location() {
11641165
11651166is_version_current() {
11661167 case "$INSTALL_RELEASE" in
1167 */*)
1168 ,*/*)
11681169 # Snapshot
11691170 hash=${INSTALL_RELEASE#*/}
11701171 test "0.0.0-$hash" = "$CURRENT_RELEASE"
11711172 ;;
1172 *)
1173 ,*)
11731174 test "$INSTALL_RELEASE" = "$CURRENT_RELEASE"
11741175 ;;
11751176 esac
@@ -1194,7 +1195,7 @@ get_release() {
11941195 curl) cmd="curl -A curl -s" ;;
11951196 wget) cmd="wget -qO- -U curl" ;;
11961197 openssl) cmd="openssl_get" ;;
1197 *)
1198 ,*)
11981199 log_error "Cannot retrieve latest version"
11991200 return
12001201 ;;
@@ -1246,4 +1247,4 @@ openssl_get() {
12461247
12471248umask 0022
12481249main
1249```
1250#+end_src
content/blog/2024-01-08-dont-say-hello.md deleted −29
@@ -1,29 +0,0 @@
1+++
2date = 2024-01-08
3title = "Don't Say Hello"
4description = ""
5draft = false
6+++
7
8I recently came back from a winter break and have started working again... only
9to immediately run into the dilemma of people sending me cliffhanger messages
10again.
11
12# No Hello
13
14A year or two ago, I discovered [no hello](https://nohello.net/en/) and have
15thought about it often since then. I've even sent it to a few people since then
16(who wouldn't take offense to it).
17
18I work in a fast-paced environment where efficiency is extremely valuable.
19Therefore, I have always held a deep displeasure for conversations where people
20start with "Hello" and then.
21
22I searched back through my work messages and found that I received ~50 messages
23from ~10 people last year from people that contained "hi", "hey", or "hello" and
24did not contain any indication of the purpose of the conversation. I also
25noticed that a few of the users were responsible for the large majority of the
26cliffhangers.
27
28There's no real point to this post, just a desparate request for people to
29please stop doing this.
content/blog/2024-01-08-dont-say-hello.org added +27
@@ -0,0 +1,27 @@
1#+date: <2024-01-08>
2#+title: Don't Say Hello
3#+description:
4
5
6I recently came back from a winter break and have started working
7again... only to immediately run into the dilemma of people sending me
8cliffhanger messages again.
9
10* No Hello
11
12A year or two ago, I discovered [[https://nohello.net/en/][no hello]]
13and have thought about it often since then. I've even sent it to a few
14people since then (who wouldn't take offense to it).
15
16I work in a fast-paced environment where efficiency is extremely
17valuable. Therefore, I have always held a deep displeasure for
18conversations where people start with "Hello" and then.
19
20I searched back through my work messages and found that I received ~50
21messages from ~10 people last year from people that contained "hi",
22"hey", or "hello" and did not contain any indication of the purpose of
23the conversation. I also noticed that a few of the users were
24responsible for the large majority of the cliffhangers.
25
26There's no real point to this post, just a desparate request for people
27to please stop doing this.
content/blog/2024-01-09-macos-customization.md deleted −147
@@ -1,147 +0,0 @@
1+++
2date = 2024-01-09
3title = "Customizing macOS"
4description = ""
5draft = false
6+++
7
8I have been using macOS more than Linux lately, so I wrote this post to describe
9some simple options to customize macOS beyond the normal built-in settings menu.
10
11While not all-encompassing, the options below should be a good start for anyone
12looking to dive down the rabbit hole.
13
14# Basics
15
16## Package Management
17
18To install a lot of software on macOS, you will need
19[Homebrew](https://brew.sh/). You can use their installation script to get
20started. Simply open the `Terminal` application and paste the following snippet:
21
22```sh
23/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
24```
25
26This will allow you to easily install and manage applications and other software
27easily through the `brew` command.
28
29## Terminal
30
31If you're serious about customizing your macOS system, I highly recommend
32installing a terminal emulator that you like and if you're not comfortable on
33the command line yet, start learning. A lot of customization options require you
34to edit hidden files, which is easiest in a terminal.
35
36There are options like iTerm2, Kitty, Alacritty, Hyper, Warp, or the built-in
37Terminal app.
38
39I use [iTerm2](https://iterm2.com/), which can be installed with Homebrew:
40
41```sh
42brew install iterm2
43```
44
45To install color schemes, such as the Dracula scheme, you visit [iTerm
46Themes](https://iterm2colorschemes.com/) and follow their installation
47instructions to install any of the themes.
48
49# Desktop
50
51## Window Management
52
53[yabai](https://github.com/koekeishiya/yabai) is a tiling window manager for
54macOS. While other window managers exist, I found that most of them struggled to
55create logical layouts and to allow me to easily move windows around the screen.
56
57Some advanced settings for yabai are only available if partially disable System
58Integrity Protection (SIP). However, I chose not to do this and it hasn't
59affected my basic usage of yabai at all.
60
61Refer to the [yabai
62wiki](<https://github.com/koekeishiya/yabai/wiki/Installing-yabai-(latest-release)>)
63for installation instructions. You will need to ensure that yabai is allowed to
64access the accessibility and screen recording APIs.
65
66## Keyboard Shortcuts
67
68[skhd](https://github.com/koekeishiya/skhd) is a simple hotkey daemon that
69allows you to define hotkeys in a file for usage on your system.
70
71Installation is simple:
72
73```sh
74brew install koekeishiya/formulae/skhd
75skhd --start-service
76```
77
78After installation, be sure to allow `skhd` access to the accessibility API in
79the macOS privacy settings.
80
81You can configure your hotkeys in the `~/.config/skhd/skhdrc` file:
82
83```sh
84nano ~/.config/skhd/skhdrc
85```
86
87For example, I have hotkeys to open my browser and terminal:
88
89```conf
90# Terminal
91cmd - return : /Applications/iTerm.app/Contents/MacOS/iTerm2
92
93# Browser
94cmd + shift - return : /Applications/LibreWolf.app/Contents/MacOS/librewolf
95```
96
97## Widgets
98
99[uebersicht](https://github.com/felixhageloh/uebersicht/) is a handy
100desktop-based widget tool with a plethora of community-made widgets available in
101the [widgets gallery](https://tracesof.net/uebersicht-widgets/). You can also
102write your own widgets with this tool.
103
104To install, simply download the latest release from the [uebersicht
105website](https://tracesof.net/uebersicht/) and copy it to the Applications
106folder.
107
108See below for an example of the
109[Mond](https://tracesof.net/uebersicht-widgets/#Mond) widget in action.
110
111## Status Bar
112
113[SketchyBar](https://github.com/FelixKratz/SketchyBar) is a customizable
114replacement for the macOS status or menu bar.
115
116You can browse a discussion where various users shared their
117[configurations](https://github.com/FelixKratz/SketchyBar/discussions/47?sort=top)
118for inspiration or to copy their dotfiles.
119
120## Dock
121
122The easiest way to customize the dock is to install
123[uBar](https://ubarapp.com/), which uses a Windows-like menu bar as the default
124style.
125
126However, the built-in macOS dock cannot be disabled and can only be set to
127"always hidden". This can be annoying as it will pop out any time your mouse
128cursor passes closely to the dock edge of the screen. Because of this, I simply
129use the built-in dock instead of customizing it with third-party software.
130
131Regardless, see below for the default installation style of uBar.
132
133## Application Icons
134
135You can also customize the icon of any application in macOS, which will show up
136in Finder, the Dock, Launchpad, search results, etc. I recommend using
137[macOSicons](https://macosicons.com/) to download icons you want, and then apply
138them by following this process.
139
1401. Open the Finder application.
1412. Navigate to the `Applications` folder.
1423. Right-click an application of your choice, and select `Get Info`.
1434. Drag the image you downloaded on top of the application's icon at the top of
144 information window (you will see a green "plus" symbol when you're hovering
145 over it).
1465. Release the new icon on top of the old icon and it will update!
147
content/blog/2024-01-09-macos-customization.org added +151
@@ -0,0 +1,151 @@
1#+date: <2024-01-09>
2#+title: Customizing macOS
3#+description:
4
5
6I have been using macOS more than Linux lately, so I wrote this post to
7describe some simple options to customize macOS beyond the normal
8built-in settings menu.
9
10While not all-encompassing, the options below should be a good start for
11anyone looking to dive down the rabbit hole.
12
13* Basics
14
15** Package Management
16
17To install a lot of software on macOS, you will need
18[[https://brew.sh/][Homebrew]]. You can use their installation script to
19get started. Simply open the =Terminal= application and paste the
20following snippet:
21
22#+begin_src sh
23/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
24#+end_src
25
26This will allow you to easily install and manage applications and other
27software easily through the =brew= command.
28
29** Terminal
30
31If you're serious about customizing your macOS system, I highly
32recommend installing a terminal emulator that you like and if you're not
33comfortable on the command line yet, start learning. A lot of
34customization options require you to edit hidden files, which is easiest
35in a terminal.
36
37There are options like iTerm2, Kitty, Alacritty, Hyper, Warp, or the
38built-in Terminal app.
39
40I use [[https://iterm2.com/][iTerm2]], which can be installed with
41Homebrew:
42
43#+begin_src sh
44brew install iterm2
45#+end_src
46
47To install color schemes, such as the Dracula scheme, you visit
48[[https://iterm2colorschemes.com/][iTerm Themes]] and follow their
49installation instructions to install any of the themes.
50
51* Desktop
52
53** Window Management
54
55[[https://github.com/koekeishiya/yabai][yabai]] is a tiling window
56manager for macOS. While other window managers exist, I found that most
57of them struggled to create logical layouts and to allow me to easily
58move windows around the screen.
59
60Some advanced settings for yabai are only available if partially disable
61System Integrity Protection (SIP). However, I chose not to do this and
62it hasn't affected my basic usage of yabai at all.
63
64Refer to the
65[[https://github.com/koekeishiya/yabai/wiki/Installing-yabai-(latest-release)][yabai
66wiki]] for installation instructions. You will need to ensure that yabai
67is allowed to access the accessibility and screen recording APIs.
68
69** Keyboard Shortcuts
70
71[[https://github.com/koekeishiya/skhd][skhd]] is a simple hotkey daemon
72that allows you to define hotkeys in a file for usage on your system.
73
74Installation is simple:
75
76#+begin_src sh
77brew install koekeishiya/formulae/skhd
78skhd --start-service
79#+end_src
80
81After installation, be sure to allow =skhd= access to the accessibility
82API in the macOS privacy settings.
83
84You can configure your hotkeys in the =~/.config/skhd/skhdrc= file:
85
86#+begin_src sh
87nano ~/.config/skhd/skhdrc
88#+end_src
89
90For example, I have hotkeys to open my browser and terminal:
91
92#+begin_src conf
93# Terminal
94cmd - return : /Applications/iTerm.app/Contents/MacOS/iTerm2
95
96# Browser
97cmd + shift - return : /Applications/LibreWolf.app/Contents/MacOS/librewolf
98#+end_src
99
100** Widgets
101
102[[https://github.com/felixhageloh/uebersicht/][uebersicht]] is a handy
103desktop-based widget tool with a plethora of community-made widgets
104available in the [[https://tracesof.net/uebersicht-widgets/][widgets
105gallery]]. You can also write your own widgets with this tool.
106
107To install, simply download the latest release from the
108[[https://tracesof.net/uebersicht/][uebersicht website]] and copy it to
109the Applications folder.
110
111See below for an example of the
112[[https://tracesof.net/uebersicht-widgets/#Mond][Mond]] widget in
113action.
114
115** Status Bar
116
117[[https://github.com/FelixKratz/SketchyBar][SketchyBar]] is a
118customizable replacement for the macOS status or menu bar.
119
120You can browse a discussion where various users shared their
121[[https://github.com/FelixKratz/SketchyBar/discussions/47?sort=top][configurations]]
122for inspiration or to copy their dotfiles.
123
124** Dock
125
126The easiest way to customize the dock is to install
127[[https://ubarapp.com/][uBar]], which uses a Windows-like menu bar as
128the default style.
129
130However, the built-in macOS dock cannot be disabled and can only be set
131to "always hidden". This can be annoying as it will pop out any time
132your mouse cursor passes closely to the dock edge of the screen. Because
133of this, I simply use the built-in dock instead of customizing it with
134third-party software.
135
136Regardless, see below for the default installation style of uBar.
137
138** Application Icons
139
140You can also customize the icon of any application in macOS, which will
141show up in Finder, the Dock, Launchpad, search results, etc. I recommend
142using [[https://macosicons.com/][macOSicons]] to download icons you
143want, and then apply them by following this process.
144
1451. Open the Finder application.
1462. Navigate to the =Applications= folder.
1473. Right-click an application of your choice, and select =Get Info=.
1484. Drag the image you downloaded on top of the application's icon at the
149 top of information window (you will see a green "plus" symbol when
150 you're hovering over it).
1515. Release the new icon on top of the old icon and it will update!
content/blog/2024-01-13-local-llm.md deleted −92
@@ -1,92 +0,0 @@
1+++
2date = 2024-01-13
3title = "Running Local LLMs on macOS and iOS"
4description = ""
5draft = false
6+++
7
8# Requirements
9
10I've recently started playing with large language models (LLMs), mostly in the
11popular chatbot form, as part of my job and have decided to see if there's a
12consistent and reliable way to interact with these models on Apple devices
13without sacrificing privacy or requiring in-depth technical setup.
14
15My requirements for this test:
16
17- Open source platform
18- On-device model files
19- Minimal required configuration
20- Preferably pre-built, but a simple build process is acceptable
21
22I tested a handful of apps and have summarized my favorite (so far) for macOS
23and iOS below.
24
25> TL;DR - Here are the two that met my requirements and I have found the easiest
26> to install and use so far:
27
28- macOS: [Ollama](https://ollama.ai/)
29- iOS : [LLM Farm](https://llmfarm.site/)
30
31# macOS
32
33[Ollama](https://ollama.ai/) is a simple Go application for macOS and Linux that
34can run various LLMs locally.
35
36For macOS, you can download the pplication on the [Ollama download
37page](https://ollama.ai/download/mac) and install it by unzipping the
38`Ollama.app` file and moving it to the `Applications` folder.
39
40If you prefer the command line, you can run these commands after the download
41finished:
42
43```sh
44cd ~/Downloads && \
45unzip Ollama-darwin.zip && \
46mv ~/Downloads/Ollama.app /Applications/
47```
48
49After running the app, the app will ask you to open a terminal and run the
50default `llama2` model, which will open an interactive chat session in the
51terminal. You can startfully using the application at this point.
52
53If you don't want to use the default `llama2` model, you can download and run
54additional models found on the [Models](https://ollama.ai/library) page.
55
56To see the information for the currently-used model, you can run the `/show
57info` command in the chat.
58
59## Community Integrations
60
61I highly recommend browsing the [Community
62Integrations](https://github.com/jmorganca/ollama#community-integrations)
63section of the project to see how you would prefer to extend Ollama beyond a
64simple command-line interface. There are options for APIs, browser UIs, advanced
65terminal configurations, and more.
66
67# iOS
68
69While there are a handful of decent macOS options, it was quite difficult to
70find an iOS app that offered an open source platform without an extensive
71configuration and building process. I found LLM Farm to be decent enough in
72quality to sit at the top of my list - however, it's definitely not user
73friendly enough for me to consider using it on a daily basis.
74
75[LLM Farm](https://llmfarm.site/) is available on TestFlight, so there's no
76manual build process required. However, you can view the [LLMFarm
77repository](https://github.com/guinmoon/LLMFarm) if you wish.
78
79The caveat is that you will have to manually download the model files from the
80links in the
81[models.md](https://github.com/guinmoon/LLMFarm/blob/main/models.md) file to
82your iPhone to use the app - there's currently no option in the app to reach out
83and grab the latest version of any supported model.
84
85Once you have a file downloaded, you simply create a new chat and select the
86downloaded model file and ensure the inference matches the requirement in the
87`models.md` file.
88
89[Enchanted](https://github.com/AugustDev/enchanted) is also an iOS for private
90AI models, but it requires a public-facing Ollama API, which did not meet my "on
91device requirement." Nonetheless, it's an interesting looking app and I will
92likely set it up to test soon.
content/blog/2024-01-13-local-llm.org added +96
@@ -0,0 +1,96 @@
1#+date: <2024-01-13>
2#+title: Running Local LLMs on macOS and iOS
3#+description:
4
5
6* Requirements
7
8I've recently started playing with large language models (LLMs), mostly
9in the popular chatbot form, as part of my job and have decided to see
10if there's a consistent and reliable way to interact with these models
11on Apple devices without sacrificing privacy or requiring in-depth
12technical setup.
13
14My requirements for this test:
15
16- Open source platform
17- On-device model files
18- Minimal required configuration
19- Preferably pre-built, but a simple build process is acceptable
20
21I tested a handful of apps and have summarized my favorite (so far) for
22macOS and iOS below.
23
24#+begin_quote
25TL;DR - Here are the two that met my requirements and I have found the
26easiest to install and use so far:
27#+end_quote
28
29- macOS: [[https://ollama.ai/][Ollama]]
30- iOS : [[https://llmfarm.site/][LLM Farm]]
31
32* macOS
33
34[[https://ollama.ai/][Ollama]] is a simple Go application for macOS and
35Linux that can run various LLMs locally.
36
37For macOS, you can download the pplication on the
38[[https://ollama.ai/download/mac][Ollama download page]] and install it
39by unzipping the =Ollama.app= file and moving it to the =Applications=
40folder.
41
42If you prefer the command line, you can run these commands after the
43download finished:
44
45#+begin_src sh
46cd ~/Downloads && \
47unzip Ollama-darwin.zip && \
48mv ~/Downloads/Ollama.app /Applications/
49#+end_src
50
51After running the app, the app will ask you to open a terminal and run
52the default =llama2= model, which will open an interactive chat session
53in the terminal. You can startfully using the application at this point.
54
55If you don't want to use the default =llama2= model, you can download
56and run additional models found on the
57[[https://ollama.ai/library][Models]] page.
58
59To see the information for the currently-used model, you can run the
60=/show info= command in the chat.
61
62** Community Integrations
63
64I highly recommend browsing the
65[[https://github.com/jmorganca/ollama#community-integrations][Community
66Integrations]] section of the project to see how you would prefer to
67extend Ollama beyond a simple command-line interface. There are options
68for APIs, browser UIs, advanced terminal configurations, and more.
69
70* iOS
71
72While there are a handful of decent macOS options, it was quite
73difficult to find an iOS app that offered an open source platform
74without an extensive configuration and building process. I found LLM
75Farm to be decent enough in quality to sit at the top of my list -
76however, it's definitely not user friendly enough for me to consider
77using it on a daily basis.
78
79[[https://llmfarm.site/][LLM Farm]] is available on TestFlight, so
80there's no manual build process required. However, you can view the
81[[https://github.com/guinmoon/LLMFarm][LLMFarm repository]] if you wish.
82
83The caveat is that you will have to manually download the model files
84from the links in the
85[[https://github.com/guinmoon/LLMFarm/blob/main/models.md][models.md]]
86file to your iPhone to use the app - there's currently no option in the
87app to reach out and grab the latest version of any supported model.
88
89Once you have a file downloaded, you simply create a new chat and select
90the downloaded model file and ensure the inference matches the
91requirement in the =models.md= file.
92
93[[https://github.com/AugustDev/enchanted][Enchanted]] is also an iOS for
94private AI models, but it requires a public-facing Ollama API, which did
95not meet my "on device requirement." Nonetheless, it's an interesting
96looking app and I will likely set it up to test soon.
content/blog/2024-01-26-audit-dashboard.md deleted −150
@@ -1,150 +0,0 @@
1+++
2date = 2024-01-26
3title = "Building an Audit Status Dashboard"
4description = ""
5draft = false
6+++
7
8Alteryx and Power BI are powerful tools that can help turn your old-school audit
9trackers into interactive tools that provide useful insights and potential
10action plans.
11
12With these tools, we are going to build a dashboard that can effectively
13communicate project status.
14
15# Requirements
16
17This project assumes the following:
18
19- You have access to Alteryx Designer and Power BI Desktop.
20 - If you only have Power BI Desktop, you may need to perform some analysis
21 in Power BI instead of Alteryx.
22- Your data is in a format that can be imported into Alteryx and/or Power BI.
23- You have a basic understanding of data types and visualization.
24
25# Alteryx: Data Preparation & Analysis
26
27## Import Data
28
29With Alteryx, importing data is easy with the use of the `Input Data` tool.
30Simply drag this tool onto the canvas from the `In/Out` tab in the Ribbon to
31create it as a node.
32
33You can choose the File Format manually or simply connect to your file/database
34and let Alteryx determine the format for you. For this example, we will be
35importing an Excel file and changing the `Start Data Import on Line` variable to
36`2`.
37
38## Transform Data
39
40Next, let's replace null data and remove whitespace to clean up our data. We can
41do this with the `Data Cleansing` tool in the `Preparation` tab in the Ribbon.
42
43Ensure that the following options are enabled:
44
45- Replace Nulls
46 - Replace with Blanks (String Fields)
47 - Replace with 0 (Numeric Fields)
48- Remove Unwanted Characters
49 - Leading and Trailing Whitespace
50
51For our next step, we will transform the date fields from strings to datetime
52format. Add a `Datetime` tool for each field you want to transform - in the
53example below, I am using the tool twice for the "Started On" and "Submitted On"
54fields.
55
56Now that the dates are in the correct format, let's perform a calculation based
57on those fields. Start by adding a `Filter` tool, naming a new Output Column,
58and pasting the formula below into it (the two fields used in this formula must
59match the output of the `Datetime` tools above):
60
61```txt
62DateTimeDiff([SubmittedOn_Out],[StartedOn_Out], "days")
63`
64
65## Export Data
66
67Finalize the process by exporting the transformed data set to a new file, for
68use in the following visualization step.
69
70# Power BI: Data Visualization
71
72## Import Data
73
74To start, open the Power BI Desktop application. Upon first use, Power BI will
75ask if you want to open an existing dashboard or import new data.
76
77As we are creating our first dashboard, let's import our data. In my example
78below, I'm importing data from the "Tracker" sheet of the Excel file I'm using
79for this project.
80
81During this process, I also imported the export from the Alteryx workflow above.
82Therefore, we have two different files available for use in our dashboard.
83
84## Add Visuals
85
86To create the dashboard, you will need to follow the list instructions
87below and format as needed.
88
89Instructions to create the visuals above:
90
91- `Text Box`: Explain the name and purpose of the dashboard. You can also add
92 images and logos at the top of the dashboard.
93- `Donut Chart`: Overall status of the project.
94 - `Legend`: Status
95 - `Values`: Count of Status
96- `Stacked Column Chart`: Task count by assignee.
97 - `X-axis`: Preparer
98 - `Y-axis`: Count of Control ID
99 - `Legend`: Status
100- `Treemap`: Top N client submitters by average days to submit.
101 - `Details`: Preparer
102 - `Values`: Sum of Avg~DaysToSubmit~
103- `Line Chart`: Projected vs. actual hours over time.
104- `Clustered Bar Chart`: Projected vs. actual hours per person.
105- `Slicer & Table` - Upcoming due dates.
106 - `Slicer`:
107 - `Values`: Date Due
108 - `Table`:
109 - `Columns`: Count of Control ID, Date Due, Preparer, Status
110
111## Format the Dashboard
112
113You can choose a theme in the View tab of the Ribbon. You can even browse for
114custom JSON files that define themes, such as ones found online or custom ones
115created by your organization.
116
117For each visual, you can click the `Format` button in the `Visualizations` side
118pane and explore the options. You can custom options such as:
119
120- Visual
121 - Legend
122 - Colors
123 - Data labels
124 - Category labels
125- General
126 - Properties
127 - Title
128 - Effects
129 - Header icons
130 - Tooltips
131 - Alt text
132
133You can always look online for inspiration when trying to decide how best to
134organize and style your dashboard.
135
136# Sharing the Results
137
138Generally, you have a few different options for sharing your dashboards with
139others:
140
1411. Export the dashboard as a PDF in the file menu of Power BI. This will export
142 all tabs and visuals as they are set when the export button is pressed. You
143 will lose all interactivity with this option.
1442. Send the full Power BI file to those you wish to share the dashboard. This
145 will retain all settings and interactivity. However, you will also need to
146 send the source files if they need to refresh the dashboard and you will need
147 to re-send the files if you make updates.
1483. Store the dashboard in a synced location, such as a shared drive or Microsoft
149 Teams. Depending on how a user configures their local Windows paths, the data
150 source paths may not be compatible for all users with such a setup.
content/blog/2024-01-26-audit-dashboard.org added +153
@@ -0,0 +1,153 @@
1#+date: <2024-01-26>
2#+title: Building an Audit Status Dashboard
3#+description:
4
5
6Alteryx and Power BI are powerful tools that can help turn your
7old-school audit trackers into interactive tools that provide useful
8insights and potential action plans.
9
10With these tools, we are going to build a dashboard that can effectively
11communicate project status.
12
13* Requirements
14
15This project assumes the following:
16
17- You have access to Alteryx Designer and Power BI Desktop.
18 - If you only have Power BI Desktop, you may need to perform some
19 analysis in Power BI instead of Alteryx.
20- Your data is in a format that can be imported into Alteryx and/or
21 Power BI.
22- You have a basic understanding of data types and visualization.
23
24* Alteryx: Data Preparation & Analysis
25
26** Import Data
27
28With Alteryx, importing data is easy with the use of the =Input Data=
29tool. Simply drag this tool onto the canvas from the =In/Out= tab in the
30Ribbon to create it as a node.
31
32You can choose the File Format manually or simply connect to your
33file/database and let Alteryx determine the format for you. For this
34example, we will be importing an Excel file and changing the
35=Start Data Import on Line= variable to =2=.
36
37** Transform Data
38
39Next, let's replace null data and remove whitespace to clean up our
40data. We can do this with the =Data Cleansing= tool in the =Preparation=
41tab in the Ribbon.
42
43Ensure that the following options are enabled:
44
45- Replace Nulls
46 - Replace with Blanks (String Fields)
47 - Replace with 0 (Numeric Fields)
48- Remove Unwanted Characters
49 - Leading and Trailing Whitespace
50
51For our next step, we will transform the date fields from strings to
52datetime format. Add a =Datetime= tool for each field you want to
53transform - in the example below, I am using the tool twice for the
54"Started On" and "Submitted On" fields.
55
56Now that the dates are in the correct format, let's perform a
57calculation based on those fields. Start by adding a =Filter= tool,
58naming a new Output Column, and pasting the formula below into it (the
59two fields used in this formula must match the output of the =Datetime=
60tools above):
61
62``=txt DateTimeDiff([SubmittedOn_Out],[StartedOn_Out], "days")=
63
64** Export Data
65
66Finalize the process by exporting the transformed data set to a new
67file, for use in the following visualization step.
68
69* Power BI: Data Visualization
70
71** Import Data
72
73To start, open the Power BI Desktop application. Upon first use, Power
74BI will ask if you want to open an existing dashboard or import new
75data.
76
77As we are creating our first dashboard, let's import our data. In my
78example below, I'm importing data from the "Tracker" sheet of the Excel
79file I'm using for this project.
80
81During this process, I also imported the export from the Alteryx
82workflow above. Therefore, we have two different files available for use
83in our dashboard.
84
85** Add Visuals
86
87To create the dashboard, you will need to follow the list instructions
88below and format as needed.
89
90Instructions to create the visuals above:
91
92- =Text Box=: Explain the name and purpose of the dashboard. You can
93 also add images and logos at the top of the dashboard.
94- =Donut Chart=: Overall status of the project.
95 - =Legend=: Status
96 - =Values=: Count of Status
97- =Stacked Column Chart=: Task count by assignee.
98 - =X-axis=: Preparer
99 - =Y-axis=: Count of Control ID
100 - =Legend=: Status
101- =Treemap=: Top N client submitters by average days to submit.
102 - =Details=: Preparer
103 - =Values=: Sum of Avg_{DaysToSubmit}
104- =Line Chart=: Projected vs. actual hours over time.
105- =Clustered Bar Chart=: Projected vs. actual hours per person.
106- =Slicer & Table= - Upcoming due dates.
107 - =Slicer=:
108 - =Values=: Date Due
109 - =Table=:
110 - =Columns=: Count of Control ID, Date Due, Preparer, Status
111
112** Format the Dashboard
113
114You can choose a theme in the View tab of the Ribbon. You can even
115browse for custom JSON files that define themes, such as ones found
116online or custom ones created by your organization.
117
118For each visual, you can click the =Format= button in the
119=Visualizations= side pane and explore the options. You can custom
120options such as:
121
122- Visual
123 - Legend
124 - Colors
125 - Data labels
126 - Category labels
127- General
128 - Properties
129 - Title
130 - Effects
131 - Header icons
132 - Tooltips
133 - Alt text
134
135You can always look online for inspiration when trying to decide how
136best to organize and style your dashboard.
137
138* Sharing the Results
139
140Generally, you have a few different options for sharing your dashboards
141with others:
142
1431. Export the dashboard as a PDF in the file menu of Power BI. This will
144 export all tabs and visuals as they are set when the export button is
145 pressed. You will lose all interactivity with this option.
1462. Send the full Power BI file to those you wish to share the dashboard.
147 This will retain all settings and interactivity. However, you will
148 also need to send the source files if they need to refresh the
149 dashboard and you will need to re-send the files if you make updates.
1503. Store the dashboard in a synced location, such as a shared drive or
151 Microsoft Teams. Depending on how a user configures their local
152 Windows paths, the data source paths may not be compatible for all
153 users with such a setup.
content/blog/2024-01-27-tableau-dashboard.md deleted −148
@@ -1,148 +0,0 @@
1+++
2date = 2024-01-27
3title = "Data Visualization: Mapping Omaha Crime Data with Tableau"
4description = ""
5draft = false
6+++
7
8In this project, I am going to show you how to use Tableau Public for free to
9create simple dashboards.
10
11I will be creating simple visuals from an Omaha crime data set and combining
12them to create the dashboard below. You can view this dashboard interactively
13online here: [Omaha Crime Data (2015 -
142023)](https://public.tableau.com/app/profile/c.c7042/viz/OmahaCrimeData2015-2023/OmahaCrimeData2015-2023#1).
15
16![Tableau
17Dashboard](https://img.cleberg.net/blog/20240127-tableau-dashboard/dashboard.png)
18
19# Gather the Data
20
21You can download incident data from the Omaha Police Department on their
22[Incident Data
23Download](https://police.cityofomaha.org/crime-information/incident-data-download)
24page. They currently have files for the years 2015 through 2023.
25
26Each file will be downloaded as a CSV file, approximately 3 MB - 8 MB.
27
28# Clean and Transform the Data
29
30I have used Python to combine the files into a single CSV file, as well as
31adding a custom `datetime` column. You could do this step in any software you
32prefer, but I prefer Python as its free, easy to use, and has a plethora of
33support resources online.
34
35Start by opening a terminal, navigating to your Downloads directory, and
36creating a python script.
37
38```sh
39cd ~/Downloads
40nano data_processing.py
41```
42
43Within the Python script, paste the following:
44
45```python
46# Import modules
47import pandas as pd
48import glob
49import os
50
51# Import the data
52path = r"~/Downloads/*.csv"
53files = glob.glob(path)
54
55list = []
56
57for file in files:
58 df_tmp = pd.read_csv(file)
59 li.append(df_tmp)
60
61df = pd.concat(list, axis=0, ignore_index=True)
62
63# Create a combined datetime column
64df["datetime"] = pd.to_datetime(
65 df["date"] + " " + df["time"],
66 format="%m/%d/%Y %H:%M:%S"
67)
68df.head()
69
70# Export the combined data
71df.to_csv(r"~/Downloads/combined_incidents.csv")
72```
73
74Once pasted, save and close the file. You can execute the file like so:
75
76```sh
77python3 data_processing.py
78```
79
80After this, you should have a combined data file that contains all incidents
81between 2015 and 2023. Mine was approximately 55 MB.
82
83# Tableau Public
84
85[Tableau Public](https://public.tableau.com/) is a free-to-use web application
86that allows you to create visualizations by uploading data sources. Note that
87there's no way to keep the data and visualizations private, so don't upload
88anything private.
89
90After creating an account, you can click the `Create` > `Web Authoring` link to
91create your first visualization.
92
93## Upload the Data
94
95Once you've opened your first project, Tableau will ask you to connect to your
96data. For this project, click the `Upload from computer` button and select the
97CSV file previously combined in the step above.
98
99Once connected, you can refresh the preview of the data with the `Refresh Data
100Source` button in the toolbar.
101
102If you need to edit any of the data types, column names, etc., you can do so
103now. Once complete, generate an extract so that you can start creating
104visualizations.
105
106## Create Visualizations
107
108To start, create a worksheet in the toolbar at the bottom of the screen.
109
110Within this screen, select a column from the `Data` side bar on the left and
111drag it into the `Columns` or `Rows` area of the canvas.
112
113See below for the map visualization. You can recreate this by adding the
114following fields:
115
116- `Columns`: Lon
117- `Rows`: Lat
118- `Marks`:
119 - Description
120 - Datetime
121- `Filters`: Datetime
122
123You can repeat this process for each visualization you want to create. Explore
124your options by dragging data fields to different areas and by opening the field
125options to explore what operations can be performed on different data types
126(e.g., average, count, etc.).
127
128## Create Dashboard
129
130To create a dashboard, click the button on the toolbar at the bottom of the
131screen. Within the dashboard, drag each sheet from the left side bar onto the
132dashboard canvas.
133
134## Formatting
135
136You can explore a ton of different formatting options throughout the worksheets
137and dashboard. Specifically for maps, you can alter the map layers, background,
138and visible features through the `Map` menu in the top file menu of the editing
139screen.
140
141In the finished dashboard below, I opted for a dark mode with a map that showed
142county lines and city names.
143
144There's a ton of other options available to be used in a dashboard like this,
145but this project shows a quick preview of what you can do in Tableau Public.
146
147![Tableau
148Dashboard](https://img.cleberg.net/blog/20240127-tableau-dashboard/dashboard.png)
content/blog/2024-01-27-tableau-dashboard.org added +148
@@ -0,0 +1,148 @@
1#+date: <2024-01-27>
2#+title: Data Visualization: Mapping Omaha Crime Data with Tableau
3#+description:
4
5
6In this project, I am going to show you how to use Tableau Public for
7free to create simple dashboards.
8
9I will be creating simple visuals from an Omaha crime data set and
10combining them to create the dashboard below. You can view this
11dashboard interactively online here:
12[[https://public.tableau.com/app/profile/c.c7042/viz/OmahaCrimeData2015-2023/OmahaCrimeData2015-2023#1][Omaha
13Crime Data (2015 - 2023)]].
14
15#+caption: Tableau Dashboard
16[[https://img.cleberg.net/blog/20240127-tableau-dashboard/dashboard.png]]
17
18* Gather the Data
19
20You can download incident data from the Omaha Police Department on their
21[[https://police.cityofomaha.org/crime-information/incident-data-download][Incident
22Data Download]] page. They currently have files for the years 2015
23through 2023.
24
25Each file will be downloaded as a CSV file, approximately 3 MB - 8 MB.
26
27* Clean and Transform the Data
28
29I have used Python to combine the files into a single CSV file, as well
30as adding a custom =datetime= column. You could do this step in any
31software you prefer, but I prefer Python as its free, easy to use, and
32has a plethora of support resources online.
33
34Start by opening a terminal, navigating to your Downloads directory, and
35creating a python script.
36
37#+begin_src sh
38cd ~/Downloads
39nano data_processing.py
40#+end_src
41
42Within the Python script, paste the following:
43
44#+begin_src python
45# Import modules
46import pandas as pd
47import glob
48import os
49
50# Import the data
51path = r"~/Downloads/*.csv"
52files = glob.glob(path)
53
54list = []
55
56for file in files:
57 df_tmp = pd.read_csv(file)
58 li.append(df_tmp)
59
60df = pd.concat(list, axis=0, ignore_index=True)
61
62# Create a combined datetime column
63df["datetime"] = pd.to_datetime(
64 df["date"] + " " + df["time"],
65 format="%m/%d/%Y %H:%M:%S"
66)
67df.head()
68
69# Export the combined data
70df.to_csv(r"~/Downloads/combined_incidents.csv")
71#+end_src
72
73Once pasted, save and close the file. You can execute the file like so:
74
75#+begin_src sh
76python3 data_processing.py
77#+end_src
78
79After this, you should have a combined data file that contains all
80incidents between 2015 and 2023. Mine was approximately 55 MB.
81
82* Tableau Public
83
84[[https://public.tableau.com/][Tableau Public]] is a free-to-use web
85application that allows you to create visualizations by uploading data
86sources. Note that there's no way to keep the data and visualizations
87private, so don't upload anything private.
88
89After creating an account, you can click the =Create= > =Web Authoring=
90link to create your first visualization.
91
92** Upload the Data
93
94Once you've opened your first project, Tableau will ask you to connect
95to your data. For this project, click the =Upload from computer= button
96and select the CSV file previously combined in the step above.
97
98Once connected, you can refresh the preview of the data with the
99=Refresh Data Source= button in the toolbar.
100
101If you need to edit any of the data types, column names, etc., you can
102do so now. Once complete, generate an extract so that you can start
103creating visualizations.
104
105** Create Visualizations
106
107To start, create a worksheet in the toolbar at the bottom of the screen.
108
109Within this screen, select a column from the =Data= side bar on the left
110and drag it into the =Columns= or =Rows= area of the canvas.
111
112See below for the map visualization. You can recreate this by adding the
113following fields:
114
115- =Columns=: Lon
116- =Rows=: Lat
117- =Marks=:
118 - Description
119 - Datetime
120- =Filters=: Datetime
121
122You can repeat this process for each visualization you want to create.
123Explore your options by dragging data fields to different areas and by
124opening the field options to explore what operations can be performed on
125different data types (e.g., average, count, etc.).
126
127** Create Dashboard
128
129To create a dashboard, click the button on the toolbar at the bottom of
130the screen. Within the dashboard, drag each sheet from the left side bar
131onto the dashboard canvas.
132
133** Formatting
134
135You can explore a ton of different formatting options throughout the
136worksheets and dashboard. Specifically for maps, you can alter the map
137layers, background, and visible features through the =Map= menu in the
138top file menu of the editing screen.
139
140In the finished dashboard below, I opted for a dark mode with a map that
141showed county lines and city names.
142
143There's a ton of other options available to be used in a dashboard like
144this, but this project shows a quick preview of what you can do in
145Tableau Public.
146
147#+caption: Tableau Dashboard
148[[https://img.cleberg.net/blog/20240127-tableau-dashboard/dashboard.png]]
content/blog/2024-02-06-zfs.md → content/blog/2024-02-06-zfs.org renamed +140 −131
@@ -1,70 +1,70 @@
1+++
2date = 2024-02-06
3title = "How to Create a ZFS Pool on Ubuntu Linux"
4description = ""
5draft = false
6+++
1#+date: <2024-02-06>
2#+title: How to Create a ZFS Pool on Ubuntu Linux
3#+description:
4
75
86This post details the process I used to create ZFS pools, datasets, and
97snapshots on Ubuntu Server.
108
11I found the following pages very helpful while going through this process:
9I found the following pages very helpful while going through this
10process:
1211
13- [Setup a ZFS storage
14 pool](https://ubuntu.com/tutorials/setup-zfs-storage-pool)
15- [Kernel/Reference/ZFS](https://wiki.ubuntu.com/Kernel/Reference/ZFS)
16- [ZFS for
17 Dummies](https://blog.victormendonca.com/2020/11/03/zfs-for-dummies/)
12- [[https://ubuntu.com/tutorials/setup-zfs-storage-pool][Setup a ZFS
13 storage pool]]
14- [[https://wiki.ubuntu.com/Kernel/Reference/ZFS][Kernel/Reference/ZFS]]
15- [[https://blog.victormendonca.com/2020/11/03/zfs-for-dummies/][ZFS for
16 Dummies]]
1817
19# Installation
18* Installation
2019
2120To start, I installed the ZFS package with the following command:
2221
23```sh
22#+begin_src sh
2423sudo apt install zfsutils-linux
25```
24#+end_src
2625
27Once installed, you can check the version to see if it installed correctly.
26Once installed, you can check the version to see if it installed
27correctly.
2828
29```sh
29#+begin_src sh
3030> zsf --version
3131
3232zfs-2.1.5-1ubuntu6~22.04.2
3333zfs-kmod-2.1.5-1ubuntu6~22.04.1
34```
34#+end_src
3535
36# ZFS Configuration
36* ZFS Configuration
3737
3838Now that ZFS is installed, we can create and configure the pool.
3939
40You have various options for configuring ZFS pools that all come different pros
41and cons. I suggest visiting the links at the top of this post or searching
42online for the best configuration for your use-case.
40You have various options for configuring ZFS pools that all come
41different pros and cons. I suggest visiting the links at the top of this
42post or searching online for the best configuration for your use-case.
4343
44- Striped VDEVs (Raid0)
45- Mirrored VDEVs (Raid1)
46- Striped Mirrored VDEVs (Raid10)
47- RAIDz (Raid5)
48- RAIDz2 (Raidd6)
49- RAIDz3
50- Nested RAIDz (Raid50, Raid60)
44- Striped VDEVs (Raid0)
45- Mirrored VDEVs (Raid1)
46- Striped Mirrored VDEVs (Raid10)
47- RAIDz (Raid5)
48- RAIDz2 (Raidd6)
49- RAIDz3
50- Nested RAIDz (Raid50, Raid60)
5151
52I will be using Raid10 in this guide. However, the majority of the steps are the
53same regardless of your chosen pool configuration.
52I will be using Raid10 in this guide. However, the majority of the steps
53are the same regardless of your chosen pool configuration.
5454
55## Creating the Pool
55** Creating the Pool
5656
57To start, let's list the disks available to use. You can use `fdisk` command to
58see all available disks.
57To start, let's list the disks available to use. You can use =fdisk=
58command to see all available disks.
5959
60```sh
60#+begin_src sh
6161sudo fdisk -l
62```
62#+end_src
6363
64Or, if you currently have them mounted, you can use the `df` command to view
65your disks.
64Or, if you currently have them mounted, you can use the =df= command to
65view your disks.
6666
67```sh
67#+begin_src sh
6868> sudo df -h
6969
7070Filesystem Size Used Avail Use% Mounted on
@@ -75,34 +75,35 @@ Filesystem Size Used Avail Use% Mounted on
7575/dev/sdd1 9.1T 8.7T 435G 96% /mnt/white-01
7676/dev/sde1 7.3T 28K 6.9T 1% /mnt/red-03
7777/dev/sdf1 7.3T 28K 6.9T 1% /mnt/red-04
78```
78#+end_src
7979
80If you're going to use mounted disks, make sure to umount them before creating
81the pool.
80If you're going to use mounted disks, make sure to umount them before
81creating the pool.
8282
83```sh
83#+begin_src sh
8484sudo umount /dev/sda1
8585sudo umount /dev/sdb1
86```
86#+end_src
8787
88Now that I've identified the disks I want to use and have them unmounted, let's
89create the pool. For this example, I will call it `tank`.
88Now that I've identified the disks I want to use and have them
89unmounted, let's create the pool. For this example, I will call it
90=tank=.
9091
91```sh
92#+begin_src sh
9293sudo zpool create -f -m /mnt/pool tank mirror /dev/sda /dev/sdb
93```
94#+end_src
9495
95See below for the results of the new ZFS pool named `tank`, with a vdev
96automatically named `mirror-0`.
96See below for the results of the new ZFS pool named =tank=, with a vdev
97automatically named =mirror-0=.
9798
98```sh
99#+begin_src sh
99100> zfs list
100101
101102NAME USED AVAIL REFER MOUNTPOINT
102103tank 396K 7.14T 96K /tank
103```
104#+end_src
104105
105```sh
106#+begin_src sh
106107> zpool status
107108
108109 pool: tank
@@ -116,75 +117,78 @@ config:
116117 sdb ONLINE 0 0 0
117118
118119errors: No known data errors
119```
120#+end_src
120121
121We can also look at the mounted filesystem to see where the pool is mounted and
122some quick stats.
122We can also look at the mounted filesystem to see where the pool is
123mounted and some quick stats.
123124
124```sh
125#+begin_src sh
125126> df -h
126127
127128Filesystem Size Used Avail Use% Mounted on
128129...
129130tank 7.2T 128K 7.2T 1% /tank
130```
131#+end_src
131132
132## Expanding the Pool
133** Expanding the Pool
133134
134If you want to expand this pool, you will need to add a new VDEV to the pool.
135Since I am using 2 disks per VDEV, I will need to add a new 2-disk VDEV to the
136existing `tank` pool.
135If you want to expand this pool, you will need to add a new VDEV to the
136pool. Since I am using 2 disks per VDEV, I will need to add a new 2-disk
137VDEV to the existing =tank= pool.
137138
138```sh
139#+begin_src sh
139140sudo zpool add tank mirror /dev/sdX /dev/sdY
140```
141#+end_src
141142
142If you're adding disks of different sizes, you'll need to use the `-f` flag.
143Keep in mind that the max size will be limited to the smallest disk added.
143If you're adding disks of different sizes, you'll need to use the =-f=
144flag. Keep in mind that the max size will be limited to the smallest
145disk added.
144146
145```sh
147#+begin_src sh
146148sudo zpool add -f tank mirror /dev/sdX /dev/sdY
147```
149#+end_src
148150
149I added two 8TB hard drives and this process took around 10 seconds to complete.
151I added two 8TB hard drives and this process took around 10 seconds to
152complete.
150153
151When viewing the pool again, you can see that the pool has now doubled in size.
152We have 14.3 TB useable space and the same space used for mirroring.
154When viewing the pool again, you can see that the pool has now doubled
155in size. We have 14.3 TB useable space and the same space used for
156mirroring.
153157
154```sh
158#+begin_src sh
155159> zfs list
156160
157161NAME USED AVAIL REFER MOUNTPOINT
158162tank 145G 14.3T 104K /tank
159163tank/cloud 145G 14.3T 145G /tank/cloud
160164tank/media 96K 14.3T 96K /tank/media
161```
165#+end_src
162166
163### Converting Disks
167*** Converting Disks
164168
165Some disks, such as NTFS-formatted drives, will need to be partitioned and
166formatted prior to being added to the pool.
169Some disks, such as NTFS-formatted drives, will need to be partitioned
170and formatted prior to being added to the pool.
167171
168172Start by identifying the disks you want to format and add to the pool.
169173
170```sh
174#+begin_src sh
171175sudo fdisk -l | grep /dev
172```
176#+end_src
173177
174I am going to format my `/dev/sdc` and `/dev/sdd` disks with the `fdisk`
178I am going to format my =/dev/sdc= and =/dev/sdd= disks with the =fdisk=
175179command.
176180
177See below for instructions on how to use `fdisk`. Here's what I did to create
178basic Linux formatted disks:
181See below for instructions on how to use =fdisk=. Here's what I did to
182create basic Linux formatted disks:
179183
180- `g` : Create GPT partition table
181- `n` : Create a new partition, hit Enter for all default options
182- `t` : Change partition type to `20` for `Linux filesystem`
183- `w` : Write the changes to disk and exit
184- =g= : Create GPT partition table
185- =n= : Create a new partition, hit Enter for all default options
186- =t= : Change partition type to =20= for =Linux filesystem=
187- =w= : Write the changes to disk and exit
184188
185189I repeated this process for both disks.
186190
187```sh
191#+begin_src sh
188192> sudo fdisk /dev/sdc
189193
190194Welcome to fdisk (util-linux 2.37.2).
@@ -230,95 +234,100 @@ Help:
230234 G create a new empty SGI (IRIX) partition table
231235 o create a new empty DOS partition table
232236 s create a new empty Sun partition table
233```
237#+end_src
234238
235239Once the drives are formatted, we can add these disks to the pool.
236240
237```sh
241#+begin_src sh
238242sudo zpool add tank mirror /dev/sdc /dev/sdd
239```
243#+end_src
240244
241245When we list the pool again, we can see that our size is now updated to
242approximately 22TB. This represents my hard drives totalling 45.6TB when shown
243with `fdisk -l`, with a Raid10 configuration using 22TB for mirroring and 22TB
244of useable space.
246approximately 22TB. This represents my hard drives totalling 45.6TB when
247shown with =fdisk -l=, with a Raid10 configuration using 22TB for
248mirroring and 22TB of useable space.
245249
246```sh
250#+begin_src sh
247251> zfs list
248252
249253NAME USED AVAIL REFER MOUNTPOINT
250254tank 145G 21.7T 104K /tank
251255tank/cloud 145G 21.7T 145G /tank/cloud
252256tank/media 145GT 21.7T 96K /tank/media
253```
257#+end_src
254258
255## Creating Datasets
259** Creating Datasets
256260
257According to [ZFS
258Terminology](https://docs.oracle.com/cd/E18752_01/html/819-5461/ftyue.html), a
259`dataset` can refer to "clones, file systems, snapshots, and volumes.
261According to
262[[https://docs.oracle.com/cd/E18752_01/html/819-5461/ftyue.html][ZFS
263Terminology]], a =dataset= can refer to “clones, file systems,
264snapshots, and volumes.
260265
261For this guide, I will use the `dataset` term to refer to file systems created
262under a pool.
266For this guide, I will use the =dataset= term to refer to file systems
267created under a pool.
263268
264Within my `tank` pool, I am going to create some datasets to help organize my
265files. This will give me location to store data rather than simply dumping
266everything at the `/tank/` location.
269Within my =tank= pool, I am going to create some datasets to help
270organize my files. This will give me location to store data rather than
271simply dumping everything at the =/tank/= location.
267272
268```sh
273#+begin_src sh
269274sudo zfs create tank/cloud
270275sudo zfs create tank/media
271```
276#+end_src
272277
273Once created, you can see these datasets in the output of your pool list:
278Once created, you can see these datasets in the output of your pool
279list:
274280
275```sh
281#+begin_src sh
276282> zfs list
277283NAME USED AVAIL REFER MOUNTPOINT
278284tank 752K 7.14T 104K /tank
279285tank/cloud 96K 7.14T 96K /tank/cloud
280286tank/media 96K 7.14T 96K /tank/media
281```
287#+end_src
282288
283## Creating Snapshots
289** Creating Snapshots
284290
285Next, let's create our first snapshot. We can do this by calling the `snapshot`
286command and give it an output name. I will be throwing the current date and time
287into my example.
291Next, let's create our first snapshot. We can do this by calling the
292=snapshot= command and give it an output name. I will be throwing the
293current date and time into my example.
288294
289```sh
295#+begin_src sh
290296sudo zfs snapshot tank@$(date '+%Y-%m-%d_%H-%M')
291```
297#+end_src
292298
293299We can list the snapshots in our pool with the following command:
294300
295```sh
301#+begin_src sh
296302> zfs list -t snapshot
297303NAME USED AVAIL REFER MOUNTPOINT
298304tank@2024-02-06_19-41 0B - 104K -
299```
305#+end_src
300306
301## Destroy Snapshots
307** Destroy Snapshots
302308
303309You can always destroy snapshots that are no longer needed:
304310
305```sh
311#+begin_src sh
306312sudo zfs destroy tank@2024-02-06_19-41
307```
313#+end_src
308314
309315Once deleted, they will no longer appear in the list:
310316
311```sh
317#+begin_src sh
312318> zfs list -t snapshot
313319no datasets available
314```
315
316# My Thoughts on ZFS So Far
317
318- I sacrificed 25TB to be able to mirror my data, but I feel more comfortable
319 with the potential to save my data by quickly replacing a disk if I need to.
320- The set-up was surprisingly easy and fast.
321- Disk I/O is fast as well. I was worried that the data transfer speeds would
322 be slower due to the RAID configuration.
323- Media streaming and transcoding has seen no noticeable drop in performance.
324- My only limitation really is the number of HDD bays in my server HDD cage.
320#+end_src
321
322* My Thoughts on ZFS So Far
323
324- I sacrificed 25TB to be able to mirror my data, but I feel more
325 comfortable with the potential to save my data by quickly replacing a
326 disk if I need to.
327- The set-up was surprisingly easy and fast.
328- Disk I/O is fast as well. I was worried that the data transfer speeds
329 would be slower due to the RAID configuration.
330- Media streaming and transcoding has seen no noticeable drop in
331 performance.
332- My only limitation really is the number of HDD bays in my server HDD
333 cage.
content/blog/2024-02-13-ubuntu-emergency-mode.md deleted −72
@@ -1,72 +0,0 @@
1+++
2date = 2024-02-13
3title = "Stuck in Ubuntu's Emergency Mode? Try Fixing the Fstab File"
4description = ""
5draft = false
6+++
7
8# The Problem
9
10I recently [migrated my hard drives to a ZFS pool](../zfs/) and found myself
11stuck in Ubuntu's emergency mode after the first reboot I performed after
12creating the ZFS pool.
13
14My server was stuck in the boot process and showed the following error on the
15screen:
16
17```txt
18You are in emergency mode.
19After logging in, type "journalctl -xb" to view system logs,
20"systemctl reboot" to reboot, "systemctl default"
21or ^D to try again to boot into default mode".
22```
23
24After rebooting the server and watching the logs scroll on a monitor, I noticed
25the root cause was related to a very long search for certain drives. I kept
26seeing errors like this:
27
28```txt
29[ TIME ] Timed out waiting of device dev-disk-by/[disk-uuid]
30```
31
32I realized that I had not removed the `/etc/fstab` references that asked Ubuntu
33to mount two disks on boot, but I had recently changed those disks to be part of
34my ZFS pool instead. Therefore, Ubuntu was trying to identify and mount a disk
35that was not available.
36
37Now that we have an idea of the issue, let's move to solution.
38
39# The Solution
40
41In order to fix the issue, I waited until I was allowed to type the root user's
42password, and then I executed the following command:
43
44```sh
45nano /etc/fstab
46```
47
48Within the `fstab` file, I needed to comment/remove the following lines at the
49bottom of the file. You can comment-out a line by prepending a `#` symbol at the
50beginning of the line. You can also delete the line entirely.
51
52```conf
53# What it looked like when running into the issue:
54UUID=B64E53824E5339F7 /mnt/white-01 ntfs-3g uid=1000,gid=1000 0 0
55UUID=E69867E59867B32B /mnt/white-02 ntfs-3g uid=1000,gid=1000 0 0
56
57# What I changed it to, in order to fix the issue:
58# UUID=B64E53824E5339F7 /mnt/white-01 ntfs-3g uid=1000,gid=1000 0 0
59# UUID=E69867E59867B32B /mnt/white-02 ntfs-3g uid=1000,gid=1000 0 0
60```
61
62Once removing the lines above from the `/etc/fstab` file, save and exit the file
63by hitting the `Ctrl` + `x` key combo.
64
65You can now hit `Ctrl` + `D` to continue, or reboot:
66
67```sh
68systemctl reboot
69```
70
71Once rebooted, I was able to watch the machine boot properly and launch to the
72TTY login screen without errors!
content/blog/2024-02-13-ubuntu-emergency-mode.org added +71
@@ -0,0 +1,71 @@
1#+date: <2024-02-13>
2#+title: Stuck in Ubuntu's Emergency Mode? Try Fixing the Fstab File
3#+description:
4
5
6* The Problem
7
8I recently [[../zfs/][migrated my hard drives to a ZFS pool]] and found
9myself stuck in Ubuntu's emergency mode after the first reboot I
10performed after creating the ZFS pool.
11
12My server was stuck in the boot process and showed the following error
13on the screen:
14
15#+begin_src txt
16You are in emergency mode.
17After logging in, type "journalctl -xb" to view system logs,
18"systemctl reboot" to reboot, "systemctl default"
19or ^D to try again to boot into default mode".
20#+end_src
21
22After rebooting the server and watching the logs scroll on a monitor, I
23noticed the root cause was related to a very long search for certain
24drives. I kept seeing errors like this:
25
26#+begin_src txt
27[ TIME ] Timed out waiting of device dev-disk-by/[disk-uuid]
28#+end_src
29
30I realized that I had not removed the =/etc/fstab= references that asked
31Ubuntu to mount two disks on boot, but I had recently changed those
32disks to be part of my ZFS pool instead. Therefore, Ubuntu was trying to
33identify and mount a disk that was not available.
34
35Now that we have an idea of the issue, let's move to solution.
36
37* The Solution
38
39In order to fix the issue, I waited until I was allowed to type the root
40user's password, and then I executed the following command:
41
42#+begin_src sh
43nano /etc/fstab
44#+end_src
45
46Within the =fstab= file, I needed to comment/remove the following lines
47at the bottom of the file. You can comment-out a line by prepending a
48=#= symbol at the beginning of the line. You can also delete the line
49entirely.
50
51#+begin_src conf
52# What it looked like when running into the issue:
53UUID=B64E53824E5339F7 /mnt/white-01 ntfs-3g uid=1000,gid=1000 0 0
54UUID=E69867E59867B32B /mnt/white-02 ntfs-3g uid=1000,gid=1000 0 0
55
56# What I changed it to, in order to fix the issue:
57# UUID=B64E53824E5339F7 /mnt/white-01 ntfs-3g uid=1000,gid=1000 0 0
58# UUID=E69867E59867B32B /mnt/white-02 ntfs-3g uid=1000,gid=1000 0 0
59#+end_src
60
61Once removing the lines above from the =/etc/fstab= file, save and exit
62the file by hitting the =Ctrl= + =x= key combo.
63
64You can now hit =Ctrl= + =D= to continue, or reboot:
65
66#+begin_src sh
67systemctl reboot
68#+end_src
69
70Once rebooted, I was able to watch the machine boot properly and launch
71to the TTY login screen without errors!
content/blog/2024-02-21-self-hosting-otter-wiki.md → content/blog/2024-02-21-self-hosting-otter-wiki.org renamed +44 −44
@@ -1,39 +1,39 @@
1+++
2date = 2024-02-21
3title = "Self-Hosting an Otter Wiki"
4description = ""
5draft = false
6+++
1#+date: <2024-02-21>
2#+title: Self-Hosting an Otter Wiki
3#+description:
74
8# An Otter Wiki
95
10[An Otter Wiki](https://otterwiki.com/) is an easy to use wiki software that
11takes almost no effort to set up and maintain.
6* An Otter Wiki
127
13# Installation
8[[https://otterwiki.com/][An Otter Wiki]] is an easy to use wiki
9software that takes almost no effort to set up and maintain.
10
11* Installation
1412
1513To install An Otter Wiki, I'm going to use Docker Compose to create the
16container and then use Nginx as a reverse proxy to allow external access via a
17subdomain I own.
14container and then use Nginx as a reverse proxy to allow external access
15via a subdomain I own.
1816
19## Docker Compose
17** Docker Compose
2018
2119Start by creating a directory for the container's files.
2220
23```sh
21#+begin_src sh
2422mkdir ~/otterwiki
25```
23#+end_src
2624
27Next, create the `docker-compose.yml` file to define the container's parameters.
25Next, create the =docker-compose.yml= file to define the container's
26parameters.
2827
29```sh
28#+begin_src sh
3029nano ~/otterwiki/docker-compose.yml
31```
30#+end_src
3231
3332Within the file, paste the following content. You can read the project's
34documentation if you want to further override or customize the container.
33documentation if you want to further override or customize the
34container.
3535
36```conf
36#+begin_src conf
3737version: '3'
3838services:
3939 otterwiki:
@@ -45,33 +45,33 @@ services:
4545 - 8337:80
4646 volumes:
4747 - ./app-data:/app-data
48```
48#+end_src
4949
5050Once the file is saved and closed, you can launch the container.
5151
52```sh
52#+begin_src sh
5353cd ~/otterwiki
5454sudo docker-compose up -d
55```
55#+end_src
5656
57The container is now available at `localhost:8337`. Next, we will use Nginx to
58serve this app externally.
57The container is now available at =localhost:8337=. Next, we will use
58Nginx to serve this app externally.
5959
60## Nginx
60** Nginx
6161
62To access the app externally, let's set up a reverse proxy. I'll start by
63creating the Nginx configuration file for my wiki.
62To access the app externally, let's set up a reverse proxy. I'll start
63by creating the Nginx configuration file for my wiki.
6464
65```sh
65#+begin_src sh
6666sudo nano /etc/nginx/sites-available/wiki
67```
67#+end_src
6868
69Within the file, I have saved the following content. This assumes you already
70have a TLS/SSL cert to use with this subdomain. If not, simply remove the
71`ssl_*` variables, remove the `80` server block, and change the `443` server
72block to `80` to serve the app without SSL.
69Within the file, I have saved the following content. This assumes you
70already have a TLS/SSL cert to use with this subdomain. If not, simply
71remove the =ssl_*= variables, remove the =80= server block, and change
72the =443= server block to =80= to serve the app without SSL.
7373
74```conf
74#+begin_src conf
7575server {
7676 if ($host ~ ^[^.]+\.example\.com$) {
7777 return 301 https://$host$request_uri;
@@ -105,18 +105,18 @@ server {
105105 proxy_set_header X-Forwarded-Host $http_host;
106106 }
107107}
108```
108#+end_src
109109
110Save and close the configuration file. On Nginx, we need to symlink the file to
111enable it.
110Save and close the configuration file. On Nginx, we need to symlink the
111file to enable it.
112112
113```sh
113#+begin_src sh
114114sudo ln -s /etc/nginx/sites-available/wiki /etc/nginx/sites-enabled/wiki
115```
115#+end_src
116116
117Once enabled, restart the Nginx server to start serving the app externally.
117Once enabled, restart the Nginx server to start serving the app
118externally.
118119
119```sh
120#+begin_src sh
120121sudo systemctl restart nginx.service
121```
122
122#+end_src
content/blog/2024-03-13-doom-emacs.md deleted −343
@@ -1,343 +0,0 @@
1+++
2date = 2024-03-13
3title = "Doom Emacs & Org-Mode"
4description = ""
5draft = false
6+++
7
8## Screenshots
9
10These screenshots are showing a project opened with projectile, a treemacs side
11pane open with the project contents, multiple buffers tiled next to each other,
12and the help pane open at the bottomm.
13
14The themes are `doom-homage-white` and `doom-homage-black`.
15
16![Doom Emacs Light
17Mode](https://img.cleberg.net/blog/20240314-doom-emacs/light.png)
18
19![Doom Emacs Dark
20Mode](https://img.cleberg.net/blog/20240314-doom-emacs/dark.png)
21
22## Getting Started
23
24I have been switching back and forth between
25[markdown](https://en.wikipedia.org/wiki/Markdown) and
26[org-mode](https://en.wikipedia.org/wiki/Org-mode) recently for my personal note
27taking, wiki, and even this blog. As a result, I have been stumbling further
28into the world of Emacs and found myself at a point where I now prefer to do
29most of my basic editing within Emacs.
30
31I'll leave the markdown vs. org-mode debate for another post, but I love
32org-mode's extensibility and interactive nature within Emacs, but it becomes
33very unwieldy in any other client implementation of org-mode - especially on
34iOS. On the flip side, markdown is limited in functionality and fractured into
35different standards, but it's simple and popular enough that there are a
36plethora of great clients to choose from that will get the job done.
37
38For now, I want to focus on how I have been using Emacs and some of the things
39that would have helped me learn it faster had I known where to start.
40
41### Installation
42
43This post focuses on [Doom Emacs](https://github.com/doomemacs/doomemacs), which
44is an Emacs framework that provides an alternative experience to the vanilla
45[GNU Emacs](https://www.gnu.org/software/emacs/).
46
47The [Getting Start
48Guide](https://github.com/doomemacs/doomemacs/blob/master/docs/getting_started.org)
49has an extremely detailed walkthrough of installation for all systems, so please
50refer to that guide for up-to-date instructions.
51
52I chose to install on macOS, using the Homebrew option with the
53`railwaycat/emacsmacport` version of Emacs.
54
55Once the program is installed, you can run the program by typing `emacs` in a
56terminal. If you installed a version of Emacs that supports both a GUI and TUI,
57you will have to run `emacs -nw` to get the TUI instead of the default GUI.
58
59### Configuration
60
61Once installed, you can configure Doom by editing the files within the
62`~/.doom.d/` directory. This directory holds four files:
63
641. `config.el` - Personal configuration file
652. `custom.el` - Custom set variables
663. `init.el` - Doom modules and load order, must run `doom sync` after
67 modifying
684. `packages.el` - Declare packages to install in this file, then run `doom
69sync` to install
70
71I only needed a few customizations for my configuration, so I'll list them
72below.
73
74```lisp
75;; ~/.doom.d/config.el
76(setq doom-theme 'doom-homage-black)
77(setq display-line-numbers-type t)
78(setq org-directory "~/Documents/Notes/")
79
80;; lengthy org-publish directives at the bottom of the file
81```
82
83```lisp
84;; ~/.doom.d/init.el
85(doom! :input
86 :completion
87 company ; the ultimate code completion backend
88 vertico ; the search engine of the future
89
90 :ui
91 doom ; what makes DOOM look the way it does
92 doom-dashboard ; a nifty splash screen for Emacs
93 (emoji +unicode) ; 🙂
94 hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
95 minimap ; show a map of the code on the side
96 modeline ; snazzy, Atom-inspired modeline, plus API
97 ophints ; highlight the region an operation acts on
98 (popup +defaults) ; tame sudden yet inevitable temporary windows
99 tabs ; a tab bar for Emacs
100 treemacs ; a project drawer, like neotree but cooler
101 (vc-gutter +pretty) ; vcs diff in the fringe
102 vi-tilde-fringe ; fringe tildes to mark beyond EOB
103 workspaces ; tab emulation, persistence & separate workspaces
104
105 :editor
106 (evil +everywhere); come to the dark side, we have cookies
107 file-templates ; auto-snippets for empty files
108 fold ; (nigh) universal code folding
109 snippets ; my elves. They type so I don't have to
110
111 :emacs
112 dired ; making dired pretty [functional]
113 electric ; smarter, keyword-based electric-indent
114 undo ; persistent, smarter undo for your inevitable mistakes
115 vc ; version-control and Emacs, sitting in a tree
116
117 :term
118 term ; basic terminal emulator for Emacs
119
120 :checkers
121 syntax ; tasing you for every semicolon you forget
122
123 :tools
124 (eval +overlay) ; run code, run (also, repls)
125 lookup ; navigate your code and its documentation
126 magit ; a git porcelain for Emacs
127
128 :os
129 (:if (featurep :system 'macos) macos) ; improve compatibility with macOS
130
131 :lang
132 common-lisp ; if you've seen one lisp, you've seen them all
133 emacs-lisp ; drown in parentheses
134 markdown ; writing docs for people to ignore
135 org ; organize your plain life in plain text
136 python ; beautiful is better than ugly
137 sh ; she sells {ba,z,fi}sh shells on the C xor
138
139 :app
140 irc ; how neckbeards socialize
141 (rss +org) ; emacs as an RSS reader
142
143 (default +bindings +smartparens))
144```
145
146If you're editing these files within Doom directly, remember to run `SPC h r r`
147to reload the configuration. Also remember to run `doom sync` for any changes to
148the `init.el` or `packages.el` files.
149
150## Basic Functionality
151
152I kept a cheat sheet note open at first with all of the basic functions typed
153out, copied as I went through the tutorial. After a little while, I no longer
154needed it. I highly recommend writing down the most applicable shortcuts for
155your preferred functionality and refer back to it until you've memorized it.
156
157Memorizing the shortcuts will differ based on the type of Emacs framework being
158used. Personally, migrating from vanilla Emacs to Doom Emacs simplified
159everything by a large factor and instantly enabled me to start working on my
160projects, eliminating most of the hurdles I was running into. The vanilla emacs
161hotkeys became obnoxious and I actually stopped using Emacs entirely for about a
162month before trying Doom.
163
164For me, the first logical step is to interact with the local filesystem. To do
165this, I needed to know how to open directories, open files, save files, discard
166changes, close files, and switch between open files. Here are some example
167shortcuts I've written down in order to accomplish file-based actions.
168
169| Doom Hotkey | Emacs Hotkey | Description |
170| --------------- | ------------ | -------------------------------------- |
171| `SPC :` | `C-x` | Run functions |
172| `SPC f f` | `C-x f` | Open file in buffer |
173| `SPC f d` | `C-x d` | Open directory with `dired` |
174| `i` | `C-x C-q` | Edit current buffer (insert mode) |
175| `q` | `C-x C-q` | Quit out of insert mode |
176| `SPC f s` | `C-x s` | Save current buffer |
177| `SPC b k` | `C-x k` | Kill current buffer |
178| `SPC w h/j/k/l` | `C-x o`[^1] | Move left/down/up/right to next buffer |
179
180[^1]: Doom's evil-window functionality is a bit different from GNU Emacs, but
181 you can always switch to the "other" buffer with `C-x o` or `C-x b` to get a
182 list of buffers to select.
183
184In general, when in Doom, you can press `SPC` and wait a second for the help
185pane to appear with all available hotkey options. For example, you can press
186`SPC`, wait for the help pane, and then select a key such as `g` to enter the
187git help pane and explore further command options.
188
189## Editing
190
191Next in my process is to dive into editing for any languages I'm currently
192using. In this post, I will just cover Markdown and Org-Mode but I have also
193been slowly adoping some Python and general web dev tools as well.
194
195### Markdown
196
197![Markdown
198Preview](https://img.cleberg.net/blog/20240314-doom-emacs/markdown.png)
199
200Markdown is fairly simple as the syntax is limited, so just make sure the
201`~/.doom.d/init.el` includes the `markdown` declaration in the `:lang` section.
202
203This package includes the following hotkey menus. The insert and toggle menu
204expands further, allowing you to insert various markdown elements and toggle
205things like link hiding.
206
207| Doom Hotkey | Function |
208| ---------------------------- | ------------------------ |
209| `SPC m '` | markdown-edit-code-block |
210| `SPC m e` | markdown-export |
211| `SPC m i` | +insert |
212| `SPC m o` | markdown-open |
213| `SPC m p` | markdown-preview |
214| `SPC m t` | +toggle |
215| `SPC : markdown-table-align` | markdown-table-align |
216
217### Org-Mode
218
219![Org-Mode Preview](https://img.cleberg.net/blog/20240314-doom-emacs/org.png)
220
221Similar to the markdown section above, ensure that the `~/.doom.d/init.el`
222includes the `org` declaration in the `:lang` section.
223
224There are a few hot keys, but a quick search with `SPC : org` shows that there
225are 865 possible org-related functions you can run. I won't possibly be able to
226list them all, so I will simply cover a few of the basic commands I use myself.
227
228| Doom Hotkey | Function |
229| -------------- | ------------------------------------- |
230| `SPC m t` | org-todo |
231| `SPC n t` | org-todo-list |
232| `SPC o A` | org-agenda |
233| `SPC X` | org-capture |
234| `SPC m p p` | org-priority |
235| `SPC m d s` | org-schedule |
236| `TAB` | org-cycle |
237| `SHIFT TAB` | Collapse/open all headings in buffer |
238| `M-q` | Format/wrap current section |
239| `M-Left/Right` | Demote/promote current heading |
240| `M-Down/Up` | Shift current heading section down/up |
241
2421. Org-Publish
243
244 Org includes a [publishing management
245 system](https://orgmode.org/manual/Publishing.html) by default that allows
246 you to export org files to Org, iCalendar, HTML, LaTex, Markdown, ODT, and
247 Plain Text. Most of these can be exported into another buffer and opened, or
248 simply to an external file.
249
250 While inside an org file, simply run `SPC m e` or `M-x org-export-dispatch`
251 to open the export menu. This menu will show all options and ask you to
252 select an option. If you want to export to HTML, simply press `h` and then
253 `H` (As HTML buffer), `h` (As HTML file), or `o` (As HTML file and open).
254
2552. Projects
256
257 Some publishing options are easier with a defined project in Emacs. To
258 create a project within Emacs, I use two methods:
259
260 1. Add the project via the projectile command `SPC p a`. Does not always
261 work for me.
262 2. Add an empty `.projectile` file in the project root.
263
264 Once a project has been created, you can create custom publishing actions
265 within your `~/.doom.d/config.el` file. For example, here's a test project I
266 created to try and convert this blog to org-mode recently.
267
268 ```lisp
269 ;; org-publish
270 (require 'ox-publish)
271
272 (defun my/org-sitemap-date-entry-format (entry style project) "Format ENTRY in
273 org-publish PROJECT Sitemap format ENTRY ENTRY STYLE format that includes
274 date." (let ((filename (org-publish-find-title entry project))) (if (= (length
275 filename) 0) (format "*%s*" entry) (format "{{{timestamp(%s)}}}
276 [[file:%s][%s]]" (format-time-string "%Y-%m-%d" (org-publish-find-date entry
277 project)) entry filename))))
278
279 (setq org-export-global-macros '(("timestamp" . "@@html:<time datetime='[$1]'
280 class='timestamp'>[$1]</time>@@")))
281
282 (setq org-publish-project-alist
283 `(("blog"
284 :base-directory "~/Source/cleberg.net/"
285 :base-extension "org"
286 :recursive t
287 :publishing-directory "~/Source/cleberg.net/public/"
288 :publishing-function org-html-publish-to-html
289 ;; HTML5
290 :html-doctype "html5"
291 :html-html5-fancy t
292 ;; Disable some Org's HTML defaults
293 :html-head-include-scripts nil
294 :html-head-include-default-style nil
295 :section-numbers nil
296 :with-title nil
297 ;; Sitemap
298 :auto-sitemap t
299 :sitemap-title: "Sitemap"
300 :sitemap-sort-files anti-chronologically
301 ; :sitemap-function my/org-sitemap-date-entry-format
302 ;; Customize HTML output
303 :html-divs ((preamble "header" "preamble")
304 (content "main" "content")
305 (postamble "footer" "postamble"))
306 :html-head "<meta name='theme-color' content='#111' media='(prefers-color-scheme: dark)'>
307 <meta name='theme-color' content='#fff' media='(prefers-color-scheme: light)'>
308 <link rel='stylesheet' href='/syntax-theme-dark.css' media='(prefers-color-scheme: dark)'>
309 <link rel='stylesheet' href='/syntax-theme-light.css' media='(prefers-color-scheme: light)'>
310 <link rel='stylesheet' href='/styles.css' type='text/css'>"
311 :html-preamble "<nav class='site-nav' aria-label='site-nav' role='navigation'>
312 <ul>
313 <li><a href='/'>Home</a></li>
314 <li><a href='/blog/'>Blog</a></li>
315 <li><a href='/services/'>Services</a></li>
316 <li><a href='/wiki/'>Wiki</a></li>
317 </ul></nav>
318 <h1>%t</h1>
319 <time datetime='%d'>%d</time>"
320 :html-postamble "
321 <p>Last build: %T</p>
322 <p>Created with %c</p>"
323 )
324
325 ("static"
326 :base-directory "~/Source/cleberg.net/static/"
327 :base-extension "css\\|txt\\|jpg\\|gif\\|png"
328 :recursive t
329 :publishing-directory "~/Source/cleberg.net/public/"
330 :publishing-function org-publish-attachment)
331
332 ("cleberg.net" :components ("blog" "static"))))
333 ```
334
335## General Thoughts
336
337I have enjoyed Doom Emacs (far more than GNU Emacs) and will likely continue to
338use it as my main editor for the time being. Org-Mode is certainly the largest
339factor here, as I far prefer it over Markdown due to its inherent features and
340detailed markup options. However, working with org-mode on iOS has been a pain
341and I will have to see if there's an easier way to resolve those issues or if
342going back to separate Markdown, Reminders, and Calendar apps is easier to work
343with than an all-in-one org solution.
content/blog/2024-03-13-doom-emacs.org added +360
@@ -0,0 +1,360 @@
1#+date: <2024-03-13>
2#+title: Doom Emacs & Org-Mode
3#+description:
4
5
6** Screenshots
7
8These screenshots are showing a project opened with projectile, a
9treemacs side pane open with the project contents, multiple buffers
10tiled next to each other, and the help pane open at the bottomm.
11
12The themes are =doom-homage-white= and =doom-homage-black=.
13
14#+caption: Doom Emacs Light Mode
15[[https://img.cleberg.net/blog/20240314-doom-emacs/light.png]]
16
17#+caption: Doom Emacs Dark Mode
18[[https://img.cleberg.net/blog/20240314-doom-emacs/dark.png]]
19
20** Getting Started
21
22I have been switching back and forth between
23[[https://en.wikipedia.org/wiki/Markdown][markdown]] and
24[[https://en.wikipedia.org/wiki/Org-mode][org-mode]] recently for my
25personal note taking, wiki, and even this blog. As a result, I have been
26stumbling further into the world of Emacs and found myself at a point
27where I now prefer to do most of my basic editing within Emacs.
28
29I'll leave the markdown vs. org-mode debate for another post, but I love
30org-mode's extensibility and interactive nature within Emacs, but it
31becomes very unwieldy in any other client implementation of org-mode -
32especially on iOS. On the flip side, markdown is limited in
33functionality and fractured into different standards, but it's simple
34and popular enough that there are a plethora of great clients to choose
35from that will get the job done.
36
37For now, I want to focus on how I have been using Emacs and some of the
38things that would have helped me learn it faster had I known where to
39start.
40
41*** Installation
42
43This post focuses on [[https://github.com/doomemacs/doomemacs][Doom
44Emacs]], which is an Emacs framework that provides an alternative
45experience to the vanilla [[https://www.gnu.org/software/emacs/][GNU
46Emacs]].
47
48The
49[[https://github.com/doomemacs/doomemacs/blob/master/docs/getting_started.org][Getting
50Start Guide]] has an extremely detailed walkthrough of installation for
51all systems, so please refer to that guide for up-to-date instructions.
52
53I chose to install on macOS, using the Homebrew option with the
54=railwaycat/emacsmacport= version of Emacs.
55
56Once the program is installed, you can run the program by typing =emacs=
57in a terminal. If you installed a version of Emacs that supports both a
58GUI and TUI, you will have to run =emacs -nw= to get the TUI instead of
59the default GUI.
60
61*** Configuration
62
63Once installed, you can configure Doom by editing the files within the
64=~/.doom.d/= directory. This directory holds four files:
65
661. =config.el= - Personal configuration file
672. =custom.el= - Custom set variables
683. =init.el= - Doom modules and load order, must run =doom sync= after
69 modifying
704. =packages.el= - Declare packages to install in this file, then run
71 =doom sync= to install
72
73I only needed a few customizations for my configuration, so I'll list
74them below.
75
76#+begin_src lisp
77;; ~/.doom.d/config.el
78(setq doom-theme 'doom-homage-black)
79(setq display-line-numbers-type t)
80(setq org-directory "~/Documents/Notes/")
81
82;; lengthy org-publish directives at the bottom of the file
83#+end_src
84
85#+begin_src lisp
86;; ~/.doom.d/init.el
87(doom! :input
88 :completion
89 company ; the ultimate code completion backend
90 vertico ; the search engine of the future
91
92 :ui
93 doom ; what makes DOOM look the way it does
94 doom-dashboard ; a nifty splash screen for Emacs
95 (emoji +unicode) ; 🙂
96 hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
97 minimap ; show a map of the code on the side
98 modeline ; snazzy, Atom-inspired modeline, plus API
99 ophints ; highlight the region an operation acts on
100 (popup +defaults) ; tame sudden yet inevitable temporary windows
101 tabs ; a tab bar for Emacs
102 treemacs ; a project drawer, like neotree but cooler
103 (vc-gutter +pretty) ; vcs diff in the fringe
104 vi-tilde-fringe ; fringe tildes to mark beyond EOB
105 workspaces ; tab emulation, persistence & separate workspaces
106
107 :editor
108 (evil +everywhere); come to the dark side, we have cookies
109 file-templates ; auto-snippets for empty files
110 fold ; (nigh) universal code folding
111 snippets ; my elves. They type so I don't have to
112
113 :emacs
114 dired ; making dired pretty [functional]
115 electric ; smarter, keyword-based electric-indent
116 undo ; persistent, smarter undo for your inevitable mistakes
117 vc ; version-control and Emacs, sitting in a tree
118
119 :term
120 term ; basic terminal emulator for Emacs
121
122 :checkers
123 syntax ; tasing you for every semicolon you forget
124
125 :tools
126 (eval +overlay) ; run code, run (also, repls)
127 lookup ; navigate your code and its documentation
128 magit ; a git porcelain for Emacs
129
130 :os
131 (:if (featurep :system 'macos) macos) ; improve compatibility with macOS
132
133 :lang
134 common-lisp ; if you've seen one lisp, you've seen them all
135 emacs-lisp ; drown in parentheses
136 markdown ; writing docs for people to ignore
137 org ; organize your plain life in plain text
138 python ; beautiful is better than ugly
139 sh ; she sells {ba,z,fi}sh shells on the C xor
140
141 :app
142 irc ; how neckbeards socialize
143 (rss +org) ; emacs as an RSS reader
144
145 (default +bindings +smartparens))
146#+end_src
147
148If you're editing these files within Doom directly, remember to run
149=SPC h r r= to reload the configuration. Also remember to run
150=doom sync= for any changes to the =init.el= or =packages.el= files.
151
152** Basic Functionality
153
154I kept a cheat sheet note open at first with all of the basic functions
155typed out, copied as I went through the tutorial. After a little while,
156I no longer needed it. I highly recommend writing down the most
157applicable shortcuts for your preferred functionality and refer back to
158it until you've memorized it.
159
160Memorizing the shortcuts will differ based on the type of Emacs
161framework being used. Personally, migrating from vanilla Emacs to Doom
162Emacs simplified everything by a large factor and instantly enabled me
163to start working on my projects, eliminating most of the hurdles I was
164running into. The vanilla emacs hotkeys became obnoxious and I actually
165stopped using Emacs entirely for about a month before trying Doom.
166
167For me, the first logical step is to interact with the local filesystem.
168To do this, I needed to know how to open directories, open files, save
169files, discard changes, close files, and switch between open files. Here
170are some example shortcuts I've written down in order to accomplish
171file-based actions.
172
173| Doom Hotkey | Emacs Hotkey | Description |
174|-----------------+---------------+----------------------------------------|
175| =SPC := | =C-x= | Run functions |
176| =SPC f f= | =C-x f= | Open file in buffer |
177| =SPC f d= | =C-x d= | Open directory with =dired= |
178| =i= | =C-x C-q= | Edit current buffer (insert mode) |
179| =q= | =C-x C-q= | Quit out of insert mode |
180| =SPC f s= | =C-x s= | Save current buffer |
181| =SPC b k= | =C-x k= | Kill current buffer |
182| =SPC w h/j/k/l= | =C-x o=[fn:2] | Move left/down/up/right to next buffer |
183
184In general, when in Doom, you can press =SPC= and wait a second for the
185help pane to appear with all available hotkey options. For example, you
186can press =SPC=, wait for the help pane, and then select a key such as
187=g= to enter the git help pane and explore further command options.
188
189** Editing
190
191Next in my process is to dive into editing for any languages I'm
192currently using. In this post, I will just cover Markdown and Org-Mode
193but I have also been slowly adoping some Python and general web dev
194tools as well.
195
196*** Markdown
197
198#+caption: Markdown Preview
199[[https://img.cleberg.net/blog/20240314-doom-emacs/markdown.png]]
200
201Markdown is fairly simple as the syntax is limited, so just make sure
202the =~/.doom.d/init.el= includes the =markdown= declaration in the
203=:lang= section.
204
205This package includes the following hotkey menus. The insert and toggle
206menu expands further, allowing you to insert various markdown elements
207and toggle things like link hiding.
208
209| Doom Hotkey | Function |
210|------------------------------+--------------------------|
211| =SPC m '= | markdown-edit-code-block |
212| =SPC m e= | markdown-export |
213| =SPC m i= | +insert |
214| =SPC m o= | markdown-open |
215| =SPC m p= | markdown-preview |
216| =SPC m t= | +toggle |
217| =SPC : markdown-table-align= | markdown-table-align |
218
219*** Org-Mode
220
221#+caption: Org-Mode Preview
222[[https://img.cleberg.net/blog/20240314-doom-emacs/org.png]]
223
224Similar to the markdown section above, ensure that the
225=~/.doom.d/init.el= includes the =org= declaration in the =:lang=
226section.
227
228There are a few hot keys, but a quick search with =SPC : org= shows that
229there are 865 possible org-related functions you can run. I won't
230possibly be able to list them all, so I will simply cover a few of the
231basic commands I use myself.
232
233| Doom Hotkey | Function |
234|----------------+---------------------------------------|
235| =SPC m t= | org-todo |
236| =SPC n t= | org-todo-list |
237| =SPC o A= | org-agenda |
238| =SPC X= | org-capture |
239| =SPC m p p= | org-priority |
240| =SPC m d s= | org-schedule |
241| =TAB= | org-cycle |
242| =SHIFT TAB= | Collapse/open all headings in buffer |
243| =M-q= | Format/wrap current section |
244| =M-Left/Right= | Demote/promote current heading |
245| =M-Down/Up= | Shift current heading section down/up |
246
2471. Org-Publish
248
249 Org includes a
250 [[https://orgmode.org/manual/Publishing.html][publishing management
251 system]] by default that allows you to export org files to Org,
252 iCalendar, HTML, LaTex, Markdown, ODT, and Plain Text. Most of these
253 can be exported into another buffer and opened, or simply to an
254 external file.
255
256 While inside an org file, simply run =SPC m e= or
257 =M-x org-export-dispatch= to open the export menu. This menu will
258 show all options and ask you to select an option. If you want to
259 export to HTML, simply press =h= and then =H= (As HTML buffer), =h=
260 (As HTML file), or =o= (As HTML file and open).
261
2622. Projects
263
264 Some publishing options are easier with a defined project in Emacs.
265 To create a project within Emacs, I use two methods:
266
267 1. Add the project via the projectile command =SPC p a=. Does not
268 always work for me.
269 2. Add an empty =.projectile= file in the project root.
270
271 Once a project has been created, you can create custom publishing
272 actions within your =~/.doom.d/config.el= file. For example, here's a
273 test project I created to try and convert this blog to org-mode
274 recently.
275
276 #+begin_src lisp
277 ;; org-publish
278 (require 'ox-publish)
279
280 (defun my/org-sitemap-date-entry-format (entry style project) "Format ENTRY in
281 org-publish PROJECT Sitemap format ENTRY ENTRY STYLE format that includes
282 date." (let ((filename (org-publish-find-title entry project))) (if (= (length
283 filename) 0) (format "*%s*" entry) (format "{{{timestamp(%s)}}}
284 [[file:%s][%s]]" (format-time-string "%Y-%m-%d" (org-publish-find-date entry
285 project)) entry filename))))
286
287 (setq org-export-global-macros '(("timestamp" . "@@html:<time datetime='[$1]'
288 class='timestamp'>[$1]</time>@@")))
289
290 (setq org-publish-project-alist
291 `(("blog"
292 :base-directory "~/Source/cleberg.net/"
293 :base-extension "org"
294 :recursive t
295 :publishing-directory "~/Source/cleberg.net/public/"
296 :publishing-function org-html-publish-to-html
297 ;; HTML5
298 :html-doctype "html5"
299 :html-html5-fancy t
300 ;; Disable some Org's HTML defaults
301 :html-head-include-scripts nil
302 :html-head-include-default-style nil
303 :section-numbers nil
304 :with-title nil
305 ;; Sitemap
306 :auto-sitemap t
307 :sitemap-title: "Sitemap"
308 :sitemap-sort-files anti-chronologically
309 ; :sitemap-function my/org-sitemap-date-entry-format
310 ;; Customize HTML output
311 :html-divs ((preamble "header" "preamble")
312 (content "main" "content")
313 (postamble "footer" "postamble"))
314 :html-head "<meta name='theme-color' content='#111' media='(prefers-color-scheme: dark)'>
315 <meta name='theme-color' content='#fff' media='(prefers-color-scheme: light)'>
316 <link rel='stylesheet' href='/syntax-theme-dark.css' media='(prefers-color-scheme: dark)'>
317 <link rel='stylesheet' href='/syntax-theme-light.css' media='(prefers-color-scheme: light)'>
318 <link rel='stylesheet' href='/styles.css' type='text/css'>"
319 :html-preamble "<nav class='site-nav' aria-label='site-nav' role='navigation'>
320 <ul>
321 <li><a href='/'>Home</a></li>
322 <li><a href='/blog/'>Blog</a></li>
323 <li><a href='/services/'>Services</a></li>
324 <li><a href='/wiki/'>Wiki</a></li>
325 </ul></nav>
326 <h1>%t</h1>
327 <time datetime='%d'>%d</time>"
328 :html-postamble "
329 <p>Last build: %T</p>
330 <p>Created with %c</p>"
331 )
332
333 ("static"
334 :base-directory "~/Source/cleberg.net/static/"
335 :base-extension "css\\|txt\\|jpg\\|gif\\|png"
336 :recursive t
337 :publishing-directory "~/Source/cleberg.net/public/"
338 :publishing-function org-publish-attachment)
339
340 ("cleberg.net" :components ("blog" "static"))))
341 #+end_src
342
343** General Thoughts
344
345I have enjoyed Doom Emacs (far more than GNU Emacs) and will likely
346continue to use it as my main editor for the time being. Org-Mode is
347certainly the largest factor here, as I far prefer it over Markdown due
348to its inherent features and detailed markup options. However, working
349with org-mode on iOS has been a pain and I will have to see if there's
350an easier way to resolve those issues or if going back to separate
351Markdown, Reminders, and Calendar apps is easier to work with than an
352all-in-one org solution.
353
354[fn:1] Doom's evil-window functionality is a bit different from GNU
355 Emacs, but you can always switch to the "other" buffer with
356 =C-x o= or =C-x b= to get a list of buffers to select.
357
358[fn:2] Doom's evil-window functionality is a bit different from GNU
359 Emacs, but you can always switch to the "other" buffer with
360 =C-x o= or =C-x b= to get a list of buffers to select.
content/blog/2024-03-15-self-hosting-ddns-updater.md → content/blog/2024-03-15-self-hosting-ddns-updater.org renamed +91 −86
@@ -1,73 +1,78 @@
1+++
2date = 2024-03-15
3title = "Self-Hosting DDNS Updater"
4description = ""
5draft = false
6+++
1#+date: <2024-03-15>
2#+title: Self-Hosting DDNS Updater
3#+description:
74
8![DDNS Updater Web
9View](https://img.cleberg.net/blog/20240315-ddns-updater/ddns.png)
105
11[DDNS Updater](https://github.com/qdm12/ddns-updater) is a program to keep DNS A
12and/or AAAA records updated for multiple DNS providers.
6#+caption: DDNS Updater Web View
7[[https://img.cleberg.net/blog/20240315-ddns-updater/ddns.png]]
138
14If you've read any of my other posts, you'll notice that I have been searching
15for and using a few different DDNS updating solutions for years. You'll also
16notice that I love any projects that offer a Docker Compose solution.
9[[https://github.com/qdm12/ddns-updater][DDNS Updater]] is a program to
10keep DNS A and/or AAAA records updated for multiple DNS providers.
11
12If you've read any of my other posts, you'll notice that I have been
13searching for and using a few different DDNS updating solutions for
14years. You'll also notice that I love any projects that offer a Docker
15Compose solution.
1716
1817Luckily, DDNS Upater fits both of these preferences.
1918
20## Installation
19** Installation
2120
2221To get started, always make sure to review the project's
23[README](https://github.com/qdm12/ddns-updater/blob/master/README.md). I'll be
24documenting my steps below, but they may have changed by the time you read this.
22[[https://github.com/qdm12/ddns-updater/blob/master/README.md][README]].
23I'll be documenting my steps below, but they may have changed by the
24time you read this.
2525
26The first step is to set up the directories and files required for the project.
26The first step is to set up the directories and files required for the
27project.
2728
28```sh
29#+begin_src sh
2930mkdir ~/ddns-updater
3031mkdir ~/ddns-updater/data
3132touch ~/ddns-updater/data/config.json
32```
33#+end_src
3334
34### Configuration
35*** Configuration
3536
36The main configuration you need to update is the `data/config.json` file. There
37is a large list of supported providers in the README, but I'm going to use
38Cloudflare in this example.
37The main configuration you need to update is the =data/config.json=
38file. There is a large list of supported providers in the README, but
39I'm going to use Cloudflare in this example.
3940
40```sh
41#+begin_src sh
4142nano ~/ddns-updater/data/config.json
42```
43
44When setting up the configuration for Cloudflare, you'll need the following:
45
46- Required Parameters
47 - `"zone_identifier"` is the Zone ID of your site from the domain overview
48 page
49 - `"host"` is your host and can be `"@"`, a subdomain or the wildcard
50 `"*"`. See [this issue comment for
51 context](https://github.com/qdm12/ddns-updater/issues/243#issuecomment-928313949).
52 - `"ttl"` integer value for record TTL in seconds (specify 1 for
53 automatic)
54 - One of the following ([how to find API
55 keys](https://developers.cloudflare.com/fundamentals/api/get-started/)):
56 - Email `"email"` and Global API Key `"key"`
57 - User service key `"user_service_key"`
58 - API Token `"token"`, configured with DNS edit permissions for your
59 DNS name's zone
60- Optional Parameters
61 - `"proxied"` can be set to `true` to use the proxy services of Cloudflare
62 - `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or
63 `ipv4 or ipv6` (update one of the two, depending on the public ip
64 found). It defaults to `ipv4 or ipv6`.
65 - `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can
66 be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it
67 defaults to no suffix and the raw public IPv6 address obtained is used
68 in the record updating.
69
70```conf
43#+end_src
44
45When setting up the configuration for Cloudflare, you'll need the
46following:
47
48- Required Parameters
49 - ="zone_identifier"= is the Zone ID of your site from the domain
50 overview page
51 - ="host"= is your host and can be ="@"=, a subdomain or the wildcard
52 ="*"=. See
53 [[https://github.com/qdm12/ddns-updater/issues/243#issuecomment-928313949][this
54 issue comment for context]].
55 - ="ttl"= integer value for record TTL in seconds (specify 1 for
56 automatic)
57 - One of the following
58 ([[https://developers.cloudflare.com/fundamentals/api/get-started/][how
59 to find API keys]]):
60 - Email ="email"= and Global API Key ="key"=
61 - User service key ="user_service_key"=
62 - API Token ="token"=, configured with DNS edit permissions for your
63 DNS name's zone
64- Optional Parameters
65 - ="proxied"= can be set to =true= to use the proxy services of
66 Cloudflare
67 - ="ip_version"= can be =ipv4= (A records), or =ipv6= (AAAA records)
68 or =ipv4 or ipv6= (update one of the two, depending on the public
69 ip found). It defaults to =ipv4 or ipv6=.
70 - ="ipv6_suffix"= is the IPv6 interface identifier suffix to use. It
71 can be for example =0:0:0:0:72ad:8fbb:a54e:bedd/64=. If left empty,
72 it defaults to no suffix and the raw public IPv6 address obtained is
73 used in the record updating.
74
75#+begin_src conf
7176{
7277 "settings": [
7378 {
@@ -83,12 +88,12 @@ When setting up the configuration for Cloudflare, you'll need the following:
8388 }
8489 ]
8590}
86```
91#+end_src
8792
88Once you have configured the provider of your choice, correct the file and
89directory permissions and ownership.
93Once you have configured the provider of your choice, correct the file
94and directory permissions and ownership.
9095
91```sh
96#+begin_src sh
9297cd ~/ddns_updater
9398# Owned by user ID of Docker container (1000)
9499chown -R 1000 data
@@ -96,18 +101,18 @@ chown -R 1000 data
96101chmod 700 data
97102# read access only
98103chmod 400 data/config.json
99```
104#+end_src
100105
101### Docker Compose
106*** Docker Compose
102107
103After creating the project structure, let's create the `docker-compose.yml`
104file.
108After creating the project structure, let's create the
109=docker-compose.yml= file.
105110
106```sh
111#+begin_src sh
107112nano ~/ddns_-pdater/docker-compose.yml
108```
113#+end_src
109114
110```config
115#+begin_src config
111116version: "3.7"
112117services:
113118 ddns-updater:
@@ -143,34 +148,34 @@ services:
143148 - LOG_CALLER=hidden
144149 - SHOUTRRR_ADDRESSES=
145150 restart: always
146```
151#+end_src
147152
148After configuring your preferences in the `docker-compose.yml`, launch the
149container.
153After configuring your preferences in the =docker-compose.yml=, launch
154the container.
150155
151```sh
156#+begin_src sh
152157cd ~/ddns-updater
153158sudo docker-compose up -d
154```
159#+end_src
155160
156If you've launched this on your local machine, you can launch `localhost:8097`
157in your browser to see the results.
161If you've launched this on your local machine, you can launch
162=localhost:8097= in your browser to see the results.
158163
159### Nginx Reverse Proxy
164*** Nginx Reverse Proxy
160165
161If you launched this service on a server, other machine, or just want to access
162it remotely via a domain name, you can use Nginx as a reverse proxy to expose
163the service publicly.
166If you launched this service on a server, other machine, or just want to
167access it remotely via a domain name, you can use Nginx as a reverse
168proxy to expose the service publicly.
164169
165170Start by creating the Nginx configuration file.
166171
167```sh
172#+begin_src sh
168173sudo nano /etc/nginx/sites-available/ddns
169```
174#+end_src
170175
171176Here's a basic example that should work properly.
172177
173```conf
178#+begin_src conf
174179server {
175180 # If using 443, remember to include your ssl_certificate
176181 # and ssl_certificate_key
@@ -186,12 +191,12 @@ server {
186191 # see the full example below if necessary
187192 }
188193}
189```
194#+end_src
190195
191Here's a full example that uses my Authelia authentication service to require
192authentication before someone can access the web page.
196Here's a full example that uses my Authelia authentication service to
197require authentication before someone can access the web page.
193198
194```conf
199#+begin_src conf
195200server {
196201 if ($host ~ ^[^.]+\.example\.com$) {
197202 return 301 https://$host$request_uri;
@@ -298,13 +303,13 @@ server {
298303 # real_ip_recursive on;
299304 }
300305}
301```
306#+end_src
302307
303308When complete, simply link the file and restart the web server.
304309
305```sh
310#+begin_src sh
306311sudo ln -s /etc/nginx/sites-available/ddns /etc/nginx/sites-enabled/ddns
307312sudo systemctl restart nginx.service
308```
313#+end_src
309314
310Your ddns-updater service will now be available via `ddns.example.com`!
315Your ddns-updater service will now be available via =ddns.example.com=!
content/blog/2024-03-29-org-blog.md → content/blog/2024-03-29-org-blog.org renamed +109 −104
@@ -1,80 +1,82 @@
1+++
2date = 2024-03-29
3title = "Blogging in Org-Mode"
4description = ""
5draft = false
6+++
7
8First and foremost, apologies to those who subscribe via RSS as I know that my
9feed duplicated itself when I moved this blog over to org-mode last night.
10
11This post focuses specifically on the configuration and tools I use to blog from
12Emacs with Org-Mode and does not focus on Emacs or Org-Mode themselves. Refer to
13the post I wrote about [Doom Emacs &
14Org-Mode](file:///blog/doom-emacs-org-mode.html) for more information about my
15base Emacs configuration.
16
17# Weblorg
18
19The first step in blogging with Org-Mode is to choose a method to convert the
20source files to HTML and publish them. The Worg site maintains a nice list of
21[Blogs and Wikis with Org](https://orgmode.org/worg/org-blog-wiki.html), but the
22tools are inevitably different and opinionated, so you'll need to find what
23works for you.
24
25I tried using Jekyll, Hugo, ox-hugo, Nikola, Blorg, org-static-blog, and the
26native org-publish functions before finally settling on Weblorg. For one reason
27or another, the other solutions were a drastic step down from my previous
28workflow that used [Zola](https://www.getzola.org/) with Markdown content.
29
30[Weblorg](https://github.com/emacs-love/weblorg) is a static site generator for
31[org-mode](https://orgmode.org/), built for use within
32[Emacs](https://www.gnu.org/software/emacs/). Since it's written in Emacs Lisp,
33there's no need to install other languages or frameworks to get started. More
34than that, you can write in any editor you please and simply invoke the Emacs
35build process with the `--script` parameter instead of requiring you to blog
36inside Emacs.
37
38## Installation
39
40The [Getting Started](https://emacs.love/weblorg/doc/index.html) page details
41broad installation requirements. I am using Doom Emacs on macOS, which requires
42you to add the package to the `~/.doom.d/packages.el` file and configure the
43`publish.el` file slightly differently.
44
45To start, add the `htmlize` and `weblorg` packages to Doom, sync the changes,
46and reload.
47
48```sh
1#+date: <2024-03-29>
2#+title: Blogging in Org-Mode
3#+description:
4
5
6First and foremost, apologies to those who subscribe via RSS as I know
7that my feed duplicated itself when I moved this blog over to org-mode
8last night.
9
10This post focuses specifically on the configuration and tools I use to
11blog from Emacs with Org-Mode and does not focus on Emacs or Org-Mode
12themselves. Refer to the post I wrote about
13[[file:///blog/doom-emacs-org-mode.html][Doom Emacs & Org-Mode]] for
14more information about my base Emacs configuration.
15
16* Weblorg
17
18The first step in blogging with Org-Mode is to choose a method to
19convert the source files to HTML and publish them. The Worg site
20maintains a nice list of
21[[https://orgmode.org/worg/org-blog-wiki.html][Blogs and Wikis with
22Org]], but the tools are inevitably different and opinionated, so you'll
23need to find what works for you.
24
25I tried using Jekyll, Hugo, ox-hugo, Nikola, Blorg, org-static-blog, and
26the native org-publish functions before finally settling on Weblorg. For
27one reason or another, the other solutions were a drastic step down from
28my previous workflow that used [[https://www.getzola.org/][Zola]] with
29Markdown content.
30
31[[https://github.com/emacs-love/weblorg][Weblorg]] is a static site
32generator for [[https://orgmode.org/][org-mode]], built for use within
33[[https://www.gnu.org/software/emacs/][Emacs]]. Since it's written in
34Emacs Lisp, there's no need to install other languages or frameworks to
35get started. More than that, you can write in any editor you please and
36simply invoke the Emacs build process with the =--script= parameter
37instead of requiring you to blog inside Emacs.
38
39** Installation
40
41The [[https://emacs.love/weblorg/doc/index.html][Getting Started]] page
42details broad installation requirements. I am using Doom Emacs on macOS,
43which requires you to add the package to the =~/.doom.d/packages.el=
44file and configure the =publish.el= file slightly differently.
45
46To start, add the =htmlize= and =weblorg= packages to Doom, sync the
47changes, and reload.
48
49#+begin_src sh
4950nano ~/.doom.d/packages.el
50```
51#+end_src
5152
52```lisp
53#+begin_src lisp
5354(package! htmlize)
5455(package! weblorg)
55```
56#+end_src
5657
57```sh
58#+begin_src sh
5859doom sync
59```
60#+end_src
6061
61Either re-open Emacs or hit `SPC h r r` to reload the changes.
62Either re-open Emacs or hit =SPC h r r= to reload the changes.
6263
63## Configuration
64** Configuration
6465
65Now that I've installed weblorg, I need to configure the project. I'll start by
66navigating to my site's source code and creating a `publish.el` file.
66Now that I've installed weblorg, I need to configure the project. I'll
67start by navigating to my site's source code and creating a =publish.el=
68file.
6769
68```sh
70#+begin_src sh
6971cd ~/Source/cleberg.net && nano publish.el
70```
72#+end_src
7173
72Since I'm using Doom, Emacs will not automatically load the packages I need
73later in the build process. To compensate, my `publish.el` file needs to
74explicitly tell Emacs where Doom stores the `htmlize`, `weblorg`, and
75`templatel` packages.
74Since I'm using Doom, Emacs will not automatically load the packages I
75need later in the build process. To compensate, my =publish.el= file
76needs to explicitly tell Emacs where Doom stores the =htmlize=,
77=weblorg=, and =templatel= packages.
7678
77```lisp
79#+begin_src lisp
7880;; explicity load packages since I'm using Doom Emacs
7981(add-to-list 'load-path "~/.emacs.d/.local/straight/repos/emacs-htmlize")
8082(add-to-list 'load-path "~/.emacs.d/.local/straight/repos/weblorg")
@@ -164,9 +166,9 @@ explicitly tell Emacs where Doom stores the `htmlize`, `weblorg`, and
164166 :name "rss"
165167 :input-pattern "content/blog/*.org"
166168 :input-aggregate #'weblorg-input-aggregate-all-desc
167 :template "atom.xml"
168 :output ".build/atom.xml"
169 :url "/atom.xml")
169 :template "feed.xml"
170 :output ".build/feed.xml"
171 :url "/feed.xml")
170172
171173;; route for static assets that also copies files to .build directory
172174(weblorg-copy-static
@@ -175,19 +177,21 @@ explicitly tell Emacs where Doom stores the `htmlize`, `weblorg`, and
175177
176178;; fire the engine and export all the files declared in the routes above
177179(weblorg-export)
178```
180#+end_src
179181
180# Project
182* Project
181183
182## Structure
184** Structure
183185
184186The project structure for weblorg is highly customizable and the main
185restriction is that the `publish.el` file must point to the correct paths.
187restriction is that the =publish.el= file must point to the correct
188paths.
186189
187For my blog, I prefer to keep the blog content out of the top-level directory.
188This results in the following structure (shortened for brevity):
190For my blog, I prefer to keep the blog content out of the top-level
191directory. This results in the following structure (shortened for
192brevity):
189193
190```txt
194#+begin_src txt
191195.build/
192196content/
193197 blog/
@@ -211,55 +215,56 @@ theme/
211215 wiki.html
212216build.sh
213217publish.el
214```
218#+end_src
215219
216This is simply my preferred structure and you can alter it to fit your needs.
217The key here really is that you can customize at will, as long as the
218`publish.el` file matches.
220This is simply my preferred structure and you can alter it to fit your
221needs. The key here really is that you can customize at will, as long as
222the =publish.el= file matches.
219223
220## Build & Deploy
224** Build & Deploy
221225
222Once you're content with the status of the project, you're ready to build and
223deploy the blog.
226Once you're content with the status of the project, you're ready to
227build and deploy the blog.
224228
225My process utilizes a `build.sh` script that combines the steps I take every
226time.
229My process utilizes a =build.sh= script that combines the steps I take
230every time.
227231
228```sh
232#+begin_src sh
229233touch build.sh && chmod +x build.sh && nano build.sh
230```
234#+end_src
231235
232236Within this script, I do the following:
233237
2341. Remove any files within the `.build` directory that I use to store published
235 files.
2362. Set the environment variable to `prod` to ensure the `base_url` matches my
237 configuration in `publish.el`.
2383. Build the site with Emacs & `publish.el`.
2394. Use `scp` to copy files to my site's public directory on my server.
2381. Remove any files within the =.build= directory that I use to store
239 published files.
2402. Set the environment variable to =prod= to ensure the =base_url=
241 matches my configuration in =publish.el=.
2423. Build the site with Emacs & =publish.el=.
2434. Use =scp= to copy files to my site's public directory on my server.
240244
241```sh
245#+begin_src sh
242246rm -rf .build/* && \
243247ENV=prod emacs --script publish.el && \
244248scp -r .build/* ubuntu:/var/www/cleberg.net/
245```
249#+end_src
246250
247### Time to Build
251*** Time to Build
248252
249253My only current complaints are:
250254
2511. Errors messages are not helpful. It takes work to determine what the error is
252 and where it's coming from. I generally have to sit and watch the build
253 process to see the file that weblorg pubslishes right before the error
254 occurred.
2552. The build process re-builds every single file on each run, which takes a long
256 time for a blog of my size. See below for the last time I measured.
2551. Errors messages are not helpful. It takes work to determine what the
256 error is and where it's coming from. I generally have to sit and
257 watch the build process to see the file that weblorg pubslishes right
258 before the error occurred.
2592. The build process re-builds every single file on each run, which
260 takes a long time for a blog of my size. See below for the last time
261 I measured.
257262
258```sh
263#+begin_src sh
259264> time ./build.sh
260265
261266./build.sh 35.46s user 0.59s system 85% cpu 41.965 total
262```
267#+end_src
263268
264Overall, I have thoroughly enjoyed using weblog and will continue to use it
265going forward until I find something better.
269Overall, I have thoroughly enjoyed using weblog and will continue to use
270it going forward until I find something better.
content/blog/2024-04-06-convert-onenote-to-markdown.md deleted −95
@@ -1,95 +0,0 @@
1+++
2date = 2024-04-06
3title = "Convert OneNote to Markdown or Org-Mode"
4description = ""
5draft = false
6+++
7
8If you're looking to convert your OneNote content to another format, such as
9Markdown or Org-Mode, you're in luck. I use a solution that doesn't require
10other programs, such as Evernote or Notion. Personally, I used this solution on
11a managed corporate laptop that doesn't allow installation of other programs
12like these.
13
14This solution uses OneNote and Pandoc on Windows 10.
15
16# Export OneNote Content to Word
17
18To start, export any pages or tabs from OneNote to the Word format (`.docx`):
19
201. Open OneNote desktop.
212. Select `File` and then `Export`.
223. Select the scope of content to export, such as `Tab` or `Page`.
234. Name and save the file in an easy to remember location. I recommend your
24 Downloads or Desktop folder.
25
26See below for a visual walkthrough of the export process.
27
28# Download Pandoc
29
30Start by downloading Pandoc from their [GitHub
31releases](https://github.com/jgm/pandoc/releases) page. I cannot install `.msi`
32files on my corporate laptop, so I downloaded the
33`pandoc-3.1.12.3-windows-x86_64.zip` file, which contains a simple `.exe` file
34that you do not need to install - you will simply run it from the command line
35below.
36
37Once downloaded, unzip the archive and move the `pandoc.exe` file to the same
38folder where your Word documents were saved above. If you prefer, you can move
39this file to an easier location, such as `C:\Users\youruser\Downloads`.
40
41# Convert Word to Markdown
42
43In this example, I will be converting the Word documents to Markdown, but Pandoc
44supports [a ton of different formats for
45conversion](https://github.com/jgm/pandoc?tab=readme-ov-file#the-universal-markup-converter).
46Choose the format you prefer and then modify the following commands as needed.
47
48To perform the conversion, open the Command Prompt. If you can't find it, open
49the start menu and search for it.
50
51Within the command prompt, navigate to the directory where you stored the
52`pandoc.exe` file and the Word documents.
53
54```ps1
55cd "C:\Users\yourusername\Downloads"
56```
57
58You can verify that you're in the correct directory with the `dir` command.
59
60```ps1
61dir
62```
63
64Once you have verified that you have the command prompt open in the correct
65directory with the `pandoc.exe` and the Word documents, you can run the
66following loop to convert all Word documents to Markdown.
67
68```ps1
69for %f in (*.docx) do (pandoc.exe --extract-media=. --wrap=preserve "%f" -o "%f.md")
70```
71
72This loop will perform the following actions:
73
741. Find all documents matching the pattern `*.docx`, which means all Word
75 documents ending with that file extension.
762. Iterate through all files found in step 1.
773. For each file, perform the pandoc command.
784. Within the pandoc command, `--extract-media` saves all media found in the
79 files to the current folder, with pandoc automatically creating a `media`
80 subfolder to hold all images.
815. Within the pandoc command, `--wrap=preserve` will attempt to prseerve the
82 wrapping from the source document.
836. Within the pandoc command, the final step is to specify the output path with
84 `-o`. This option adds the `.md` file extension to recognize the output files
85 as Markdown files.
86
87If you want to export to another format, simply specify the `-f=`/`--from=` and
88`-t=`/`--to=` options.
89
90For example, you can convert the Word document to org-mode. You can also convert
91to one format and subsequently convert to other formats as needed.
92
93```ps1
94pandoc.exe -f docx -t org file.docx
95```
content/blog/2024-04-06-convert-onenote-to-markdown.org added +98
@@ -0,0 +1,98 @@
1#+date: <2024-04-06>
2#+title: Convert OneNote to Markdown or Org-Mode
3#+description:
4
5
6If you're looking to convert your OneNote content to another format,
7such as Markdown or Org-Mode, you're in luck. I use a solution that
8doesn't require other programs, such as Evernote or Notion. Personally,
9I used this solution on a managed corporate laptop that doesn't allow
10installation of other programs like these.
11
12This solution uses OneNote and Pandoc on Windows 10.
13
14* Export OneNote Content to Word
15
16To start, export any pages or tabs from OneNote to the Word format
17(=.docx=):
18
191. Open OneNote desktop.
202. Select =File= and then =Export=.
213. Select the scope of content to export, such as =Tab= or =Page=.
224. Name and save the file in an easy to remember location. I recommend
23 your Downloads or Desktop folder.
24
25See below for a visual walkthrough of the export process.
26
27* Download Pandoc
28
29Start by downloading Pandoc from their
30[[https://github.com/jgm/pandoc/releases][GitHub releases]] page. I
31cannot install =.msi= files on my corporate laptop, so I downloaded the
32=pandoc-3.1.12.3-windows-x86_64.zip= file, which contains a simple
33=.exe= file that you do not need to install - you will simply run it
34from the command line below.
35
36Once downloaded, unzip the archive and move the =pandoc.exe= file to the
37same folder where your Word documents were saved above. If you prefer,
38you can move this file to an easier location, such as
39=C:\Users\youruser\Downloads=.
40
41* Convert Word to Markdown
42
43In this example, I will be converting the Word documents to Markdown,
44but Pandoc supports
45[[https://github.com/jgm/pandoc?tab=readme-ov-file#the-universal-markup-converter][a
46ton of different formats for conversion]]. Choose the format you prefer
47and then modify the following commands as needed.
48
49To perform the conversion, open the Command Prompt. If you can't find
50it, open the start menu and search for it.
51
52Within the command prompt, navigate to the directory where you stored
53the =pandoc.exe= file and the Word documents.
54
55#+begin_src ps1
56cd "C:\Users\yourusername\Downloads"
57#+end_src
58
59You can verify that you're in the correct directory with the =dir=
60command.
61
62#+begin_src ps1
63dir
64#+end_src
65
66Once you have verified that you have the command prompt open in the
67correct directory with the =pandoc.exe= and the Word documents, you can
68run the following loop to convert all Word documents to Markdown.
69
70#+begin_src ps1
71for %f in (*.docx) do (pandoc.exe --extract-media=. --wrap=preserve "%f" -o "%f.md")
72#+end_src
73
74This loop will perform the following actions:
75
761. Find all documents matching the pattern =*.docx=, which means all
77 Word documents ending with that file extension.
782. Iterate through all files found in step 1.
793. For each file, perform the pandoc command.
804. Within the pandoc command, =--extract-media= saves all media found in
81 the files to the current folder, with pandoc automatically creating a
82 =media= subfolder to hold all images.
835. Within the pandoc command, =--wrap=preserve= will attempt to prseerve
84 the wrapping from the source document.
856. Within the pandoc command, the final step is to specify the output
86 path with =-o=. This option adds the =.md= file extension to
87 recognize the output files as Markdown files.
88
89If you want to export to another format, simply specify the
90=-f==/=--from== and =-t==/=--to== options.
91
92For example, you can convert the Word document to org-mode. You can also
93convert to one format and subsequently convert to other formats as
94needed.
95
96#+begin_src ps1
97pandoc.exe -f docx -t org file.docx
98#+end_src
content/blog/2024-04-08-docker-local-web-server.md deleted −140
@@ -1,140 +0,0 @@
1+++
2date = 2024-04-08
3title = "Using Docker Desktop to Create a Development Web Server"
4description = ""
5draft = false
6+++
7
8When developing websites locally, I often use a simple Python web server to
9observe the changes.
10
11```sh
12python3 -m http.server
13```
14
15However, this approach has its limitations. For example, this approach does not
16enable logging or access controls. You also need to customize `SimpleHTTPServer`
17if you have advanced needs from your web server.
18
19So, I went to find an alternative that is almost as easy and far more extensible
20and found Docker Desktop to be a suitable replacement.
21
22# Docker Desktop
23
24## Installation
25
26[Docker Desktop](https://www.docker.com/products/docker-desktop/) is a desktop
27GUI for the phenomenal Docker container software. This allows you to manage
28containers, images, volumes, environments, and extensions via an easy-to-use
29GUI.
30
31To install, open the link above and click the `Download` button for your
32platform. I'm going through this process on an M2 Macbook, so I downloaded the
33Mac - Apple Chip version.
34
35Open the installer and follow the installation process until the application
36finishes the installation process.
37
38![Docker Desktop on
39macOS](https://img.cleberg.net/blog/20240408-docker-local-web-server/docker-desktop.png)
40
41## Creating an Nginx Container
42
43I prefer to use the command line to create containers, so the following commands
44will be input via the terminal.
45
46The following command will create a container, using the `nginx` image:
47
481. `-d`: Run this container as a daemon (detach)
492. `-p`: Allocate a port in the format `<external>:<internal>`
503. `-i`: Keep STDIN open even if not attached
514. `-t`: Allocate a pseudo-TTY
525. `-p`: Allocate a port in the format `<external>:<internal>`
536. `--rm`: Remove the container once it's done running
54
55```sh
56docker run -it --rm -d -p 8000:80 --name web nginx
57```
58
59You can navigate to <http://localhost:8000> to see the resulting page.
60
61![Default Nginx
62Container](https://img.cleberg.net/blog/20240408-docker-local-web-server/default-container.png)
63
64## Customizing the Nginx Container
65
66Now that I have a container running the Nginx web server, I need to link some
67volumes so that I can modify the site configuration and provide the web files to
68serve.
69
70Let's start with the new command, which adds two volumes:
71
721. `<your_content>:/usr/share/nginx/html`: This is the directory where you will
73 provide the web pages for the server to serve.
742. `<your_config>:/etc/nginx/conf.d/default.conf`: This is the Nginx
75 configuration file for your site.
76
77To see the updates, you can delete the previous container in the GUI or run
78`docker stop web` to stop the container. Once stopped, you can run the new
79`docker run` command below.
80
81```sh
82docker run -it -d -p 8000:80 --name web -v ~/Source/cleberg.net/.build:/usr/share/nginx/html -v ~/Source/cleberg.net/nginx-config.conf:/etc/nginx/conf.d/default.conf nginx
83```
84
85Here's an example of my development configuration file.
86
87```conf
88# nginx-config.conf
89server {
90 server_name cleberg.net www.cleberg.net;
91
92 root /usr/share/nginx/html;
93 index index.html;
94 autoindex on;
95
96 access_log /var/log/nginx/cleberg.net.access.log;
97 error_log /var/log/nginx/cleberg.net.error.log;
98
99 location / {
100 try_files $uri $uri/ =404;
101 }
102
103 listen [::]:80;
104 listen 80;
105}
106```
107
108![](https://img.cleberg.net/blog/20240408-docker-local-web-server/custom-container.png)
109
110# Customizing Deployment Actions
111
112I am currently blogging with [weblorg](https://emacs.love/weblorg/), which uses
113a custom `publish.el` file to build the static site. Within this file, I have
114configured my deployment process to check for the `ENV` variable in thesh and if
115it's set to `prod`, the script will set the base URLs to `https://cleberg.net`.
116If not, it sets the base URLs to `localhost:8000` (which matches the port used
117in the container above).
118
119Therefore, I have modified my `build.sh` script to build with `localhost` URLs
120if `ENV` is not set to `prod`. It also prevents the build process from sending
121the built files to the production web server.
122
123```sh
124#!/bin/bash
125
126if [ "$ENV" == "prod" ]; then
127 echo "Environment = Production" && \
128 rm -rf .build/* && \
129 emacs --script publish.el && \
130 scp -r .build/* ubuntu:/var/www/cleberg.net/
131else
132 echo "Environment = Development" && \
133 rm -rf .build/* && \
134 emacs --script publish.el
135fi
136```
137
138You can modify the container in numerous ways and this approach allows you to
139create complex scenarios for your web development purposes. I highly recommend
140switching over to a container-based approach for simple, local web development.
content/blog/2024-04-08-docker-local-web-server.org added +139
@@ -0,0 +1,139 @@
1#+date: <2024-04-08>
2#+title: Using Docker Desktop to Create a Development Web Server
3#+description:
4
5
6When developing websites locally, I often use a simple Python web server
7to observe the changes.
8
9#+begin_src sh
10python3 -m http.server
11#+end_src
12
13However, this approach has its limitations. For example, this approach
14does not enable logging or access controls. You also need to customize
15=SimpleHTTPServer= if you have advanced needs from your web server.
16
17So, I went to find an alternative that is almost as easy and far more
18extensible and found Docker Desktop to be a suitable replacement.
19
20* Docker Desktop
21
22** Installation
23
24[[https://www.docker.com/products/docker-desktop/][Docker Desktop]] is a
25desktop GUI for the phenomenal Docker container software. This allows
26you to manage containers, images, volumes, environments, and extensions
27via an easy-to-use GUI.
28
29To install, open the link above and click the =Download= button for your
30platform. I'm going through this process on an M2 Macbook, so I
31downloaded the Mac - Apple Chip version.
32
33Open the installer and follow the installation process until the
34application finishes the installation process.
35
36#+caption: Docker Desktop on macOS
37[[https://img.cleberg.net/blog/20240408-docker-local-web-server/docker-desktop.png]]
38
39** Creating an Nginx Container
40
41I prefer to use the command line to create containers, so the following
42commands will be input via the terminal.
43
44The following command will create a container, using the =nginx= image:
45
461. =-d=: Run this container as a daemon (detach)
472. =-p=: Allocate a port in the format =<external>:<internal>=
483. =-i=: Keep STDIN open even if not attached
494. =-t=: Allocate a pseudo-TTY
505. =-p=: Allocate a port in the format =<external>:<internal>=
516. =--rm=: Remove the container once it's done running
52
53#+begin_src sh
54docker run -it --rm -d -p 8000:80 --name web nginx
55#+end_src
56
57You can navigate to [[http://localhost:8000]] to see the resulting page.
58
59#+caption: Default Nginx Container
60[[https://img.cleberg.net/blog/20240408-docker-local-web-server/default-container.png]]
61
62** Customizing the Nginx Container
63
64Now that I have a container running the Nginx web server, I need to link
65some volumes so that I can modify the site configuration and provide the
66web files to serve.
67
68Let's start with the new command, which adds two volumes:
69
701. =<your_content>:/usr/share/nginx/html=: This is the directory where
71 you will provide the web pages for the server to serve.
722. =<your_config>:/etc/nginx/conf.d/default.conf=: This is the Nginx
73 configuration file for your site.
74
75To see the updates, you can delete the previous container in the GUI or
76run =docker stop web= to stop the container. Once stopped, you can run
77the new =docker run= command below.
78
79#+begin_src sh
80docker run -it -d -p 8000:80 --name web -v ~/Source/cleberg.net/.build:/usr/share/nginx/html -v ~/Source/cleberg.net/nginx-config.conf:/etc/nginx/conf.d/default.conf nginx
81#+end_src
82
83Here's an example of my development configuration file.
84
85#+begin_src conf
86# nginx-config.conf
87server {
88 server_name cleberg.net www.cleberg.net;
89
90 root /usr/share/nginx/html;
91 index index.html;
92 autoindex on;
93
94 access_log /var/log/nginx/cleberg.net.access.log;
95 error_log /var/log/nginx/cleberg.net.error.log;
96
97 location / {
98 try_files $uri $uri/ =404;
99 }
100
101 listen [::]:80;
102 listen 80;
103}
104#+end_src
105
106[[https://img.cleberg.net/blog/20240408-docker-local-web-server/custom-container.png]]
107
108* Customizing Deployment Actions
109
110I am currently blogging with [[https://emacs.love/weblorg/][weblorg]],
111which uses a custom =publish.el= file to build the static site. Within
112this file, I have configured my deployment process to check for the
113=ENV= variable in thesh and if it's set to =prod=, the script will set
114the base URLs to =https://cleberg.net=. If not, it sets the base URLs to
115=localhost:8000= (which matches the port used in the container above).
116
117Therefore, I have modified my =build.sh= script to build with
118=localhost= URLs if =ENV= is not set to =prod=. It also prevents the
119build process from sending the built files to the production web server.
120
121#+begin_src sh
122#!/bin/bash
123
124if [ "$ENV" == "prod" ]; then
125 echo "Environment = Production" && \
126 rm -rf .build/* && \
127 emacs --script publish.el && \
128 scp -r .build/* ubuntu:/var/www/cleberg.net/
129else
130 echo "Environment = Development" && \
131 rm -rf .build/* && \
132 emacs --script publish.el
133fi
134#+end_src
135
136You can modify the container in numerous ways and this approach allows
137you to create complex scenarios for your web development purposes. I
138highly recommend switching over to a container-based approach for
139simple, local web development.
content/blog/2024-04-18-mu4e.md → content/blog/2024-04-18-mu4e.org renamed +97 −93
@@ -1,83 +1,82 @@
1+++
2date = 2024-04-18
3title = "Email in Doom Emacs with Mu4e on macOS"
4description = ""
5draft = false
6+++
1#+date: <2024-04-18>
2#+title: Email in Doom Emacs with Mu4e on macOS
3#+description:
74
8This post was heavily inspired by [Email setup in Emacs with Mu4e on
9macOS](https://macowners.club/posts/email-emacs-mu4e-macos/), but with my own
10tweaks for a single-account configuration and some Doom-specific configurations.
115
12# Overview
6This post was heavily inspired by
7[[https://macowners.club/posts/email-emacs-mu4e-macos/][Email setup in
8Emacs with Mu4e on macOS]], but with my own tweaks for a single-account
9configuration and some Doom-specific configurations.
1310
14[Mu4e](https://github.com/emacsmirror/mu4e) is an Emacs-based email client based
15on [mu](https://www.djcbsoftware.nl/code/mu/), an indexer that stores email in
16the Maildir format.
11* Overview
1712
18This blog post covers the installation of Mu4e in Doom Emacs on macOS. This
19guide should be very similar to GNU Emacs and Linux-based systems, with a few
20tweaks required in the various configuration files.
13[[https://github.com/emacsmirror/mu4e][Mu4e]] is an Emacs-based email
14client based on [[https://www.djcbsoftware.nl/code/mu/][mu]], an indexer
15that stores email in the Maildir format.
2116
22# Installation
17This blog post covers the installation of Mu4e in Doom Emacs on macOS.
18This guide should be very similar to GNU Emacs and Linux-based systems,
19with a few tweaks required in the various configuration files.
2320
24## Prerequisites
21* Installation
2522
26### Create the Folder Structure
23** Prerequisites
24
25*** Create the Folder Structure
2726
2827Start by creating the base folder structure:
2928
30```sh
29#+begin_src sh
3130mkdir ~/.maildir
3231mkdir ~/.maildir/example # use whatever name you want to call your email account
3332mkdir ~/.maildir/certificates # used to store system root certificates
34```
33#+end_src
3534
36### Store Email Account Passwords in macOS Keychain
35*** Store Email Account Passwords in macOS Keychain
3736
38Next, I will be using the macOS Keychain to store my email account passwords
39using the command below.
37Next, I will be using the macOS Keychain to store my email account
38passwords using the command below.
4039
41```sh
40#+begin_src sh
4241security add-generic-password -s mu4e-example -a you@example.com -w
43```
42#+end_src
4443
4544This will prompt you to input your password twice to confirm. Keep the
46`mu4e-example` name in mind, as you will need to reference it later in the IMAP
47and SMTP configuration files.
45=mu4e-example= name in mind, as you will need to reference it later in
46the IMAP and SMTP configuration files.
4847
49### Store Root Certificates
48*** Store Root Certificates
5049
51In order to use IMAP and SMTP, we need to provide certificates to the local
52services. We will use the macOS defaults for this.
50In order to use IMAP and SMTP, we need to provide certificates to the
51local services. We will use the macOS defaults for this.
5352
541. Open `Keychain Access.app`.
552. Select `System Roots` in the sidebar.
563. Select all items with `CMD + a`.
574. Export selected items with `SHIFT + CMD + a` to the file
58 `~/.maildir/certificates/root-certificates.pem`.
531. Open =Keychain Access.app=.
542. Select =System Roots= in the sidebar.
553. Select all items with =CMD + a=.
564. Export selected items with =SHIFT + CMD + a= to the file
57 =~/.maildir/certificates/root-certificates.pem=.
5958
60### Install Dependencies
59*** Install Dependencies
6160
62Install `mbsync` (via `isync`) to fetch emails via IMAP, `mu` to index emails,
63and `msmtp` to send emails via SMTP.
61Install =mbsync= (via =isync=) to fetch emails via IMAP, =mu= to index
62emails, and =msmtp= to send emails via SMTP.
6463
65```sh
64#+begin_src sh
6665brew install mu isync msmtp
67```
66#+end_src
6867
69## Installing Mu4e
68** Installing Mu4e
7069
7170Within Doom Emacs, we can install Mu4e by enabling the package.
7271
73```sh
72#+begin_src sh
7473nano ~/.doom.d/init.el
75```
74#+end_src
7675
77In this file, uncomment the `mu4e` line within the `:email` section. You can
78also enable the `+org` and `+gmail` options if you prefer.
76In this file, uncomment the =mu4e= line within the =:email= section. You
77can also enable the =+org= and =+gmail= options if you prefer.
7978
80```lisp
79#+begin_src lisp
8180(doom! :input
8281 ...
8382 :email
@@ -85,34 +84,35 @@ also enable the `+org` and `+gmail` options if you prefer.
8584 ;;(mu4e +org +gmail)
8685 ...
8786 (default +bindings +smartparens))
88```
87#+end_src
8988
90# Configuration
89* Configuration
9190
92As an overall suggestion, I create the following configuration files in the
93`~/.maildir` directory and using symlinks to their proper locations so that I
94can backup and restore these files easily.
91As an overall suggestion, I create the following configuration files in
92the =~/.maildir= directory and using symlinks to their proper locations
93so that I can backup and restore these files easily.
9594
96```sh
95#+begin_src sh
9796touch ~/.maildir/.mbsyncrc && \
9897touch ~/.maildir/.msmtprc && \
9998ln -s /Users/username/.maildir/.mbsyncrc /Users/username/.mbsyncrc && \
10099ln -s /Users/username/.maildir/.msmtprc /Users/username/.msmtprc
101```
100#+end_src
102101
103You can also create these files in your home directory and skip the symlinking
104process above.
102You can also create these files in your home directory and skip the
103symlinking process above.
105104
106## IMAP
105** IMAP
107106
108Next, let's configure `mbsync` in the file created above. Paste the following
109information and customize it to match your mail provider's information.
107Next, let's configure =mbsync= in the file created above. Paste the
108following information and customize it to match your mail provider's
109information.
110110
111```sh
111#+begin_src sh
112112nano ~/.maildir/.mbsyncrc
113```
113#+end_src
114114
115```conf
115#+begin_src conf
116116IMAPAccount example
117117Host imap.example.com
118118User dummy@example.com
@@ -138,18 +138,19 @@ Create Near
138138Sync All
139139Expunge Both
140140SyncState *
141```
141#+end_src
142142
143## SMTP
143** SMTP
144144
145Next, let's configured `msmtprc` in the file created above. Paste the following
146information and customize it to match your mail provider's information.
145Next, let's configured =msmtprc= in the file created above. Paste the
146following information and customize it to match your mail provider's
147information.
147148
148```sh
149#+begin_src sh
149150nano ~/.maildir/.msmtprc
150```
151#+end_src
151152
152```conf
153#+begin_src conf
153154# Set default values for all the accounts.
154155defaults
155156logfile ~/.maildir/msmtp.log
@@ -171,18 +172,18 @@ tls_starttls off
171172# ======================================================================
172173
173174account default : startmail
174```
175#+end_src
175176
176## Doom Emacs
177** Doom Emacs
177178
178Finally, we need to configure Doom Emacs to use the proper packages and set some
179variables and functions.
179Finally, we need to configure Doom Emacs to use the proper packages and
180set some variables and functions.
180181
181```sh
182#+begin_src sh
182183nano ~/.doom.d/config.el
183```
184#+end_src
184185
185```lisp
186#+begin_src lisp
186187;; load packages and programs
187188(use-package mu4e
188189 :load-path "/Users/username/.emacs.d/modules/email/mu4e/")
@@ -266,40 +267,43 @@ nano ~/.doom.d/config.el
266267
267268;; mu4e address completion
268269(add-hook 'mu4e-compose-mode-hook 'company-mode)
269```
270#+end_src
270271
271272Be sure to sync Doom to update the current configurations.
272273
273```sh
274#+begin_src sh
274275doom sync
275```
276#+end_src
276277
277If you have Doom open, execute `SPC h r r` to reload the new configurations.
278If you have Doom open, execute =SPC h r r= to reload the new
279configurations.
278280
279# Initial Sync
281* Initial Sync
280282
281Once you have configured all of the relevant files, you can perform an initial
282sync. Note that you can perform syncing within Mu4e itself after this.
283Once you have configured all of the relevant files, you can perform an
284initial sync. Note that you can perform syncing within Mu4e itself after
285this.
283286
284```sh
287#+begin_src sh
285288mbsync -aV
286```
289#+end_src
287290
288291Once you sync the data, you can index the emails.
289292
290```sh
293#+begin_src sh
291294mu init -m ~/.maildir --my-address you@example.com && \
292295mu index
293```
296#+end_src
294297
295298The emails will now to be ready to use!
296299
297# Screenshots
298
299You can now launch Doom and open Mu4e with `SPC o m`. You can also explore the
300Mu4e options with `SPC : mu4e`.
300* Screenshots
301301
302The home page shows various options and metadata about the account you've opened.
302You can now launch Doom and open Mu4e with =SPC o m=. You can also
303explore the Mu4e options with =SPC : mu4e=.
303304
304![Mu4e Home Page](https://img.cleberg.net/blog/20240418-mu4e/mu4e.png)
305The home page shows various options and metadata about the account
306you've opened.
305307
308#+caption: Mu4e Home Page
309[[https://img.cleberg.net/blog/20240418-mu4e/mu4e.png]]
content/blog/2024-05-03-ubuntu-on-macos.md deleted −96
@@ -1,96 +0,0 @@
1+++
2date = 2024-05-03
3title = "Running Ubuntu Linux on macOS with OrbStack"
4description = "Learn how to run Linux machines as applications on macOS with minimal hassle."
5draft = false
6+++
7
8Being a macOS user who previously used Linux for many years, I often find myself
9searching for alternatives to the Linux-native tools and methods that I had
10become used to over the years.
11
12Luckily, there's an option to simply use Linux directly on your macOS device
13without having to boot the whole device into Linux or having to SSH into another
14Linux machine.
15
16In this post, I use [OrbStack](https://orbstack.dev/) to create an Ubuntu Linux
17machine to take advantage of the Linux system.
18
19## Installing OrbStack
20
21OrbStack has a [download page](https://orbstack.dev/download) if you prefer to
22use `*.dmg` files to install applications.
23
24Personally, I prefer to install everything through Homebrew. For OrbStack, run
25the following to install the application via Homebrew:
26
27```sh
28brew install orbstack
29```
30
31## Creating a Linux Machine
32
33Once installed, you can launch OrbStack and create a Linux machine in a matter
34of seconds. The creation dialog defaults to Ubuntu.
35
36![OrbStack's Linux Machine Creation
37Page](https://img.cleberg.net/blog/20240503-ubuntu-on-macos/create.png)
38
39## Launching the Machine
40
41Once created, the dialog will close and you will be presented with your new list
42of machines. Simply double-click on a machine to run it.
43
44When you run the machine for the first time, it may warn you that a program is
45attempting to run a shell script. You can choose to manually inspect this shell
46script via the provided path if you wish. When you're comfortable with the
47script, you can toggle the `Suppress this message permanently` button and click
48OK to run the machine.
49
50> **Quick Tip**!
51>
52> You will always be able to launch the OrbStack application and
53> control the machine via the Machines page, but you can also use the OrbStack
54> icon in the macOS menu bar to quickly control the machine without launching
55> OrbStack itself.
56
57## Working in Linux
58
59Once successful, OrbStack will launch your default terminal and log you into the
60Linux machine you created above.
61
62In the example below, we can see that the machine is using `Linux ubuntu 6.9.6`
63and operates similar to a fresh Ubuntu install on bare metal.
64
65![Ubuntu CLI](https://img.cleberg.net/blog/20240503-ubuntu-on-macos/cli.png)
66
67You can also click the machine's folder icon in OrbStack to open the machine's
68file system within macOS's native Files app. Within Files, you can double-click
69to open any files and they will open in the macOS defaults for those file types
70(e.g., TextEdit).
71
72![Ubuntu Files](https://img.cleberg.net/blog/20240503-ubuntu-on-macos/files.png)
73
74Here's an example of opening the `test.md` file in the screenshot above, which
75defaults to opening in Xcode on my system.
76
77![Ubuntu Files](https://img.cleberg.net/blog/20240503-ubuntu-on-macos/xcode.png)
78
79### Test Examples
80
81As a quick example of the system, I installed `neofetch`, switched to the `zsh`
82shell, and ran a few common commands.
83
84![Test Examples](https://img.cleberg.net/blog/20240503-ubuntu-on-macos/test.png)
85
86## Thoughts
87
88The installation, creation, and launch processes are seamless and provide an
89easy way to test or use Linux on macOS with little to no hassle.
90
91The shell and file integrations are incredibly snappy and I have not experienced
92any lag so far. Further, being able to open files in the Files app allows for me
93to use macOS native applications, such as TextEdit for quick editing without
94having to resort to other programs that may not be as familiar to macOS users.
95
96I love this method and will be happily looking for further use cases to apply.
content/blog/2024-05-03-ubuntu-on-macos.org added +105
@@ -0,0 +1,105 @@
1#+date: <2024-05-03>
2#+title: Running Ubuntu Linux on macOS with OrbStack
3#+description: Learn how to run Linux machines as applications on macOS with minimal hassle.
4
5
6Being a macOS user who previously used Linux for many years, I often
7find myself searching for alternatives to the Linux-native tools and
8methods that I had become used to over the years.
9
10Luckily, there's an option to simply use Linux directly on your macOS
11device without having to boot the whole device into Linux or having to
12SSH into another Linux machine.
13
14In this post, I use [[https://orbstack.dev/][OrbStack]] to create an
15Ubuntu Linux machine to take advantage of the Linux system.
16
17** Installing OrbStack
18
19OrbStack has a [[https://orbstack.dev/download][download page]] if you
20prefer to use =*.dmg= files to install applications.
21
22Personally, I prefer to install everything through Homebrew. For
23OrbStack, run the following to install the application via Homebrew:
24
25#+begin_src sh
26brew install orbstack
27#+end_src
28
29** Creating a Linux Machine
30
31Once installed, you can launch OrbStack and create a Linux machine in a
32matter of seconds. The creation dialog defaults to Ubuntu.
33
34#+caption: OrbStack's Linux Machine Creation Page
35[[https://img.cleberg.net/blog/20240503-ubuntu-on-macos/create.png]]
36
37** Launching the Machine
38
39Once created, the dialog will close and you will be presented with your
40new list of machines. Simply double-click on a machine to run it.
41
42When you run the machine for the first time, it may warn you that a
43program is attempting to run a shell script. You can choose to manually
44inspect this shell script via the provided path if you wish. When you're
45comfortable with the script, you can toggle the
46=Suppress this message permanently= button and click OK to run the
47machine.
48
49#+begin_quote
50*Quick Tip*!
51
52You will always be able to launch the OrbStack application and control
53the machine via the Machines page, but you can also use the OrbStack
54icon in the macOS menu bar to quickly control the machine without
55launching OrbStack itself.
56#+end_quote
57
58** Working in Linux
59
60Once successful, OrbStack will launch your default terminal and log you
61into the Linux machine you created above.
62
63In the example below, we can see that the machine is using
64=Linux ubuntu 6.9.6= and operates similar to a fresh Ubuntu install on
65bare metal.
66
67#+caption: Ubuntu CLI
68[[https://img.cleberg.net/blog/20240503-ubuntu-on-macos/cli.png]]
69
70You can also click the machine's folder icon in OrbStack to open the
71machine's file system within macOS's native Files app. Within Files, you
72can double-click to open any files and they will open in the macOS
73defaults for those file types (e.g., TextEdit).
74
75#+caption: Ubuntu Files
76[[https://img.cleberg.net/blog/20240503-ubuntu-on-macos/files.png]]
77
78Here's an example of opening the =test.md= file in the screenshot above,
79which defaults to opening in Xcode on my system.
80
81#+caption: Ubuntu Files
82[[https://img.cleberg.net/blog/20240503-ubuntu-on-macos/xcode.png]]
83
84*** Test Examples
85
86As a quick example of the system, I installed =neofetch=, switched to
87the =zsh= shell, and ran a few common commands.
88
89#+caption: Test Examples
90[[https://img.cleberg.net/blog/20240503-ubuntu-on-macos/test.png]]
91
92** Thoughts
93
94The installation, creation, and launch processes are seamless and
95provide an easy way to test or use Linux on macOS with little to no
96hassle.
97
98The shell and file integrations are incredibly snappy and I have not
99experienced any lag so far. Further, being able to open files in the
100Files app allows for me to use macOS native applications, such as
101TextEdit for quick editing without having to resort to other programs
102that may not be as familiar to macOS users.
103
104I love this method and will be happily looking for further use cases to
105apply.
content/blog/2024-06-19-deprecated-trusted-gpg-fix.md → content/blog/2024-06-19-deprecated-trusted-gpg-fix.org renamed +56 −56
@@ -1,49 +1,49 @@
1+++
2date = 2024-06-19 08:00:00
3title = "Fixing Ubuntu Error: 'Key is stored in legacy trusted.gpg keyring'"
4description = "Learn how to update GPG keys from the trusted.gpg keyring in Ubuntu."
5+++
1#+date: <2024-06-19 08:00:00>
2#+title: Fixing Ubuntu Error: 'Key is stored in legacy trusted.gpg keyring'
3#+description: Learn how to update GPG keys from the trusted.gpg keyring in Ubuntu.
64
7## System Warning
85
9When running an update on an Ubuntu system, you may have run into a system
10warning that looks like the example below.
6** System Warning
117
12```txt
8When running an update on an Ubuntu system, you may have run into a
9system warning that looks like the example below.
10
11#+begin_src txt
1312W: https://dl.yarnpkg.com/debian/dists/stable/InRelease: Key is stored in legacy
1413trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in
1514apt-key(8) for details.
16```
15#+end_src
1716
18While this example references the `yarn` package, the warning message is the
19same for any repository using the deprecated `trusted.gpg` key ring.
17While this example references the =yarn= package, the warning message is
18the same for any repository using the deprecated =trusted.gpg= key ring.
2019
21The issue arises from managing keys with the `apt-key` command, which utilizes
22the `/etc/apt/trusted.gpg` file by default. Instead, Ubuntu has moved to
23managing key rings with individual `.gpg` files in the `/etc/apt/trusted.gpg.d/`
24directory.
20The issue arises from managing keys with the =apt-key= command, which
21utilizes the =/etc/apt/trusted.gpg= file by default. Instead, Ubuntu has
22moved to managing key rings with individual =.gpg= files in the
23=/etc/apt/trusted.gpg.d/= directory.
2524
26To fix this issue, let's check to see which keys are using the `trusted.gpg` key
27ring and move them into their own dedicated key ring.
25To fix this issue, let's check to see which keys are using the
26=trusted.gpg= key ring and move them into their own dedicated key ring.
2827
29## Finding All Keys in the Keyring
28** Finding All Keys in the Keyring
3029
31Let's start by simply listing the keys used by the `apt` commands. To do this,
32run the following command.
30Let's start by simply listing the keys used by the =apt= commands. To do
31this, run the following command.
3332
34```sh
33#+begin_src sh
3534sudo apt-key list
36```
35#+end_src
3736
3837This command will show an output similar to the one below. You may see
39additional keys in the `/etc/apt/trusted.gpg.d/` directory - this is where we
40will be moving any keys currently found in the `trusted.gpg` key ring.
38additional keys in the =/etc/apt/trusted.gpg.d/= directory - this is
39where we will be moving any keys currently found in the =trusted.gpg=
40key ring.
4141
42In the below example, we can see that this system has four different GPG keys
43stored within the `trusted.gpg` key ring. Let's go ahead and move them into
44their own files.
42In the below example, we can see that this system has four different GPG
43keys stored within the =trusted.gpg= key ring. Let's go ahead and move
44them into their own files.
4545
46```txt
46#+begin_src txt
4747Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead
4848(see apt-key(8)).
4949
@@ -67,32 +67,32 @@ uid [ unknown] nginx signing key <signing-key-2@nginx.com>
6767pub rsa4096 2024-05-29 [SC]
6868 9E9B E90E ACBC DE69 FE9B 204C BCDC D8A3 8D88 A2B3
6969uid [ unknown] nginx signing key <signing-key-3@nginx.com>
70```
70#+end_src
7171
72## Moving Keys to the Proper Location
72** Moving Keys to the Proper Location
7373
74### Exporting Keys to New Files
74*** Exporting Keys to New Files
7575
76Now that we know the keys, we will need to move them into their own key ring. We
77can do this by copying the last eight (8) characters from the key's signature
78and exporting it from this key ring into its own.
76Now that we know the keys, we will need to move them into their own key
77ring. We can do this by copying the last eight (8) characters from the
78key's signature and exporting it from this key ring into its own.
7979
80Using the yarn example from the beginning, here's the command to move this key
81into its own key ring.
80Using the yarn example from the beginning, here's the command to move
81this key into its own key ring.
8282
83```sh
83#+begin_src sh
8484sudo apt-key export 86E50310 | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/yarn.gpg
85```
85#+end_src
8686
87You can repeat this process for any other keys, such as the `nginx` keys in the
88example above.
87You can repeat this process for any other keys, such as the =nginx= keys
88in the example above.
8989
90### Cleaning Up
90*** Cleaning Up
9191
92If you run `sudo apt-key list` again, you should see the keys within their own
93key rings:
92If you run =sudo apt-key list= again, you should see the keys within
93their own key rings:
9494
95```txt
95#+begin_src txt
9696/etc/apt/trusted.gpg.d/nginx-archive-keyring.gpg
9797------------------------------------------------
9898pub rsa4096 2024-05-29 [SC]
@@ -115,19 +115,19 @@ uid [ unknown] Yarn Packaging <yarn@dan.cx>
115115sub rsa4096 2016-10-05 [E]
116116sub rsa4096 2019-01-02 [S] [expires: 2026-01-23]
117117sub rsa4096 2019-01-11 [S] [expires: 2026-01-23]
118```
118#+end_src
119119
120Once you have verified that the keys are valid and stored in their own key
121rings, you can archive the `trusted.gpg` file and run a system update to test
122the new files.
120Once you have verified that the keys are valid and stored in their own
121key rings, you can archive the =trusted.gpg= file and run a system
122update to test the new files.
123123
124```sh
124#+begin_src sh
125125sudo mv /etc/apt/trusted.gpg /etc/apt/trusted.gpg.bkp
126126sudo apt update
127```
128
129Once you've verified that updates work as expected and that the keys are working
130as intended, you can delete the `.bkp` file created above. If you're storing
131keys that are not easily re-attainable, I suggest keeping the `.bkp` file stored
132in a safe location until you are positive that you no longer need it.
127#+end_src
133128
129Once you've verified that updates work as expected and that the keys are
130working as intended, you can delete the =.bkp= file created above. If
131you're storing keys that are not easily re-attainable, I suggest keeping
132the =.bkp= file stored in a safe location until you are positive that
133you no longer need it.
content/blog/2024-07-11-emacs-on-ipad.md deleted −65
@@ -1,65 +0,0 @@
1+++
2date = 2024-07-11T20:24:02
3title = "Emacs on iPadOS"
4description = "Learn how to install and use Emacs on the Apple Silicon iPad natively."
5draft = false
6+++
7
8This post describes the process to install and use Emacs on the iPad Air 13-inch
9(M2). The iPad used in this post is running iPadOS 17.6.
10
11## Shell Application
12
13In order to use Emacs on an iPad, you will need a terminal emulator application.
14I recommend [iSH](https://apps.apple.com/us/app/ish-shell/id1436902243), since
15it runs a version of Alpine Linux within the app itself and will allow you to
16install packages that you need.
17
18![iSH Application](https://img.cleberg.net/blog/20240711-emacs-on-ipad/ish.png)
19
20## Require Packages
21
22I started by adding the required packages directly within iSH. Emacs should
23install dependencies by default, but I include a few other packages that I use
24in my terminal as well.
25
26```sh
27apk add emacs ripgrep fd findutils
28```
29
30![Package
31!Installation](https://img.cleberg.net/blog/20240711-emacs-on-ipad/dependencies.png)
32
33## Emacs
34
35Once this is complete, you should be able to run Emacs natively on your iPad.
36It's effective, but can be slow at times.
37
38I attempted to also install Doom Emacs, which technically worked, but was so
39incredibly slow and buggy that I was not even able to take screenshots. Someone
40smarter than me could likely get it to work with a little tinkering.
41
42![Emacs](https://img.cleberg.net/blog/20240711-emacs-on-ipad/emacs.png)
43
44### MELPA
45
46You also have to remember to hook up MELPA yourself in the `.emacs` file to be
47able to search through their 5700+ packages instead of just ELPA packages. If
48you don't, you will only have access to ELPA packages like the ones below.
49
50![package-install](https://img.cleberg.net/blog/20240711-emacs-on-ipad/melpa.png)
51
52Once you have MELPA, you can install packages like the `dashboard` package shown
53below.
54
55![emacs-dashboard](https://img.cleberg.net/blog/20240711-emacs-on-ipad/dashboard.png)
56
57### Speed
58
59While Emacs will run on my iPad, it's not perfect. The largest issue on my
60iPad is speed - loading Emacs takes 6-7 seconds and installing the `magit`
61package took 129 seconds.
62
63I haven't played around enough to optimize loading times and poke around to see
64why the network requests take so long, but it's a big enough issue that I
65wouldn't see casual Emacs users dealing with the lag.
content/blog/2024-07-11-emacs-on-ipad.org added +70
@@ -0,0 +1,70 @@
1#+date: <2024-07-11 Thu 20:24:02>
2#+title: Emacs on iPadOS
3#+description: Learn how to install and use Emacs on the Apple Silicon iPad natively.
4
5
6This post describes the process to install and use Emacs on the iPad Air
713-inch (M2). The iPad used in this post is running iPadOS 17.6.
8
9** Shell Application
10
11In order to use Emacs on an iPad, you will need a terminal emulator
12application. I recommend
13[[https://apps.apple.com/us/app/ish-shell/id1436902243][iSH]], since it
14runs a version of Alpine Linux within the app itself and will allow you
15to install packages that you need.
16
17#+caption: iSH Application
18[[https://img.cleberg.net/blog/20240711-emacs-on-ipad/ish.png]]
19
20** Require Packages
21
22I started by adding the required packages directly within iSH. Emacs
23should install dependencies by default, but I include a few other
24packages that I use in my terminal as well.
25
26#+begin_src sh
27apk add emacs ripgrep fd findutils
28#+end_src
29
30#+caption: Package !Installation
31[[https://img.cleberg.net/blog/20240711-emacs-on-ipad/dependencies.png]]
32
33** Emacs
34
35Once this is complete, you should be able to run Emacs natively on your
36iPad. It's effective, but can be slow at times.
37
38I attempted to also install Doom Emacs, which technically worked, but
39was so incredibly slow and buggy that I was not even able to take
40screenshots. Someone smarter than me could likely get it to work with a
41little tinkering.
42
43#+caption: Emacs
44[[https://img.cleberg.net/blog/20240711-emacs-on-ipad/emacs.png]]
45
46*** MELPA
47
48You also have to remember to hook up MELPA yourself in the =.emacs= file
49to be able to search through their 5700+ packages instead of just ELPA
50packages. If you don't, you will only have access to ELPA packages like
51the ones below.
52
53#+caption: package-install
54[[https://img.cleberg.net/blog/20240711-emacs-on-ipad/melpa.png]]
55
56Once you have MELPA, you can install packages like the =dashboard=
57package shown below.
58
59#+caption: emacs-dashboard
60[[https://img.cleberg.net/blog/20240711-emacs-on-ipad/dashboard.png]]
61
62*** Speed
63
64While Emacs will run on my iPad, it's not perfect. The largest issue on
65my iPad is speed - loading Emacs takes 6-7 seconds and installing the
66=magit= package took 129 seconds.
67
68I haven't played around enough to optimize loading times and poke around
69to see why the network requests take so long, but it's a big enough
70issue that I wouldn't see casual Emacs users dealing with the lag.
content/blog/_index.md deleted −5
@@ -1,5 +0,0 @@
1+++
2title = "Blog"
3sort_by = "date"
4template = "blog.html"
5+++
\ No newline at end of file
content/index.org added +2
@@ -0,0 +1,2 @@
1#+title: cleberg.net
2#+description: Just a blip of ones and zeroes.
content/salary.md deleted −62
@@ -1,62 +0,0 @@
1+++
2title = "Salary"
3draft = false
4+++
5
6## Salary Transparency
7
8The data below details the base salary information for each job I've held. This
9information is posted publicly to ensure others in my position have a solid
10reference point when determining if their current or proposed salary is
11appropriate.
12
13While sites like Glassdoor are locking salary data behind a paywall, LinkedIn is
14discontinuing LinkedIn Salary, and helpful websites like Big 4 Transparency are
15extremely rare, I wanted to provide my personal data publicly and freely to
16those who need it.
17
18I have seen what can happen when great employees don't know the market values
19for their skills and I happily help those in my teams, so I'm happy to extend
20this information to the online community.
21
22As a final note, there are numerous reasons that people in the same role are
23paid differently (expertise, years of experience, certifications, education,
24etc.) and that the data in this table should only be used as a single point of
25reference, not the whole story.
26
27## Salary Data
28
29Note: When in a role that gives periodic raises, I will create a new record with
30the new base salary in the table below. See the KPMG records for an example of a
31raise while in the same role.
32
33| Title | Company | Location | Start | End | Salary |
34| ---------------------------------------------- | ---------------------- | -------------- | ------- | ------- | -------- |
35| Senior Associate, Technology Assurance - Audit | KPMG | Omaha, NE | 2023-10 | Current | $116,700 |
36| Senior Associate, Technology Assurance - Audit | KPMG | Omaha, NE | 2022-06 | 2023-10 | $110,000 |
37| Senior Technology Risk Consultant | Ernst & Young | Des Moines, IA | 2021-09 | 2022-06 | $89,500 |
38| Senior IT Auditor | Ameritas | Lincoln, NE | 2021-05 | 2021-09 | $72,000 |
39| IT Auditor | Ameritas | Lincoln, NE | 2020-04 | 2021-05 | $65,000 |
40| IS Auditor II | Nelnet | Lincoln, NE | 2019-12 | 2020-04 | $58,000 |
41| IS Auditor I | Nelnet | Lincoln, NE | 2019-06 | 2019-12 | $20/hour |
42| Internal Audit Intern | Ameritas | Lincoln, NE | 2018-02 | 2019-06 | $16/hour |
43| Teaching Assistant | University of Nebraska | Lincoln, NE | 2017-08 | 2018-05 | $7/hour |
44| Community Management Intern | Walgreens | Lincoln, NE | 2017-06 | 2018-02 | $14/hour |
45
46[[source data](/static/salary.csv)]
47
48## Salary Visualization
49
50See the graph below for a visualization of each job's salary over time, as well
51as percentage increases or decreases between jobs.
52
53![Salary Visualization](https://img.cleberg.net/blog/salary/salary.png)
54
55This page was inspired by [Xe](https://xeiaso.net/salary-transparency/), and I'm
56quoting the following wording from them as I want to reiterate this piece:
57
58> Please consider publishing your salary data like this as well. By open,
59> voluntary transparency we can help to end stigmas around discussing pay and
60> help ensure that the next generations of people in tech are treated fairly.
61> Stigmas thrive in darkness but die in the light of day. You can help end the
62> stigma by playing your cards out in the open like this.
content/salary/index.org added +70
@@ -0,0 +1,70 @@
1#+title: Salary
2#+slug: index
3
4** Salary Transparency
5:PROPERTIES:
6:CUSTOM_ID: salary-transparency
7:END:
8The data below details the base salary information for each job I've
9held. This information is posted publicly to ensure others in my
10position have a solid reference point when determining if their current
11or proposed salary is appropriate.
12
13While sites like Glassdoor are locking salary data behind a paywall,
14LinkedIn is discontinuing LinkedIn Salary, and helpful websites like Big
154 Transparency are extremely rare, I wanted to provide my personal data
16publicly and freely to those who need it.
17
18I have seen what can happen when great employees don't know the market
19values for their skills and I happily help those in my teams, so I'm
20happy to extend this information to the online community.
21
22As a final note, there are numerous reasons that people in the same role
23are paid differently (expertise, years of experience, certifications,
24education, etc.) and that the data in this table should only be used as
25a single point of reference, not the whole story.
26
27** Salary Data
28:PROPERTIES:
29:CUSTOM_ID: salary-data
30:END:
31Note: When in a role that gives periodic raises, I will create a new
32record with the new base salary in the table below. See the KPMG records
33for an example of a raise while in the same role.
34
35| Title | Company | Location | Start | End | Salary |
36|------------------------------------------------+------------------------+----------------+---------+---------+----------|
37| Senior Associate, Technology Assurance - Audit | KPMG | Omaha, NE | 2023-10 | Current | $116,700 |
38| Senior Associate, Technology Assurance - Audit | KPMG | Omaha, NE | 2022-06 | 2023-10 | $110,000 |
39| Senior Technology Risk Consultant | Ernst & Young | Des Moines, IA | 2021-09 | 2022-06 | $89,500 |
40| Senior IT Auditor | Ameritas | Lincoln, NE | 2021-05 | 2021-09 | $72,000 |
41| IT Auditor | Ameritas | Lincoln, NE | 2020-04 | 2021-05 | $65,000 |
42| IS Auditor II | Nelnet | Lincoln, NE | 2019-12 | 2020-04 | $58,000 |
43| IS Auditor I | Nelnet | Lincoln, NE | 2019-06 | 2019-12 | $20/hour |
44| Internal Audit Intern | Ameritas | Lincoln, NE | 2018-02 | 2019-06 | $16/hour |
45| Teaching Assistant | University of Nebraska | Lincoln, NE | 2017-08 | 2018-05 | $7/hour |
46| Community Management Intern | Walgreens | Lincoln, NE | 2017-06 | 2018-02 | $14/hour |
47
48[[[/static/salary.csv][source data]]]
49
50** Salary Visualization
51:PROPERTIES:
52:CUSTOM_ID: salary-visualization
53:END:
54See the graph below for a visualization of each job's salary over time,
55as well as percentage increases or decreases between jobs.
56
57#+caption: Salary Visualization
58[[https://img.cleberg.net/blog/salary/salary.png]]
59
60This page was inspired by
61[[https://xeiaso.net/salary-transparency/][Xe]], and I'm quoting the
62following wording from them as I want to reiterate this piece:
63
64#+begin_quote
65Please consider publishing your salary data like this as well. By open,
66voluntary transparency we can help to end stigmas around discussing pay
67and help ensure that the next generations of people in tech are treated
68fairly. Stigmas thrive in darkness but die in the light of day. You can
69help end the stigma by playing your cards out in the open like this.
70#+end_quote
content/services.md deleted −21
@@ -1,21 +0,0 @@
1+++
2title = "Services"
3draft = false
4+++
5
6- [AnonymousOverflow](https://ao.cleberg.net/) - A StackOverflow proxy
7- [CyberChef](https://cyberchef.cleberg.net/) - The Cyber Swiss Army Knife
8- [FileArchive](https://files.cleberg.net/) - An interesting file archive
9- [FlashPaper](https://paste.cleberg.net/) - One-time encrypted
10 password/secret sharing
11- [Invidious](https://invidious.cleberg.net/) - A YouTube proxy
12- [Office](https://office.cleberg.net/) - The world's smallest office suite
13- [Org-Live](https://org.cleberg.net/) - A basic org-mode editor for the web I
14 built
15- [Rimgo](https://rimgo.cleberg.net/) - An alternative frontend for Imgur
16- [SearXNG](https://search.cleberg.net/) - A privacy-respecting, open
17 metasearch engine
18
19See the [git
20log](https://git.sr.ht/~cyborg/cleberg.net/log/main/item/content/services.md) if
21you want to see changes that have been made.
content/services/index.org added +23
@@ -0,0 +1,23 @@
1#+title: Services
2#+slug: index
3
4- [[https://ao.cleberg.net/][AnonymousOverflow]] - A StackOverflow proxy
5- [[https://cyberchef.cleberg.net/][CyberChef]] - The Cyber Swiss Army
6 Knife
7- [[https://files.cleberg.net/][FileArchive]] - An interesting file
8 archive
9- [[https://paste.cleberg.net/][FlashPaper]] - One-time encrypted
10 password/secret sharing
11- [[https://invidious.cleberg.net/][Invidious]] - A YouTube proxy
12- [[https://office.cleberg.net/][Office]] - The world's smallest office
13 suite
14- [[https://org.cleberg.net/][Org-Live]] - A basic org-mode editor for
15 the web I built
16- [[https://rimgo.cleberg.net/][Rimgo]] - An alternative frontend for
17 Imgur
18- [[https://search.cleberg.net/][SearXNG]] - A privacy-respecting, open
19 metasearch engine
20
21See the
22[[https://git.sr.ht/~cyborg/cleberg.net/log/main/item/content/services.md][git
23log]] if you want to see changes that have been made.
content/wiki/_index.md deleted −5
@@ -1,5 +0,0 @@
1+++
2title = "Wiki"
3sort_by = "title"
4template = "wiki.html"
5+++
\ No newline at end of file
content/wiki/ai.md deleted −5
@@ -1,5 +0,0 @@
1+++
2title = "AI"
3updated = 2024-03-07
4draft = true
5+++
\ No newline at end of file
content/wiki/audit.md deleted −5
@@ -1,5 +0,0 @@
1+++
2title = "Audit"
3updated = 2024-03-07
4draft = true
5+++
\ No newline at end of file
content/wiki/automation.md deleted −5
@@ -1,5 +0,0 @@
1+++
2title = "Automation"
3updated = 2024-03-07
4draft = true
5+++
\ No newline at end of file
content/wiki/blogroll.md deleted −40
@@ -1,40 +0,0 @@
1+++
2title = "Blogroll"
3updated = 2024-04-12
4draft = false
5+++
6
7## Aggregators
8
9- [1MB Club](https://1mb.club/)
10- [250KB Club](https://250kb.club/)
11- [512KB Club](https://512kb.club/)
12- [Darktheme Club](https://darktheme.club/)
13- [No CSS Club](https://nocss.club/)
14- [No-JS Club](https://no-js.club/)
15- [Ye Olde Blogroll](https://blogroll.org/)
16
17## Plain Text
18
19A list of various plaintext websites and lists.
20
21- [A List Of Text-Only & Minimalist News
22 Sites](https://greycoder.com/a-list-of-text-only-new-sites/)
23- [Harvard Law Review](https://harvardlawreview.org/)
24- [Hyperlinked Text](https://sjmulder.nl/en/textonly.html)
25- [Plain-text web
26 design](https://medium.com/@letsworkshop/plain-text-web-design-a78ccaf9dbc0)
27- [Plaintext World](https://plaintextworld.com/)
28- [txt2web](https://github.com/masonarmand/txt2web)
29- [Words](https://justinjackson.ca/words.html)
30
31## Webrings
32
33Instead of listing my personal favorites, I'm just going to drop a link to
34[brisay's webring list](https://brisray.com/web/webring-list.htm), which
35contains 237 webrings for a total of 7078 websites, as of 2024-03-15.
36
37## Everything Else
38
39- [Dead Simple Sites](https://deadsimplesites.com/)
40- [Planet Emacslife](https://planet.emacslife.com/)
content/wiki/blogroll.org added +46
@@ -0,0 +1,46 @@
1#+title: Blogroll
2#+date: <2024-04-12>
3
4** Aggregators
5:PROPERTIES:
6:CUSTOM_ID: aggregators
7:END:
8- [[https://1mb.club/][1MB Club]]
9- [[https://250kb.club/][250KB Club]]
10- [[https://512kb.club/][512KB Club]]
11- [[https://darktheme.club/][Darktheme Club]]
12- [[https://nocss.club/][No CSS Club]]
13- [[https://no-js.club/][No-JS Club]]
14- [[https://blogroll.org/][Ye Olde Blogroll]]
15
16** Plain Text
17:PROPERTIES:
18:CUSTOM_ID: plain-text
19:END:
20A list of various plaintext websites and lists.
21
22- [[https://greycoder.com/a-list-of-text-only-new-sites/][A List Of
23 Text-Only & Minimalist News Sites]]
24- [[https://harvardlawreview.org/][Harvard Law Review]]
25- [[https://sjmulder.nl/en/textonly.html][Hyperlinked Text]]
26- [[https://medium.com/@letsworkshop/plain-text-web-design-a78ccaf9dbc0][Plain-text
27 web design]]
28- [[https://plaintextworld.com/][Plaintext World]]
29- [[https://github.com/masonarmand/txt2web][txt2web]]
30- [[https://justinjackson.ca/words.html][Words]]
31
32** Webrings
33:PROPERTIES:
34:CUSTOM_ID: webrings
35:END:
36Instead of listing my personal favorites, I'm just going to drop a link
37to [[https://brisray.com/web/webring-list.htm][brisay's webring list]],
38which contains 237 webrings for a total of 7078 websites, as of
392024-03-15.
40
41** Everything Else
42:PROPERTIES:
43:CUSTOM_ID: everything-else
44:END:
45- [[https://deadsimplesites.com/][Dead Simple Sites]]
46- [[https://planet.emacslife.com/][Planet Emacslife]]
content/wiki/cli.md deleted −5
@@ -1,5 +0,0 @@
1+++
2title = "CLI"
3updated = 2024-03-07
4draft = true
5+++
\ No newline at end of file
content/wiki/data.md deleted −5
@@ -1,5 +0,0 @@
1+++
2title = "Data"
3updated = 2024-03-07
4draft = true
5+++
\ No newline at end of file
content/wiki/editors.md deleted −5
@@ -1,5 +0,0 @@
1+++
2title = "Editors"
3updated = 2024-03-07
4draft = true
5+++
\ No newline at end of file
content/wiki/foss.md deleted −5
@@ -1,5 +0,0 @@
1+++
2title = "FOSS"
3updated = 2024-03-07
4draft = true
5+++
\ No newline at end of file
content/wiki/git.md deleted −5
@@ -1,5 +0,0 @@
1+++
2title = "Git"
3updated = 2024-03-07
4draft = true
5+++
\ No newline at end of file
content/wiki/hardware.md deleted −117
@@ -1,117 +0,0 @@
1+++
2title = "Hardware"
3updated = 2024-03-16
4draft = false
5+++
6
7## Desktop
8
9### macOS
10
11Probably should have added more RAM but Macbooks are stupid expensive.
12
13| Category | Details |
14| -------- | ----------------------------------------------------- |
15| Model | [Macbook Pro 16"](https://www.apple.com/macbook-pro/) |
16| CPU | Apple M2 Pro |
17| RAM | 16GB |
18| Storage | 512GB SSD |
19
20### Linux
21
22A beauty.
23
24| Category | Details |
25| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
26| Model | [Lenovo ThinkPad E15 Gen 4, model 21ED0048US](<https://www.lenovo.com/us/en/p/laptops/thinkpad/thinkpade/thinkpad--e15-gen-4-(15-inch-amd)/len101t0023>) |
27| CPU | AMD Ryzen 5 5625U with Radeon Graphics |
28| RAM | 16 GB |
29| Storage | 256 GB SSD |
30
31## Mobile
32
33Previously used a Pixel 6 & Pixel 7 with GrapheneOS.
34
35| Category | Details |
36| -------- | --------------------------------------------------------- |
37| Model | [iPhone 15 Pro Max](https://www.apple.com/iphone-15-pro/) |
38| CPU | A17 Pro |
39| RAM | 8GB |
40| Storage | 256GB |
41
42## Homelab
43
44I run a small homelab with a mix of consumer (compute/storage) and enterprise
45(network) hardware. I try to keep the lab energy efficient and quiet as my top
46priorities.
47
48### IoT
49
50A collection of mainly smart lights, sensors, and smart appliances. My first
51preference is to disable all networking for new smart devices or simply not
52connect internet in the first place (e.g. I never enable internet on my smart
53TVs). If the smart device requires LAN access, I will connect the device to my
54guest-restricted IoT network. As a last resort, I will set-up the internet but
55monitor the DNS lookups via NextDNS and forcibly block any domains I do not want
56the device to be using. If the device is egregious or shady, I'll just sell it
57and either replace it or live without it.
58
59- Other Appliances (washer, dryer, humidifier, fans, etc.)
60- [Roomba
61 i7+](https://about.irobot.com/sitecore/content/north-america/irobot-us/home/roomba/i7-series)
62- [Philips Hue A19
63 Bulbs](https://www.philips-hue.com/en-us/p/hue-white-and-color-ambiance-a19---e26-smart-bulb---60-w--3-pack-/046677562786)
64 x 15
65- [Philips Hue Play Light
66 Bars](https://www.philips-hue.com/en-us/p/hue-bundle-play-blk-ext/33001)
67- [Philips Hue Smart
68 Bridge](https://www.philips-hue.com/en-us/p/hue-bridge/046677458478) + play
69 light bars and a ton of bulbs
70- [UP
71 Chime](https://store.ui.com/us/en/collections/unifi-camera-security-special-chime)
72- [UP-Sense](https://store.ui.com/us/en/collections/unifi-camera-security-special-sensor)
73 x 2
74- [USP-Plug](https://store.ui.com/us/en/products/unifi-smart-power)
75- [UVC G4
76 Instant](https://store.ui.com/us/en/collections/unifi-camera-security-compact-wifi-connected)
77 x 3
78- [UVC G4 Doorbell
79 Pro](https://store.ui.com/us/en/collections/unifi-camera-security-special-wifi-doorbell)
80
81### Network
82
83A rack-mounted Dream Machine Pro, connected downstream to an access point, mesh
84extender, and a couple ethernet switches.
85
86- [UDM-Pro](https://store.ui.com/us/en/collections/unifi-dream-machine/products/udm-pro)
87- [USW-24-PoE](https://store.ui.com/us/en/collections/unifi-switching-standard-power-over-ethernet/products/usw-24-poe)
88- [USW-Lite-8-PoE](https://store.ui.com/us/en/collections/unifi-switching-utility-poe/products/usw-lite-8-poe)
89- [U6-Pro](https://store.ui.com/us/en/collections/unifi-wifi-flagship-high-capacity/products/u6-pro)
90- [U6-Extender](https://store.ui.com/us/en/collections/unifi-wifi-inwall-outlet-mesh)
91- [USW 24-Port Patch
92 Panel](https://store.ui.com/us/en/collections/unifi-accessory-tech-installations-rackmount/products/uacc-rack-panel-patch-blank-24)
93
94### Servers
95
961. Rack-Mount Server
97
98 I wasn't happy with using low-powered PCs as servers and I knew I did not
99 want the ear-shattering enterprise rack-mounted servers, so I built my own.
100
101 | Category | Details |
102 | ------------------ | -------------------------------------- |
103 | Case | Rosewill RSV-R4100U 4U |
104 | Motherboard | NZXT B550 |
105 | CPU | AMD Ryzen 7 5700G with Radeon Graphics |
106 | RAM | 64GB RAM (2x32GB) |
107 | Storage (On-board) | Western Digital 500GB M.2 NVME SSD |
108 | Storage (HDD Bay) | 48TB HDD |
109 | PSU | Corsair RM850 PSU |
110
1112. Other
112
113 These ran as my main servers before I built the rack-mounted server above. I
114 have shut these down indefinitely for now as I have no use for them.
115
116 - Dell OptiPlex
117 - Raspberry Pi 4
content/wiki/hardware.org added +130
@@ -0,0 +1,130 @@
1#+title: Hardware
2#+date: <2024-03-16>
3
4** Desktop
5:PROPERTIES:
6:CUSTOM_ID: desktop
7:END:
8*** macOS
9:PROPERTIES:
10:CUSTOM_ID: macos
11:END:
12Probably should have added more RAM but Macbooks are stupid expensive.
13
14| Category | Details |
15|----------+---------------------------------------------------------|
16| Model | [[https://www.apple.com/macbook-pro/][Macbook Pro 16”]] |
17| CPU | Apple M2 Pro |
18| RAM | 16GB |
19| Storage | 512GB SSD |
20
21*** Linux
22:PROPERTIES:
23:CUSTOM_ID: linux
24:END:
25A beauty.
26
27| Category | Details |
28|----------+----------------------------------------------------------------------------------------------------------------------------------------------------------|
29| Model | [[https://www.lenovo.com/us/en/p/laptops/thinkpad/thinkpade/thinkpad--e15-gen-4-(15-inch-amd)/len101t0023][Lenovo ThinkPad E15 Gen 4, model 21ED0048US]] |
30| CPU | AMD Ryzen 5 5625U with Radeon Graphics |
31| RAM | 16 GB |
32| Storage | 256 GB SSD |
33
34** Mobile
35:PROPERTIES:
36:CUSTOM_ID: mobile
37:END:
38Previously used a Pixel 6 & Pixel 7 with GrapheneOS.
39
40| Category | Details |
41|----------+-------------------------------------------------------------|
42| Model | [[https://www.apple.com/iphone-15-pro/][iPhone 15 Pro Max]] |
43| CPU | A17 Pro |
44| RAM | 8GB |
45| Storage | 256GB |
46
47** Homelab
48:PROPERTIES:
49:CUSTOM_ID: homelab
50:END:
51I run a small homelab with a mix of consumer (compute/storage) and
52enterprise (network) hardware. I try to keep the lab energy efficient
53and quiet as my top priorities.
54
55*** IoT
56:PROPERTIES:
57:CUSTOM_ID: iot
58:END:
59A collection of mainly smart lights, sensors, and smart appliances. My
60first preference is to disable all networking for new smart devices or
61simply not connect internet in the first place (e.g. I never enable
62internet on my smart TVs). If the smart device requires LAN access, I
63will connect the device to my guest-restricted IoT network. As a last
64resort, I will set-up the internet but monitor the DNS lookups via
65NextDNS and forcibly block any domains I do not want the device to be
66using. If the device is egregious or shady, I'll just sell it and either
67replace it or live without it.
68
69- Other Appliances (washer, dryer, humidifier, fans, etc.)
70- [[https://about.irobot.com/sitecore/content/north-america/irobot-us/home/roomba/i7-series][Roomba
71 i7+]]
72- [[https://www.philips-hue.com/en-us/p/hue-white-and-color-ambiance-a19---e26-smart-bulb---60-w--3-pack-/046677562786][Philips
73 Hue A19 Bulbs]] x 15
74- [[https://www.philips-hue.com/en-us/p/hue-bundle-play-blk-ext/33001][Philips
75 Hue Play Light Bars]]
76- [[https://www.philips-hue.com/en-us/p/hue-bridge/046677458478][Philips
77 Hue Smart Bridge]] + play light bars and a ton of bulbs
78- [[https://store.ui.com/us/en/collections/unifi-camera-security-special-chime][UP
79 Chime]]
80- [[https://store.ui.com/us/en/collections/unifi-camera-security-special-sensor][UP-Sense]]
81 x 2
82- [[https://store.ui.com/us/en/products/unifi-smart-power][USP-Plug]]
83- [[https://store.ui.com/us/en/collections/unifi-camera-security-compact-wifi-connected][UVC
84 G4 Instant]] x 3
85- [[https://store.ui.com/us/en/collections/unifi-camera-security-special-wifi-doorbell][UVC
86 G4 Doorbell Pro]]
87
88*** Network
89:PROPERTIES:
90:CUSTOM_ID: network
91:END:
92A rack-mounted Dream Machine Pro, connected downstream to an access
93point, mesh extender, and a couple ethernet switches.
94
95- [[https://store.ui.com/us/en/collections/unifi-dream-machine/products/udm-pro][UDM-Pro]]
96- [[https://store.ui.com/us/en/collections/unifi-switching-standard-power-over-ethernet/products/usw-24-poe][USW-24-PoE]]
97- [[https://store.ui.com/us/en/collections/unifi-switching-utility-poe/products/usw-lite-8-poe][USW-Lite-8-PoE]]
98- [[https://store.ui.com/us/en/collections/unifi-wifi-flagship-high-capacity/products/u6-pro][U6-Pro]]
99- [[https://store.ui.com/us/en/collections/unifi-wifi-inwall-outlet-mesh][U6-Extender]]
100- [[https://store.ui.com/us/en/collections/unifi-accessory-tech-installations-rackmount/products/uacc-rack-panel-patch-blank-24][USW
101 24-Port Patch Panel]]
102
103*** Servers
104:PROPERTIES:
105:CUSTOM_ID: servers
106:END:
1071. Rack-Mount Server
108
109 I wasn't happy with using low-powered PCs as servers and I knew I did
110 not want the ear-shattering enterprise rack-mounted servers, so I
111 built my own.
112
113 | Category | Details |
114 |--------------------+----------------------------------------|
115 | Case | Rosewill RSV-R4100U 4U |
116 | Motherboard | NZXT B550 |
117 | CPU | AMD Ryzen 7 5700G with Radeon Graphics |
118 | RAM | 64GB RAM (2x32GB) |
119 | Storage (On-board) | Western Digital 500GB M.2 NVME SSD |
120 | Storage (HDD Bay) | 48TB HDD |
121 | PSU | Corsair RM850 PSU |
122
1232. Other
124
125 These ran as my main servers before I built the rack-mounted server
126 above. I have shut these down indefinitely for now as I have no use
127 for them.
128
129 - Dell OptiPlex
130 - Raspberry Pi 4
content/wiki/internet.md → content/wiki/internet.org renamed +50 −44
@@ -1,38 +1,38 @@
1+++
2title = "Internet"
3updated = 2024-05-02
4draft = false
5+++
6
7## Gemini
8
9The [Gemini Protocol](https://geminiprotocol.net/) is a network of
1#+title: Internet
2#+date: <2024-05-02>
3
4** Gemini
5:PROPERTIES:
6:CUSTOM_ID: gemini
7:END:
8The [[https://geminiprotocol.net/][Gemini Protocol]] is a network of
109interconnected plaintext documents. If you've used
11[Gopher](https://en.wikipedia.org/wiki/Gopher_(protocol)) before, Gemini will
12feel very familiar.
13
14Instead of web sites, Gemini uses the term "capsules" that can be access via
15`gemini://` links.
16
17In order to browse Gemini capsules, you will need a client capable of supporting
18Gemini. The Gemini Protocol website has a [Gemini
19software](https://geminiprotocol.net/software/) page with a list of clients for
20each platform.
10[[https://en.wikipedia.org/wiki/Gopher_(protocol)][Gopher]] before,
11Gemini will feel very familiar.
2112
13Instead of web sites, Gemini uses the term "capsules" that can be access
14via =gemini://= links.
2215
23### Gemtext
16In order to browse Gemini capsules, you will need a client capable of
17supporting Gemini. The Gemini Protocol website has a
18[[https://geminiprotocol.net/software/][Gemini software]] page with a
19list of clients for each platform.
2420
25[Gemtext](https://geminiprotocol.net/docs/cheatsheet.gmi) is a plaintext syntax,
26very similar to Markdown.
21*** Gemtext
22:PROPERTIES:
23:CUSTOM_ID: gemtext
24:END:
25[[https://geminiprotocol.net/docs/cheatsheet.gmi][Gemtext]] is a
26plaintext syntax, very similar to Markdown.
2727
2828To quote the Gemini Protocol's documentation:
2929
30- Long lines get wrapped by the client to fit the screen.
31- Short lines _don't_ get joined together.
32- Write paragraphs as single long lines.
33- Blank lines are rendered verbatim.
30- Long lines get wrapped by the client to fit the screen.
31- Short lines /don't/ get joined together.
32- Write paragraphs as single long lines.
33- Blank lines are rendered verbatim.
3434
35```md
35#+begin_src md
3636[//]: <> (Headings)
3737# Heading
3838## Sub-heading
@@ -54,16 +54,19 @@ To quote the Gemini Protocol's documentation:
5454Any line starting with three backticks (```) will tell the client to toggle to
5555"preformatted mode", which disables the client's logical checks to render links,
5656headings, etc. and will render the text as-is.
57```
58
59## Gopher
60
61The [Gopher Protocol](https://en.wikipedia.org/wiki/Gopher_(protocol)) is a
62communication protocol, similar to Gemini, that allows for browsing of any ones
63of [the 14 supported item
64types](https://datatracker.ietf.org/doc/html/rfc1436#section-3.8):
65
66```txt
57#+end_src
58
59** Gopher
60:PROPERTIES:
61:CUSTOM_ID: gopher
62:END:
63The [[https://en.wikipedia.org/wiki/Gopher_(protocol)][Gopher Protocol]]
64is a communication protocol, similar to Gemini, that allows for browsing
65of any ones of
66[[https://datatracker.ietf.org/doc/html/rfc1436#section-3.8][the 14
67supported item types]]:
68
69#+begin_src txt
67700 Item is a file
68711 Item is a directory
69722 Item is a CSO phone-book server
@@ -80,14 +83,17 @@ types](https://datatracker.ietf.org/doc/html/rfc1436#section-3.8):
8083T Item points to a text-based tn3270 session.
8184g Item is a GIF format graphics file.
8285I Item is some kind of image file. Client decides how to display.
83```
84
85### Simplicity is Intentional
86#+end_src
8687
87Gopher is [meant to be
88simplistic](https://datatracker.ietf.org/doc/html/rfc1436#section-4).
88*** Simplicity is Intentional
89:PROPERTIES:
90:CUSTOM_ID: simplicity-is-intentional
91:END:
92Gopher is
93[[https://datatracker.ietf.org/doc/html/rfc1436#section-4][meant to be
94simplistic]].
8995
90```txt
96#+begin_src txt
9197As far as possible we desire any new features to be carried as new
9298protocols that will be hidden behind new document-types. The
9399internet Gopher philosophy is:
@@ -110,4 +116,4 @@ internet Gopher philosophy is:
110116 (c) The well-tempered client should do something reasonable with
111117 funny characters received in text; filter them out, leave them in,
112118 whatever.
113```
\ No newline at end of file
119#+end_src
content/wiki/ios.md deleted −204
@@ -1,204 +0,0 @@
1+++
2title = "iOS"
3updated = 2024-05-01
4draft = false
5+++
6
7Related:
8
9- [Hardware](/wiki/hardware.html)
10
11My primary mobile OS. Currently running iOS 17. This wiki page contains most of
12the apps I have used at one point or another across my different iPhones.
13
14(`*`) = My favorites
15
16## Configuration
17
18### Display
19
20- Light Mode
21 - 10:00 to 16:00
22- Dark Mode
23 - 16:00 to 10:00
24
25### Focus Modes
26
27- Personal Focus
28 - 06:00 to 21:00
29 - Allow Notifications From:
30 - Alarms
31 - Calendar
32 - Contacts (1 person)
33 - Messages
34 - Phone
35 - Reminders
36 - Signal
37 - UniFi Protect
38- Sleep Focus
39 - 21:00 to 06:00
40 - Allow Notifications From:
41 - Alarms
42 - Contacts (1 person)
43 - Reminders
44 - Signal
45
46### Privacy & Security
47
48I generally follow the [principle of least
49privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege) by only
50permitting the bare minimum privileges and revoking as soon as they are no
51longer required.
52
53Here's the baseline I start with:
54
55- Disable:
56 - Analytics & Improvements
57 - Apple Advertising
58 - Apple ID > Sign-In & Security > Two-Factor Authentication
59 - Location Services > System Services > Product Improvement
60 - Tracking > Allow Apps to Request to Track
61 - Safari > Advanced > Privacy Preserving Ad Measurement
62- Enable:
63 - Apple ID > iCloud > Advanced Data Protection
64 - Apple ID > Personal Information > Communication Preferences
65 - App Privacy Report
66 - Location Services only for Camera, Find My, UDisc, & WiFiman (`While
67 Using`)
68 - Safari > Prevent Cross-Site Tracking
69 - Safari > Hide IP Address
70 - Safari > Advanced > Advanced Tracking and Fingerprinting Protection
71
72## Native Apps
73
74### Business
75
76- [Element](https://apps.apple.com/us/app/element-messenger/id1083446067) - A
77 cross-platform messenger, based on Matrix
78- [LinkedIn](https://apps.apple.com/us/app/linkedin-network-job-finder/id288429040)
79 - One of the only social media apps I use
80
81### Developer Tools
82
83- [Harbour](https://testflight.apple.com/join/F2vK7xo4) - Easily manage your
84 Portainer service
85- [iSH](https://apps.apple.com/us/app/ish-shell/id1436902243) - A local shell
86 with SSH functionality
87
88### Entertainment
89
90- [Plex](https://apps.apple.com/us/app/plex-watch-live-tv-and-movies/id383457673)
91 - A client for the Plex Media Server
92- [Steam](https://apps.apple.com/us/app/steam-mobile/id495369748) - The top
93 gaming marketplace for computers
94
95### Lifestyle
96
97- [Home](https://apps.apple.com/us/app/home/id1110145103) (`*`) - Apple
98 homekit powered smart home manager
99- [Hue](https://apps.apple.com/us/app/philips-hue/id1055281310) - Philips Hue
100 smart home manager
101- [iRobot](https://apps.apple.com/us/app/irobot-home/id1012014442) - Manage
102 iRobot Roomba devices
103- [UniFi Protect](https://apps.apple.com/us/app/unifi-protect/id1392492235) -
104 View and manage most UniFi Protect cameras and settings
105
106### Music
107
108- [Apple Music](https://apps.apple.com/us/app/apple-music/id1108187390) (`*`)
109 - Apple's native music streaming app
110- [Plexamp](https://apps.apple.com/us/app/plexamp/id1500797510) (`*`) -
111 Top-notch music app for your Plex Media Server, with a neural network that
112 provides excellent radio/shuffle suggestions
113
114### Navigation
115
116- [deedum](https://apps.apple.com/us/app/deedum/id1546810946) - A Gemini
117 Protocol browser
118
119### News
120
121- [NetNewsWire](https://apps.apple.com/us/app/netnewswire-rss-reader/id1480640210)
122 - A free and open source RSS reader for Mac, iPhone, and iPad
123
124### Photo & Video
125
126- [Aislingeach](https://testflight.apple.com/join/Q6WyyEpS) - A quick way to
127 generate and rate images from the Stable Horde
128- [Unsplash](https://apps.apple.com/us/app/unsplash/id1290631746) - Premium
129 images, mostly free
130
131### Productivity
132
133- [beorg](https://apps.apple.com/us/app/beorg-to-do-list-agenda/id1238649962)
134 - An org-mode editor, outline, and scheduler with paid extensions
135- [Bitwarden](https://apps.apple.com/us/app/bitwarden-password-manager/id1137397744)
136 (`*`) - An open source password manager
137- [Bitwarden
138 Authenticator](https://apps.apple.com/us/app/bitwarden-authenticator/id6497335175)
139 (`*`) - Generate 2FA on your device
140- [Cryptomator](https://apps.apple.com/us/app/cryptomator/id1560822163) - A
141 cross-platform encryption program
142- [Obsidian](https://apps.apple.com/us/app/obsidian-connected-notes/id1557175442)
143 (`*`) - A nice Markdown-based editor based on a "vault" structure. Offers a
144 paid sync solution and community extensions
145- [Strongbox](https://apps.apple.com/us/app/strongbox-password-manager/id897283731)
146 - Keepass password manager for iOS & macOS
147- [UniFi Network](https://apps.apple.com/us/app/unifi/id1057750338) - View and
148 manage most UniFi Network settings
149
150### Safari Extensions
151
152- [AdGuard](https://apps.apple.com/us/app/adguard-adblock-privacy/id1047223162)
153 - Ad blocker
154- [Dark
155 Reader](https://apps.apple.com/us/app/dark-reader-for-safari/id1438243180) -
156 Dark mode for all the sites
157- [PiPifier](https://apps.apple.com/us/app/pipifier/id1234771095) - Force
158 videos to support PiP
159- [Privacy
160 Redirect](https://apps.apple.com/us/app/privacy-redirect/id1578144015) -
161 Redirect select websites to others, usually to privacy-focused alternatives
162
163### Social Networking
164
165- [MultiTab T](https://apps.apple.com/us/app/multitab-for-tumblr/id1071533778)
166 (`*`) - A gallery-based Tumblr client with some unique features, such as tab
167 history and sync
168- [Signal](https://apps.apple.com/us/app/signal-private-messenger/id874139669)
169 (`*`) - A simple, powerful, and secure messenger
170- [Three Cheers](https://testflight.apple.com/join/mpVk1qIy) - A client for
171 Tildes.net with a design focus that matches the intent of Tildes
172- [Voyager](https://apps.apple.com/us/app/voyager-for-lemmy/id6451429762) - A
173 Lemmy client
174
175### Sports
176
177- [Apple Sports](https://apps.apple.com/us/app/apple-sports/id6446788829) -
178 Apple's new sports app - lacks notifications and live events
179- [UDisc](https://apps.apple.com/us/app/udisc-disc-golf/id1072228953) - Disc
180 golf course maps, score cards, and more
181
182### Utilities
183
184- [Backblaze](https://apps.apple.com/us/app/backblaze/id628638330) - Quickly
185 view and manage Backblaze b2 cloud storage
186- [Mullvad VPN](https://apps.apple.com/us/app/mullvad-vpn/id1488466513) (`*`)
187 - A private VPN service
188- [OTP Auth](https://apps.apple.com/us/app/otp-auth/id659877384) (`*`) - A
189 minimalistic OTP app with support for biometrics, custom icons,
190 import/export, and iCloud sync
191- [Plex Dash](https://apps.apple.com/us/app/plex-dash/id1500797677) - Stats
192 about your Plex Media Server
193- [Safari](https://apps.apple.com/us/app/safari/id1146562112) - iOS default
194 browser
195- [Unifi WiFiman](https://apps.apple.com/us/app/ubiquiti-wifiman/id1385561119)
196 - Create visual layouts of WiFi strength and save heat maps to your phone
197
198## Web Apps & Shortcuts
199
200- [Brutalist Report](https://brutalist.report/) - Minimal news aggregator
201- [\_Cyber.Report](https://cyber.report/) - Cybersecurity news aggregator
202- [Hacker News](https://news.ycombinator.com/) - Mostly technical news
203- [NextDNS](https://nextdns.io/) - NextDNS statistics dashboard
204- [Readspike](https://readspike.com/) - Minimal news aggregator
content/wiki/ios.org added +246
@@ -0,0 +1,246 @@
1#+title: iOS
2#+date: <2024-05-01>
3
4Related:
5
6- [[/wiki/hardware.html][Hardware]]
7
8My primary mobile OS. Currently running iOS 17. This wiki page contains
9most of the apps I have used at one point or another across my different
10iPhones.
11
12(=*=) = My favorites
13
14** Configuration
15:PROPERTIES:
16:CUSTOM_ID: configuration
17:END:
18*** Display
19:PROPERTIES:
20:CUSTOM_ID: display
21:END:
22- Light Mode
23 - 10:00 to 16:00
24- Dark Mode
25 - 16:00 to 10:00
26
27*** Focus Modes
28:PROPERTIES:
29:CUSTOM_ID: focus-modes
30:END:
31- Personal Focus
32 - 06:00 to 21:00
33 - Allow Notifications From:
34 - Alarms
35 - Calendar
36 - Contacts (1 person)
37 - Messages
38 - Phone
39 - Reminders
40 - Signal
41 - UniFi Protect
42- Sleep Focus
43 - 21:00 to 06:00
44 - Allow Notifications From:
45 - Alarms
46 - Contacts (1 person)
47 - Reminders
48 - Signal
49
50*** Privacy & Security
51:PROPERTIES:
52:CUSTOM_ID: privacy-security
53:END:
54I generally follow the
55[[https://en.wikipedia.org/wiki/Principle_of_least_privilege][principle
56of least privilege]] by only permitting the bare minimum privileges and
57revoking as soon as they are no longer required.
58
59Here's the baseline I start with:
60
61- Disable:
62 - Analytics & Improvements
63 - Apple Advertising
64 - Apple ID > Sign-In & Security > Two-Factor Authentication
65 - Location Services > System Services > Product Improvement
66 - Tracking > Allow Apps to Request to Track
67 - Safari > Advanced > Privacy Preserving Ad Measurement
68- Enable:
69 - Apple ID > iCloud > Advanced Data Protection
70 - Apple ID > Personal Information > Communication Preferences
71 - App Privacy Report
72 - Location Services only for Camera, Find My, UDisc, & WiFiman
73 (=While Using=)
74 - Safari > Prevent Cross-Site Tracking
75 - Safari > Hide IP Address
76 - Safari > Advanced > Advanced Tracking and Fingerprinting Protection
77
78** Native Apps
79:PROPERTIES:
80:CUSTOM_ID: native-apps
81:END:
82*** Business
83:PROPERTIES:
84:CUSTOM_ID: business
85:END:
86- [[https://apps.apple.com/us/app/element-messenger/id1083446067][Element]] -
87 A cross-platform messenger, based on Matrix
88- [[https://apps.apple.com/us/app/linkedin-network-job-finder/id288429040][LinkedIn]]
89 - One of the only social media apps I use
90
91*** Developer Tools
92:PROPERTIES:
93:CUSTOM_ID: developer-tools
94:END:
95- [[https://testflight.apple.com/join/F2vK7xo4][Harbour]] - Easily
96 manage your Portainer service
97- [[https://apps.apple.com/us/app/ish-shell/id1436902243][iSH]] - A
98 local shell with SSH functionality
99
100*** Entertainment
101:PROPERTIES:
102:CUSTOM_ID: entertainment
103:END:
104- [[https://apps.apple.com/us/app/plex-watch-live-tv-and-movies/id383457673][Plex]]
105 - A client for the Plex Media Server
106- [[https://apps.apple.com/us/app/steam-mobile/id495369748][Steam]] -
107 The top gaming marketplace for computers
108
109*** Lifestyle
110:PROPERTIES:
111:CUSTOM_ID: lifestyle
112:END:
113- [[https://apps.apple.com/us/app/home/id1110145103][Home]] (=*=) -
114 Apple homekit powered smart home manager
115- [[https://apps.apple.com/us/app/philips-hue/id1055281310][Hue]] -
116 Philips Hue smart home manager
117- [[https://apps.apple.com/us/app/irobot-home/id1012014442][iRobot]] -
118 Manage iRobot Roomba devices
119- [[https://apps.apple.com/us/app/unifi-protect/id1392492235][UniFi
120 Protect]] - View and manage most UniFi Protect cameras and settings
121
122*** Music
123:PROPERTIES:
124:CUSTOM_ID: music
125:END:
126- [[https://apps.apple.com/us/app/apple-music/id1108187390][Apple
127 Music]] (=*=)
128 - Apple's native music streaming app
129- [[https://apps.apple.com/us/app/plexamp/id1500797510][Plexamp]]
130 (=*=) - Top-notch music app for your Plex Media Server, with a neural
131 network that provides excellent radio/shuffle suggestions
132
133*** Navigation
134:PROPERTIES:
135:CUSTOM_ID: navigation
136:END:
137- [[https://apps.apple.com/us/app/deedum/id1546810946][deedum]] - A
138 Gemini Protocol browser
139
140*** News
141:PROPERTIES:
142:CUSTOM_ID: news
143:END:
144- [[https://apps.apple.com/us/app/netnewswire-rss-reader/id1480640210][NetNewsWire]]
145 - A free and open source RSS reader for Mac, iPhone, and iPad
146
147*** Photo & Video
148:PROPERTIES:
149:CUSTOM_ID: photo-video
150:END:
151- [[https://testflight.apple.com/join/Q6WyyEpS][Aislingeach]] - A quick
152 way to generate and rate images from the Stable Horde
153- [[https://apps.apple.com/us/app/unsplash/id1290631746][Unsplash]] -
154 Premium images, mostly free
155
156*** Productivity
157:PROPERTIES:
158:CUSTOM_ID: productivity
159:END:
160- [[https://apps.apple.com/us/app/beorg-to-do-list-agenda/id1238649962][beorg]]
161 - An org-mode editor, outline, and scheduler with paid extensions
162- [[https://apps.apple.com/us/app/bitwarden-password-manager/id1137397744][Bitwarden]]
163 (=*=) - An open source password manager
164- [[https://apps.apple.com/us/app/bitwarden-authenticator/id6497335175][Bitwarden
165 Authenticator]] (=*=) - Generate 2FA on your device
166- [[https://apps.apple.com/us/app/cryptomator/id1560822163][Cryptomator]] -
167 A cross-platform encryption program
168- [[https://apps.apple.com/us/app/obsidian-connected-notes/id1557175442][Obsidian]]
169 (=*=) - A nice Markdown-based editor based on a "vault" structure.
170 Offers a paid sync solution and community extensions
171- [[https://apps.apple.com/us/app/strongbox-password-manager/id897283731][Strongbox]]
172 - Keepass password manager for iOS & macOS
173- [[https://apps.apple.com/us/app/unifi/id1057750338][UniFi Network]] -
174 View and manage most UniFi Network settings
175
176*** Safari Extensions
177:PROPERTIES:
178:CUSTOM_ID: safari-extensions
179:END:
180- [[https://apps.apple.com/us/app/adguard-adblock-privacy/id1047223162][AdGuard]]
181 - Ad blocker
182- [[https://apps.apple.com/us/app/dark-reader-for-safari/id1438243180][Dark
183 Reader]] - Dark mode for all the sites
184- [[https://apps.apple.com/us/app/pipifier/id1234771095][PiPifier]] -
185 Force videos to support PiP
186- [[https://apps.apple.com/us/app/privacy-redirect/id1578144015][Privacy
187 Redirect]] - Redirect select websites to others, usually to
188 privacy-focused alternatives
189
190*** Social Networking
191:PROPERTIES:
192:CUSTOM_ID: social-networking
193:END:
194- [[https://apps.apple.com/us/app/multitab-for-tumblr/id1071533778][MultiTab
195 T]] (=*=) - A gallery-based Tumblr client with some unique features,
196 such as tab history and sync
197- [[https://apps.apple.com/us/app/signal-private-messenger/id874139669][Signal]]
198 (=*=) - A simple, powerful, and secure messenger
199- [[https://testflight.apple.com/join/mpVk1qIy][Three Cheers]] - A
200 client for Tildes.net with a design focus that matches the intent of
201 Tildes
202- [[https://apps.apple.com/us/app/voyager-for-lemmy/id6451429762][Voyager]] -
203 A Lemmy client
204
205*** Sports
206:PROPERTIES:
207:CUSTOM_ID: sports
208:END:
209- [[https://apps.apple.com/us/app/apple-sports/id6446788829][Apple
210 Sports]] - Apple's new sports app - lacks notifications and live
211 events
212- [[https://apps.apple.com/us/app/udisc-disc-golf/id1072228953][UDisc]] -
213 Disc golf course maps, score cards, and more
214
215*** Utilities
216:PROPERTIES:
217:CUSTOM_ID: utilities
218:END:
219- [[https://apps.apple.com/us/app/backblaze/id628638330][Backblaze]] -
220 Quickly view and manage Backblaze b2 cloud storage
221- [[https://apps.apple.com/us/app/mullvad-vpn/id1488466513][Mullvad
222 VPN]] (=*=)
223 - A private VPN service
224- [[https://apps.apple.com/us/app/otp-auth/id659877384][OTP Auth]]
225 (=*=) - A minimalistic OTP app with support for biometrics, custom
226 icons, import/export, and iCloud sync
227- [[https://apps.apple.com/us/app/plex-dash/id1500797677][Plex Dash]] -
228 Stats about your Plex Media Server
229- [[https://apps.apple.com/us/app/safari/id1146562112][Safari]] - iOS
230 default browser
231- [[https://apps.apple.com/us/app/ubiquiti-wifiman/id1385561119][Unifi
232 WiFiman]]
233 - Create visual layouts of WiFi strength and save heat maps to your
234 phone
235
236** Web Apps & Shortcuts
237:PROPERTIES:
238:CUSTOM_ID: web-apps-shortcuts
239:END:
240- [[https://brutalist.report/][Brutalist Report]] - Minimal news
241 aggregator
242- [[https://cyber.report/][_Cyber.Report]] - Cybersecurity news
243 aggregator
244- [[https://news.ycombinator.com/][Hacker News]] - Mostly technical news
245- [[https://nextdns.io/][NextDNS]] - NextDNS statistics dashboard
246- [[https://readspike.com/][Readspike]] - Minimal news aggregator
content/wiki/linux.md deleted −51
@@ -1,51 +0,0 @@
1+++
2title = "Linux"
3updated = 2024-03-07
4draft = true
5+++
6
7Related:
8
9- [Hardware](/wiki/hardware/)
10
11## Alpine Linux
12
13My secondary laptop OS.
14
15## Ubuntu Server
16
17My primary server OS.
18
19- Applications
20 - CrowdSec
21 - Netdata
22 - Nginx
23 - Plex Media Server
24- Docker Containers
25 - AnonymousOverflow
26 - Authelia
27 - Convos
28 - CyberChef
29 - FlashPaper
30 - FreshRSS
31 - GotHub
32 - Gramps
33 - Invidious
34 - Libreddit
35 - NocoDB
36 - OtterWiki
37 - Portainer
38 - rgit
39 - Scrutiny
40 - SearXNG
41 - Stash
42 - Tautulli
43 - Teddit
44 - Watchtower
45- Utilities
46 - byobu
47 - Certbot
48 - exiftool
49 - fail2ban
50 - ufw
51 - zfs
content/wiki/macos.md deleted −206
@@ -1,206 +0,0 @@
1+++
2title = "macOS"
3updated = 2024-05-01
4draft = false
5+++
6
7Related:
8
9- [Hardware](/wiki/hardware/)
10
11My primary OS. Currently running macOS Sonoma 14. This wiki page contains most
12of the apps I have used at one point or another across my different Macbooks.
13
14(`*`) = My favorites
15
16## Configuration
17
18### Disable System Services
19
20- [Disabling and Enabling System Integrity
21 Protection](https://developer.apple.com/documentation/security/disabling_and_enabling_system_integrity_protection)
22- Disable Gatekeeper: `sudo spctl --master-disable`
23
24### Dotfiles
25
26These are probably out of date, but they give a general idea of how I configure
27my machine.
28
29```conf
30# ~/.zshrc
31export PATH="/opt/homebrew/bin:$PATH"
32export PATH="$HOME/.emacs.d/bin:$PATH"
33export EDITOR="/opt/homebrew/bin/emacs -nw"
34ZSH_THEME="bureau"
35plugins=(git zsh-autosuggestions)
36source $ZSH/oh-my-zsh.sh
37```
38
39```conf
40# ~/.zprofile
41eval "$(/opt/homebrew/bin/brew shellenv)"
42```
43
44```conf
45# ~/.config/skhd/skhdrc
46cmd - return : /Applications/iTerm.app/Contents/MacOS/iTerm2
47cmd + shift - return : /Applications/LibreWolf.app/Contents/MacOS/librewolf
48```
49
50```conf
51# ~/.config/yabai/yabairc
52yabai -m config \
53 mouse_follows_focus off \
54 focus_follows_mouse off \
55 window_origin_display default \
56 window_placement second_child \
57 window_zoom_persist on \
58 window_shadow on \
59 window_animation_duration 0.0 \
60 window_animation_frame_rate 120 \
61 window_opacity_duration 0.0 \
62 active_window_opacity 1.0 \
63 normal_window_opacity 0.90 \
64 window_opacity off \
65 insert_feedback_color 0xffd75f5f \
66 split_ratio 0.50 \
67 split_type auto \
68 auto_balance off \
69 top_padding 15 \
70 bottom_padding 15 \
71 left_padding 15 \
72 right_padding 15 \
73 window_gap 10 \
74 layout bsp \
75 mouse_modifier fn \
76 mouse_action1 move \
77 mouse_action2 resize \
78 mouse_drop_action swap
79echo "yabai configuration loaded.."
80```
81
82## Software
83
84### Browsers
85
86- [Librewolf](https://librewolf.net/) (`*`) - Custom version of Firefox,
87 focused on privacy and security
88 - [Bitwarden](https://bitwarden.com/) - An open source password manager
89 - [Dark Reader](https://darkreader.org/) - Dark mode for all the websites
90 - [Libredirect](https://libredirect.github.io/) - Automatic web
91 redirections
92 - [Strongbox](https://strongboxsafe.com/) - Keepass password manager for
93 iOS & macOS
94 - [uBlock Origin](https://ublockorigin.com/) - Free, open-source ad
95 content blocker
96- [Ungoogled
97 Chromium](https://github.com/ungoogled-software/ungoogled-chromium) - Google
98 Chromium, sans integration with Google
99- [eww](https://www.gnu.org/software/emacs/manual/html_mono/eww.html) - Emacs
100 Web Wowser, for TUI browsing
101
102### Command Line Tools
103
104- [doggo](https://github.com/mr-karan/doggo) - Command-line DNS Client for
105 Humans
106- [duf](https://github.com/muesli/duf) - Disk Usage/Free Utility - a better
107 'df' alternative
108- [dust](https://github.com/bootandy/dust) - A more intuitive version of du in
109 rust
110- [eza](https://github.com/eza-community/eza) - A modern alternative to ls
111- [macchina](https://github.com/Macchina-CLI/macchina) - A system information
112 frontend with an emphasis on performance.
113- [yt-dlp](https://github.com/yt-dlp/yt-dlp) - A youtube-dl fork with
114 additional features and fixes
115
116### Communications
117
118- [Element](https://element.io/) (`*`) - Matrix's default GUI client
119- [gomuks](https://github.com/tulir/gomuks) - A terminal based Matrix client
120- [Thunderbird](https://www.thunderbird.net/) (`*`) - An open source email
121 client by Mozilla
122- [Signal](https://signal.org/) (`*`) - A simple, powerful, and secure
123 messenger
124
125### Development
126
127- [Docker Desktop](https://www.docker.com/products/docker-desktop/) - Docker
128 containers for your desktop
129 - [open-webui](https://github.com/open-webui/open-webui) - User-friendly
130 WebUI for LLMs
131- [iTerm2](https://iterm2.com/) (`*`) - The best terminal for macOS, hands
132 down
133- [Podman Desktop](https://podman-desktop.io/) (`*`) - Open source tool for
134 containers and Kubernetes
135- [Xcode](https://developer.apple.com/xcode/) - Apple's IDE
136- [zsh](https://en.wikipedia.org/wiki/Z_shell) (`*`) - My shell preference due
137 to its plugin and theme community
138 - [zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions)
139 - Fish-like autosuggestions for zsh
140 - [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting)
141 - Fish shell like syntax highlighting for Zsh
142
143### Editors
144
145- [Doom Emacs](https://github.com/doomemacs/doomemacs) (`*`) - An Emacs
146 framework, great for working in org-mode
147- [Obsidian](https://obsidian.md/) - A nice Markdown-based editor based on a
148 "vault" structure. Offers a paid sync solution and community extensions
149- [Standard Notes](https://standardnotes.com/) - A simple text editor focused
150 on privacy and security. Offers a paid sync solution and community
151 extensions
152- [VSCodium](https://vscodium.com/) - VS Code without proprietary blobs
153
154### Media
155
156- [Luminar](https://skylum.com/luminar) - Luminar offers top-notch photo
157 editing features
158- [Minecraft](https://www.minecraft.net/) - Block mining simulator
159- [NetNewsWire](https://netnewswire.com/) - A free and open source RSS reader
160 for Mac, iPhone, and iPad
161- [Plex](https://www.plex.tv/) (`*`) - Desktop client for the Plex Media
162 Server
163- [Steam](https://store.steampowered.com/) - The top gaming marketplace for
164 computers
165- [Transmission](https://transmissionbt.com/) (`*`) - A Fast, Easy and Free
166 Bittorrent Client
167- [VLC](https://www.videolan.org/vlc/) - A free and open source cross-platform
168 multimedia player
169
170### Package Management
171
172- [Homebrew](https://brew.sh/) (`*`) - The Missing Package Manager for macOS
173 (or Linux)
174- [MacPorts](https://www.macports.org/) - A system to compile, install, and
175 manage open source software
176
177### Utilities
178
179- [BetterDisplay](https://betterdisplay.pro/) - Allows you to tweak a ton of
180 features of built-in and external screens, such as scaling, configuration
181 overrides, and color/brightness upscaling
182- [Bitwarden](https://bitwarden.com/) - An open source password manager
183- [Ice](https://icemenubar.app/) (`*`)- A powerful menu bar management tool
184- [LittleSnitch](https://obdev.at/products/littlesnitch/index.html) - Shows
185 all network connections on your Macbook, including system and privileged
186 services
187- [MicroSnitch](https://obdev.at/products/microsnitch/index.html) - Camera &
188 microphone monitoring and alterting service
189- [Mullvad](https://mullvad.net/) (`*`) - A private VPN service
190- [Ollama](https://ollama.com/) - Run Llama 2, Code Llama, and other models
191 locally on your machine
192 - [Ollama Swift](https://github.com/kghandour/Ollama-SwiftUI) - User
193 Interface made for Ollama.ai using Swift
194- [OrbStack](https://orbstack.dev/) - A fast and convenient GUI to manage
195 Docker contains and Linux VMs
196- [Raycast](https://www.raycast.com/) - A collection of tools and shortcuts,
197 an alternative to Spotlight
198- [skhd](https://github.com/koekeishiya/skhd) (`*`) - Simple hotkey daemon for
199 macOS
200- [Strongbox](https://strongboxsafe.com/) - Keepass password manager for iOS &
201 macOS
202- [Syncthing](https://syncthing.net/) (`*`) - Continuous file synchronization
203- [TinkerTool](https://www.bresink.com/osx/TinkerTool.html) - Unlock hidden
204 configuration options for macOS
205- [yabai](https://github.com/koekeishiya/yabai) (`*`) - Automatic window
206 tiling
content/wiki/macos.org added +236
@@ -0,0 +1,236 @@
1#+title: macOS
2#+date: <2024-05-01>
3
4Related:
5
6- [[/wiki/hardware/][Hardware]]
7
8My primary OS. Currently running macOS Sonoma 14. This wiki page
9contains most of the apps I have used at one point or another across my
10different Macbooks.
11
12(=*=) = My favorites
13
14** Configuration
15:PROPERTIES:
16:CUSTOM_ID: configuration
17:END:
18*** Disable System Services
19:PROPERTIES:
20:CUSTOM_ID: disable-system-services
21:END:
22- [[https://developer.apple.com/documentation/security/disabling_and_enabling_system_integrity_protection][Disabling
23 and Enabling System Integrity Protection]]
24- Disable Gatekeeper: =sudo spctl --master-disable=
25
26*** Dotfiles
27:PROPERTIES:
28:CUSTOM_ID: dotfiles
29:END:
30These are probably out of date, but they give a general idea of how I
31configure my machine.
32
33#+begin_src conf
34# ~/.zshrc
35export PATH="/opt/homebrew/bin:$PATH"
36export PATH="$HOME/.emacs.d/bin:$PATH"
37export EDITOR="/opt/homebrew/bin/emacs -nw"
38ZSH_THEME="bureau"
39plugins=(git zsh-autosuggestions)
40source $ZSH/oh-my-zsh.sh
41#+end_src
42
43#+begin_src conf
44# ~/.zprofile
45eval "$(/opt/homebrew/bin/brew shellenv)"
46#+end_src
47
48#+begin_src conf
49# ~/.config/skhd/skhdrc
50cmd - return : /Applications/iTerm.app/Contents/MacOS/iTerm2
51cmd + shift - return : /Applications/LibreWolf.app/Contents/MacOS/librewolf
52#+end_src
53
54#+begin_src conf
55# ~/.config/yabai/yabairc
56yabai -m config \
57 mouse_follows_focus off \
58 focus_follows_mouse off \
59 window_origin_display default \
60 window_placement second_child \
61 window_zoom_persist on \
62 window_shadow on \
63 window_animation_duration 0.0 \
64 window_animation_frame_rate 120 \
65 window_opacity_duration 0.0 \
66 active_window_opacity 1.0 \
67 normal_window_opacity 0.90 \
68 window_opacity off \
69 insert_feedback_color 0xffd75f5f \
70 split_ratio 0.50 \
71 split_type auto \
72 auto_balance off \
73 top_padding 15 \
74 bottom_padding 15 \
75 left_padding 15 \
76 right_padding 15 \
77 window_gap 10 \
78 layout bsp \
79 mouse_modifier fn \
80 mouse_action1 move \
81 mouse_action2 resize \
82 mouse_drop_action swap
83echo "yabai configuration loaded.."
84#+end_src
85
86** Software
87:PROPERTIES:
88:CUSTOM_ID: software
89:END:
90*** Browsers
91:PROPERTIES:
92:CUSTOM_ID: browsers
93:END:
94- [[https://librewolf.net/][Librewolf]] (=*=) - Custom version of
95 Firefox, focused on privacy and security
96 - [[https://bitwarden.com/][Bitwarden]] - An open source password
97 manager
98 - [[https://darkreader.org/][Dark Reader]] - Dark mode for all the
99 websites
100 - [[https://libredirect.github.io/][Libredirect]] - Automatic web
101 redirections
102 - [[https://strongboxsafe.com/][Strongbox]] - Keepass password manager
103 for iOS & macOS
104 - [[https://ublockorigin.com/][uBlock Origin]] - Free, open-source ad
105 content blocker
106- [[https://github.com/ungoogled-software/ungoogled-chromium][Ungoogled
107 Chromium]] - Google Chromium, sans integration with Google
108- [[https://www.gnu.org/software/emacs/manual/html_mono/eww.html][eww]] -
109 Emacs Web Wowser, for TUI browsing
110
111*** Command Line Tools
112:PROPERTIES:
113:CUSTOM_ID: command-line-tools
114:END:
115- [[https://github.com/mr-karan/doggo][doggo]] - Command-line DNS Client
116 for Humans
117- [[https://github.com/muesli/duf][duf]] - Disk Usage/Free Utility - a
118 better 'df' alternative
119- [[https://github.com/bootandy/dust][dust]] - A more intuitive version
120 of du in rust
121- [[https://github.com/eza-community/eza][eza]] - A modern alternative
122 to ls
123- [[https://github.com/Macchina-CLI/macchina][macchina]] - A system
124 information frontend with an emphasis on performance.
125- [[https://github.com/yt-dlp/yt-dlp][yt-dlp]] - A youtube-dl fork with
126 additional features and fixes
127
128*** Communications
129:PROPERTIES:
130:CUSTOM_ID: communications
131:END:
132- [[https://element.io/][Element]] (=*=) - Matrix's default GUI client
133- [[https://github.com/tulir/gomuks][gomuks]] - A terminal based Matrix
134 client
135- [[https://www.thunderbird.net/][Thunderbird]] (=*=) - An open source
136 email client by Mozilla
137- [[https://signal.org/][Signal]] (=*=) - A simple, powerful, and secure
138 messenger
139
140*** Development
141:PROPERTIES:
142:CUSTOM_ID: development
143:END:
144- [[https://www.docker.com/products/docker-desktop/][Docker Desktop]] -
145 Docker containers for your desktop
146 - [[https://github.com/open-webui/open-webui][open-webui]] -
147 User-friendly WebUI for LLMs
148- [[https://iterm2.com/][iTerm2]] (=*=) - The best terminal for macOS,
149 hands down
150- [[https://podman-desktop.io/][Podman Desktop]] (=*=) - Open source
151 tool for containers and Kubernetes
152- [[https://developer.apple.com/xcode/][Xcode]] - Apple's IDE
153- [[https://en.wikipedia.org/wiki/Z_shell][zsh]] (=*=) - My shell
154 preference due to its plugin and theme community
155 - [[https://github.com/zsh-users/zsh-autosuggestions][zsh-autosuggestions]]
156 - Fish-like autosuggestions for zsh
157 - [[https://github.com/zsh-users/zsh-syntax-highlighting][zsh-syntax-highlighting]]
158 - Fish shell like syntax highlighting for Zsh
159
160*** Editors
161:PROPERTIES:
162:CUSTOM_ID: editors
163:END:
164- [[https://github.com/doomemacs/doomemacs][Doom Emacs]] (=*=) - An
165 Emacs framework, great for working in org-mode
166- [[https://obsidian.md/][Obsidian]] - A nice Markdown-based editor
167 based on a "vault" structure. Offers a paid sync solution and
168 community extensions
169- [[https://standardnotes.com/][Standard Notes]] - A simple text editor
170 focused on privacy and security. Offers a paid sync solution and
171 community extensions
172- [[https://vscodium.com/][VSCodium]] - VS Code without proprietary
173 blobs
174
175*** Media
176:PROPERTIES:
177:CUSTOM_ID: media
178:END:
179- [[https://skylum.com/luminar][Luminar]] - Luminar offers top-notch
180 photo editing features
181- [[https://www.minecraft.net/][Minecraft]] - Block mining simulator
182- [[https://netnewswire.com/][NetNewsWire]] - A free and open source RSS
183 reader for Mac, iPhone, and iPad
184- [[https://www.plex.tv/][Plex]] (=*=) - Desktop client for the Plex
185 Media Server
186- [[https://store.steampowered.com/][Steam]] - The top gaming
187 marketplace for computers
188- [[https://transmissionbt.com/][Transmission]] (=*=) - A Fast, Easy and
189 Free Bittorrent Client
190- [[https://www.videolan.org/vlc/][VLC]] - A free and open source
191 cross-platform multimedia player
192
193*** Package Management
194:PROPERTIES:
195:CUSTOM_ID: package-management
196:END:
197- [[https://brew.sh/][Homebrew]] (=*=) - The Missing Package Manager for
198 macOS (or Linux)
199- [[https://www.macports.org/][MacPorts]] - A system to compile,
200 install, and manage open source software
201
202*** Utilities
203:PROPERTIES:
204:CUSTOM_ID: utilities
205:END:
206- [[https://betterdisplay.pro/][BetterDisplay]] - Allows you to tweak a
207 ton of features of built-in and external screens, such as scaling,
208 configuration overrides, and color/brightness upscaling
209- [[https://bitwarden.com/][Bitwarden]] - An open source password
210 manager
211- [[https://icemenubar.app/][Ice]] (=*=)- A powerful menu bar management
212 tool
213- [[https://obdev.at/products/littlesnitch/index.html][LittleSnitch]] -
214 Shows all network connections on your Macbook, including system and
215 privileged services
216- [[https://obdev.at/products/microsnitch/index.html][MicroSnitch]] -
217 Camera & microphone monitoring and alterting service
218- [[https://mullvad.net/][Mullvad]] (=*=) - A private VPN service
219- [[https://ollama.com/][Ollama]] - Run Llama 2, Code Llama, and other
220 models locally on your machine
221 - [[https://github.com/kghandour/Ollama-SwiftUI][Ollama Swift]] - User
222 Interface made for Ollama.ai using Swift
223- [[https://orbstack.dev/][OrbStack]] - A fast and convenient GUI to
224 manage Docker contains and Linux VMs
225- [[https://www.raycast.com/][Raycast]] - A collection of tools and
226 shortcuts, an alternative to Spotlight
227- [[https://github.com/koekeishiya/skhd][skhd]] (=*=) - Simple hotkey
228 daemon for macOS
229- [[https://strongboxsafe.com/][Strongbox]] - Keepass password manager
230 for iOS & macOS
231- [[https://syncthing.net/][Syncthing]] (=*=) - Continuous file
232 synchronization
233- [[https://www.bresink.com/osx/TinkerTool.html][TinkerTool]] - Unlock
234 hidden configuration options for macOS
235- [[https://github.com/koekeishiya/yabai][yabai]] (=*=) - Automatic
236 window tiling
content/wiki/management.md deleted −11
@@ -1,11 +0,0 @@
1+++
2title = "Management"
3updated = 2024-03-07
4draft = true
5+++
6
7## Time Management
8
9## People Management
10
11## Project Management
content/wiki/minimalism.md deleted −5
@@ -1,5 +0,0 @@
1+++
2title = "Minimalism"
3updated = 2024-03-07
4draft = true
5+++
\ No newline at end of file
content/wiki/networking.md deleted −27
@@ -1,27 +0,0 @@
1+++
2title = "Networking"
3updated = 2024-03-07
4draft = true
5+++
6
7## Bluetooth
8
9## DNS
10
11## Gemini
12
13## Gopher
14
15## HTTP
16
17## IoT
18
19## Nginx
20
21## SSH
22
23## SSL
24
25## VPN
26
27## Wi-Fi
content/wiki/plaintext.md deleted −11
@@ -1,11 +0,0 @@
1+++
2title = "Plaintext"
3updated = 2024-03-07
4draft = true
5+++
6
7## Markdown
8
9## Org-Mode
10
11## AsciiDoc
content/wiki/privacy.md deleted −5
@@ -1,5 +0,0 @@
1+++
2title = "Privacy"
3updated = 2024-03-07
4draft = true
5+++
\ No newline at end of file
content/wiki/python.md deleted −5
@@ -1,5 +0,0 @@
1+++
2title = "Python"
3updated = 2024-03-07
4draft = true
5+++
\ No newline at end of file
content/wiki/security.md deleted −5
@@ -1,5 +0,0 @@
1+++
2title = "Security"
3updated = 2024-03-07
4draft = true
5+++
\ No newline at end of file
content/wiki/selfhosted.md deleted −5
@@ -1,5 +0,0 @@
1+++
2title = "Self-Hosted"
3updated = 2024-03-07
4draft = true
5+++
\ No newline at end of file
content/wiki/windows.md deleted −18
@@ -1,18 +0,0 @@
1+++
2title = "Windows"
3updated = 2024-03-07
4draft = true
5+++
6
7## Windows (Work)
8
9I am required to use a Windows 10 laptop for work, but I have some
10limited customization options.
11
12- Alteryx
13- Notepad++
14- Obsidian
15- Power BI
16- Task Scheduler
17 - Auto Dark Mode
18 - Auto Light Mode
publish.el added +98
@@ -0,0 +1,98 @@
1;; explicity load packages since I'm using Doom Emacs
2(add-to-list 'load-path "~/.emacs.d/.local/straight/repos/emacs-htmlize")
3(add-to-list 'load-path "~/.emacs.d/.local/straight/repos/weblorg")
4(add-to-list 'load-path "~/.emacs.d/.local/straight/repos/templatel")
5(require 'htmlize)
6(require 'weblorg)
7
8;; defaults to http://localhost:8000
9;; ENV=prod emacs --script publish.el
10(if (string= (getenv "ENV") "prod")
11 (setq weblorg-default-url "https://cleberg.net"))
12
13(weblorg-site
14 :theme nil
15 :template-vars '(("site_name" . "cleberg.net")
16 ("site_owner" . "Christian Cleberg <hello@cleberg.net>")
17 ("site_description" . "Just a blip of ones and zeroes.")))
18
19;; route for rendering the index page of the website
20(weblorg-route
21 :name "index"
22 :input-pattern "content/index.org"
23 :template "index.html"
24 :output ".build/index.html"
25 :url "/")
26
27;; route for rendering each post
28(weblorg-route
29 :name "blog"
30 :input-pattern "content/blog/*.org"
31 :template "post.html"
32 :output ".build/blog/{{ slug }}.html"
33 :url "/blog/{{ slug }}.html")
34
35;; route for rendering the index page of the blog
36(weblorg-route
37 :name "blog-index"
38 :input-pattern "content/blog/*.org"
39 :input-aggregate #'weblorg-input-aggregate-all-desc
40 :template "blog.html"
41 :output ".build/blog/index.html"
42 :url "/blog/")
43
44;; route for rendering each wiki post
45(weblorg-route
46 :name "wiki"
47 :input-pattern "content/wiki/*.org"
48 :template "post.html"
49 :output ".build/wiki/{{ slug }}.html"
50 :url "/wiki/{{ slug }}.html")
51
52;; route for rendering the index page of the wiki
53(weblorg-route
54 :name "wiki-index"
55 :input-pattern "content/wiki/*.org"
56 :input-aggregate #'weblorg-input-aggregate-all
57 :template "wiki.html"
58 :output ".build/wiki/index.html"
59 :url "/wiki/")
60
61;; route for rendering each page
62(weblorg-route
63 :name "pages"
64 :input-pattern "content/*.org"
65 :template "page.html"
66 :output ".build/{{ slug }}.html"
67 :url "/{{ slug }}.html")
68
69(weblorg-route
70 :name "salary"
71 :input-pattern "content/salary/*.org"
72 :template "page.html"
73 :output ".build/salary/{{ slug }}.html"
74 :url "/salary/{{ slug }}.html")
75
76(weblorg-route
77 :name "services"
78 :input-pattern "content/services/*.org"
79 :template "page.html"
80 :output ".build/services/{{ slug }}.html"
81 :url "/services/{{ slug }}.html")
82
83;; RSS Feed
84(weblorg-route
85 :name "rss"
86 :input-pattern "content/blog/*.org"
87 :input-aggregate #'weblorg-input-aggregate-all-desc
88 :template "feed.xml"
89 :output ".build/feed.xml"
90 :url "/feed.xml")
91
92;; route for static assets that also copies files to .build directory
93(weblorg-copy-static
94 :output ".build/{{ file }}"
95 :url "/{{ file }}")
96
97;; fire the engine and export all the files declared in the routes above
98(weblorg-export)
static/syntax-theme-dark.css deleted −280
@@ -1,280 +0,0 @@
1/*
2 * theme "ayu" generated by syntect
3 */
4
5.z-code {
6 color: #bfbab0;
7 background-color: #0f1419;
8}
9
10.z-comment, .z-punctuation.z-definition.z-comment {
11 color: #5c6773;
12font-style: italic;
13}
14.z-variable {
15 color: #bfbab0;
16}
17.z-string, .z-constant.z-other.z-symbol {
18 color: #c2d94c;
19}
20.z-constant.z-numeric {
21 color: #f29718;
22}
23.z-string.z-regexp, .z-constant.z-character.z-escape {
24 color: #95e6cb;
25}
26.z-constant.z-language {
27 color: #f29718;
28}
29.z-constant.z-character, .z-constant.z-other {
30 color: #f29718;
31}
32.z-variable.z-member {
33 color: #ec5f67;
34}
35.z-keyword, .z-keyword.z-operator.z-word {
36 color: #ff7733;
37}
38.z-keyword.z-operator {
39 color: #f29668;
40}
41.z-punctuation.z-separator, .z-punctuation.z-terminator {
42 color: #bfbab0;
43}
44.z-punctuation.z-section {
45 color: #bfbab0;
46}
47.z-punctuation.z-accessor {
48 color: #f29668;
49}
50.z-punctuation.z-definition.z-annotation {
51 color: #bfbab0;
52}
53.z-variable.z-other.z-dollar.z-only.z-js, .z-variable.z-other.z-object.z-dollar.z-only.z-js, .z-variable.z-type.z-dollar.z-only.z-js, .z-support.z-class.z-dollar.z-only.z-js {
54 color: #e6b673;
55}
56.z-storage {
57 color: #ff7733;
58}
59.z-storage.z-type {
60 color: #ff7733;
61}
62.z-entity.z-name.z-function {
63 color: #ffb454;
64}
65.z-entity.z-name, .z-entity.z-name.z-tag, .z-entity.z-name.z-label) {
66 color: #59c2ff;
67}
68.z-entity.z-other.z-inherited-class {
69 color: #59c2ff;
70text-decoration: underline;
71}
72.z-variable.z-parameter {
73 color: #f29718;
74}
75.z-variable.z-language {
76 color: #39bae6;
77font-style: italic;
78}
79.z-entity.z-name.z-tag, .z-meta.z-tag.z-sgml {
80 color: #39bae6;
81}
82.z-punctuation.z-definition.z-tag.z-end, .z-punctuation.z-definition.z-tag.z-begin, .z-punctuation.z-definition.z-tag {
83 color: #39bae6;
84}
85.z-entity.z-other.z-attribute-name {
86 color: #ffb454;
87}
88.z-variable.z-function, .z-variable.z-annotation {
89 color: #ffb454;
90}
91.z-support.z-function, .z-support.z-macro {
92 color: #f07178;
93}
94.z-support.z-constant {
95 color: #f29668;
96font-style: italic;
97}
98.z-support.z-type, .z-support.z-class {
99 color: #39bae6;
100font-style: italic;
101}
102.z-invalid {
103 color: #ff3333;
104}
105.z-invalid.z-deprecated {
106 color: #ffffff;
107 background-color: #ff7733;
108}
109.z-meta.z-diff, .z-meta.z-diff.z-header {
110 color: #c594c5;
111}
112.z-source.z-ruby .z-variable.z-other.z-readwrite {
113 color: #ffb454;
114}
115.z-source.z-css .z-entity.z-name.z-tag, .z-source.z-sass .z-entity.z-name.z-tag, .z-source.z-scss .z-entity.z-name.z-tag, .z-source.z-less .z-entity.z-name.z-tag, .z-source.z-stylus .z-entity.z-name.z-tag {
116 color: #59c2ff;
117}
118.z-source.z-css .z-support.z-type, .z-source.z-sass .z-support.z-type, .z-source.z-scss .z-support.z-type, .z-source.z-less .z-support.z-type, .z-source.z-stylus .z-support.z-type {
119 color: #5c6773;
120}
121.z-support.z-type.z-property-name {
122 color: #39bae6;
123}
124.z-constant.z-numeric.z-line-number.z-find-in-files {
125 color: #5c6773;
126}
127.z-constant.z-numeric.z-line-number.z-match {
128 color: #ff7733;
129}
130.z-entity.z-name.z-filename.z-find-in-files {
131 color: #c2d94c;
132}
133.z-message.z-error {
134 color: #ff3333;
135}
136.z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string {
137 color: #39bae6;
138}
139.z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string {
140 color: #39bae6;
141}
142.z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string {
143 color: #39bae6;
144}
145.z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string {
146 color: #39bae6;
147}
148.z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string {
149 color: #39bae6;
150}
151.z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string {
152 color: #39bae6;
153}
154.z-source.z-json .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string {
155 color: #39bae6;
156}
157.z-source.z-json .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string {
158 color: #39bae6;
159}
160.z-source.z-json .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string {
161 color: #39bae6;
162}
163.z-markup.z-heading {
164 color: #ff7733;
165font-weight: bold;
166}
167.z-string.z-other.z-link, .z-markup.z-underline.z-link {
168 color: #95e6cb;
169text-decoration: underline;
170font-style: italic;
171}
172.z-punctuation.z-definition.z-image {
173 color: #ffb454;
174}
175.z-markup.z-italic {
176 color: #f07178;
177font-style: italic;
178}
179.z-markup.z-bold {
180 color: #f07178;
181font-weight: bold;
182}
183.z-markup.z-italic .z-markup.z-bold, .z-markup.z-bold .z-markup.z-italic {
184font-weight: bold;
185font-style: italic;
186}
187.z-markup.z-raw {
188 background-color: #bfbab0;
189}
190.z-markup.z-raw.z-inline {
191 background-color: #bfbab0;
192}
193.z-meta.z-separator {
194 color: #5c6773;
195 background-color: #bfbab0;
196font-weight: bold;
197}
198.z-markup.z-quote {
199 color: #f29718;
200font-style: italic;
201}
202.z-markup.z-list.z-numbered.z-bullet, .z-markup.z-list .z-punctuation.z-definition.z-list_item {
203 color: #95e6cb;
204}
205.z-markup.z-inserted {
206 color: #c2d94c;
207}
208.z-markup.z-changed {
209 color: #39bae6;
210}
211.z-markup.z-deleted {
212 color: #f07178;
213}
214.z-markup.z-strike {
215 color: #e6b673;
216}
217.z-markup.z-table {
218 color: #39bae6;
219 background-color: #bfbab0;
220}
221.z-text.z-html.z-markdown .z-markup.z-raw.z-inline {
222 color: #f29668;
223}
224.z-text.z-html.z-markdown .z-meta.z-dummy.z-line-break {
225 color: #5c6773;
226}
227.z-markup.z-raw.z-block.z-fenced.z-markdown {
228 color: #bfbab0;
229 background-color: #bfbab0;
230}
231.z-punctuation.z-definition.z-fenced.z-markdown, .z-variable.z-language.z-fenced.z-markdown {
232 color: #5c6773;
233 background-color: #bfbab0;
234}
235.z-variable.z-language.z-fenced.z-markdown {
236 color: #5c6773;
237}
238.z-markup.z-inserted.z-git_gutter {
239 color: #c2d94c;
240}
241.z-markup.z-changed.z-git_gutter {
242 color: #39bae6;
243}
244.z-markup.z-deleted.z-git_gutter {
245 color: #ff3333;
246}
247.z-markup.z-ignored.z-git_gutter {
248 color: #20272c;
249}
250.z-markup.z-untracked.z-git_gutter {
251 color: #20272c;
252}
253.z-gutter_color {
254 color: #ffffff;
255}
256.z-acejump.z-label.z-blue {
257 color: #ffffff;
258 background-color: #39bae6;
259}
260.z-acejump.z-label.z-green {
261 color: #ffffff;
262 background-color: #c2d94c;
263}
264.z-acejump.z-label.z-orange {
265 color: #ffffff;
266 background-color: #ff7733;
267}
268.z-acejump.z-label.z-purple {
269 color: #ffffff;
270 background-color: #f07178;
271}
272.z-sublimelinter.z-mark.z-warning {
273 color: #39bae6;
274}
275.z-sublimelinter.z-gutter-mark {
276 color: #ffffff;
277}
278.z-sublimelinter.z-mark.z-error {
279 color: #ff3333;
280}
static/syntax-theme-light.css deleted −407
@@ -1,407 +0,0 @@
1/*
2 * theme "GitHub" generated by syntect
3 */
4
5.z-code {
6 color: #323232;
7 background-color: #ffffff;
8}
9
10.z-comment {
11 color: #969896;
12font-style: italic;
13}
14.z-string {
15 color: #183691;
16}
17.z-regexp-operator {
18 color: #a71d5d;
19}
20.z-string.z-regexp.z-characterclass .z-punctuation.z-definition.z-string.z-begin, .z-string.z-regexp.z-characterclass .z-punctuation.z-definition.z-string.z-end {
21 color: #a71d5d;
22}
23.z-constant.z-numeric {
24 color: #0086b3;
25}
26.z-constant.z-language {
27 color: #0086b3;
28}
29.z-constant.z-character, .z-constant.z-other, .z-variable.z-other.z-constant {
30 color: #0086b3;
31}
32.z-variable {
33 color: #323232;
34}
35.z-keyword {
36 color: #a71d5d;
37font-weight: bold;
38}
39.z-bitwise-operator {
40 color: #a71d5d;
41font-weight: bold;
42}
43.z-storage {
44 color: #a71d5d;
45font-weight: bold;
46}
47.z-storage.z-type {
48 color: #a71d5d;
49font-weight: bold;
50}
51.z-entity.z-name.z-class {
52 color: #0086b3;
53}
54.z-entity.z-other.z-inherited-class {
55 color: #0086b3;
56}
57.z-entity.z-name.z-function {
58 color: #795da3;
59font-weight: bold;
60}
61.z-variable.z-parameter {
62 color: #323232;
63}
64.z-entity.z-name.z-tag {
65 color: #63a35c;
66}
67.z-entity.z-other.z-attribute-name {
68 color: #795da3;
69}
70.z-support.z-function {
71 color: #62a35c;
72}
73.z-support.z-constant {
74 color: #0086b3;
75}
76.z-support.z-type, .z-support.z-class {
77 color: #0086b3;
78}
79.z-support.z-other.z-variable {
80 color: #323232;
81}
82.z-invalid, .z-invalid.z-illegal, .z-invalid.z-deprecated {
83 color: #b52a1d;
84 background-color: #f5f5f5;
85font-weight: bold;
86}
87.z-entity.z-name.z-filename.z-find-in-files {
88 color: #323232;
89font-weight: bold;
90}
91.z-constant.z-numeric.z-line-number.z-find-in-files, .z-constant.z-numeric.z-line-number.z-match.z-find-in-files {
92 color: #b3b3b3;
93}
94.z-meta.z-diff.z-header {
95 color: #969896;
96 background-color: #ffffff;
97font-style: italic;
98}
99.z-meta.z-diff.z-header .z-punctuation.z-definition.z-from-file.z-diff {
100 color: #bd2c00;
101 background-color: #ffecec;
102font-weight: bold;
103font-style: italic;
104}
105.z-meta.z-diff.z-header .z-punctuation.z-definition.z-to-file.z-diff {
106 color: #55a532;
107 background-color: #eaffea;
108font-weight: bold;
109font-style: italic;
110}
111.z-meta.z-diff.z-range {
112 color: #969896;
113font-weight: bold;
114font-style: italic;
115}
116.z-markup.z-deleted {
117 background-color: #ffecec;
118}
119.z-markup.z-deleted .z-punctuation.z-definition.z-inserted {
120 color: #bd2c00;
121font-weight: bold;
122}
123.z-markup.z-inserted {
124 background-color: #eaffea;
125}
126.z-markup.z-inserted .z-punctuation.z-definition.z-inserted {
127 color: #55a532;
128font-weight: bold;
129}
130.z-markup.z-deleted.z-git_gutter {
131 color: #bd2c00;
132}
133.z-markup.z-inserted.z-git_gutter {
134 color: #55a532;
135}
136.z-markup.z-changed.z-git_gutter {
137 color: #0086b3;
138}
139.z-markup.z-ignored.z-git_gutter {
140 color: #b3b3b3;
141}
142.z-markup.z-untracked.z-git_gutter {
143 color: #b3b3b3;
144}
145.z-source.z-css .z-punctuation.z-definition.z-entity {
146 color: #323232;
147}
148.z-source.z-css .z-entity.z-other.z-attribute-name.z-pseudo-class, .z-source.z-css .z-entity.z-other.z-attribute-name.z-pseudo-element {
149 color: #a71d5d;
150}
151.z-source.z-css .z-meta.z-value, .z-source.z-css .z-support.z-constant, .z-source.z-css .z-support.z-function {
152 color: #323232;
153}
154.z-source.z-css .z-constant.z-other.z-color {
155 color: #ed6a43;
156}
157.z-source.z-scss .z-punctuation.z-definition.z-entity {
158 color: #323232;
159}
160.z-source.z-scss .z-entity.z-other.z-attribute-name.z-pseudo-class, .z-source.z-scss .z-entity.z-other.z-attribute-name.z-pseudo-element {
161 color: #a71d5d;
162}
163.z-source.z-scss .z-support.z-constant.z-property-value, .z-source.z-scss .z-support.z-function {
164 color: #323232;
165}
166.z-source.z-scss .z-variable {
167 color: #a71d5d;
168}
169.z-variable.z-language.z-this.z-js {
170 color: #ed6a43;
171}
172.z-source.z-js .z-entity.z-name.z-function {
173 color: #323232;
174}
175.z-source.z-js .z-meta.z-function .z-entity.z-name.z-function, .z-source.z-js .z-entity.z-name.z-function .z-meta.z-function {
176 color: #795da3;
177font-weight: bold;
178}
179.z-entity.z-name.z-type.z-new.z-js {
180 color: #795da3;
181}
182.z-variable.z-language.z-prototype.z-js {
183 color: #0086b3;
184}
185.z-source.z-js .z-support.z-function {
186 color: #0086b3;
187}
188.z-support.z-type.z-object.z-console.z-js {
189 color: #795da3;
190}
191.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
192 color: #183691;
193font-weight: bold;
194}
195.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
196 color: #323232;
197}
198.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
199 color: #183691;
200font-weight: bold;
201}
202.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
203 color: #323232;
204}
205.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
206 color: #183691;
207font-weight: bold;
208}
209.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
210 color: #323232;
211}
212.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
213 color: #183691;
214font-weight: bold;
215}
216.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
217 color: #323232;
218}
219.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
220 color: #183691;
221font-weight: bold;
222}
223.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
224 color: #323232;
225}
226.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
227 color: #183691;
228font-weight: bold;
229}
230.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
231 color: #323232;
232}
233.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
234 color: #183691;
235font-weight: bold;
236}
237.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
238 color: #323232;
239}
240.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
241 color: #183691;
242font-weight: bold;
243}
244.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
245 color: #323232;
246}
247.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
248 color: #183691;
249font-weight: bold;
250}
251.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
252 color: #323232;
253}
254.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
255 color: #183691;
256font-weight: bold;
257}
258.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
259 color: #323232;
260}
261.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
262 color: #183691;
263font-weight: bold;
264}
265.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
266 color: #323232;
267}
268.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
269 color: #183691;
270font-weight: bold;
271}
272.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
273 color: #323232;
274}
275.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
276 color: #183691;
277font-weight: bold;
278}
279.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
280 color: #323232;
281}
282.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
283 color: #183691;
284font-weight: bold;
285}
286.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
287 color: #323232;
288}
289.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
290 color: #183691;
291font-weight: bold;
292}
293.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
294 color: #323232;
295}
296.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
297 color: #183691;
298font-weight: bold;
299}
300.z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
301 color: #323232;
302}
303.z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
304 color: #183691;
305font-weight: bold;
306}
307.z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
308 color: #323232;
309}
310.z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
311 color: #183691;
312font-weight: bold;
313}
314.z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
315 color: #323232;
316}
317.z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
318 color: #183691;
319font-weight: bold;
320}
321.z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
322 color: #323232;
323}
324.z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
325 color: #183691;
326font-weight: bold;
327}
328.z-meta .z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
329 color: #323232;
330}
331.z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json {
332 color: #183691;
333font-weight: bold;
334}
335.z-meta.z-structure.z-dictionary.z-json .z-meta.z-structure.z-dictionary.z-value.z-json .z-string.z-quoted.z-double.z-json {
336 color: #323232;
337}
338.z-source.z-python .z-keyword {
339font-weight: bold;
340}
341.z-source.z-python .z-storage {
342font-weight: bold;
343}
344.z-source.z-python .z-storage.z-type {
345font-weight: bold;
346}
347.z-source.z-python .z-entity.z-name.z-function {
348 color: #323232;
349font-weight: bold;
350}
351.z-source.z-php .z-entity.z-name.z-type.z-class {
352 color: #323232;
353font-weight: bold;
354}
355.z-variable.z-language.z-ruby {
356 color: #ed6a43;
357}
358.z-entity.z-name.z-type.z-module.z-ruby {
359 color: #795da3;
360font-weight: bold;
361}
362.z-entity.z-name.z-type.z-class.z-ruby {
363 color: #795da3;
364font-weight: bold;
365}
366.z-entity.z-other.z-inherited-class.z-ruby {
367 color: #795da3;
368font-weight: bold;
369}
370.z-punctuation.z-definition.z-heading.z-markdown, .z-punctuation.z-definition.z-italic.z-markdown, .z-punctuation.z-definition.z-bold.z-markdown, .z-punctuation.z-definition.z-string.z-begin.z-markdown, .z-punctuation.z-definition.z-string.z-end.z-markdown, .z-punctuation.z-definition.z-metadata.z-markdown, .z-punctuation.z-definition.z-link.z-markdown, .z-punctuation.z-definition.z-blockquote.z-markdown, .z-punctuation.z-definition.z-raw.z-markdown {
371 color: #a71d5d;
372}
373.z-text.z-html.z-markdown .z-meta.z-separator {
374 color: #b3b3b3;
375}
376.z-text.z-html.z-markdown .z-markup.z-heading {
377font-weight: bold;
378}
379.z-text.z-html.z-markdown .z-markup.z-raw.z-block {
380 color: #323232;
381}
382.z-text.z-html.z-markdown .z-markup.z-raw.z-inline {
383 color: #323232;
384}
385.z-text.z-html.z-markdown .z-meta.z-link, .z-text.z-html.z-markdown .z-meta.z-image {
386 color: #4183c4;
387}
388.z-text.z-html.z-markdown .z-markup.z-underline.z-link, .z-text.z-html.z-markdown .z-constant.z-other.z-reference {
389font-style: italic;
390}
391.z-text.z-html.z-markdown .z-markup.z-list {
392 color: #ed6a43;
393}
394.z-text.z-html.z-markdown .z-markup.z-bold {
395font-weight: bold;
396}
397.z-text.z-html.z-markdown .z-markup.z-italic {
398font-style: italic;
399}
400.z-text.z-html.z-markdown .z-markup.z-bold .z-markup.z-italic {
401font-weight: bold;
402font-style: italic;
403}
404.z-text.z-html.z-markdown .z-markup.z-italic .z-markup.z-bold {
405font-weight: bold;
406font-style: italic;
407}
templates/base.html deleted −53
@@ -1,53 +0,0 @@
1<!doctype html>
2<html lang="en">
3<head>
4 {% block title %}
5 <title>cleberg.net</title>
6 {% endblock %}
7 <meta name="viewport" content="width=device-width, initial-scale=1.0">
8 <meta name="theme-color" content="#111" media="(prefers-color-scheme:
9 dark)">
10 <meta name="theme-color" content="#fff" media="(prefers-color-scheme:
11 light)">
12 {% if page %} {% if page.description %}
13 <meta name="description" content="{{ page.description }}">
14 {% elif config.description %}
15 <meta name="description" content="{{ config.description }}">
16 {% endif %} {% elif config.description %}
17 <meta name="description" content="{{ config.description }}" /> {% endif
18 %}
19
20 {% block rss %}
21 <link rel="alternate" type="application/atom+xml" title="RSS" href="{{
22 get_url(path="atom.xml", trailing_slash=false) }}">
23 {% endblock %}
24
25 {% block css %}
26 <link rel="stylesheet" href="/styles.min.css">
27 {% endblock css %}
28
29 {% block extra_head %}{% endblock extra_head %}
30</head>
31<body>
32 <nav class="site-nav" aria-label="site-nav" role="navigation">
33 <ul>
34 <li><a href="/">Home</a></li>
35 <li><a href="/blog/">Blog</a></li>
36 <li><a href="/services/">Services</a></li>
37 <li><a href="/wiki/">Wiki</a></li>
38 </ul>
39 </nav>
40
41 <main>{% block content %}{% endblock content %}</main>
42
43 <footer>
44 <p>Last build: &lt;{{ now() | date(format='%Y-%m-%d %a %H:%M:%S') }}&gt;</p>
45 <p>
46 <a href="https://git.sr.ht/~cyborg/cleberg.net"
47 target="_blank" rel="noopener">Source Code</a>
48 &middot;
49 <a href="/atom.xml">Atom RSS Feed</a>
50 </p>
51 </footer>
52</body>
53</html>
templates/blog.html deleted −20
@@ -1,20 +0,0 @@
1{% extends "base.html" %}
2
3{% block content %}
4<h1>{{ section.title }}</h1>
5<p>Use <code>Ctrl + F</code> to search blog post titles for keywords.</p>
6<p>You can also add the <a href="/atom.xml">Atom RSS Feed</a> to your feed
7reader.</p>
8{% set post_date = "1970-01-01" | date(format="%Y") %}
9{% for page in section.pages %}
10{% if post_date != page.date | date(format="%Y") %}
11{% set_global post_date = page.date | date(format="%Y") %}
12 <h2>{{ post_date }}</h2>
13 {% endif %}
14 <div class="post">
15 <time datetime="{{ page.date | date(format='%Y-%m-%d %H:%M:%S') }}">{{
16 page.date | date(format='%Y-%m-%d') }}</time>
17 <a href="/blog/{{ page.slug }}/">{{ page.title }}</a>
18 </div>
19{% endfor %}
20{% endblock content %}
templates/index.html deleted −58
@@ -1,58 +0,0 @@
1{% extends "base.html" %} {% block content %}
2<section>
3 <h1>cleberg.net</h1>
4 <pre>pub rsa4096 2022-11-16 [SC]
5 <a href="/gpg.txt">3917 973F B159 BBB8 6194 5385 6945 1A51 7AC0 CB37</a>
6uid [ultimate] Christian Cleberg &lt;hello@cleberg.net&gt;</pre>
7</section>
8<section>
9 <h2>Recent Blog Posts</h2>
10 {% set section = get_section(path="blog/_index.md") %} {% set i = 0 %} {% for
11 page in section.pages %} {% if i < 3%} {% set_global i = i + 1%}
12 <div class="post">
13 <time
14 datetime="{{ page.date | date(format='%Y-%m-%d %H:%M:%S')
15 }}"
16 >{{ page.date | date(format='%Y-%m-%d') }}</time
17 >
18 <a href="/blog/{{ page.slug }}/">{{ page.title }}</a>
19 </div>
20 {% endif %} {% endfor %}
21 <br />
22 <a href="/blog/">All Posts &rarr;</a>
23</section>
24<section>
25 <h2>Everything Else</h2>
26 <ul>
27 <li><a href="https://cv.cleberg.net">Curriculum Vitae</a></li>
28 <li><a href="/salary/">Salary</a></li>
29 <li><a href="/services/">Services</a></li>
30 <li><a href="/wiki/">Wiki</a></li>
31 </ul>
32</section>
33<section>
34 <h2>Contact</h2>
35 <ul>
36 <li>hello [at] cleberg.net (<a href="/gpg.txt">GPG</a>)</li>
37 <li>
38 cyb0rg on
39 <a href="https://libera.chat" target="_blank" rel="noopener"
40 >libera.chat</a
41 >
42 </li>
43 <li>
44 <a
45 href="https://matrix.to/#/@cyborg:matrix.tchncs.de"
46 target="_blank"
47 rel="noopener"
48 >@cyborg:@matrix.tchncs.de</a
49 >
50 </li>
51 <li>
52 <a href="https://git.sr.ht/~cyborg/" target="_blank" rel="noopener"
53 >Sourcehut</a
54 >
55 </li>
56 </ul>
57</section>
58{% endblock content %}
templates/page.html deleted −55
@@ -1,55 +0,0 @@
1{% extends "base.html" %}
2
3{% block title %}
4<title>{{ page.title }} | cleberg.net</title>
5{% endblock %}
6
7{% block css %}
8<link rel="stylesheet" href="/syntax-theme-dark.css"
9media="(prefers-color-scheme: dark)">
10<link rel="stylesheet" href="/syntax-theme-light.css"
11media="(prefers-color-scheme: light)">
12<link rel="stylesheet" href="/styles.css">
13{% endblock css %}
14
15{% block content %}
16<section class="post-metadata">
17 <h1>{{ page.title }}</h1>
18 {% if page.date %}
19 <p><time datetime="{{ page.date | date(format='%Y-%m-%d %H:%M:%S') }}">{{
20 page.date }}</time></p>
21 {% endif %} {% if page.updated %}
22 <p>Last Updated: <time datetime="{{ page.updated | date(format='%Y-%m-%d
23 %H:%M:%S') }}">{{ page.updated }}</time></p>
24 {% endif %}
25 <p>{{ page.word_count }} words; {{ page.reading_time }} minute(s)</p>
26{% if page.toc %}
27 <details open>
28 <summary>Table of Contents</summary>
29 <nav class="page-nav" aria-label="page-nav" role="navigation">
30 <ul>
31 {% for h1 in page.toc %}
32 <li>
33 <a href="{{ h1.permalink | safe }}">{{ h1.title }}</a>
34 {% if h1.children %}
35 <ul>
36 {% for h2 in h1.children %}
37 <li>
38 <a href="{{ h2.permalink | safe }}">{{
39 h2.title }}</a>
40 </li>
41 {% endfor %}
42 </ul>
43 {% endif %}
44 </li>
45 {% endfor %}
46 </ul>
47 </nav>
48 </details>
49{% endif %}
50</section>
51
52<article role="article">
53{{ page.content | safe }}
54</article>
55{% endblock content %}
templates/wiki.html deleted −16
@@ -1,16 +0,0 @@
1{% extends "base.html" %}
2
3{% block content %}
4<h1>{{ section.title }}</h1>
5<p>Use <code>Ctrl + F</code> to search wiki pages for keywords.</p>
6<div class="wiki-post">
7 <p>Wiki Page</p>
8 <p>Last Updated</p>
9</div>
10{% for page in section.pages %}
11 <div class="wiki-post">
12 <p><a href="/wiki/{{ page.slug }}/">{{ page.title }}</a></p>
13 <time datetime="{{ page.updated | date(format='%Y-%m-%d %H:%M:%S')
14 }}">{{ page.updated | date(format='%Y-%m-%d')}}</p>
15 </div>
16{% endfor %} {% endblock content %}
static/.well-known/security.txt → theme/static/.well-known/security.txt renamed
static/gpg.txt → theme/static/gpg.txt renamed
static/robots.txt → theme/static/robots.txt renamed
static/salary.csv → theme/static/salary.csv renamed
static/styles.css → theme/static/styles.css renamed
static/styles.min.css → theme/static/styles.min.css renamed
theme/templates/base.html added +39
@@ -0,0 +1,39 @@
1<!doctype html>
2<html lang="en-us">
3<head>
4 {% block head %}
5 <meta charset="utf-8">
6 <title>
7 {% block subtitle %}{% endblock %}
8 {% block title %}{{ site_name | default("weblorg website") }}{% endblock %}
9 </title>
10 <meta name="viewport" content="width=device-width, initial-scale=1">
11 {% if site_owner is defined %}<meta name="author" content="{{ site_owner }}" >{% endif %}
12 {% if site_description is defined %}<meta name="description" content="{{ site_description }}" >{% endif %}
13 {% if site_keywords is defined %}<meta name="keywords" content="{{ site_keywords }}" >{% endif %}
14 <link rel="stylesheet" href="{{ url_for("static", file="styles.min.css") }}" type="text/css">
15 <link rel="icon" href="data:,">
16 {% block meta %}{% endblock %}
17 {% endblock %}
18</head>
19<body>
20 <nav class="site-nav" aria-label="site-nav" role="navigation">
21 <ul>
22 <li><a href="/">Home</a></li>
23 <li><a href="/blog/">Blog</a></li>
24 <li><a href="/services/">Services</a></li>
25 <li><a href="/wiki/">Wiki</a></li>
26 </ul>
27 </nav>
28 <main>{% block main %}{% endblock %}</main>
29 <footer>
30 <p>Last build: &lt;{{ now() | strftime("%Y-%m-%d %a %H:%M:%S") }}&gt;</p>
31 <p>
32 <a href="https://git.sr.ht/~cyborg/cleberg.net"
33 target="_blank" rel="noopener">Source Code</a>
34 &middot;
35 <a href="/feed.xml">RSS Feed</a>
36 </p>
37 </footer>
38</body>
39</html>
theme/templates/blog.html added +15
@@ -0,0 +1,15 @@
1{% extends "base.html" %} {% block subtitle %}Blog | {% endblock %} {% block
2main %}
3<h1>Blog</h1>
4<p>Use <code>Ctrl + F</code> to search blog post titles for keywords.</p>
5<p>
6 You can also add the <a href="/feed.xml">RSS Feed</a> to your feed reader.
7</p>
8{% for post in posts %}
9<div class="post">
10 <time datetime='{{ post.date | strftime("%Y-%m-%d") }}'
11 >{{ post.date|strftime("%Y-%m-%d") }}</time
12 >
13 <a href='{{ url_for("blog", slug=post.slug) }}'>{{ post.title }}</a>
14</div>
15{% endfor %} {% endblock %}
theme/templates/feed.xml added +37
@@ -0,0 +1,37 @@
1<?xml version="1.0" encoding="UTF-8" ?>
2<rss version="2.0"
3 xmlns:content="http://purl.org/rss/1.0/modules/content/"
4 xmlns:wfw="http://wellformedweb.org/CommentAPI/"
5 xmlns:dc="http://purl.org/dc/elements/1.1/"
6 xmlns:atom="http://www.w3.org/2005/Atom"
7 xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
8 xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
9 xmlns:georss="http://www.georss.org/georss"
10 xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
11 xmlns:media="http://search.yahoo.com/mrss/">
12 <channel>
13 <title>{{ site_name | default("cleberg.net") }}</title>
14 <atom:link
15 href="{{ url_for("rss") }}"
16 rel="self" type="application/rss+xml" />
17 <link>{{ url_for("index") }}</link>
18 <description><![CDATA[]]></description>
19 <language>en</language>
20 <pubDate>{{ posts | first | getattr("date") | strftime("%a, %d %b %Y %H:%M:%S %z") }}</pubDate>
21 <lastBuildDate>{{ now() | strftime("%a, %d %b %Y %H:%M:%S %z") }}</lastBuildDate>
22 <generator>{{ meta.generator }}</generator>
23 <webMaster>{{ site_owner | default("hello@cleberg.net") }}</webMaster>
24
25 {% for post in posts %}
26 <item>
27 <title>{{ post.title }}</title>
28 <link>{{ url_for("blog", slug=post.slug) }}</link>
29 <author>{{ post.author|default("hello@cleberg.net") }}</author>
30 <guid isPermaLink="false">{{ url_for("blog", slug=post.slug) }}</guid>
31 <pubDate>{{ post.date|strftime("%a, %d %b %Y %H:%M:%S %z") }}</pubDate>
32 <description><![CDATA[{{ post.html|safe }}]]></description>
33 </item>
34 {% endfor %}
35
36 </channel>
37</rss>
theme/templates/index.html added +60
@@ -0,0 +1,60 @@
1{% extends "base.html" %} {% block main %}
2<section>
3 <h1>cleberg.net</h1>
4 <pre>pub rsa4096 2022-11-16 [SC]
5 <a href="/gpg.txt">3917 973F B159 BBB8 6194 5385 6945 1A51 7AC0 CB37</a>
6uid [ultimate] Christian Cleberg &lt;hello@cleberg.net&gt;</pre>
7</section>
8<section>
9 <h2>Recent Blog Posts</h2>
10
11 <div class="post">
12 <time datetime="2024-07-11 20:24:02">2024-07-11</time>
13 <a href="/blog/emacs-on-ipad/">Emacs on iPadOS</a>
14 </div>
15
16 <div class="post">
17 <time datetime="2024-06-19 08:00:00">2024-06-19</time>
18 <a href="/blog/deprecated-trusted-gpg-fix/"
19 >Fixing Ubuntu Error: 'Key is stored in legacy trusted.gpg keyring'</a
20 >
21 </div>
22
23 <div class="post">
24 <time datetime="2024-05-03 00:00:00">2024-05-03</time>
25 <a href="/blog/ubuntu-on-macos/"
26 >Running Ubuntu Linux on macOS with OrbStack</a
27 >
28 </div>
29
30 <br />
31 <a href="/blog/">All Posts →</a>
32</section>
33
34<section>
35 <h2>Everything Else</h2>
36 <ul>
37 <li><a href="https://cv.cleberg.net">Curriculum Vitae</a></li>
38 <li><a href="/salary/">Salary</a></li>
39 <li><a href="/services/">Services</a></li>
40 <li><a href="/wiki/">Wiki</a></li>
41 </ul>
42</section>
43
44<section>
45 <h2>Contact</h2>
46 <ul>
47 <li>
48 hello [at] cleberg.net (<a href="https://cleberg.net/gpg.txt">GPG</a>)
49 </li>
50 <li>cyb0rg on <a href="https://libera.chat/">libera.chat</a></li>
51 <li>
52 <a href="https://matrix.to/#/@cyborg:matrix.tchncs.de"
53 >@cyborg:@matrix.tchncs.de</a
54 >
55 </li>
56 <li><a href="https://git.sr.ht/~cyborg/">Sourcehut</a></li>
57 </ul>
58</section>
59
60{% endblock %}
theme/templates/page.html added +11
@@ -0,0 +1,11 @@
1{% extends "base.html" %}
2
3{% block subtitle %}
4 {{ post.title }}
5{% endblock %}
6{% block main %}
7<article role="article">
8 <h1>{{ post.title }}</h1>
9 <section>{{ post.html | safe }}</section>
10</article>
11{% endblock %}
theme/templates/post.html added +21
@@ -0,0 +1,21 @@
1{% extends "base.html" %} {% block subtitle %} {{ post.title }} | {% endblock %}
2{% block meta %}
3<meta property="og:title" content="{{ post.title }}" />
4<meta property="og:url" content="{{ post.url }}" />
5{% if post.description is defined %}
6<meta property="og:description" content="{{ post.description }}" />
7{% elif project_description is defined %}
8<meta property="og:description" content="{{ project_description }}" />
9{% endif %} {% endblock %} {% block main %}
10<article role="article">
11 <section class="post-metadata">
12 <h1 class="post__title">{{ post.title }}</h1>
13 {% if post.date is defined %}
14 <time datetime='{{ post.date|strftime("%Y-%m-%d") }}'
15 >&lt;{{ post.date|strftime("%Y-%m-%d %a %H:%M:%S") }}&gt;</time
16 >
17 {% endif %}
18 </section>
19 <section>{{ post.html | safe }}</section>
20</article>
21{% endblock %}
theme/templates/wiki.html added +12
@@ -0,0 +1,12 @@
1{% extends "base.html" %} {% block subtitle %}Wiki | {% endblock %} {% block
2main %}
3<h1>Wiki</h1>
4<p>Use <code>Ctrl + F</code> to search wiki pages for keywords.</p>
5{% for post in posts %}
6<div class="post">
7 <time datetime='{{ post.date | strftime("%Y-%m-%d") }}'
8 >{{ post.date|strftime("%Y-%m-%d") }}</time
9 >
10 <a href='{{ url_for("wiki", slug=post.slug) }}'>{{ post.title }}</a>
11</div>
12{% endfor %} {% endblock %}
utils/nginx-dev.conf added +17
@@ -0,0 +1,17 @@
1server {
2 server_name cleberg.net www.cleberg.net;
3
4 root /usr/share/nginx/html;
5 index index.html;
6 autoindex on;
7
8 access_log /var/log/nginx/cleberg.net.access.log;
9 error_log /var/log/nginx/cleberg.net.error.log;
10
11 location / {
12 try_files $uri $uri/ =404;
13 }
14
15 listen [::]:80;
16 listen 80;
17}
salary_visualization.py → utils/salary_visualization.py renamed