Compare commits

..

3 Commits

7 changed files with 433 additions and 247 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
public public
resources/_gen resources/_gen
.hugo_build.lock .hugo_build.lock
themes

3
.gitmodules vendored
View File

@@ -4,3 +4,6 @@
[submodule "themes/dark-theme-editor"] [submodule "themes/dark-theme-editor"]
path = themes/dark-theme-editor path = themes/dark-theme-editor
url = https://github.com/JingWangTW/dark-theme-editor.git url = https://github.com/JingWangTW/dark-theme-editor.git
[submodule "themes/typo"]
path = themes/typo
url = https://github.com/tomfran/typo.git

271
content-org/posts.org Normal file
View File

@@ -0,0 +1,271 @@
#+HUGO_BASE_DIR: ../
#+HUGO_SECTION: posts
:PROPERTIES:
:EXPORT_OPTIONS: author:nil
:END:
* Homelab :homelab:
** DONE HTTPS @ Home
:PROPERTIES:
:EXPORT_FILE_NAME: https-at-home
:EXPORT_DATE: 2022-11-08
:EXPORT_HUGO_MENU: :menu "main"
:EXPORT_AUTHOR:
:END:
I run a lot of services at home.
This includes, but isn't limited to
- [[https://archivebox.io/][ArchiveBox]]
- [[https://github.com/dani-garcia/vaultwarden][VaultWarden]]
- [[https://github.com/navidrome/navidrome][Navidrome]]
- [[https://plex.tv][Plex]]
- [[https://github.com/LibrePhotos/librephotos][LibrePhotos]]
- This blog
and a lot more.
Pretty much anything that's served up over HTTP is always nice if not
necessary to have behind TLS.
[[https://letsencrypt.org/][LetsEncrypt]] long ago brought free certs to
the masses and there are a lot of tools for automating that nowadays.
My preferred approach for getting all the unnecessary nonsense I
self-host at home behind TLS is [[https://caddyserver.com][Caddy]].
I have a super straight forward setup, generally:
- Run Caddy in a docker container
- Create a wildcard CNAME record in my DNS pointing at my home's
(effectively) static IP
- Add an entry in my Caddyfile for each services I'm running at home on
its own subdomain
- If it's a service then I add it with a =reverse_proxy= block
- If it's a static site (like this) then there's a block for
- If it's something I want only accessible on my home network then I put
a block like
#+BEGIN_EXAMPLE
@local_network {
path *
remote_ip
}
#+END_EXAMPLE
in the directive. And voila.
Then tell Caddy to reload the config and I'm done.
+++ title = "My multiroom audio setup" date = "2022-11-08" +++
I've put my home audio solution together out of the following
components.
- [[https://github.com/badaix/snapcast][Snapcast]]
- [[https://www.musicpd.org/][MPD]]
- [[https://github.com/librespot-org/librespot][Librespot]]
- [[https://github.com/mikebrady/shairport-sync][Shairport-sync]]
- A mini-PC in my closet running the above software
- Two Raspberry Pi 4s
- Four Raspberry Pi Zero Ws
- Some desktop speakers and some Bluetooth speakers (wired to the Pis)
Each of the Raspberry Pis is in a room or porch attached to a speaker.
Snapcast lets me take an audio source and synchronize it across multiple
clients. Each of the Raspberry Pis are running a =snapclient= instance
and play whatever the =snapserver= instance tells them to.
Snapcast is setup to send whichever of the streams (MPD, Spotify,
Shairport-sync/AirPlay) is playing audio to each of the clients that are
connected to it.
This lets me or anyone else on my WiFi network play directly on one or
more of the speakers - each named for the room that they're in using
either Spotify, AirPlay, picking from my own music collection or by
pointing at a URL (like to a podcast episode).
This works out great and we've used it at home for the past year.
I'd like to get the podcast experience to a more seamless place but it's
pretty OK right now using AirMusic on my phone to play audio to the
speakers over AirPlay.
** TODO My multiroom audio setup
:PROPERTIES:
:EXPORT_FILE_NAME: snapcast
:EXPORT_DATE: 2022-11-08
:EXPORT_HUGO_MENU: :menu "main"
:END:
I've put my home audio solution together out of the following
components.
- [[https://github.com/badaix/snapcast][Snapcast]]
- [[https://www.musicpd.org/][MPD]]
- [[https://github.com/librespot-org/librespot][Librespot]]
- [[https://github.com/mikebrady/shairport-sync][Shairport-sync]]
- A mini-PC in my closet running the above software
- Two Raspberry Pi 4s
- Four Raspberry Pi Zero Ws
- Some desktop speakers and some Bluetooth speakers (wired to the Pis)
Each of the Raspberry Pis is in a room or porch attached to a speaker.
Snapcast lets me take an audio source and synchronize it across multiple
clients. Each of the Raspberry Pis are running a =snapclient= instance
and play whatever the =snapserver= instance tells them to.
Snapcast is setup to send whichever of the streams (MPD, Spotify,
Shairport-sync/AirPlay) is playing audio to each of the clients that are
connected to it.
This lets me or anyone else on my WiFi network play directly on one or
more of the speakers - each named for the room that they're in using
either Spotify, AirPlay, picking from my own music collection or by
pointing at a URL (like to a podcast episode).
This works out great and we've used it at home for the past year.
I'd like to get the podcast experience to a more seamless place but it's
pretty OK right now using AirMusic on my phone to play audio to the
speakers over AirPlay.
* Tooling :tooling:
** vi modal editing in most places
:PROPERTIES:
:EXPORT_FILE_NAME: vi-mode-everywhere
:EXPORT_DATE: 2022-11-13
:EXPORT_HUGO_MENU: :menu "main"
:END:
For my sake, I prefer to have Vim bindings in as many places as
possible.
Most shells can be configured to use Vim bindings by putting =set -o vi=
somewhere in your shell startup script.
If you're using ZSH then you'll probably want an additional binding to
restore CTRL-R reverse history search.
=bindkey '^R' history-incremental-search-backward=
For CLI tools that use the =readline= library then you can configure its
input mode using a =.inputrc= file in your =$HOME= directory.
This affects REPLs like =ghci= and tools like =psql=.
#+begin_src txt
set editing-mode vi
$if mode=vi
set keymap vi-command
# these are for vi-command mode
Control-l: clear-screen
set keymap vi-insert
# these are for vi-insert mode
Control-l: clear-screen
$endif
#+end_src
* AWS :aws:
** Structed and passively collected metrics via AWS CloudWatch
:EXPORT_FILE_NAME: cloudwatch-metric-filters
:EXPORT_DATE: 2022-11-12
:EXPORT_HUGO_MENU: :menu "main"
AWS is a vast and sprawling set of services. It can be hard to find the
hidden gems like this one so I wanted to point this one out.
Structured metrics are very helpful to monitoring the health and
function of an software system.
- Do you want to know how long a particular transaction typically takes?
- How fast your database queries are?
- How long external APIs take to respond?
- Fire an alert when a particular function on the site happens too many
times? Or too few times?
...plus a million other things specific to whatever system you're
working on.
There are a lot of great tools for doing this and one that you might not
know about is AWS CloudWatch Metric Filters. If you're already on AWS
then you should consider these because it requires only that your
application logs to CloudWatch.
If you're on ECS then the
[[https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_awslogs.html][awslogs]]
log driver for Docker gets you that nearly for free. By "free" I mean
that your application itself can have /zero/ dependencies on AWS
services and not require any AWS credentials or libraries to start
pumping out metrics that you can visualize, alert on and record over
time.
The
[[https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/MonitoringLogData.html][AWS
docs]] themselves offer the canonical reference for configuring these so
I won't go into detail here.
However, the gist is that for a log filter you define the following
properties
- A filter pattern for extracting a discrete metric value out of a log
entry
- A metric name to store the value in
- An optional dimension for sub-classifying the value
- And finally a log group to extract the metric values from
After that you just run the application and as the logs roll in the
metric values get pumped out. Then you can
[[https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Create-alarm-on-metric-math-expression.html][define
alarms for alerting]] on them,
[[https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Dashboards.html][graph
them]],
[[https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-simple-step.html#policy-creating-alarm-console][define
autoscaling rules]] from them and more.
To conclude - AWS is big and hairy. While there are benefits to staying
platform agnostic, some AWS services don't require much or any coupling
of your application code to take advantage of. Cloudwatch Metrics is one
of those services and you can get a lot of value out of it with not much
effort.
* Musings :musings:
** TODO My job description
** TODO Managing expectations
** TODO Just let people be wrong
:EXPORT_FILE_NAME: trust-through-failure
:EXPORT_DATE: 2024-10-01
:EXPORT_HUGO_MENU: :menu "main"
Warning: This, like most things, will involve a fair bit of projection.
I have some thoughts about collaboration.
While a lot of this is obvious and well accepted, I think there are some fine points worth elaborating on.
The obvious part is that people work better together when they believe they are trusted. Trust breeds initiative and independence. Distrust breeds resentment and inaction.
Consider the flip side of trust, for a moment.
A common way that people show _distrust_ when collaborating is either micromanaging or just coming in behind someone and redoing their work.
If that demonstrates distrust then
It's not enough that you simply _do_ trust someone else to get the benefits, you need to show it. I think this is the part that many people skip or ignore.
This is, of course, true in general.

View File

@@ -0,0 +1,99 @@
+++
title = "HTTPS @ Home"
date = 2022-11-08
tags = ["homelab"]
draft = false
[menu]
[menu.main]
weight = 2001
identifier = "https-home"
+++
I run a lot of services at home.
This includes, but isn't limited to
- [ArchiveBox](https://archivebox.io/)
- [VaultWarden](https://github.com/dani-garcia/vaultwarden)
- [Navidrome](https://github.com/navidrome/navidrome)
- [Plex](https://plex.tv)
- [LibrePhotos](https://github.com/LibrePhotos/librephotos)
- This blog
and a lot more.
Pretty much anything that's served up over HTTP is always nice if not
necessary to have behind TLS.
[LetsEncrypt](https://letsencrypt.org/) long ago brought free certs to
the masses and there are a lot of tools for automating that nowadays.
My preferred approach for getting all the unnecessary nonsense I
self-host at home behind TLS is [Caddy](https://caddyserver.com).
I have a super straight forward setup, generally:
- Run Caddy in a docker container
- Create a wildcard CNAME record in my DNS pointing at my home's
(effectively) static IP
- Add an entry in my Caddyfile for each services I'm running at home on
its own subdomain
- If it's a service then I add it with a `reverse_proxy` block
- If it's a static site (like this) then there's a block for
- If it's something I want only accessible on my home network then I put
a block like
<!--listend-->
```text
@local_network {
path *
remote_ip
}
```
in the directive. And voila.
Then tell Caddy to reload the config and I'm done.
~~+~~ title = "My multiroom audio setup" date = "2022-11-08" ~~+~~
I've put my home audio solution together out of the following
components.
- [Snapcast](https://github.com/badaix/snapcast)
- [MPD](https://www.musicpd.org/)
- [Librespot](https://github.com/librespot-org/librespot)
- [Shairport-sync](https://github.com/mikebrady/shairport-sync)
- A mini-PC in my closet running the above software
- Two Raspberry Pi 4s
- Four Raspberry Pi Zero Ws
- Some desktop speakers and some Bluetooth speakers (wired to the Pis)
Each of the Raspberry Pis is in a room or porch attached to a speaker.
Snapcast lets me take an audio source and synchronize it across multiple
clients. Each of the Raspberry Pis are running a `snapclient` instance
and play whatever the `snapserver` instance tells them to.
Snapcast is setup to send whichever of the streams (MPD, Spotify,
Shairport-sync/AirPlay) is playing audio to each of the clients that are
connected to it.
This lets me or anyone else on my WiFi network play directly on one or
more of the speakers - each named for the room that they're in using
either Spotify, AirPlay, picking from my own music collection or by
pointing at a URL (like to a podcast episode).
This works out great and we've used it at home for the past year.
I'd like to get the podcast experience to a more seamless place but it's
pretty OK right now using AirMusic on my phone to play audio to the
speakers over AirPlay.

250
hugo.toml Normal file → Executable file
View File

@@ -1,210 +1,74 @@
baseURL = 'https://drafts.jamesbrechtel.com/' baseURL = 'https://willfullyobtuse.com/'
languageCode = 'en-us' languageCode = 'en-us'
title = 'james brechtel' title = 'circumlocution'
theme = 'dark-theme-editor' theme = 'typo'
# Support Taxonomies
[taxonomies] [taxonomies]
autor = "James brechtel" tag = 'tags'
tag = "tags"
category = "categories" # Google analytics code
_merge = "deep" googleAnalytics = ""
# Theme parameters
[params] [params]
# Meta description
description = "point free notes"
# Parameters applied in HTML <head> # Appearance settings
[params.site] theme = 'light'
# Website ICON colorPalette = 'default'
faviconUrl = "" hideHeader = false
# Do you have any CSS in local? List them in an array. # Intro on main page, content is markdown
# They should be placed inside "/assets" dir. homeIntroTitle = 'Hi!'
# Please list your files relative to the /assets directory. homeIntroContent = """
# Glob pattern is acceptable Notes, journal, details, rants, clarifications
# If only a few pages need the extra style files, list them in the page metadata. """
localCss = []
# Do you need to add any external CSS? List their URL in an array. # Collection to display on home
# If only a few pages need the extra style files, list them in the page metadata. homeCollectionTitle = 'Posts'
# These URLs will be inserted into <link> tags directly without any check. homeCollection = 'posts'
externalCss = []
# Like the one above, but this will download a local copy and combine it with # Lists parameters
# other CSS files into a single file while in production mode. paginationSize = 100
externalCssDownload = [] listSummaries = true
listDateFormat = '2 Jan 2006'
# Do you have any script in local? List them in an array. # Breadcrumbs
# They should be placed inside "/assets" dir. breadcrumbs = true
# Please list your files relative to the /assets directory.
# Glob pattern is acceptable
# If only a few pages need the extra script files, list them in the page metadata.
localJs = []
# Do you have any external Script need to add on? List their URL in an array. # Social icons
# If only a few pages need the extra script files, list them in the page metadata. [[params.social]]
# These URLs will be inserted into <script> tags directly without any check. name = "Mastodon"
externalJs = [] url = "https://hachyderm.io/@eightball"
# Like the one above, but this will download a local copy and combine it with [[params.social]]
# other JS files into a single file while in production mode. name = "github"
externalJsDownload = [] url = "https://github.com/jbrechtel"
# The code you could get from Google Search Console. # Main menu pages
# Please patse the value of content xxx in the following items [[params.menu]]
# <meta name="google-site-verification" content="xxxxxxxxxxxxxxxxxxxxxxxxxx" /> name = "home"
googleVerification = "" url = "/"
# The code you could get from Microsoft Bing Webmater [[params.menu]]
# Please patse the value of content xxx in the following items name = "posts"
# <meta name="msvalidate.01" content="xxxxxxxxxxxxxxxxxxxx" /> url = "/posts"
bingVerifivation = ""
# Customized info shown in header of the page [[params.menu]]
[params.header] name = "about"
# Website title for header banner. url = "/about"
title = "circumlocution"
# Subtitle for this site, used in homepage only # Syntax highligth on code blocks
subtitle = "absolutely nothing important" [markup]
[markup.highlight]
style = 'algol'
# Config about your's site logo, remove this item to hide the logo # Giscus comments
[params.header.logo] [params.giscus]
# Where is your site's URL enable = false
imgUrl = "" repo = "user/repo"
repoid = "repoId"
# If the logo is clickable, where should it be linked? category = "General"
# In default, it will linked to the homepage of the site. categoryid = "categoryId"
logoLink = "" mapping = "pathname"
theme = "preferred_color_scheme"
# Customized info shown in footer of the page
[params.footer]
# CopyRight string shown in the footer. Keep it an empty string or remove this item will hide it from the page.
copyrightStr = "...."
# Should show the counter in the footer or not
# In the home page, it will show the numbers of all pages
# In the sections pages, it will show the numbers of pages within the section
# In the taxonomy pages, it will show the numbers of pages belong to the taxonomy.
# In the regular content pages, it will show the word count.
counter = true
# Should show the language of the page or not
language = true
# Should show the hugo version or not
hugoVersion = true
# Should show the theme info or not
theme = true
# Should show the edited time of the page or not
modifiedTime = true
# The format of the `modifiedTime`.
# Refer to page https://gohugo.io/functions/format/ for more detail.
# Below is the default format, please do not remove it, unless you set `false` in `modifiedTime` field.
dateFormat = "Jan 02 2006 15:04:05"
# Should show the current git HEAD hash or not
# To make this show up correctly, please follow the prerequisites in page
# https://gohugo.io/variables/git/
gitHash = true
# Social link in the footer, listed items are supported, delete unwanted items to hide it.
[params.footer.socialLink]
github = "jbrechtel"
facebook = ""
twitter = "8brechtel"
email = ""
linkedin = ""
instagram = "eightbald"
telegram = ""
medium = ""
vimeo = ""
youtube = ""
# Metadata of the site, value will be used in HTML <header>
# These value would be used when they didn't appear in the frontmatter of a single page.
# In other words, these value will be overwritten by the frontmatter in the single page.
[params.globalFrontmatter]
# The author of this site. This will be shown in
# 1. the footer of all page
# 2. the author filed in the single page. (this could be overwritten by the frontmatter of the single page.)
# Keep it an empty string or remove this item will hide it from the page
author = "James Brechtel"
# Website description for RSS and SEO. Theme will generate a <meta> tag for this item
description = ""
# Website keywords. Theme will generate a <meta> tag for this item.
keywords = "haskell, cycling, hiking, personal, programming"
# Parameters applied in the homepage only
[params.homePage]
# Long Descripition shown in home page "Start Block". Is is recommended to have the paragraph shorter than 100 words.
siteLongDescription = "This site is a simple outlet to help me clarify my thoughts."
# If you don't like the title of "siteLongDescription" be "Start" (default),
# you may change the value of this item to "Description" or something you like.
siteLongDescriptionTitle = "Start"
# Param to decide whether to show the most recent blog posts or not. (Default: true)
showRecentPostsBlock = true
# Param to decide how many recent posts to show in the home page. (Default: 5)
numOfRecentPosts = 10
# Parameter to decide whether to show the URL behind the title.
# It will be more like an editor if it is shown. However, in general, it can be messy if it is displayed.
# (Default: true)
recentPostShowUrl = true
# Paramater applied in the single page
# These values could be overwritten by the frontmatter in the single page.
[params.page]
# Should include Table of Content in front of the page or not.
includeToc = true
# Should show the author of the page or not.
# The author name will be shown in the single page if and only if
# 1. this items been set as true and
# 2. "aurthor" filed been provided in the
# A. single page frontmatter or
# B. "author" filed in above "globalFrontmatter" block
showAuthor = true
# Should show the date of the page or not
showDate = true
# The format of the date.
# Refer to page https://gohugo.io/functions/format/ for more detail.
# Below is the default format, please do not remove it, unless you set `false` in `showDate` field.
dateFormat = "2006.01.02"
# Should show the estimate reading time in front of the page or not.
showTimeToRead = true
# Should show the breadcrumb in front of the page or not.
showBreadcrumb = true
# Should show the "copy" button in the codeblock or not.
codeBlockCopible = true
# Should include LaTeX support on a single page or not?
# This parameter will be overwritten by the page front matter (if it has been set).
# Since enabling this configuration loads some external JavaScript,
# it will slow down the page loading speed.
# It's recommended to keep it turned off by default and only enable this configuration when required.
useMath = false
# Filed tp describe how should we pass the "trust" params to the `KaTex` module.
# Thos params would affect if we could trust the HTML related code in LaTex blocks.
# https://katex.org/docs/options.html
trustedmath = false
[params.preference]
# Use "true" to show the real fila name in both breadcrumb and the sidebar
# Use "false" to show the tile in the file in both breadcrumb and the sidebar
showFileName = false

View File

@@ -1,53 +0,0 @@
#+HUGO_BASE_DIR: .
* Homelab
** HTTPS @ Home
:PROPERTIES:
:EXPORT_FILE_NAME: https-at-home
:EXPORT_DATE: 2022-11-08
:EXPORT_HUGO_MENU: :menu "main"
:END:
I run a lot of services at home.
This includes, but isn't limited to
- [[https://archivebox.io/][ArchiveBox]]
- [[https://github.com/dani-garcia/vaultwarden][VaultWarden]]
- [[https://github.com/navidrome/navidrome][Navidrome]]
- [[https://plex.tv][Plex]]
- [[https://github.com/LibrePhotos/librephotos][LibrePhotos]]
- This blog
and a lot more.
Pretty much anything that's served up over HTTP is always nice if not
necessary to have behind TLS.
[[https://letsencrypt.org/][LetsEncrypt]] long ago brought free certs to
the masses and there are a lot of tools for automating that nowadays.
My preferred approach for getting all the unnecessary nonsense I
self-host at home behind TLS is [[https://caddyserver.com][Caddy]].
I have a super straight forward setup, generally:
- Run Caddy in a docker container
- Create a wildcard CNAME record in my DNS pointing at my home's
(effectively) static IP
- Add an entry in my Caddyfile for each services I'm running at home on
its own subdomain
- If it's a service then I add it with a =reverse_proxy= block
- If it's a static site (like this) then there's a block for
- If it's something I want only accessible on my home network then I put
a block like
#+BEGIN_EXAMPLE
@local_network {
path *
remote_ip
}
#+END_EXAMPLE
in the directive. And voila.
Then tell Caddy to reload the config and I'm done.

1
themes/typo Submodule

Submodule themes/typo added at 4fe47ad315