feat: rewrite blog in zola

Signed-off-by: Chinmay D. Pai <chinmaydpai@gmail.com>
This commit is contained in:
Chinmay D. Pai 2021-05-27 23:42:46 +05:30
commit 6d929483e2
Signed by: thunderbottom
GPG Key ID: 75507BE256F40CED
58 changed files with 2332 additions and 0 deletions

37
.gitignore vendored Normal file
View File

@ -0,0 +1,37 @@
# Local .terraform directories
.terraform
# .tfstate files
*.tfstate
*.tfstate.*
# Crash log files
crash.log
# Exclude all .tfvars files, which are likely to contain sentitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
#
*.tfvars
# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Include override files you do wish to add to version control using negated pattern
#
# !example_override.tf
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*
# Ignore CLI configuration files
.terraformrc
terraform.rc
# Exclude zola generated static site folder
public/

39
config.toml Normal file
View File

@ -0,0 +1,39 @@
# The URL the site will be built for
base_url = "https://maych.in"
title = "Chinmay D. Pai"
description = "Musings on life, tech, and other interests"
# Whether to automatically compile all Sass files in the sass directory
compile_sass = true
# Whether to build a search index to be used later on by a JavaScript library
build_search_index = false
# Whether to generate a RSS feed automatically
generate_feed = true
# minify is broken for code blocks, works on the `next` branch
# minify_html = true
[markdown]
external_links_no_follow = true
external_links_no_referrer = true
# When set to "true", all code blocks are highlighted.
highlight_code = true
highlight_theme = "ayu-dark"
smart_punctuation = true
[extra]
navigation = [
{name = "Blog", url = "/blog"},
]
keywords = "zola, blog, technology, life, linux"
[link_checker]
# Skip anchor checking for external URLs that start with these prefixes
skip_anchor_prefixes = [
"https://github.com/",
]
[extra.author]
name = "Chinmay D. Pai"
[extra.author.telegram]
link = "https://t.me/thunderbottom"
username = "thunderbottom"

21
content/_index.md Normal file
View File

@ -0,0 +1,21 @@
+++
title = "Home"
[extra]
text_justify = true
+++
<h4 class="text-center"><i>"Technically a DevOps, practically an absurdist."</i></h4>
Hey there! I'm a systems and security enthusiast from Mumbai, India. I currently work at [Zerodha](https://zerodha.com) as a DevOps and Security Engineer, where I mainly solve problems pertaining to container orchestration, pipeline optimizations, and maintaining and enhancing the security and monitoring infrastructures.
My pursuits intrinsically gravitate towards *nix, open-source, privacy, and system infrastructure. In my free time, I try to explore new tech, play video games, read, and occasionally [compose music](https://soundcloud.com/harmonicseventhnoiseprotocol). You may also see what I'm up to right [now](/now). To keep updated with my work, follow me on [GitHub](https://github.com/Thunderbottom) or subscribe to the blog through the [RSS feed](/rss.xml).
_Jinsei wa daijoubudesu._
### Contact
- **Email**: chinmaydpai-at-[world's largest email service provider]
- **Social Media**: I go by the username `@Thunderbottom` on:
- [GitHub](https://github.com/Thunderbottom)
- [Lobsters](https://lobste.rs/u/Thunderbottom)
- [Telegram](https://t.me/Thunderbottom)

View File

@ -0,0 +1,164 @@
+++
title = "Introduction to TensorFlow Variables"
date = 2018-08-01T03:48:49+05:30
[extra]
tags = "machine learning, tensorflow"
+++
Hello there, today we are going to learn about TensorFlow variables. Just like Python variables, TensorFlow variables are used to store, reference, and manipulate data. Although functionally they are very different. This article will try to help you understand what a TensorFlow variable is and how it works.
<!-- more -->
{% message() %}
**Note**: Most of the API functions demonstrated in this post have been deprecated as of [TensorFlow v2](https://www.tensorflow.org/api_docs/python/tf). If you are new to TensorFlow, I suggest checking out the API v2 documentation instead. Follow the [TensorFlow migration guide](https://www.tensorflow.org/guide/migrate) if you wish to upgrade from API v1 to v2.
{% end %}
## Creating Tensorflow Variables
Creating and assigning variables in Python is really easy:
```py
In [1]: x = 5
In [2]: y = x * 3
In [3]: print(y)
15
```
Easy, right? Here's a catch though---Tensorflow variables initialization is a _bit_ different than this. In TensorFlow, you need to create a variable by using the [`tf.Variable`][1] constructor. The constructor takes the variable initialization value as a parameter and has an optional keyword parameter "name" to assign a internal name to the variable. This internal naming different than the actual variable name that you define during assignment. It comes in handy when you need to save and restore your variable instances while using TensorFlow.
Well talk about internal variable naming in a different post. Meanwhile, heres what the `tf.Variable` constructor syntax looks like:
```py
In [4]: import tensorflow as tf
In [5]: w = tf.Variable(<initial-value>, name=<optional-name>)
```
Lets try creating a Tensorflow variable:
```py
In [6]: x = tf.constant(5, name='x')
In [7]: y = tf.Variable(x * 3, name='y')
```
Here, weve created a [`tf.constant`][2] with a value 5. Mathematically, a constant is a fixed value that never changes. We then take this constant and use it to compute another variable y. Although quite the same thing as a Python variable, neither the syntax nor the functionality bears similarity to the variables that we defined before.
What is the difference though? Lets try printing the value of the variable y:
```python
In [8]: print(y)
Tensor("y/read:0", shape=(), dtype=int32)
```
Oddly enough, the variable y does not contain the expected output yet, as TensorFlow variable does not compute any values when initialized. This is because TensorFlow handles variables differently.
###### In what ways is a Tensorflow variable different?
Unlike Python variables, there is no computation taking place at runtime. Instead, TensorFlow takes as input all the variables and constants as functions, and stores them as tensors. Using these tensors, Tensorflow generates a computational graph which is then used to compute the output for the variables defined in the graph at the time of session execution.
###### What is a computational graph?
A computational graph is a group or series of related TensorFlow operations---Consider it as a data structure graph with nodes connected to each other through links, where each node may take zero or more tensors as input. Each connected node produces a tensor as an output and passes it up ahead to the next connected node as an input. This continues until it reaches the root node, or till the end of the computational graph.
###### What are tensors?
Tensors are nothing but matrix-like objects that describe relations between scalars, vectors and other tensors. Using these tensors, we can represent the computational output of any operation. From the documentation:
>"A Tensor is a symbolic handle to one of the outputs of an Operation. It does not hold the values of that operation's output, but instead provides a means of computing those values in a TensorFlow [`tf.Session`][3]"
Simply put, tensors are a type of data structure which do not hold any computational value in them. Instead, they contain information on how to compute the given values, or basically states the flow of tensors (_\*slow claps\*_). To actually transform these tensors into a graph (or graphs), we need to initialize the variables, which creates a tensor object of the graph. A tensor object is nothing but a set of operations which may have tensors at input and output.
Multiple such related tensor objects make up a computational graph. We then execute this graph in a session to calculate the value for the initialized variables, and the session needs to be executed each time the value of the variables is to be updated.
### Generating a Tensorflow computational graph
Now that we know what a computational graph is and what it is made up of, we need to generate a graph for all the variables in the code---as the variable `y` that we have defined is computationally dependent on the constant value `x`. For this we'll use [`tf.global_variables_initializer`][4]. This is a TensorFlow helper function which initializes all the variables by creating a graph of dependencies and relationships between the variables when executed.[^1] There also exists an alternative if you want to initialize only a select bunch of variables.[^2]
This helper function does not require any parameters to execute because it initializes all the global variables defined in the code.
Okay, now let's try initializing the defined variables:
```py
In [9]: import tensorflow as tf
In [10]: x = tf.constant(5, name='x')
In [11]: y = tf.Variable(x * 3, name='y')
In [12]: compute = tf.global_variables_initializer()
```
The code doesnt actually compute anything yet either. This is because we have only initialized the graph so far.
Let us check what the variable compute contains:
```py
In [13]: print(compute)
name: "init"
op: "NoOp"
input: "^Variable/Assign"
input: "^y/Assign"
```
Although the output doesn't really make much sense, it states that the compute variable contains a variable initializer function. This also means that we have generated the computational graph by initializing the variables. Now to make TensorFlow compute the value for our operation, we need to execute the computational graph and the variable y.
### Executing the TensorFlow computational graph
To execute a graph, we first need to create a TensorFlow session wherein the output for the initialized variables can be computed. A session in TensorFlow is an instance for executing initialized variables and other operations that one may wish to perform during the session. From the documentation:
>"A Session instance encapsulates the environment in which Operations in a Graph are executed to compute Tensors."
We can create a session with [`tf.Session`][3] and use it to execute the graph. We also need to explicitly close the session after were done using it. Doing so is considered to be a "good practice". For this we need to call `close()` on the session object after we're done with it. The session can also be automatically closed by creating the session using the [`with`](https://docs.python.org/3/reference/compound_stmts.html#with) keyword in Python. Then, we can compute the graph and execute the variable y.
```py
In [14]: with tf.Session() as sess:
... sess.run(compute)
... print(sess.run(y))
15
```
Here, we first create a TensorFlow session named `sess`, and run the compute variable---the variable which contains a function to initialize all the variables that we defined, and the variable `y`. It is not always required to create a variable for `tf.global_variables_initializer`, but has been done for ease and understanding in this blog post. Also, it is also not required that you run the initializer unless there is a TensorFlow variable or a placeholder defined in the code. As an example:
```py
In [15]: with tf.Session() as sess:
... print(sess.run(x))
5
```
_**Do we need to do this every time we create a session?** Frankly, the answer is both yes, and no._
We need to execute the variable every time it is updated, but we initialize the variables just once. It is not recommended to reinitialize the variables all over because doing so generates duplicate operations.[^3]
**_Can we update the variables in the session?_** _Yes, that can be done and is a pretty normal thing to do._
Lets try updating the variable out for the sake of completeness:
```py
In [16]: import tensorflow as tf
In [17]: a = tf.Variable(0, name='a')
In [18]: init = tf.global_variables_initializer()
In [19]: with tf.Session() as sess:
... sess.run(init)
... for i in range(5):
... a = a + i
... print(sess.run(a))
0
1
3
6
10
```
The variables can be updated during the session, and each time the variable is updated, we need to run it to compute the updated value. Once again, we do not need to initialize the variables every time we run the operations. This is because every time we run `tf.global_variable_initializer()` a duplicate graph for the same operation is initialized.
So that's it for this article, I hope this brief introduction helped you understand the basic functionality of TensorFlow variables.
##### References
TensorFlow documentation : [Variables: Creation, Initialization, Saving, and Loading](https://www.tensorflow.org/programmers_guide/variables)
[//]: # (URLs referenced in the page)
[1]: https://www.tensorflow.org/versions/r1.15/api_docs/python/tf/Variable "TensorFlow variable"
[2]: https://www.tensorflow.org/versions/r1.15/api_docs/python/tf/constant "TensorFlow constant"
[3]: https://www.tensorflow.org/versions/r1.15/api_docs/python/tf/Session "Tensorflow Session"
[4]: https://www.tensorflow.org/versions/r1.15/api_docs/python/tf/initializers/global_variables "Tensorflow Global Variables Initializer"
[^1]: [TensorFlow Helper Functions.](https://github.com/tensorflow/docs/blob/r1.10/site/en/api_guides/python/state_ops.md#variable-helper-functions)
[^2]: [TensorFlow Variables initializer: `tf.variables_initializer`.](https://www.tensorflow.org/versions/r1.15/api_docs/python/tf/initializers/variables)
[^3]: [KDNuggets: How not to program TensorFlow Graphs.](http://www.kdnuggets.com/2017/05/how-not-program-tensorflow-graph.html)

View File

@ -0,0 +1,77 @@
+++
title = "Installing Termite on Void Linux"
date = 2018-08-03T12:31:26+05:30
[extra]
tags = "void linux, termite, terminal, linux, systems"
+++
Termite is a VTE-based terminal emulator for GNU/Linux, which is available as a package on most Linux distribution repositories, but not on Void Linux. This is because termite uses a custom VTE build which isn't (and won't be) included in the void packages repository.[^void-packages]
In this post, I'll show you how to build and install termite on Void Linux.
<!-- more -->
{% message() %}
Even though termite is a pretty solid choice for a terminal emulator, I suggest checking out Alacritty[^alacritty], which is a GPU-accelerated terminal emulator and is [available](https://github.com/void-linux/void-packages/blob/master/srcpkgs/alacritty/template) in the Void Linux repository.
{% end %}
## Build Dependencies
We need to install some dependencies to set up the custom VTE build and compile termite. Luckily enough, all the required dependencies are available in the Void Linux repository.
```sh
$ sudo xbps-install -Sy git gcc make automake autoconf gtk-doc glib-devel \
vala-devel gobject-introspection pkg-config intltool \
gettext-devel gnutls gnutls-devel gtk+3 gtk+3-devel \
pango pango-devel gperf pcre2-devel
```
## Building VTE-ng
Make sure all of the dependencies have been installed before proceeding. We'll now be compiling a custom VTE build, VTE-ng. If you do not have a build directory yet, making one would help with better organization.
```sh
$ mkdir build && cd build
```
We then need to clone the VTE-ng git repository and checkout the latest versioned branch.
```sh
$ git clone https://github.com/jelly/vte-ng.git
$ cd vte-ng
$ git checkout 0.50.2-ng
```
It is now time to configure and build vte-ng. We need to add `--prefix=/usr` while configuring so that it installs the library to `/usr` and not `/usr/local`, which is not used by Void Linux. Unless you choose to add it to your `$PATH`, of course.
```sh
$ ./autogen.sh --prefix=/usr
$ make
$ sudo make install
```
That's it for the custom VTE build.
## Building Termite
To build and install termite, we need to go back to the build directory and clone the termite git repository. We'll clone it recursively as termite requires some git submodules.
```sh
$ cd $BUILDDIR
$ git clone --recursive https://github.com/thestinger/termite.git
$ cd termite
```
We do not need to configure anything in here since everything is hardcoded in the Makefile. Although we still need to edit the Makefile to make termite install to `/usr` and not `/usr/local`. We can do this using sed, and then finally make and install termite.
```sh
$ sed 's/PREFIX = \/usr\/local/PREFIX = \/usr/' -i Makefile
$ make
$ sudo make install
```
That's all. If everything goes well, you should end up with termite installed on your system.
[^void-packages]: [GitHub: On why termite won't be included in void-packages](https://github.com/void-linux/void-packages/issues/9769#issuecomment-472585514)
[^alacritty]: [Alacritty: A cross-platform, GPU-accelerated terminal emulator](https://github.com/alacritty/alacritty)

View File

@ -0,0 +1,254 @@
+++
title = "Installing Void Linux with Full-Disk Encryption"
date = 2018-08-21T21:13:46+05:30
[extra]
tags = "machine learning, tensorflow"
+++
A comprehensive guide to installing Void Linux on your system with <abbr title="Full-Disk Encryption">FDE</abbr>, including an encrypted boot partition.<!-- more -->
This guide serves as an extension to the official Void documentation for Full Disk Encryption[^void-fde] in an attempt to make the steps easier to follow.
## Prerequisites
Make sure you have a Void Linux installation disk ready to plug in and boot.[^prepare-install] This is also a good opportunity to back up your current installation and start afresh, as this tutorial is aimed towards a clean system install.
## Installation
First, we shall boot into the installation disk and login as root. When you login, you will see that the default shell for root on Void Linux is set to dash. I suggest switching to bash for convenience---although it is entirely up to you to do that.
### Partitioning the Drive
We shall format and create partitions for our new system using `parted`. We'll be creating these two basic and self-explanatory partitions:
* **[UEFI Only]** An unencrypted ESP.
* An encrypted main partition for everything else.
The <abbr title="EFI System Partition">ESP</abbr> consists of the bootloader and other things that we'll require to decrypt the main partition on startup and then boot the system. The main partition will be encrypted, consisting of all the remaining partitions including `/boot`.
In this guide, we'll be referring to the drive as `/dev/sdX`. You'll need to replace `X` in the commands with the appropriate drive you want to set up. The commands are pretty straight forward:
```sh
$ parted /dev/sdX mklabel gpt # set to "msdos" for legacy mode
$ parted -a optimal /dev/sdX mkpart primary 2048s 100M # 100% for legacy mode
$ parted -a optimal /dev/sdX mkpart primary 100M 100% # UEFI ESP Partition
$ parted /dev/sdX set 1 boot on
```
{% message() %}
**Note**: If your system's BIOS compatibility is set to Legacy mode, you are not required to create the ESP partition.
{% end %}
### Setting up LVM and LUKS for Encryption
We've created the basic partitions that we need for our system. Now we will install `cryptsetup` and `lvm2`. This will help us set up the <abbr title="Logical Volume Manager">LVM</abbr> partition and encrypt the drive using LUKS[^luks].
```sh
$ xbps-install -S cryptsetup lvm2
```
We'll be creating a LUKS encrypted partition using `cryptsetup`. For this guide, we'll be naming the LUKS partition `crypt-pool`. You may choose to replace it with whatever you wish. Also replace `X` and `N` in `/dev/sdXN` with the appropriate drive name and partition number.
Type in a strong password when requested.
```sh
$ # Replace N with 1 for Legacy, and 2 for UEFI
$ cryptsetup luksFormat -c aes-xts-plain64 -s 512 /dev/sdXN
$ cryptsetup luksOpen /dev/sdXN crypt-pool
```
We will now create a logical volume group, `vgpool`, inside `crypt-pool`. Again, the name for the volume group can be set to whatever you want. This volume group will consist of all our system partitions. You may also create additional partitions for `/home` or `/var` if you wish.
```sh
$ pvcreate /dev/mapper/crypt-pool
$ vgcreate vgpool /dev/mapper/crypt-pool
$ lvcreate -L 300M -n boot vgpool
$ lvcreate -C y -L <2x RAM size> -n swap vgpool
$ lvcreate -l 100%FREE -n root vgpool
```
This will create for you:
* a 300MB partition marked as `boot`.
* a \<2x RAM size\> partition marked as `swap`.
* remaining space marked as `root`.
These markings are just names for convenience and not actual mount points. We'll be defining the mount points for these partitions in a while.
Confirm the partition table you just created using `lvs`.
```sh
$ lvs -o lv_name,lv_size -S vg_name=vgpool
```
Next, we need to format and setup filesystems for our partitions. We also need to setup a `vfat` filesystem for the ESP on UEFI.
```sh
$ mkfs.vfat -F32 /dev/sdX1 # UEFI only!
```
Setup the remaining filesystems regardless of a UEFI or legacy system.
```sh
$ mkfs.ext4 -L boot /dev/mapper/vgpool-boot
$ mkfs.ext4 -L root /dev/mapper/vgpool-root
$ mkswap -L swap /dev/mapper/vgpool-swap
$ swapon /dev/mapper/vgpool-swap
```
### Installing Void Linux
We then mount the partitions and begin the installation process. We'll be using the markings we've defined previously to reference the partitions inside our volume group.
```sh
$ mount /dev/mapper/vgpool-root /mnt
$ mkdir /mnt/{dev,proc,sys,boot,home,var}
$ mount /dev/mapper/vgpool-boot /mnt/boot
```
If you're on an UEFI system, you also need to mount your ESP to `/boot/efi`
```sh
$ mkdir /mnt/boot/efi
$ mount /dev/sdX1 /mnt/boot/efi
```
We also need to mount some special filesystems from the live installation disk--- `/dev`, `/proc`, and `/sys`.
```sh
$ mount -t proc /proc /mnt/proc
$ mount -o bind /sys /mnt/sys
$ mount -o bind /dev /mnt/dev
```
Now finally, we can install Void Linux on our system. You'll need to add `grub-x86_64-efi` and `efibootmgr` to the install list for UEFI support.
```sh
$ xbps-install -Sy -R https://alpha.de.repo.voidlinux.org -r /mnt grub \
base-system lvm2 cryptsetup vim \
grub-x86_64-efi efibootmgr # UEFI Only!
```
This should install all the required things for our system to boot. Although, we're not done yet.
### Configuring the System
The first thing to configure on the newly installed system is to set the root password.
```sh
$ passwd -R /mnt root
```
This will ask you for a new root password for the system mounted at `/mnt`.
Next, we need to setup our system language, hostname, timezone, etc. You need to change the values as per your convenience.
```sh
$ echo "YOUR-HOSTNAME" > /mnt/etc/hostname
$ echo "TIMEZONE=Europe/Zurich" >> /mnt/etc/rc.conf
$ echo "KEYMAP=us" >> /mnt/etc/rc.conf
$ echo "TTYS=2" >> /mnt/etc/rc.conf
$ echo "LANG=en_US.UTF8"
$ echo "en_US.UTF8 UTF8" >> /mnt/etc/default/libc-locales
$ chroot /mnt xbps-reconfigure -f glibc-locales
```
We then need to add the filesystems entries to `/mnt/etc/fstab`. I expect you to responsibly replace all the values in the configuration below, as per your system's configuration.
```
/dev/mapper/vgpool-boot /boot ext4 defaults 0 2
/dev/mapper/vgpool-swap none swap sw 0 0
/dev/mapper/vgpool-root / ext4 defaults 0 1
tmpfs /tmp tmpfs tsize=1G,defaults,nodev,nosuid 0 0
```
We also need to add the ESP to the fstab for UEFI systems.
```
/dev/sdX1 /boot/efi vfat defaults 0 0
```
We now need to `chroot`[^chroot] into our newly installed system and set up the bootloader.
```sh
$ chroot /mnt /bin/bash
```
This should put you in a shell inside your newly installed system.
We now need to create a keyfile to automatically decrypt the encrypted partition on booting up the system. Not doing so will require you to type the password twice on UEFI systems. This is because only the `/boot/efi` is unencrypted, and the `/boot` partition still needs to be decrypted to continue booting. You then need to enter the password again to decrpt the rootfs.
To save yourself from the hassle, it is better to have the boot partition auto-decrypt using the keyfile, and enter the password once for rootfs. Generate the keyfile and set permissions to it:
```sh
$ dd bs=512 count=4 if=/dev/urandom of=/crypto_keyfile.bin
$ cryptsetup luksAddKey /dev/sdXN /crypto_keyfile.bin
$ chmod 000 /crypto_keyfile.bin
$ chmod -R g-rwx,o-rwx /boot
```
And then we need to add this keyfile to `/etc/crypttab`. This will ensure that the system uses the keyfile on boot to decrypt the `/boot` partition.
To do so, we need to first note down the UUID of the encrypted disk. Replace `X` and `N` with the drive name and the partition number.
```sh
$ lsblk -o NAME,UUID | grep sdXN | awk '{print $2}'
```
We then need to add the disk UUID and the key to `/etc/crypttab`:
```
luks-<disk UUID here> /dev/sdXN /crypto_keyfile.bin luks
```
We need to notify `dracut` about the crypttab. Dracut is the tool used to generate initramfs images.[^dracut] This is also a good time to set `hostonly=yes` for dracut. This will ensure that dracut generates initramfs only for the current system (host) instead of generating a generic image. We will put the two things in separate files for better organization.
```sh
$ mkdir -p /etc/dracut.conf.d/
$ echo 'hostonly=yes' > /etc/dracut.conf.d/00-hostonly.conf
$ echo 'install_items+="/etc/crypttab /crypto_keyfile.bin"' > /etc/dracut.conf.d/10-crypt.conf
```
In the next step, we enable encryption support inside GRUB and make the bootloader aware of the LUKS encrypted disk.
```sh
$ echo "GRUB_PRELOAD_MODULES=\"cryptodisk luks\"" >> /etc/default/grub
$ echo "GRUB_ENABLE_CRYPTODISK=y" >> /etc/default/grub
$ echo "GRUB_CMDLINE_LINUX=\"cryptdevice=/dev/sdXN rd.luks.crypttab=1 rd.md=0 rd.dm=0 rd.lvm=1 rd.luks=1 rd.luks.allow-discards rd.luks.uuid=<device-UUID>\"" >> /etc/default/grub
```
The only thing that remains is to install the bootloader and then reconfigure the kernel:
```sh
$ grub-mkconfig -o /boot/grub/grub.cfg
$ grub-install /dev/sdX
$ xbps-reconfigure -f $(xbps-query -s linux4 | cut -f 2 -d ' ' | cut -f 1 -d -)
```
### Coda
That's it. Now all you have to do is close the volume group and the LUKS disk, reboot, and pray that it boots right into the system.
```sh
$ exit && umount -R /mnt
$ vgchange -a n vgpool
$ cryptsetup luksClose crypt-pool
$ reboot
```
At this point, I'd like to point you to the [Configuration Guide](https://docs.voidlinux.org/config/index.html) for further setup and call it a day.
[^void-fde]: [Void Linux: Full Disk Encryption](https://docs.voidlinux.org/installation/guides/fde.html)
[^prepare-install]: [Void Linux: Prepare Installation Media](https://docs.voidlinux.org/installation/live-images/prep.html)
[^luks]: [Linux Unified Key Setup](https://en.wikipedia.org/wiki/Linux_Unified_Key_Setup)
[^chroot]: [chroot: arch wiki](https://wiki.archlinux.org/index.php/Chroot)
[^dracut]: [dracut: man page](https://linux.die.net/man/8/dracut)

9
content/blog/_index.md Normal file
View File

@ -0,0 +1,9 @@
+++
title = "Blog"
template = "blog.html"
sort_by = "date"
paginate_by = 5
insert_anchor_links = "left"
[extra]
hide_title = true
+++

23
content/now/_index.md Normal file
View File

@ -0,0 +1,23 @@
+++
title = "Now"
path = "/now"
[extra]
text_justify = true
+++
### Personal
- Setting up a server infrastructure running <abbr title="Nomad, Vault, Consul, and Terraform">Hashistack</abbr>
- Building a [Pritunl](https://pritunl.com) SSH renewal client in Go
- Learning and practising [Stoicism](https://plato.stanford.edu/entries/stoicism/)
### Reading
- [The Skeptic's Guide to the Universe](https://www.goodreads.com/book/show/38485991-the-skeptics-guide-to-the-universe), by Steven Novella
- [Range](https://www.goodreads.com/book/show/41795733-range), by David Epstein
### Playing
- Hollow Knight
- Kentucky Route Zero
- Stardew Valley

10
deploy/Dockerfile Normal file
View File

@ -0,0 +1,10 @@
FROM debian:buster-slim as site-builder
COPY --from=balthek/zola:0.13.0 /usr/bin/zola /usr/bin/zola
WORKDIR /build-site
COPY . /build-site
RUN /usr/bin/zola build
FROM nginx:stable-alpine
COPY --from=site-builder /build-site/public /usr/share/nginx/html

View File

@ -0,0 +1,20 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/nomad" {
version = "1.4.14"
constraints = "1.4.14"
hashes = [
"h1:GxsjoJKg/PWeXYzpzoONBQiaGnY+bPEDDD+BsEDgc8Q=",
"zh:036cc8e0c1c6c2f91573149910eca29a7107b3415536eabeb2581861525da64a",
"zh:1414e2deb87af66a47e44ab5472b4606294cf511722beae2c0a3680041d66635",
"zh:623184a22b347fa5b696d3fbee35f5bff9ed30fbc8b067715c52b6300d655789",
"zh:7a026a57148a7c2e8a08a83c3641898911a7d9998c38eb2c6ca634107ccf49f9",
"zh:87d34e879284453b2ac825f8bb9c88c85027d404b1b9fa445ec97b519dfa59cb",
"zh:90591119307c2f3dd15a6a78964731689444fb1ce3d393eddf83e05a2f187b80",
"zh:b2cbf5e4d4f2d500804e7f1968b3fd2cebd4b164ccf76d7cb2c99ed1eb23957e",
"zh:d5f19ab3d0d172be8af098bb62b47667c632af736c60d1acab0fc1c31dbbcb99",
"zh:ee5f7f75a642eed607d4824b5888e4aacfc4dd435d54d9523d8f8165695d52a1",
"zh:f6300309339221a5f0863bec32d96b38a8e545c5a87b43c5bb8c65d2ff0492ed",
]
}

View File

@ -0,0 +1,49 @@
job "maychin-site" {
datacenters = ["asgard", "alfheim"]
type = "service"
group "site" {
count = 1
network {
mode = "bridge"
port "http" {
to = 80
}
}
restart {
attempts = 2
interval = "2m"
delay = "30s"
mode = "fail"
}
service {
name = "maychin-http"
port = "http"
check {
type = "http"
port = "http"
path = "/"
interval = "5s"
timeout = "2s"
}
}
task "maychin" {
driver = "docker"
config {
image = "thunderbottom/site:latest"
ports = ["http"]
}
resources {
cpu = 100
memory = 100
}
}
}
}

7
deploy/nomad/job.tf Normal file
View File

@ -0,0 +1,7 @@
resource "nomad_job" "site" {
jobspec = file("${path.module}/config/site.nomad")
hcl2 {
enabled = true
}
}

21
deploy/nomad/providers.tf Normal file
View File

@ -0,0 +1,21 @@
terraform {
backend "s3" {
bucket = "terraform-deku-moe"
key = "maychin-site"
region = "deku"
skip_credentials_validation = true
skip_metadata_api_check = true
skip_region_validation = true
force_path_style = true
}
required_providers {
nomad = {
source = "hashicorp/nomad"
version = "1.4.14"
}
}
}
provider "nomad" {
address = "http://localhost:4646"
}

57
sass/_anchor.scss Normal file
View File

@ -0,0 +1,57 @@
/**
* Link placement and hover behavior.
*/
.zola-anchor {
color: $link-color !important;
text-decoration: none !important; /* do not underline */
border-bottom: none;
font-style: normal;
font-variant: normal;
font-weight: normal;
font-stretch: normal;
font-size: .5em;
line-height: inherit;
font-size-adjust: none;
font-kerning: auto;
font-optical-sizing: auto;
font-language-override: normal;
font-feature-settings: normal;
font-variation-settings: normal;
position: absolute;
margin-left: -2.5em;
// padding-right: 0.5em;
padding-top: 0.5em;
opacity: 0;
background-color: transparent;
&:hover,
&:focus {
background-color: inherit;
color: inherit;
}
}
@media (max-width: $min-width-breakpoint ) {
/* Do not display AnchorJS icon on less than 768px view point */
.zola-anchor {
display: none;
}
}
h2, h3, h4, h5, h6 {
&:hover > .zola-anchor {
opacity: .75;
/* To fade links as they appear, change transition-property from 'color' to 'all' */
-webkit-transition: color .16s linear;
transition: color .16s linear;
}
&:hover > .zola-anchor:hover,
&:hover > .zola-anchor:focus {
text-decoration: none !important; /* do not underline */
opacity: 1;
}
}

81
sass/_base.scss Normal file
View File

@ -0,0 +1,81 @@
html,
body {
margin: 0;
}
html {
font-family: $font-family;
font-size: $font-size;
line-height: $line-height;
@media (min-width: $min-width-breakpoint) {
font-size: 20px;
}
@media screen and (prefers-reduced-motion: no-preference) {
scroll-behavior: smooth;
}
}
body {
padding: $body-padding;
color: $font-color;
background-color: $background-color;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
a {
color: $link-color;
text-decoration: none;
border-bottom: 1px solid $link-color;
&:hover,
&:focus {
background-color: $link-hover-color;
color: $link-color-masthead; // white
}
code, strong {
color: inherit !important;
}
}
img {
display: block;
max-width: 100%;
margin: 0 0 1rem;
border-radius: 5px;
}
table {
margin-bottom: 1rem;
width: 100%;
font-size: 85%;
border: 1px solid $table-border-color;
border-collapse: collapse;
}
.js-file-line-container td {
border: none;
}
td,
th {
padding: .25rem .5rem;
border: 1px solid $table-border-color;
}
th {
text-align: left;
}
tbody tr:nth-child(even) td,
tbody tr:nth-child(even) th {
background-color: $table-background-even;
}
tbody tr:nth-child(odd) td,
tbody tr:nth-child(odd) th {
background-color: $table-background-odd;
}

9
sass/_box-sizing.scss Normal file
View File

@ -0,0 +1,9 @@
html {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}

30
sass/_code.scss Normal file
View File

@ -0,0 +1,30 @@
code, pre {
font-family: "PT Mono", Menlo, Monaco, "Courier New", monospace;
}
p code {
padding: .25em .5em;
font-size: .75em;
color: $code-color;
background-color: $code-background-color;
border: 1px solid $code-border-color;
}
pre {
border-radius: $border-radius;
margin-top: 0;
margin-bottom: 1rem;
padding: 0.75rem;
font-size: 85%;
scrollbar-color: $scrollbar-color darken($scrollbar-color, 50);
scrollbar-width: thin;
pre::-webkit-scrollbar-thumb {
background-color: $scrollbar-color;
}
pre::-webkit-scrollbar {
height: 0.25rem;
background-color: darken($scrollbar-color, 50);
}
}

258
sass/_layout.scss Normal file
View File

@ -0,0 +1,258 @@
@mixin after-border($margin:2rem auto) {
&:after {
display: block;
content: "";
width: 150px;
height: 1px;
margin: $margin;
background-color: $divider-color;
}
}
@mixin container() {
max-width: $min-width-breakpoint;
margin-left: auto;
margin-right: auto;
}
.image-row {
display: flex;
flex-flow: row wrap;
align-items: center;
padding: 0 4px;
}
.image-column {
flex: 50%;
max-width: 50%;
padding: 0 4px;
border-radius: 8px;
width: 100%;
height: auto;
}
@media screen and (max-width: $min-width-breakpoint) {
.image-column {
flex: 100%;
max-width: 100%;
}
}
.text-center {
text-align: center;
}
.text-justify {
@media (min-width: $min-width-breakpoint) {
text-align: justify;
}
}
.text-right {
text-align: right;
}
.fa-social-icons {
font-size: 2rem;
margin-top: -1rem;
a {
border: 0;
margin-right: 0.1rem;
&:last-child {
margin-right: 0;
}
}
}
.masthead, .content, .footer {
@include container();
}
.masthead, .post, .related, .recent, .page, .comments, .index {
@include after-border();
}
.related, .recent {
.related-title, .recent-title {
margin-top: 0;
}
}
.masthead {
margin-bottom: 2rem;
a {
color: $link-color-masthead;
}
@media screen and (max-width: $min-width-breakpoint) {
text-align: center !important;
float: none !important;
}
.masthead-title {
margin: 0;
padding: 0;
float: left;
text-align: left;
a {
text-decoration: none;
border: 0;
}
small {
display: inline-block;
font-family: $font-family;
font-weight: lighter;
font-style: italic;
font-size: 75%;
}
@media screen and (max-width: $min-width-breakpoint) {
float: inherit;
text-align: inherit;
}
}
.masthead-nav {
display: inline-block;
padding-left: 0;
margin-bottom: 0;
font-family: $header-font-family;
font-weight: 600;
a {
text-decoration: none;
border: 0;
& + a {
margin-left: 0.6rem;
}
}
@media screen and (max-width: $min-width-breakpoint) {
padding-top: 1rem;
}
}
padding-top: 1rem;
text-align: right;
}
.footer {
margin-bottom: 1rem;
text-align: center;
a[rel="license"]:first-child {
display: inline-block;
margin: 0;
padding: 0;
border: 0;
text-decoration: none;
}
.icon-square {
color: transparent;
}
.icon-square:hover {
color: #252525;
}
.fa-stack-1x {
color:black;
}
p {
>span {
font-size: 110%;
a {
padding: 2px;
border: 0;
}
i:hover {
color: white;
}
}
p:last-child {
margin-bottom: 0;
}
}
}
.posts {
.posts-item {
margin-bottom: 0.2rem;
@media (min-width: $min-width-breakpoint) {
margin-bottom: 1rem;
text-align: justify;
}
.posts-item-header {
@media (min-width: $min-width-breakpoint) {
display: flex;
align-items: center;
overflow: hidden;
justify-content: space-between;
}
.post-title {
a {
color: inherit;
text-decoration: none;
}
@media screen and (max-width: $min-width-breakpoint) {
margin-bottom: 0.5rem;
}
}
.post-date {
color: $post-date-color;
font-style: italic;
@media (min-width: $min-width-breakpoint) {
margin-top: 1rem;
margin-left: 1rem;
white-space: nowrap;
}
}
}
}
a {
border: 0;
}
}
.next, .previous {
font-weight: 600;
font-size: 20px;
transition: transform 0.3s ease-out;
}
.next {
float: right;
&:hover {
transform: translateX(4px);
}
}
.previous {
float: left;
&:hover {
transform: translateX(-4px);
}
}

12
sass/_message.scss Normal file
View File

@ -0,0 +1,12 @@
// Messages
//
// Show alert messages to users. You may add it to single elements like a `<p>`,
// or to a parent if there are multiple elements to show.
.message {
margin-bottom: 1rem;
padding: 1rem;
color: $message-color;
background-color: $message-background-color;
border-radius: $border-radius;
}

427
sass/_normalize.scss Executable file
View File

@ -0,0 +1,427 @@
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: $font-fallback; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined for any HTML5 element in IE 8/9.
* Correct `block` display not defined for `details` or `summary` in IE 10/11
* and Firefox.
* Correct `block` display not defined for `main` in IE 11.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
display: block;
}
/**
* 1. Correct `inline-block` display not defined in IE 8/9.
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
*/
audio,
canvas,
progress,
video {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address `[hidden]` styling not present in IE 8/9/10.
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
*/
[hidden],
template {
display: none;
}
/* Links
========================================================================== */
/**
* Remove the gray background color from active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* Text-level semantics
========================================================================== */
/**
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/**
* Address styling not present in Safari and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address variable `h1` font-size and margin within `section` and `article`
* contexts in Firefox 4+, Safari, and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/**
* Address styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* Embedded content
========================================================================== */
/**
* Remove border when inside `a` element in IE 8/9/10.
*/
img {
border: 0;
}
/**
* Correct overflow not hidden in IE 9/10/11.
*/
svg:not(:root) {
overflow: hidden;
}
/* Grouping content
========================================================================== */
/**
* Address margin not present in IE 8/9 and Safari.
*/
figure {
margin: 0px;
}
/**
* Address differences between Firefox and other browsers.
*/
hr {
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
/**
* Contain overflow in all browsers.
*/
pre {
overflow: auto;
}
/**
* Address odd `em`-unit font size rendering in all browsers.
*/
code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
/* Forms
========================================================================== */
/**
* Known limitation: by default, Chrome and Safari on OS X allow very limited
* styling of `select`, unless a `border` property is set.
*/
/**
* 1. Correct color not being inherited.
* Known issue: affects color of disabled elements.
* 2. Correct font properties not being inherited.
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
*/
button,
input,
optgroup,
select,
textarea {
color: inherit; /* 1 */
font: inherit; /* 2 */
margin: 0; /* 3 */
}
/**
* Address `overflow` set to `hidden` in IE 8/9/10/11.
*/
button {
overflow: visible;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
* Correct `select` style inheritance in Firefox.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* Remove inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
input {
line-height: normal;
}
/**
* It's recommended that you don't attempt to style these elements.
* Firefox's implementation doesn't respect box-sizing, padding, or width.
*
* 1. Address box sizing set to `content-box` in IE 8/9/10.
* 2. Remove excess padding in IE 8/9/10.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
* `font-size` values of the `input`, it causes the cursor style of the
* decrement button to change from `default` to `text`.
*/
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
* Safari (but not Chrome) clips the cancel button when the search input has
* padding (and `textfield` appearance).
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct `color` not being inherited in IE 8/9/10/11.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* Remove default vertical scrollbar in IE 8/9/10/11.
*/
textarea {
overflow: auto;
}
/**
* Don't inherit the `font-weight` (applied by a rule above).
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
*/
optgroup {
font-weight: bold;
}
/* Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}

52
sass/_posts.scss Normal file
View File

@ -0,0 +1,52 @@
.post {
.post-header {
margin-bottom: 2rem;
.post-title {
a {
color: inherit;
text-decoration: none;
&:hover,
&:focus {
background-color: $link-hover-color;
color: $link-color-masthead; // white
}
}
margin-top: 0.3rem;
margin-bottom: 0.4rem;
}
}
p {
@media (min-width: $min-width-breakpoint) {
text-align: justify;
}
}
}
.page {
.page-header {
margin-bottom: 2rem;
.page-title {
margin-bottom: 0.3rem;
}
}
.pagination {
clear: both;
}
}
.post-date {
font-size: 85%;
color: $post-date-color;
}
.index {
margin: 2rem;
}
div#tippin-button {
display: inline-block;
margin-left: 15px;
}

133
sass/_type.scss Normal file
View File

@ -0,0 +1,133 @@
h1, h2, h3, h4, h5, h6, summary {
margin-bottom: 1.2rem;
font-family: $header-font-family;
font-weight: bold;
line-height: 1.25;
color: $heading-color;
text-rendering: optimizeLegibility;
}
h1 {
margin-top: 2.5rem;
font-size: 2rem;
}
h2 {
margin-top: 2.2rem;
font-size: 1.5rem;
}
h3 {
margin-top: 1.6rem;
font-size: 1.25rem;
}
h4, h5, h6 {
margin-top: 1.2rem;
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1.5rem;
}
li > p {
margin-bottom: 0.5rem;
}
strong {
color: $strong-color;
}
ul, ol, dl {
margin-top: 0;
margin-bottom: 1rem;
@media (max-width: $min-width-breakpoint) {
padding-left: 1rem;
}
}
dt {
font-weight: bold;
}
dd {
margin-bottom: .5rem;
}
small {
color: $small-color;
}
hr {
position: relative;
margin: 1.5rem 0;
border: 0;
border-top: 1px solid $divider-color;
border-bottom: 1px solid #fff;
}
abbr {
font-weight: bold;
color: $abbr-color;
text-decoration-color: $abbr-border-color;
&[title] {
cursor: help;
border-bottom: 2px dotted $abbr-border-color;
}
}
blockquote {
padding: .5rem 1rem;
margin: .8rem 0;
color: $blockquote-color;
border-left: .25rem solid $blockquote-border-color;
p:last-child {
margin-bottom: 0;
}
a {
color: $blockquote-color;
}
@media (min-width: $min-width-breakpoint) {
padding-right: 5rem;
padding-left: 1.25rem;
}
}
sup[id^="fnref:"] {
border-bottom: 1px solid #000;
}
a[href^="#fn:"],
a[href^="#fnref:"] {
display: inline-block;
margin-left: .1rem;
font-weight: bold;
border-bottom: 0;
}
.footnote-definition {
margin-top: 2rem;
font-size: 85%;
.footnote-definition-label {
font-weight: 600;
margin: 0 0.25rem;
}
p {
display: inline-block;
margin-bottom: 0 !important;
}
}
.lead {
font-size: 1.25rem;
font-weight: 300;
}

46
sass/_variables.scss Normal file
View File

@ -0,0 +1,46 @@
$font-fallback: 'Helvetica Neue', 'sans-serif';
$font-family: 'PT Serif', $font-fallback;
$header-font-family: 'Playfair Display', $font-fallback;
$font-size: 22px;
$line-height: 1.6;
$min-width-breakpoint: 42em;
$body-padding: 0.75rem 1.5rem;
$font-color: #555;
$background-color: #fffffa;
$heading-color: darken($font-color, 12);
$strong-color: lighten($heading-color, 5);
$small-color: lighten($font-color, 25);
$divider-color: lighten($small-color, 25);
// $link-color: #1c7ed6;
$link-color: #1c76dd;
$link-hover-color: #dee2e6;
$link-color-masthead: darken($font-color, 20);
$message-background-color: #fff5b2;
$message-color: $font-color;
$scrollbar-color: darken($link-hover-color, 10);
$table-border-color: $divider-color;
$table-background-even: #fff;
$table-background-odd: darken($table-background-even, 2);
$abbr-color: lighten($link-color-masthead, 8);
$abbr-border-color: $divider-color;
$blockquote-color: $small-color;
$blockquote-border-color: $divider-color;
$code-background-color: darken(#fff, 3);
// $code-color: #bf616a;
$code-color: #E43F6F;
$code-border-color: darken($code-background-color, 8);
$border-radius: .25rem;
$post-date-color: $small-color;

5
sass/default.scss Normal file
View File

@ -0,0 +1,5 @@
@import "variables";
@import "normalize";
@import "box-sizing";
@import "layout";
@import "base";

9
sass/style.scss Normal file
View File

@ -0,0 +1,9 @@
@import "variables";
@import "type";
@import "posts";
@import "code";
@import "message";
@import "anchor";
@import url('fonts/playfair-display.css');
@import url('fonts/ptserif.css');
@import url('fonts/ptmono.css');

41
static/feed.xslt.xml Normal file
View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<xsl:transform version="1.0"
xmlns:a="http://www.w3.org/2005/Atom"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output method="text"/>
<xsl:template match="*"/>
<xsl:template match="a:feed">
<xsl:text>Atom Feed: </xsl:text><xsl:value-of select="a:id"/>
<xsl:text>&#10;</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="a:entry">
<xsl:text> ----------------------------------------&#10;</xsl:text>
<xsl:text> Feed entry: </xsl:text><xsl:value-of select="a:id"/>
<xsl:text>&#10;</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="a:title">
<xsl:if test="parent::a:entry">
<xsl:value-of select="' '"/>
</xsl:if>
<xsl:value-of select="local-name()"/>: <xsl:apply-templates/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="a:published|a:updated">
<xsl:if test="parent::a:entry">
<xsl:value-of select="' '"/>
</xsl:if>
<xsl:value-of select="local-name()"/>: <xsl:apply-templates/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
</xsl:transform>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,32 @@
/* playfair-display-regular - latin */
@font-face {
font-family: 'Playfair Display';
font-style: normal;
font-weight: 400;
font-display: auto;
src: local(''),
url('playfair-display-v22-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
url('playfair-display-v22-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
}
/* playfair-display-600 - latin */
@font-face {
font-family: 'Playfair Display';
font-style: normal;
font-weight: 600;
font-display: auto;
src: local(''),
url('playfair-display-v22-latin-600.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
url('playfair-display-v22-latin-600.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
}
/* playfair-display-italic - latin */
@font-face {
font-family: 'Playfair Display';
font-style: italic;
font-weight: 400;
font-display: auto;
src: local(''),
url('playfair-display-v22-latin-italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
url('playfair-display-v22-latin-italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

9
static/fonts/ptmono.css Normal file
View File

@ -0,0 +1,9 @@
/* pt-mono-regular - latin */
@font-face {
font-family: 'PT Mono';
font-style: normal;
font-weight: 400;
src: local('PT Mono'), local('PTMono-Regular'),
url('pt-mono-v7-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
url('pt-mono-v7-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
}

19
static/fonts/ptserif.css Normal file
View File

@ -0,0 +1,19 @@
/* pt-serif-regular - latin */
@font-face {
font-family: 'PT Serif';
font-style: normal;
font-weight: 400;
src: local('PT Serif'), local('PTSerif-Regular'),
url('pt-serif-v11-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
url('pt-serif-v11-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
}
/* pt-serif-italic - latin */
@font-face {
font-family: 'PT Serif';
font-style: italic;
font-weight: 400;
src: local('PT Serif Italic'), local('PTSerif-Italic'),
url('pt-serif-v11-latin-italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
url('pt-serif-v11-latin-italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
static/icons/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

7
templates/404.html Normal file
View File

@ -0,0 +1,7 @@
{% extends "index.html" %}
{% block title %}Page not found{% endblock title %}
{% block content %}
Sorry, we've misplaced that URL or it's pointing to something that doesn't exist. <a href="{{ config.base_url }}/">Head back home</a> to try finding it again.
{% endblock content %}

View File

@ -0,0 +1 @@
<a class="zola-anchor" href="#{{ id }}" aria-label="Anchor link for: {{ id }}"><small>[link]</small></a>

48
templates/atom.xml Normal file
View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xml" href="/feed.xslt.xml"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="{{ lang }}">
<title>{{ config.title }}
{%- if term %} - {{ term.name }}
{%- endif -%}
</title>
{%- if config.description %}
<subtitle>{{ config.description }}</subtitle>
{%- endif %}
<link href="{{ feed_url | safe }}" rel="self" type="application/atom+xml"/>
<link href="{{ config.base_url | safe }}" rel="alternate" type="text/html" />
<generator uri="https://www.getzola.org/">Zola</generator>
<updated>{{ last_updated | date(format="%a, %d %b %Y %H:%M:%S %z") }}</updated>
<id>{{ feed_url | safe }}</id>
{% if config.extra.author %}
<author>
<name>{{ config.extra.author.name }}</name>
{% if config.extra.author.email %}
<email>{{ config.extra.author.email }}</email>
{% endif %}
{% if config.extra.author.uri %}
<uri>{{ config.extra.author.uri }}</uri>
{% endif %}
</author>
{% endif %}
{%- for page in pages | filter(attribute="draft", value=false) %}
<entry xml:lang="{{ page.lang }}">
<title>{{ page.title }}</title>
<link href="{{ page.permalink | safe }}" type="text/html"/>
<published>{{ page.date | date(format="%a, %d %b %Y %H:%M:%S %z") }}</published>
<updated>{{ page.updated | default(value=page.date) | date(format="%a, %d %b %Y %H:%M:%S %z") }}</updated>
<id>{{ page.permalink | safe }}</id>
<content type="html" xml:base="{{ page.permalink | safe }}">{{ page.content }}</content>
{% if page.extra.tags %}
<category term="{{ page.extra.tags }}" />
{% endif %}
{% if page.summary %}
<summary type="html">{{ page.summary | striptags }}</summary>
{% endif %}
</entry>
{%- endfor %}
</feed>

50
templates/blog.html Normal file
View File

@ -0,0 +1,50 @@
{% extends "section.html" %}
{% import "macros.html" as macros %}
{% block seccontent %}
{% for post in paginator.pages | filter(attribute="draft", value=false) %}
{% if loop.first %}
<h2 class="text-right">{{post.year}}</h2>
<nav class="posts">
{% endif %}
<div class="posts-item">
<div class="posts-item-header">
<h2 class="post-title"><a href="{{ post.path | safe }}">{{ post.title }}</a></h2>
<time class="post-date" datetime="{{ post.date | date(format="%Y-%m-%dT%H:%M:%S") }}">
{{ post.date | date(format="%B %d") }}
· {{ macros::read_time(words=post.word_count) }}
</time>
</div>
{% if post.extra.image %}
{% set basepath = post.path | trim_start_matches(pat="/") %}
<img src="{{ resize_image(path= basepath ~ post.extra.image,
width=1600, height=1600, op="fit" ) }}" />
{% endif %}
<p>{{ post.summary | striptags | replace(from="[link]", to="") | safe }}</p>
</div>
{% if loop.last %}
</nav>
{% else %}
{% set next_year = loop.index0+1 %}
{% set previous_year = paginator.pages[next_year].year %}
{% if post.year != previous_year %}
</nav>
<h2 class="text-right">{{previous_year}}</h2>
<nav class="posts">
{% endif %}
{% endif %}
{% endfor %}
{% block pagination %}
<nav class="pagination">
{% if paginator.previous %}
<a class="previous" href="{{ paginator.previous }}"> Previous</a>
{% endif %}
{% if paginator.next %}
<a class="next" href="{{ paginator.next }}">Next </a>
{% endif %}
</nav>
{% endblock pagination %}
{% endblock seccontent %}

102
templates/index.html Normal file
View File

@ -0,0 +1,102 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<!-- Enable responsiveness on mobile devices-->
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Permissions-Policy" content="interest-cohort=()"/>
<title>
{% block title %}
{{ section.title }} &middot; {{ config.title }}
{% endblock title %}
</title>
{% block description %}
<meta name="description" content="{{ config.title }} &middot; {{ config.description }}">
<meta name="keywords" content="{{ config.extra.keywords }}">
{% endblock description %}
{% block meta %}
<meta name="author" content="{{ config.extra.author.name }}">
{% if current_url %}
<link href="{{ current_url }}" rel="canonical">
{% else %}
<link href="/404.html" rel="canonical">
{% endif %}
<link rel="alternate" type="application/atom+xml" title="RSS" href="{{ get_url(path=config.feed_filename) | safe }}">
{% endblock meta %}
{% block og_card %}
<meta property="og:type" content="article">
{% if current_url %}
<meta property="og:url" content="{{ current_url }}">
{% else %}
<meta property="og:site_name" content="{{ config.title }}">
{% endif %}
{% if page.excerpt %}
<meta property="og:description" content="{{ page.excerpt | strip_html }}">
{% else %}
<meta property="og:description" content="{{ config.description }}">
{% endif %}
{% endblock og_card %}
{% block og_image %}
<meta property="og:image" content="/logo.png">
{% endblock og_image %}
{% block default_css %}
<style>{{ load_data(path="./public/default.css") | safe }}</style>
<link rel="stylesheet" href="{{ get_url(path="/style.css") }}" type="text/css" charset="utf-8">
{% endblock default_css %}
</head>
<body>
{% block css %}
{% endblock css %}
{% block header %}
<header class="masthead">
<h3 class="masthead-title">
<a href="/">{{ config.extra.author.name }}</a><br>
<small>{{ config.description }}</small>
</h3>
<nav class="masthead-nav">
{% for node in config.extra.navigation %}
{% if current_url and current_url != get_url(path=node.url, trailing_slash=true) %}
<a href="{{ node.url | replace(from="$BASE_URL", to="" ) }}" class="nav-item">{{ node.name }}</a>
{% endif %}
{% endfor %}
{% if current_url and current_url != get_url(path="/", trailing_slash=true) %}
<a href="/" class="nav-item">Home</a>
{% endif %}
</nav>
</header>
{% endblock header %}
<main class="content">
{% block content %}
<article class="index {% if section.extra.text_center %}text-center{% elif section.extra.text_justify %}text-justify{% endif %}">
{{ section.content | safe }}
</article>
{% endblock content %}
</main>
{% block footer %}
<footer class="footer">
<p>
<small>
This work is licensed under the <a rel="license" href="https://opensource.org/licenses/0BSD">0BSD License</a>.
<br/>
<a href="{{ "/" ~ config.feed_filename | safe }}">RSS Feed</a>
</small>
</p>
</footer>
{% endblock footer %}
</body>
</html>

29
templates/macros.html Normal file
View File

@ -0,0 +1,29 @@
{% macro message(content) %}
<p class="message">{{ content }}</p>
{% endmacro message %}
{% macro read_time(words) %}
<span class="reading-time" title="Estimated read time">
{% if words < 470 %}
1 min read
{% else %}
{{ words / 250 | round }} min read
{% endif %}
</span>
{% endmacro read_time %}
{% macro toc(toc) %}
<details open id = "toc-inline">
<summary><b>Table of Contents</b></summary>
<ul>
{% for h2 in toc %}<li>
<a href="#{{h2.id | safe}}">{{ h2.title | safe }}</a>
{% if h2.children %}<ul>
{% for h3 in h2.children %}<li>
<a href="#{{h3.id | safe}}">{{ h3.title | safe }}</a>
</li>{% endfor %}
</ul>{% endif %}
</li>{% endfor %}
</ul>
</details>
{% endmacro toc %}

57
templates/page.html Normal file
View File

@ -0,0 +1,57 @@
{% extends "index.html" %}
{% import "macros.html" as macros %}
{% block title %}
{{ page.title }}
{% endblock title %}
{% block description %}
<meta name="description" content="{{ page.summary | striptags }}">
{% if page.extra.tags %}
<meta name="keywords" content="{{ config.extra.keywords ~ ', ' ~ page.extra.tags }}">
{% endif %}
{% endblock description %}
{% block og_card %}
{{ super() }}
<meta property="og:title" content="{{ page.title }}">
{% endblock og_card %}
{% block og_image %}
{% if page.image %}
<meta property="og:image" content="{{ get_url(path=page.image, cachebust=true) }}">
{% endif %}
{% endblock og_image %}
{% block content %}
<article class="post">
<header id="post-header" class="post-header">
<time datetime="{{ page.date }}" class="post-date">
{{ page.date | date(format="%d %b %Y") }}
</time>
{% if page.extra.last_modified_at %}
<small>
(Updated:
<time datetime="{{ page.extra.last_modified_at | date(format="%Y-%m-%dT%H:%M:%S") }}" itemprop="dateModified">
{{ page.extra.last_modified_at | date(format="%d %b %Y") }}
</time>)
</small>
{% endif %}
<small>
· {{ macros::read_time(words=page.word_count) }}
</small>
<h1 class="post-title">{{ page.title }}</h1>
</header>
{{ page.content | replace(from="<!-- toc -->", to=macros::toc(toc=page.toc)) | safe }}
</article>
{% block contact %}
<p style="text-align: center;">Got any questions or comments? Drop me a message on <a href="{{ config.extra.author.telegram.link }}" rel="nofollow">Telegram</a>!</p>
{% endblock contact %}
{# include related.html #}
{% endblock content %}

2
templates/robots.txt Normal file
View File

@ -0,0 +1,2 @@
User-agent: *
Sitemap: https://shaleenjain.com/sitemap.xml

30
templates/rss.xml Normal file
View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel>
<title>{{ config.title }}
{%- if term %} - {{ term.name }}
{%- elif section.title %} - {{ section.title }}
{%- endif -%}
</title>
<link>{%- if section -%}
{{ section.permalink | escape_xml | safe }}
{%- else -%}
{{ config.base_url | escape_xml | safe }}
{%- endif -%}
</link>
<description>{{ config.description }}</description>
<generator>Zola</generator>
<language>{{ config.default_language }}</language>
<atom:link href="{{ feed_url | safe }}" rel="self" type="application/rss+xml"/>
<lastBuildDate>{{ last_updated | date(format="%a, %d %b %Y %H:%M:%S %z") }}</lastBuildDate>
{%- for page in pages %}
<item>
<title>{{ page.title }}</title>
<pubDate>{{ page.date | date(format="%a, %d %b %Y %H:%M:%S %z") }}</pubDate>
<link>{{ page.permalink | escape_xml | safe }}</link>
<guid>{{ page.permalink | escape_xml | safe }}</guid>
<description>{% if page.summary %}{{ page.summary }}{% else %}{{ page.content }}{% endif %}</description>
</item>
{%- endfor %}
</channel>
</rss>

15
templates/section.html Normal file
View File

@ -0,0 +1,15 @@
{% extends "index.html" %}
{% block content %}
<article class="page">
<header class="page-header">
{% if not section.extra.hide_title %}
<h1 class="page-title">{{ section.title }}</h1>
{% endif %}
</header>
{% block seccontent %}
{{ section.content | safe }}
{% endblock seccontent %}
</article>
{% endblock content %}

View File

@ -0,0 +1 @@
<p class="message">{{ body | markdown(inline=true) | safe }}</p>

View File

@ -0,0 +1,3 @@
{% set basepath = page.path | trim_start_matches(pat="/") %}
{% set imgurl = resize_image(path=basepath~path, width=1600, height=1600, op="fit") | trim_start_matches(pat=config.base_url) | trim_start_matches(pat="http://127.0.0.1:1111") %}
<img src="{{ imgurl }}" {% if class %}class="{{class}}"{% endif %} />

View File

@ -0,0 +1,16 @@
<ul>
{% for h1 in page.toc %}
<li>
<a href="{{h1.permalink | safe}}">{{ h1.title }}</a>
{% if h1.children %}
<ul>
{% for h2 in h1.children %}
<li>
<a href="{{h2.permalink | safe}}">{{ h2.title }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>

20
theme.toml Normal file
View File

@ -0,0 +1,20 @@
name = "xpoole"
description = "A theme inspired by jekyll poole"
license = "MIT"
homepage = "https://github.com/thunderbottom/xpoole"
min_version = "0.11.0"
[extra]
navigation = [
{name = "Blog", url = "/blog"},
]
[author]
name = "Chinmay Pai"
homepage = "https://maych.in"
repo = "https://github.com/thunderbottom/xpoole"
[original]
author = "poole"
homepage = "http://getpoole.com"
repo = "https://github.com/poole/poole"