Compare commits
16 Commits
98430c26bc
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c8f72b28f4 | |||
| dcf36ce8e4 | |||
| 8aaa4b146b | |||
| bcc2d85560 | |||
| 5c90096a10 | |||
| 42cf585dca | |||
| 25da58b4ed | |||
| 9ea91ff983 | |||
| c96adeb025 | |||
| 3a77bef362 | |||
| 83f3bbdd1e | |||
| 8f299c9631 | |||
| 0a98a4c6ed | |||
| 3c87527dac | |||
| 8066b04b36 | |||
| 774c713110 |
15
TODO.md
Normal file
15
TODO.md
Normal file
@@ -0,0 +1,15 @@
|
||||
## UI
|
||||
|
||||
- [ ] Outline mode on documents
|
||||
- [ ] Improve table rendering
|
||||
- [ ] More image-flow options
|
||||
- [ ] Fix capitalization in notes values/headers. Only the first letter is capitalized, but it should follow the capitalization of the folder. E.g. "NixOS" not "Nixos"
|
||||
- [x] Code Highlighting
|
||||
|
||||
## Interaction
|
||||
|
||||
- [ ] Web Mentions
|
||||
- [ ] Comments
|
||||
- [ ] Guest Book
|
||||
|
||||
## Content
|
||||
@@ -13,4 +13,8 @@ export default defineConfig({
|
||||
vite: {
|
||||
plugins: [],
|
||||
},
|
||||
|
||||
redirects: {
|
||||
"/now": "/notes/now",
|
||||
},
|
||||
});
|
||||
|
||||
Binary file not shown.
@@ -10,8 +10,9 @@ interface Props {
|
||||
|
||||
const { prefix, hierarchy: maybeHierarchy} = Astro.props;
|
||||
|
||||
const hierarchy = maybeHierarchy ?? makeHierarchy((await getCollection('notes')).map(note => [note.id, note.data.title]));
|
||||
const notes = await getCollection('notes');
|
||||
|
||||
const hierarchy = maybeHierarchy ?? makeHierarchy(notes.map(note => [note.id, note.data.title]));
|
||||
|
||||
const pathname = Astro.url.pathname.replace(import.meta.env.BASE_URL, '/');
|
||||
|
||||
@@ -40,7 +41,7 @@ const pathname = Astro.url.pathname.replace(import.meta.env.BASE_URL, '/');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<a href={notePath} class={linkClasses}>
|
||||
<a href={`/notes/${node.id}`} class={linkClasses}>
|
||||
{node.title || leader}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="social-links">
|
||||
<a href="https://m.webtoo.ls/@astro" target="_blank">
|
||||
<a href="https://mastodon.social/@periodic" target="_blank" rel="me">
|
||||
<span class="sr-only">Follow Periodic on Mastodon</span>
|
||||
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32"
|
||||
><path
|
||||
@@ -7,7 +7,7 @@
|
||||
d="M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a3.614 3.614 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522c0-.859.22-1.541.66-2.046.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764.442.505.661 1.187.661 2.046v4.203z"
|
||||
></path></svg>
|
||||
</a>
|
||||
<a href="https://github.com/periodic" target="_blank">
|
||||
<a href="https://github.com/periodic" target="_blank" rel="me">
|
||||
<span class="sr-only">Go to Periodic's GitHub repo</span>
|
||||
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32"
|
||||
><path
|
||||
@@ -16,6 +16,18 @@
|
||||
></path></svg
|
||||
>
|
||||
</a>
|
||||
<a href="/rss.xml">
|
||||
<span class="sr-only">Subscribe to Periodic's RSS feed</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" aria-hidden="true" width="32" height="32">
|
||||
<!--!Font Awesome Free v7.2.0 by @fontawesome - https://fontawesome.com
|
||||
License - CC BY 4.0 - https://fontawesome.com/license/free
|
||||
Copyright 2026 Fonticons, Inc.-->
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M96 128C96 110.3 110.3 96 128 96C357.8 96 544 282.2 544 512C544 529.7 529.7 544 512 544C494.3 544 480 529.7 480 512C480 317.6 322.4 160 128 160C110.3 160 96 145.7 96 128zM96 480C96 444.7 124.7 416 160 416C195.3 416 224 444.7 224 480C224 515.3 195.3 544 160 544C124.7 544 96 515.3 96 480zM128 224C287.1 224 416 352.9 416 512C416 529.7 401.7 544 384 544C366.3 544 352 529.7 352 512C352 388.3 251.7 288 128 288C110.3 288 96 273.7 96 256C96 238.3 110.3 224 128 224z"/>
|
||||
cial
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -23,6 +35,7 @@
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16px;
|
||||
justify-content: center;
|
||||
|
||||
a {
|
||||
color: var(--color-light-text);
|
||||
|
||||
31
src/components/TableOfContents.astro
Normal file
31
src/components/TableOfContents.astro
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
import TableOfContentsTree from "./TableOfContentsTree.astro";
|
||||
import type { MarkdownHeading } from "astro";
|
||||
import { makeHeadingsTree, removeEmptyLevels } from "../lib/headings";
|
||||
|
||||
type Props = {
|
||||
headings: Array<MarkdownHeading>;
|
||||
};
|
||||
|
||||
const {headings} = Astro.props;
|
||||
const headingsTree = removeEmptyLevels(makeHeadingsTree(headings));
|
||||
---
|
||||
|
||||
<div class="table-of-contents">
|
||||
<TableOfContentsTree headingsTree={headingsTree}></TableOfContentsTree>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.table-of-contents {
|
||||
border: 1px solid var(--color-space-blue-light);
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
& > ul {
|
||||
padding: 0;
|
||||
margin: 8px 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
15
src/components/TableOfContentsTree.astro
Normal file
15
src/components/TableOfContentsTree.astro
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
type Props = {
|
||||
headingsTree: HeadingsTree;
|
||||
};
|
||||
|
||||
const {headingsTree} = Astro.props;
|
||||
---
|
||||
|
||||
<ul>
|
||||
{headingsTree.map(node =>
|
||||
Array.isArray(node)
|
||||
? <Astro.self headingsTree={node} />
|
||||
: <li><a href={`#${node.slug}`}>{node.text}</a></li>
|
||||
)}
|
||||
</ul>
|
||||
@@ -26,7 +26,7 @@ So now that I had these notes, I needed to rebuild the structure so I could rend
|
||||
|
||||
I created a type like the following. Note that TypeScript does not support self-referential recursive types! It does allow self-referential interfaces though. Hence the odd syntax.
|
||||
|
||||
```TypeScript
|
||||
```typescript
|
||||
interface Hierarchy extends Record<string, Hierarchy> {}
|
||||
```
|
||||
|
||||
@@ -34,7 +34,7 @@ However, after messing around with it for a while I realized I needed a little m
|
||||
|
||||
This lead me to create the following data structure. Some nodes would have children, others not, and they may or may not have an ID or title. (I could write both of these as `type` declarations because they are _mutually recursive_ types. TypeScript is weird.)
|
||||
|
||||
```TypeScript
|
||||
```typescript
|
||||
interface HierarchyNode {
|
||||
id: string | null;
|
||||
title: string | null;
|
||||
@@ -45,7 +45,7 @@ type Hierarchy = Record<string, HierarchyNode>;
|
||||
|
||||
I started with algorithm of splitting all the paths into their parts, then going through and recursively grouping. However, I found this would be much simpler if I just did this as a fold/reduce so I could just focus on adding one path at a time. This gave me the following simple algorithm. Please ignore the repetition, it didn't seem worth cleaning up at the time.
|
||||
|
||||
```TypeScript
|
||||
```typescript
|
||||
function addToHierarchy(
|
||||
tree: Hierarchy,
|
||||
[noteId, title]: [string, string],
|
||||
@@ -82,7 +82,7 @@ export function makeHierarchy(notes: Array<[string, string]>): Hierarchy {
|
||||
|
||||
So now I had to render the links. Astro thankfully _does_ support recursive components through `Astro.self`, so I was able to write a single component to render the hierarchy.
|
||||
|
||||
```Astro
|
||||
```astro
|
||||
---
|
||||
import { type Hierarchy } from "../lib/hierarchy.ts";
|
||||
|
||||
|
||||
62
src/content/blog/2026-05-11-done-with-crypto.md
Normal file
62
src/content/blog/2026-05-11-done-with-crypto.md
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: "I'm done watching the crypto scene"
|
||||
description: "I've found the crypto world fascinating since it's inception. The hype, the riches, the lies, the scams! It was a fascinating case study on what people could convince themselves of and what markets could be if we ignored the last few centuries of wisdom. It was ridiculous and I couldn't believe it kept going. I didn't realize it was just a preview of what was to come."
|
||||
pubDate: 2026-05-11
|
||||
tags:
|
||||
- Crypto
|
||||
- Tech
|
||||
---
|
||||
|
||||
I've been fascinated by the crypto world for years. It started way back when I first heard of Bitcoin around 2009. It was a really interesting idea. A distributed append-only ledger that anyone could write to. I hadn't heard of anything quite like it.
|
||||
|
||||
I couldn't really figure out what it was good for. It was slow, expensive, and hard to correct mistakes. I heard lots of arguments for it, but none of them were convincing to me.
|
||||
|
||||
- You can instantly send money anywhere in the world!
|
||||
- But you still have to deal with exchanging it for local currency.
|
||||
- And the instantaneous nature means there isn't a way to reverse transactions so it's going to become a magnet for fraud.
|
||||
- You can have privacy and anonymity! Governments won't be able to stop you!
|
||||
- Except that every transaction is public so anonymity only lasts as long as you can avoid having _any identifying information_ tied to your wallet address.
|
||||
- And you'll be identified as soon as you try to convert to fiat currency.
|
||||
- It's immune to government censorship!
|
||||
- Except we mostly have that censorship for good crime-prevention reasons!
|
||||
- There's still the issue of turning it into real currency.
|
||||
- It'll become a way to give everyone a path to global currency exchange!
|
||||
- Except it relies heavily on electronics and internet access which can be censored.
|
||||
|
||||
So I watched as it slowly grew. I was amazed by how many people continued to think that the revolution was just around the corner. All I saw was a lot of speculation and fraud. Stories of people having their fortunes stolen with no recourse. Scammers using it to extort money from victims. Even major exchanges being hacked and drained belying the claim that it was all decentralized.
|
||||
|
||||
But it kept growing. We had loud proclamations that Web3 was here with very little definition of what it was. We had the NFT craze. Everyone was jumping on the bandwagon yet no one could really describe where they were hoping the wagon would go. Remember when **Long Island Ice Tea Corp.** changed their name to [**Long Blockchain Corp.**](https://en.wikipedia.org/wiki/Long_Blockchain_Corp.) and saw their stock jump 380%? Blockchain was going to be the solution to everything!
|
||||
|
||||
I started to follow Molly White's [Web3 is Going Just Great](https://www.web3isgoinggreat.com/) and later her [Citation Needed](https://www.citationneeded.news/) newsletter. There was the excellent essay/documentary [Line Goes Up](https://www.youtube.com/watch?v=YQ_xWvX1n9g) by Dan Olson. It was pretty clear that this was all crazy, but it was fascinating. How did people get sucked into this cult?
|
||||
|
||||
There was one phrase that seemed to encapsulate the whole movement: Have Fun Staying Poor. It was directed at non-believers. It was repeated so often it was often abbreviated HFSP. It made it clear that the real driver was greed. This wasn't about making the world better. It was about getting yours and sticking it to everyone else.
|
||||
|
||||
There was a moment when it seemed to recede. It was refreshing when FTX folded and Sam Bankman-Fried went to jail. There was a semblance of sanity and there were mechanisms to rein in the worst of the excess. I had hope that the craze would pass, the volatility would die down, people could safely start saving for retirement again.
|
||||
|
||||
## It's too real now
|
||||
|
||||
I was so wrong. Over the last few months the entire economy seems to have been taken over by HFSP mentality. Leaders in business and finance are happy to get theirs to the detriment of the rest of us. If you don't embrace it, have fun staying poor.
|
||||
|
||||
- Sports gambling and prediction markets are taking off.
|
||||
- Sports broadcasts are now plastered with ads for gambling platforms and odds.
|
||||
- Even though about [43% of adults see it as a bad thing for society](https://www.pewresearch.org/short-reads/2025/10/02/americans-increasingly-see-legal-sports-betting-as-a-bad-thing-for-society-and-sports/).
|
||||
- In response to an American soldier making over $400k on Polymarket we had the president of the USA say, "the whole world, unfortunately, has become somewhat of a casino."
|
||||
- AI is the ultimate in FOMO
|
||||
- AllBirds, the shoes and clothing company announced a pivot to AI and [saw a 582% jump in their stock price](https://www.cnbc.com/2026/04/15/allbirds-bird-stock-shoes-ai.html) in just one day.
|
||||
- There has been [very little demonstrated value](https://garymarcus.substack.com/p/agents-and-roi) from most AI roll-outs.
|
||||
- There's a rush to invest and spend [without any concrete plan on how to recoup that capital](https://www.wheresyoured.at/am-i-meant-to-be-impressed/).
|
||||
- Companies are taking advantage of reduced regulation
|
||||
- The [EPA recently reversed the endangerment finding](https://www.npr.org/2025/07/24/nx-s1-5302162/climate-change-trump-epa), meaning that companies no longer have to consider the health costs of air pollution.
|
||||
- The FCC seems content to only [go after enemies of the president](https://www.npr.org/2026/04/28/nx-s1-5802997/fcc-abc-license-renewal-melania-trump-jimmy-kimmel) instead of [reviewing mergers](https://arstechnica.com/tech-policy/2025/05/fcc-chair-brendan-carr-is-letting-isps-merge-as-long-as-they-end-dei-programs/) that will reduce consumer choice and power.
|
||||
|
||||
All this in the broader economy means crypto news just doesn't hit the same way anymore. I used to laugh, I now just sigh. I see the [grift](https://en.wikipedia.org/wiki/$Trump), the [political dealing](https://www.wired.com/story/trumpcoin-dinner-ticket-bidding/), the [dropped indictments](https://abcnews.com/US/sec-drops-case-crypto-firm-ties-trump-ceo/story?id=119963257), the [pardons](https://www.politico.com/news/2025/10/23/trump-pardons-crypto-billionaire-changpeng-zhao-00620175).
|
||||
|
||||
I was right that crypto would not grow to take over the economy. I was wrong in thinking that the greedy mindset would stay contained. Now it seems like naked greed is the order of the day. We live in a grift economy. Everything is about taking advantage of others and there are few mechanisms to prevent it.
|
||||
|
||||
## So what's next
|
||||
|
||||
Crypto used to be a fascinating corner of the economy. I found it puzzling and amazing that some people could make so much money while so many people lost theirs. I couldn't see the value in it.
|
||||
|
||||
The whole economy has become the same grift that crypto was. The mindset is everywhere, particularly around AI. Every week we get a breathless announcement about a new AI model or $100B round of circular financing.
|
||||
|
||||
I'm done with it. I have better things to do with my time, energy and emotions. I am going to continue to focus on building real connections with real people in a way that adds real value to their lives. I don't need another distraction or the negative emotions. It's time to focus on what I can do differently.
|
||||
@@ -1,34 +1,50 @@
|
||||
---
|
||||
title: "How to stop being a scrub and achieve your goals"
|
||||
title: "What is a scrub?"
|
||||
description: "Devin Sirlin applied the term scrub to a specific type of player he saw in the fighting-game circuit. This person stops improving and fails to win because they create self-imposed rules that get in their way. This concept goes far beyond getting good at video games or winning competitions. It applies to everything in life. How are you creating self-imposed rules that conflict with your goals?"
|
||||
---
|
||||
|
||||
[David Sirlin](https://en.wikipedia.org/wiki/David_Sirlin) wrote a great mini-book from his blog, [sirlin.net](https://sirlin.net), called Playing to Win. It's a treatise about how to learn how to win in competitive pursuits. He made his gaming career playing [Street Fighter](https://en.wikipedia.org/wiki/Street_Fighter), but also designing and balancing competitive video and table-top games, so that's the context he's coming from.
|
||||
Let me just start this by saying I have a deep fear of failure. I spend a lot of time and energy on preparation and analysis in order to avoid it. Many times the failure is only in my own eyes and that's something that I endeavor to let go of.
|
||||
|
||||
This had lead me to That can often ironically lead to failure! I've come to realize many subtle ways in which I can take responsibility for my own success. Much of this is around re-framing success and re-framing what I can do.
|
||||
|
||||
One author that had a big impact on me is [David Sirlin](https://en.wikipedia.org/wiki/David_Sirlin). He wrote a great mini-book from his blog, [sirlin.net](https://sirlin.net), called [Playing to Win](https://sirlin.squarespace.com/ptw). It's a treatise about how to learn how to win in competitive pursuits. He made his gaming career playing [Street Fighter](https://en.wikipedia.org/wiki/Street_Fighter), but also designing and balancing competitive video and table-top games, so that's the context he's coming from.
|
||||
|
||||
I dabble in competitive games. They are something I really enjoy, but I don't play them at a particularly competitive level. However, the concepts really stuck with me.
|
||||
|
||||
## What is a scrub?
|
||||
|
||||
One of his key terms is the participant he calls [the scrub](https://sirlin.squarespace.com/ptw-book/introducingthe-scrub). It's a very specific definition and a useful one, even if I think the condescension implied by the name is unhelpful. I'll let him define it with a few excerpts.
|
||||
|
||||
> A scrub is a player who is handicapped by self-imposed rules that the game knows nothing about. A scrub does not play to win.
|
||||
>
|
||||
> Now, everyone begins as a poor player—it takes time to learn a game to get to a point where you know what you’re doing. There is the mistaken notion, though, that by merely continuing to play or “learn” the game, one can become a top player. In reality, the “scrub” has many more mental obstacles to overcome than anything actually going on during the game. The scrub has lost the game even before it starts. He’s lost the game even before deciding which game to play. His problem? He does not play to win.
|
||||
>
|
||||
> ...The first step in becoming a top player is the realization that playing to win means doing whatever most increases your chances of winning. That is true by definition of playing to win. The game knows no rules of “honor” or of “cheapness.” The game only knows winning and losing.
|
||||
> [...]
|
||||
>
|
||||
> The first step in becoming a top player is the realization that playing to win means doing whatever most increases your chances of winning. That is true by definition of playing to win. The game knows no rules of “honor” or of “cheapness.” The game only knows winning and losing.
|
||||
>
|
||||
> A common call of the scrub is to cry that the kind of play in which one tries to win at all costs is “boring” or “not fun.” Who knows what objective the scrub has, but we know his objective is not truly to win. Yours is. Your objective is good and right and true, and let no one tell you otherwise. You have the power to dispatch those who would tell you otherwise, anyway. Simply beat them.
|
||||
|
||||
[Introducing the Scrub](https://sirlin.squarespace.com/ptw-book/introducingthe-scrub) by David Sirlin
|
||||
That's a pretty harsh description of a type of person we have likely known, been, are and will-be. I think it's bit too harsh, but there's something very useful here.
|
||||
|
||||
I think if you took this literally you might focus entirely on winning. However, I think that's actually scrub-behavior for most people! Most people who focus solely on winning have created a narrow mindset that actually limits their ability to thrive in their chosen sport, hobby, vocation and life.
|
||||
|
||||
## There's more than just winning
|
||||
|
||||
Sirlin examines the scrub from a very narrow context: winning the game. This is itself a self-imposed rule. It is deciding that the only thing that matters in the game is your final score and your tournament standings.
|
||||
|
||||
This is a very narrow way to play a game. There are so many other things to optimize for: teaching, growing the community, making friends, personal fulfillment.
|
||||
This is a very narrow way to play a game. What is it you might want out of a game? There are so many other things to optimize for: teaching, growing the community, making friends, personal fulfillment. It's not uncommon that people will drive themselves to succeed only to incur great physical, emotional and interpersonal costs. How many athletes damage their bodies in their youth? How many entrepreneurs sacrifice their relationships for their business?
|
||||
|
||||
You have to ask yourself, what are you working for? Why do you want to win? Do you think it will bring you happiness or is that just an assumption that creates a mental obstacle and prevents you from seeing the happiness all around you?
|
||||
|
||||
## Growth and Goals
|
||||
|
||||
There are two key aspects of a scrub that make them stand out as undesirable models of behavior: they don't learn or improve and they don't reach their goals. Any non-trivial goal requires that we grow and learn to be able to reach our goals, otherwise you could trivially achieve them and then they aren't goals anymore. Anything worth talking about requires change.
|
||||
There are two key aspects of a scrub that make them stand out as undesirable models of behavior: they don't learn or improve and they don't reach their goals. Any non-trivial goal requires that we grow and learn to be able to reach our goals, otherwise you could trivially achieve them and then they aren't goals anymore. Anything worth doing and talking about requires change.
|
||||
|
||||
Growth can be the goal in itself, but generally it's in service of a goal. Growth without a goal is really just play. It's exploring and trying new things for their own sake. Children do this really well before they learn to think ahead and compare themselves to others. Adults do it very poorly because we tend to think ahead compare ourselves to others or abstract ideas of what we should be.
|
||||
|
||||
Turning your play into work is a surefire way to make it unpleasant. The only thing that keeps us going through the unpleasant times is hope for some reward at the end.
|
||||
|
||||
## Understanding Failure
|
||||
|
||||
> If you never lose, you are never truly tested, and never forced to grow. A loss is an opportunity to learn.
|
||||
|
||||
31
src/content/notes/Music/Favorite Albums.md
Normal file
31
src/content/notes/Music/Favorite Albums.md
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
title: Favorite Albums
|
||||
---
|
||||
|
||||
I'm an album listener. I really prefer to listen to a coherent set of songs that are narratively linked and have a cohesive them and tone. The algorithmic feeds never seem to quite leave me happy with the results. Though there is the one exception of 90s rock because that's what I grew up listening to on the radio.
|
||||
|
||||
As I remember them and listen to them, I'll add more albums here under various genres.
|
||||
|
||||
## Metal
|
||||
|
||||
### Unleash the Archers
|
||||
|
||||
I've been following this Canadian band since their third album, Time Stands Still. It has some catchy tracks like Test Your Metal.
|
||||
|
||||
All their following albums are built around a narrative, which progresses throughout the album. There are a few recurring characters and themes between them such as The Matriarch. I listened to Apex many times, but didn't find myself drawn to Abyss as much initially. However, later I came back to the album and listened to it a dozen times on repeat.
|
||||
|
||||
Their latest, Phantoma, didn't have the strength of execution and I've struggled to like it as much as many of the others, even though I enjoy the story. The sand out track for me is Ghost in the Mist, but sometimes I just want to listen to Buried in Code when I'm coding.
|
||||
|
||||
### Elevation by We are the Catalyst
|
||||
|
||||
This Swedish alt-metal band got my attention with the track Askja. I was fed it by some algorithm or list and it's stuck with me. I've enjoyed that album, Elevation, but never really picked up their others.Their lead singer, Cat Fey, infuses a lot of emotion into her vocals. Their music is melodic and relatively heavy. The singing never quite loses it's clarity, but has the force of screaming.
|
||||
|
||||
## Electronic / Synthwave
|
||||
|
||||
### Unicorn by GUNSHIP
|
||||
|
||||
I've been really liking this album lately. It seems like a collection of concept pieces because GUNSHIP got various guest artists to join for single tracks. The overall album is more on hopeful and nostalgic side of retro-futurism. Bonus points for the Blood for the Blood God reference.
|
||||
|
||||
### Scandroid by Scandroid
|
||||
|
||||
I'm a sucker for robots. This was actually one of the first albums I encountered that started to embrace the retro-futuristic aesthetic, with a little bit of cyberpunk mixed it. It introduced me to two terms I hope will someday become common: "probots" vs "robophobes".
|
||||
91
src/content/notes/NixOS/Cloud.md
Normal file
91
src/content/notes/NixOS/Cloud.md
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: Deploying NixOS on the cloud
|
||||
tags:
|
||||
- NixOS
|
||||
- cloud
|
||||
---
|
||||
|
||||
There are two major things to consider when deploying NixOS systems into the cloud. The first is where to host, the other is how to set it up.
|
||||
|
||||
As always, if you have any input such as suggestions, corrections or more data, please [ping me on Mastodon](https://mastodon.social/@periodic) and I'd love to hear from you.
|
||||
|
||||
## Providers
|
||||
|
||||
There is a list of [NixOS friendly hosters](https://wiki.nixos.org/wiki/NixOS_friendly_hosters) on the wiki, but I have no idea how up-to-date it is.
|
||||
|
||||
My criteria are all skewed by the fact that I am an English speaker based on the west coast of the United States. That means additional latency to Europe and I'm subject to a lot of US laws.
|
||||
|
||||
- Location
|
||||
- Local laws and juristictions
|
||||
- Latency
|
||||
- Ease of installation
|
||||
- How easy it is it to install NixOS on their systems? Is it a native option, require a custom image, or a more specialized option?
|
||||
- Speed
|
||||
- How much latency is in the network?
|
||||
- Price
|
||||
- How much does a box cost? VPS instances are fairly commodified at this point, so prices should be fairly comparable. My benchmark is 2 vCPU with 4 GB of RAM because that will serve most small sites well with overhead for other tasks.
|
||||
- Support
|
||||
- How easy is it to contact support?
|
||||
- Do they speak my language?
|
||||
- Other offerings
|
||||
- What other services do they offer if I should choose to expand or require more specialized services? Database hosting, CDN, file hosting, etc.
|
||||
- Company Values
|
||||
- I want a company that matches my values and isn't just an autonomous extractive entity.
|
||||
|
||||
### Providers I've investigated
|
||||
|
||||
| Provider | Location | NixOS Support | Notes |
|
||||
| ----------------------------------- | -------------- | ------------- | --------------------------------------------------------------------------------------------- |
|
||||
| [Gandi.net](https://www.gandi.net/) | France | Native | Had some billing, latency and packet loss issues. |
|
||||
| Linode/Akamai | US/Global | Custom ISO | Still setting up |
|
||||
| Hetzner | Germany/US | Custom ISO | I got blocked here because they do not accept credit-card payment without contacting support. |
|
||||
| [vpsFree.cz](https://vpsfree.org/) | Czech Republic | Native | They operate as a "community" more than a company. |
|
||||
| [Crocuda.com](https://crocuda.com/) | France | Native | Only accept payment in crypto. IPv6 only. They pride themselves on having no KYC. |
|
||||
|
||||
#### Gandi.net
|
||||
|
||||
Last tried: May 2026
|
||||
|
||||
I was drawn to them initially because they are based in France and seem relatively professional. I've also heard their name come up positively in discussions of domain registrars, so I thought I'd give them a try.
|
||||
|
||||
I was able to set up an account fairly quickly, though initially my billing didn't go through and my account got stuck in a state of pending a charge to fund a pre-paid account. I tried initiating payment for that order, but that just resulted in a new order being created and paid, leaving my account hanging and pending billing verification. Eventually I figured out that I could cancel that first order, putting my account back into the initial state and allowing me to set up billing again which worked.
|
||||
|
||||
Setting up a NixOS system was trivial. It is an option in the provisioning flow. I was able to have the system up and running within minutes. They provide a very minimal Nix config whihc is mostly just hardware settings. It was a simple matter to integrate that into a new host configuration, checking that out on the host, building and reboot.
|
||||
|
||||
The major issue that I had with this provider was latency. I had a minimum latency of about 150ms, which is just enough to create a noticeable lag when typing. There were occasional spikes in latency and dropped packets which were far more annoying. These would happen for about 1% of pings that I measured and could result in dropped packets for a few seconds at a time.
|
||||
|
||||
#### Linode
|
||||
|
||||
TODO
|
||||
|
||||
#### Hetzner
|
||||
|
||||
TODO
|
||||
|
||||
#### Crocuda
|
||||
|
||||
They have a pretty slick terminal UI that you can access over SSH. It labels itself as being alpha status.
|
||||
|
||||
However, only accepting crypto for payments is a bit of a non-started for me. It's a bit odd since their pricing is all listed in dollars. They'll give you about $1 in credits, which is enough for about 5 days of usage.
|
||||
|
||||
They also pride themselves on no KYC. That's maybe nice from a privacy standpoint, but they can still access everything on the systems, so your data isn't private. This makes me think my neighbors on the boxes will not be the most savory folks. I would be wary of my data getting wrapped up in some criminal activity or having my capacity impacted by the actions of my neighbors. A company like this may attract clients who are prone to activities that would get them in trouble with other providers. That could include using exploits to break out of their VMs and affect my data or the stability of the system.
|
||||
|
||||
They seem to only support IPv6, which makes it hard to use as a web server.
|
||||
|
||||
Prices seem very competitive, at about $10/month for my target system.
|
||||
|
||||
Latency is around 180ms.
|
||||
|
||||
## Installation
|
||||
|
||||
If a provider doesn't support native NixOS then there are a few options. If they support a custom ISO then I can create a NixOS image and load that. Another option is to use one of the tools that allow you to convert an existing system into NixOS, such as [nixos-anywhere](https://github.com/nix-community/nixos-anywhere/tree/main) or [nixos-infect](https://github.com/elitak/nixos-infect).
|
||||
|
||||
TODO
|
||||
|
||||
## Deployments and management
|
||||
|
||||
TODO
|
||||
|
||||
- [morph](https://github.com/DBCDK/morph) - Morph is a tool for managing existing NixOS hosts - basically a fancy wrapper around nix-build, nix copy, nix-env, /nix/store/.../bin/switch-to-configuration, scp and more. Morph supports updating multiple hosts in a row, and with support for health checks makes it fairly safe to do so.
|
||||
- [colmena](https://github.com/zhaofengli/colmena) - Colmena is a simple, stateless NixOS deployment tool modeled after NixOps and morph, written in Rust. It's a thin wrapper over Nix commands like nix-instantiate and nix-copy-closure, and supports parallel deployment.
|
||||
- [NixOps](https://github.com/NixOS/nixops) - NixOps is a tool for deploying to NixOS machines in a network or the cloud.
|
||||
40
src/content/notes/Running Untrusted Code.md
Normal file
40
src/content/notes/Running Untrusted Code.md
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: Running Untrusted Code
|
||||
---
|
||||
|
||||
What is untrusted code? It's any code for which you cannot vouch for the entire chain of custody. Any code you don't trust should be in a sandbox with as little privilege as possible.
|
||||
|
||||
This includes a lot of scripts that you might get from the internet. Some scripts are small enough to review yourself, but honestly, who wants to do that every time? There are tons of small scripts and utilities I might want to run that I don't trust.
|
||||
|
||||
Another case is LLM output. Who knows what the LLM might output? They hallucinate and make many mistakes. No one wants to accidentally run `rm -rf /` or anything like it.
|
||||
|
||||
There's also the case of supply chain attacks. We trust a lot of code that we download implicitly. A lot of code dependencies are downloaded and executed as part of programming. Sometimes these have malicious code slipped in.
|
||||
|
||||
However, you have to draw the line somewhere. For example, it's hard to trust every package on your OS. If someone slipped malicious code into my window manager I would be pretty fucked.
|
||||
|
||||
## Using Docker
|
||||
|
||||
My favorite trick for running untrusted code is to run it in a small one-time container.
|
||||
|
||||
Here's an example for running a python script.
|
||||
|
||||
```bash
|
||||
docker run --rm \
|
||||
-v ~/src/repo:/repo:ro \ # Mount this RO unless it needs to do something like NPM builds or VENV installs
|
||||
-v ~/data:/data:ro \ # Mount RO to prevent modification.
|
||||
--network none \ # No network
|
||||
--cap-drop ALL \ # Drop other capabilities
|
||||
--security-opt no-new-privileges \ # Make sure it gets no new ones
|
||||
python:3.12-slim python3 /repo/untrusted_script.py
|
||||
```
|
||||
|
||||
Some additional optional arguments if you are concerned:
|
||||
|
||||
```bash
|
||||
--read-only \ # Make the container RO
|
||||
--tmpfs /tmp \ # Give it some dedicated scratch space
|
||||
--memory 512m \ # Limit memory
|
||||
--cpus 1 \ # Limit CPU
|
||||
--pids-limit 100 \ # Prevent fork bombing
|
||||
--user 1000:1000 # Run as a non-root user to further limit capabilities and prevent root access to whatever you do give it
|
||||
```
|
||||
BIN
src/content/notes/images/bingley.jpg
Normal file
BIN
src/content/notes/images/bingley.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 197 KiB |
BIN
src/content/notes/images/clover.jpg
Normal file
BIN
src/content/notes/images/clover.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 350 KiB |
BIN
src/content/notes/images/rosie.jpg
Normal file
BIN
src/content/notes/images/rosie.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 651 KiB |
50
src/content/notes/now.md
Normal file
50
src/content/notes/now.md
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: Now
|
||||
---
|
||||
|
||||
This is my [Now Page](https://nownownow.com/about). It hopes to answer the question, **"what are you doing these days?"**
|
||||
|
||||
## Pets
|
||||
|
||||
| Rosie | Bingley | Clover |
|
||||
| --------------------- | ----------------------------------- | --------------------------------- |
|
||||
| ![[images/rosie.jpg]] | ![[images/bingley.jpg]] | ![[images/clover.jpg]] |
|
||||
| 8 yeas old | 8 years old | 3 years old |
|
||||
| GSD / Husky / AmStaff | Husky / AmStaff | Terrier / Chihuahua / many others |
|
||||
| Loves sun, her ball | Loves food, wrestling, being a lump | Loves barking, chasing |
|
||||
|
||||
## Projects
|
||||
|
||||
- Creating and improving my personal site, Blazestar.net
|
||||
- Comments
|
||||
- [Webmentions](https://webmention.io/)
|
||||
- More content
|
||||
- Exploring ways to use Haskell for various projects, particularly the web
|
||||
- Maintaining our forest of timber bamboo
|
||||
- Logging observations of all the [Messier Objects](https://en.wikipedia.org/wiki/Messier_object)
|
||||
|
||||
## Hobbies
|
||||
|
||||
- Playing miniature war games, mostly Warhammer 40k
|
||||
- Weekly games and monthly tournaments at [Great Escape Games](https://greatescapegames.com/)
|
||||
- T'au Empire: Printed and painted around 3,000 points of [FishMech](https://pipermakes.art/collections/fishmech) proxies
|
||||
- Blood Angels: Printed and painted 2,000 points of various proxies
|
||||
- Enjoying World of Warcraft: Midnight
|
||||
- [Main Tank](https://raider.io/characters/us/tichondrius/Ironsoul) for <Mitsakes Were Made>
|
||||
- Hiking and camping in the High Sierras
|
||||
- Star Gazing with the [Sacramento Valley Astronomical Society](https://www.svas.org/)
|
||||
- Current equipment:
|
||||
- Meade 2160 254mm SCT
|
||||
- Stellarview ST102 102mm refractor
|
||||
- iOptron AX Mount Pro
|
||||
|
||||
## Media
|
||||
|
||||
- Re-reading Lord of the Rings
|
||||
- Listening to [Marketplace](https://www.marketplace.org/) for economic news
|
||||
- Reading many blogs I'll get around to listing some time
|
||||
|
||||
## Personal
|
||||
|
||||
- Developing my physical health and mental health
|
||||
- Finding my place in swiftly changing culture, economy and technology
|
||||
@@ -8,6 +8,8 @@ I do full stack web-development: A little front-end, a little back-end, database
|
||||
|
||||
I have been a software developer from a young age and have spent two decades in the tech industry. I've done freelance, start-ups (Asana, Fossa) and some big corps (Google, Visa).
|
||||
|
||||
See my [now page](/now) for more about what I'm focused on right now.
|
||||
|
||||
## What is "Blaze Star"?
|
||||
|
||||
I picked up this domain about a year ago. I was looking for some places to host some of my own things. There's a domain I've had for over a decade but never really liked. I wanted something memorable, a bit obscure, but easy to describe.
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
---
|
||||
import type { MarkdownHeading } from "astro";
|
||||
import type { CollectionEntry } from 'astro:content';
|
||||
import RootLayout from '../layouts/RootLayout.astro';
|
||||
import FormattedDate from '../components/FormattedDate.astro';
|
||||
import NotchedBox from '../components/NotchedBox.astro';
|
||||
import TableOfContents from '../components/TableOfContents.astro';
|
||||
import { Image } from 'astro:assets';
|
||||
|
||||
type Props = CollectionEntry<'blog'>['data'] & { slug: string; };
|
||||
type Props = CollectionEntry<'blog'>['data'] & { slug: string; headings: MarkdownHeading[] };
|
||||
|
||||
const { slug, title, description, pubDate, updatedDate, heroImage, tags } = Astro.props;
|
||||
const { slug, title, description, pubDate, updatedDate, heroImage, tags, headings } = Astro.props;
|
||||
---
|
||||
|
||||
<style>
|
||||
@@ -77,6 +79,7 @@ const { slug, title, description, pubDate, updatedDate, heroImage, tags } = Astr
|
||||
</div>
|
||||
}
|
||||
<hr />
|
||||
<TableOfContents headings={headings} />
|
||||
<div class="e-content">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -18,5 +18,9 @@ export async function getPublishedPosts(
|
||||
export async function getAllTags(): Promise<string[]> {
|
||||
const posts = await getCollection("blog", publishedOnly);
|
||||
|
||||
return posts.flatMap((p) => p.data.tags || []);
|
||||
return Object.keys(
|
||||
Object.fromEntries(
|
||||
posts.flatMap((p) => p.data.tags || []).map((t) => [t, 1]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
49
src/lib/headings.ts
Normal file
49
src/lib/headings.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { MarkdownHeading } from "astro";
|
||||
|
||||
export type HeadingsTree = Array<MarkdownHeading | HeadingsTree>;
|
||||
|
||||
// could take a list like [5, 1, 2, 2, 3, 5, 1] and should return
|
||||
// [ [ [ [ [ 5 ] ] ] ], 1, [ 2, 2, [3, [[5]]]], 1]
|
||||
export function makeHeadingsTree(headings: MarkdownHeading[]): HeadingsTree {
|
||||
let index = 0;
|
||||
let currDepth = 1;
|
||||
const treeStack = [[]] as Array<HeadingsTree>;
|
||||
|
||||
while (index < headings.length) {
|
||||
const currHeading = headings[index];
|
||||
|
||||
// If the next heading is at the current depth, push it into the tree
|
||||
if (currHeading.depth == currDepth) {
|
||||
treeStack[treeStack.length - 1].push(currHeading);
|
||||
index += 1;
|
||||
}
|
||||
|
||||
// If the next heading is deeper, go deeper
|
||||
if (currHeading.depth > currDepth) {
|
||||
const nextLevel = [] as HeadingsTree;
|
||||
treeStack[treeStack.length - 1].push(nextLevel);
|
||||
treeStack.push(nextLevel);
|
||||
currDepth += 1;
|
||||
}
|
||||
|
||||
// If the next heading is shallower, back out
|
||||
if (currHeading.depth < currDepth) {
|
||||
treeStack.pop();
|
||||
currDepth -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return treeStack[0];
|
||||
}
|
||||
|
||||
export function removeEmptyLevels(tree: HeadingsTree): HeadingsTree {
|
||||
const newLevel = tree.map((node) =>
|
||||
Array.isArray(node) ? removeEmptyLevels(node) : node,
|
||||
);
|
||||
|
||||
if (tree.every(Array.isArray)) {
|
||||
return tree.flatMap((node) => node);
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
@@ -14,17 +14,18 @@ function addToHierarchy(
|
||||
return tree;
|
||||
}
|
||||
|
||||
if (!tree[path[0]]) {
|
||||
tree[path[0]] = { id: null, title: null, children: null };
|
||||
}
|
||||
let curr = tree[path[0]];
|
||||
// Create a dummy node to start the loop since the Hierarchy root is not a node.
|
||||
let curr: HierarchyNode = { id: null, title: null, children: tree };
|
||||
|
||||
for (const node of path) {
|
||||
// Capitalize first letter
|
||||
const nodeName = node.charAt(0).toUpperCase() + node.slice(1);
|
||||
|
||||
for (const node of path.slice(1)) {
|
||||
curr.children ||= {};
|
||||
if (!curr.children[node]) {
|
||||
curr.children[node] = { id: null, title: null, children: null };
|
||||
if (!curr.children[nodeName]) {
|
||||
curr.children[nodeName] = { id: null, title: null, children: null };
|
||||
}
|
||||
curr = curr.children[node];
|
||||
curr = curr.children[nodeName];
|
||||
}
|
||||
|
||||
curr.id = noteId;
|
||||
|
||||
@@ -14,9 +14,9 @@ type Props = CollectionEntry<'blog'>;
|
||||
|
||||
const post = Astro.props;
|
||||
const { slug } = Astro.params;
|
||||
const { Content } = await render(post);
|
||||
const { Content, headings } = await render(post);
|
||||
---
|
||||
|
||||
<BlogPost slug={slug} {...post.data} >
|
||||
<BlogPost slug={slug} headings={headings} {...post.data} >
|
||||
<Content />
|
||||
</BlogPost>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import RootLayout from '../../layouts/RootLayout.astro';
|
||||
import NotchedBox from '../../components/NotchedBox.astro';
|
||||
import FormattedDate from '../../components/FormattedDate.astro';
|
||||
import TableOfContents from '../../components/TableOfContents.astro';
|
||||
import { type CollectionEntry, getCollection } from 'astro:content';
|
||||
import { render } from 'astro:content';
|
||||
|
||||
@@ -15,7 +16,7 @@ export async function getStaticPaths() {
|
||||
type Props = CollectionEntry<'notes'>;
|
||||
|
||||
const note = Astro.props;
|
||||
const { Content } = await render(note);
|
||||
const { Content, headings } = await render(note);
|
||||
---
|
||||
|
||||
<RootLayout>
|
||||
@@ -24,6 +25,7 @@ const { Content } = await render(note);
|
||||
<div class="last-updated">
|
||||
Last updated <FormattedDate date={note.data.updated} />
|
||||
</div>
|
||||
<TableOfContents headings={headings}/>
|
||||
<Content />
|
||||
</NotchedBox>
|
||||
</RootLayout>
|
||||
|
||||
Reference in New Issue
Block a user