Compare commits

..

1 Commits

Author SHA1 Message Date
Cadey Ratio 2fa5b904a0 blog: add third nix flakes post
Signed-off-by: Xe <me@christine.website>
2022-03-26 18:27:37 +00:00
23 changed files with 141 additions and 840 deletions

2
.envrc
View File

@ -1 +1 @@
use flake
eval "$(lorri direnv)"

2
.gitignore vendored
View File

@ -6,5 +6,3 @@ cw.tar
/result
.#*
/target
.patreon.json
.direnv

26
Cargo.lock generated
View File

@ -130,9 +130,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "axum"
version = "0.5.0"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5611d4977882c5af1c0f7a34d51b5d87f784f86912bb543986b014ea4995ef93"
checksum = "c9f346c92c1e9a71d14fe4aaf7c2a5d9932cc4e5e48d8fb6641524416eb79ddd"
dependencies = [
"async-trait",
"axum-core",
@ -142,7 +142,6 @@ dependencies = [
"http",
"http-body",
"hyper",
"itoa",
"matchit",
"memchr",
"mime",
@ -161,9 +160,9 @@ dependencies = [
[[package]]
name = "axum-core"
version = "0.2.0"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95cd109b3e93c9541dcce5b0219dcf89169dcc58c1bebed65082808324258afb"
checksum = "6dbcda393bef9c87572779cb8ef916f12d77750b27535dd6819fa86591627a51"
dependencies = [
"async-trait",
"bytes",
@ -175,9 +174,9 @@ dependencies = [
[[package]]
name = "axum-extra"
version = "0.2.0"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff3819ded1be91d7ee2cd9f0466aa345cc70ba0b0035ed47e3eac6427f83b81a"
checksum = "b5b6d79bc9c2975821d39c7df31ea766026beb9efe28c076a48cfd7d50f34f18"
dependencies = [
"axum",
"bytes",
@ -192,9 +191,9 @@ dependencies = [
[[package]]
name = "axum-macros"
version = "0.2.0"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63bcb0d395bc5dd286e61aada9fc48201eb70e232f006f9d6c330c9db2f256f5"
checksum = "c5b2a9133b2658e684c8ea04157a8bd48dac7906a2eb884ffebfb051af123394"
dependencies = [
"heck",
"proc-macro2",
@ -1191,9 +1190,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "matchit"
version = "0.5.0"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb"
checksum = "9376a4f0340565ad675d11fc1419227faf5f60cd7ac9cb2e7185a471f30af833"
[[package]]
name = "md5"
@ -1515,7 +1514,6 @@ dependencies = [
"tokio",
"tracing",
"tracing-futures",
"url",
]
[[package]]
@ -2469,9 +2467,9 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
version = "0.3.11"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bc28f93baff38037f64e6f43d34cfa1605f27a49c34e8a04c5e78b0babf2596"
checksum = "9e0ab7bdc962035a87fba73f3acca9b8a8d0034c2e6f60b84aeaaddddc155dce"
dependencies = [
"ansi_term",
"sharded-slab",

View File

@ -9,9 +9,9 @@ repository = "https://github.com/Xe/site"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
axum = "0.5"
axum-macros = "0.2"
axum-extra = "0.2"
axum = "0.4"
axum-macros = "0.1"
axum-extra = "0.1"
color-eyre = "0.6"
chrono = "0.4"
comrak = "0.12.1"

View File

@ -1,93 +0,0 @@
---
title: Compiling Code to Matter in My Living Room
date: 2022-03-28
tags:
- openscad
- 3dprinting
---
In a moment of weakness, my husband and I got a 3d printer. It's mostly been sitting around and not doing much since we got it, but recently I found a great use for it: I wanted a controller stand for my Valve Index controllers and VR full body trackers.
After doing some digging on Thingiverse, I found [this stand](https://www.thingiverse.com/thing:4587097) that looked like it had promise. So I downloaded the model, sliced it and then sent it over to Kyubey:
<blockquote class="twitter-tweet"><p lang="tl" dir="ltr">Kyuubey is happy <a href="https://t.co/atTLN8MSgc">pic.twitter.com/atTLN8MSgc</a></p>&mdash; Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1507485129907871747?ref_src=twsrc%5Etfw">March 25, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
[Kyubey's name is a reference to <a href="https://madoka.fandom.com/wiki/Kyubey">Kyubey</a> from Puella Magi Madoka Magika</a>.](conversation://Mara/hacker)
Once it was done I ended up with a stand that I could feed [these cables I got from Amazon](https://www.amazon.ca/gp/product/B09LSF8XL9/) through. The tracker holes worked great, but the controller holes were just barely too small.
This was kinda frustrating and I almost gave up on the project, but then I remembered that [OpenSCAD](https://openscad.org) existed. OpenSCAD is a weird programming environment / 3D modeling hybrid program that I've seen used on Thingiverse. It works by letting you position platonic solids into a 3d environment, and from there you can create anything you want.
One of the primitives that OpenSCAD offers is a cylinder. So I wondered if I could use one of those to widen the hole in the index stand and then reprint the part with the wider hole.
[Wait, you're using a CAD program to fix your 3D print by modifying the model instead of using, I don't know, a drill and 5 minutes to make it fit that way?](conversation://Numa/dismay)
[There's no doing like overdoing!](conversation://Cadey/enby)
After some finangling, I managed to get the cylinders in the right place with this OpenSCAD code:
```scad
//difference() {
color("magenta") translate([0, 0, 0]) import("./assets/ValveTrackerDeckEditedByInugoro.stl");
// bores for controller holders
color([0, 1, 0]) translate([63, 44, 0]) cylinder(h = 55, r = 4.75);
color([0, 1, 0]) translate([-63, 44, 0]) cylinder(h = 55, r = 4.75);
//}
```
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Some finagling required <a href="https://t.co/7T0R6x1XoP">pic.twitter.com/7T0R6x1XoP</a></p>&mdash; Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508566854926745614?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
And when I uncommented out the `difference()` block, it ends up looking good enough:
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="und" dir="ltr"><a href="https://t.co/fiShvlN8QH">pic.twitter.com/fiShvlN8QH</a></p>&mdash; Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508567556759728141?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
So then I took a good solid look at the rest of the 3D printed part to see if I could improve on anything else before I sent it to another round of the printer. The last stand took _14 hours_ to print and used a lot of material. I want to avoid waste.
Something I noticed is that the front of the print where all the cables come out was a bit too thin. All 5 of the cables wouldn't fit in there (my braided cables must have been thicker than the ones that the original modeler used). So again I grabbed a few platonic solids and managed to make it work out:
```scad
// widen the paths
color("green") translate([0, -16, 1.3]) rotate([0, 0, 90]) cube([10, 57, 7.8], center = true);
color("green") translate([0, 0, 1.7]) rotate([0, 0, 0]) cube([25, 30, 7], center = true);
```
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="und" dir="ltr"><a href="https://t.co/pKAVtiPfDS">pic.twitter.com/pKAVtiPfDS</a></p>&mdash; Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508568858650685440?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
Then I wanted to add some wedges into the underside of the part to help me get the print off the bed. Most people have a problem with bed adhesion being too little. I have too much bed adhesion. So I added some angled rectangles:
```scad
// wedges to help get the print off the bed
color([1, 1, 0]) translate([-120, 0, 0]) rotate([15, 0, 90]) cube([10, 11, 2], center = true); // right
color([1, 1, 0]) translate([120, 0, 0]) rotate([-15, 0, 90]) cube([10, 11, 2], center = true); // left
color([1, 1, 0]) translate([0, -85, 0]) rotate([0, 15, 90]) cube([10, 11, 2], center = true); // back
color([1, 1, 0]) translate([60, 56, 1]) rotate([0, -15, 90]) cube([10, 11, 2], center = true); // front left
color([1, 1, 0]) translate([-60, 56, 1]) rotate([0, -15, 90]) cube([10, 11, 2], center = true); // front right
color([1, 1, 0]) translate([32.5, 41, 1]) rotate([0, -15, 130]) cube([10, 11, 2], center = true); // front left inner
color([1, 1, 0]) translate([-32.5, 41, 1]) rotate([0, -15, 60]) cube([10, 11, 2], center = true); // front right inner
```
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="und" dir="ltr"><a href="https://t.co/XUQ9ZeYk1H">pic.twitter.com/XUQ9ZeYk1H</a></p>&mdash; Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508569796253827077?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
And then once I spun it around for a bit and thought it was good, I sliced it in PrusaSlicer and sent it off to Kyubey. It was going to take 14 hours, so I went off to do other things, ate dinner and then went to bed while the printer continued.
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="fr" dir="ltr">Diligent bean <a href="https://t.co/yPgnJA0ZdW">pic.twitter.com/yPgnJA0ZdW</a></p>&mdash; Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508397506031460352?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
Then when I woke up, Kyubey was done:
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="und" dir="ltr"><a href="https://t.co/2E1IS810EH">pic.twitter.com/2E1IS810EH</a></p>&mdash; Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508407046995156992?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
I was excited and chiseled the print off the bed (the wedges helped a little, but it ended up making the print look kinda weird so I don't know if I will do that again), but the hole for the middle tracker didn't fit perfectly. Everything else did though.
[If you want to get prints off your printer easier, see this video for the method we're starting to use: <br /><br /><iframe width="560" height="315" src="https://www.youtube.com/embed/VCCbzCvtRzU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>](conversation://Mara/hacker)
I looked on my desk and found that a random pen that I had sitting around for months was about the right size, so I pushed it into and out of the hole a few times and then the cables fit perfectly. I assume some plastic was in a weird state or something.
Then I set everything up and I had my Index controller stand:
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="en" dir="ltr">Victory! <a href="https://t.co/A3aCtQMQt5">pic.twitter.com/A3aCtQMQt5</a></p>&mdash; Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508426229464064001?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
[I really need to get a table or something for this.](conversation://Cadey/facepalm)
I've uploaded my modified version to [Thingiverse](https://www.thingiverse.com/thing:5332988). If you want to see the OpenSCAD code, you can check it out on GitHub [here](https://github.com/Xe/3dstuff/blob/main/index_stand_hack.scad). I'm really liking OpenSCAD so far. It's very weird but it lets you do whatever you want by chaining together basic shapes to build up to what you want. I imagine I will be using it a lot in the future, especially once my husband's new sim racing gear comes in.
Having a 3D printer around is like having a very weird superpower on standby. You can compile matter in your living room, but you need a very pedantic description of what that should look like. You also can have any material you like as long as it's plastic. However when it's useful, it's a lifesaver. You can make something to fit a gap or mend something broken or even add functionality to something that lacked it. The cloud's the limit!

View File

@ -1,5 +1,5 @@
---
title: "Goodbye Kubernetes"
title: "&lt;/kubernetes&gt;"
date: 2021-01-03
---

View File

@ -415,7 +415,7 @@ world. To use a private repo, your flake input URL should look something like
this:
```
git+ssh://git@github.com:user/repo
ssh+git://git@github.com:user/repo
```
[I'm pretty sure you could use private git repos outside of flakes, however it

View File

@ -1,36 +1,32 @@
---
title: "Nix Flakes: Exposing and using NixOS Modules"
date: 2022-04-07
date: 2022-03-31
series: nix-flakes
tags:
- nixos
vod:
twitch: https://www.twitch.tv/videos/1437346416
youtube: https://youtu.be/wCZ9SwmgSck
youtube: TODO-link
---
Nix flakes allow you to expose NixOS modules. NixOS modules are templates for
system configuration and they are the basis of how you configure NixOS. Today
we're going to take our Nix flake [from the last
article](/blog/nix-flakes-2-2022-02-27) and write a NixOS module for it so that
we can deploy it to a container running locally. In the next post we will deploy
this to a server.
we can deploy it to a server.
[If you haven't read <a href="/blog/series/nix-flakes">the other articles in
this series</a>, you probably should. This article builds upon the previous
ones.](conversation://Mara/hacker)
NixOS modules are building blocks that let you configure NixOS servers. Modules
expose customizable options that expand out into system configuration.
Individually, each module is fairly standalone and self-contained, but they
build up together into your server configuration like a bunch of legos build
into a house. Each module describes a subset of your desired system
configuration and any options relevant to that configuration.
NixOS modules are the main building block of how NixOS servers are configured.
They are like lego blocks that help you build up a server from off the shelf
parts. A module describes a desired system state and they build off of eachother
in order to end up with a more elaborate result.
[You can think about them like Ansible playbooks, but NixOS modules describe the
desired end state instead of the steps you need to get to that end
state. It's the end result of evaluating all of your options against all of the
modules that you use in your configuration.](conversation://Mara/hacker)
state.](conversation://Mara/hacker)
NixOS modules are functions that take in the current state of the system and
then return things to add to the state of the system. Here is a basic NixOS
@ -71,47 +67,9 @@ nix-repl> { foo = 1; } // { bar = 2; }
{ bar = 2; foo = 1; }
```
<xeblog-conv name="Mara" mood="hacker">
Important pro tip: the merge operator is NOT recursive. If you try to do
something like:
```
nix-repl> foo = { bar = { baz = "foo"; }; }
nix-repl> (foo // { bar = { spam = "eggs"; }; }).bar
```
You will get:
```
{ spam = "eggs"; }
```
And not:
```
{ baz = "foo"; spam = "eggs"; }
```
This is because the `//` operator prefers things in the right hand side over the
left hand side if both conflict. To recursively merge two attribute sets (using
all elements from both sides), use
[lib.recursiveUpdate](https://nixos.org/manual/nixpkgs/stable/#function-library-lib.attrsets.recursiveUpdate):
```
nix-repl> (pkgs.lib.recursiveUpdate foo bar).bar
{ baz = "foo"; spam = "eggs"; }
```
</xeblog-conv>
We will use this to add the container configuration to the flake at the end of
the flake.nix file. We need to do this because the upper part of the flake with
the `forAllSystems` call will generate a bunch of system-specific attributes for
each system we support. NixOS configurations don't support this level of
granularity.
At the end of your flake.nix (just before the final closing `}`), there should
be a line that looks like this:
the flake.nix file. At the end of your flake.nix (just before the final closing
`}`), there should be a line that looks like this:
```nix
});
@ -250,20 +208,9 @@ config = mkIf cfg.enable {
};
```
<xeblog-conv name="Mara" mood="hacker">
NOTE: If you have been following along since before this article was published,
you will want to be sure to do the following things to your copy of gohello:
* Move the definition of `defaultPackage` into the `packages` attribute set with
the name `default`
* Update `defaultApp` and the other entries to point to
`self.packages.${system}.default` instead of `self.defaultPackage.${system}`
We have updated previous articles and the template accordingly. Annoyingly it
seems that this change is new enough that it isn't totally documented on the
NixOS wiki. We are working on fixing this.
</xeblog-conv>
[NOTE: You will want to be sure to do the following things to your copy of
gohello: <ul><li>Move the definition of `defaultPackage` into the `packages` attribute set with the name `default` </li><li>Update `defaultApp` and the other entries to point to `self.packages.${system}.default` instead of `self.defaultPackage.${system}`</li></ul> We have updated previous articles and the template
accordingly.](conversation://Mara/hacker)
This will do the following things:
@ -271,9 +218,7 @@ This will do the following things:
booted" and the network is active)
- Automatically restarts the service when it crashes
- Starts our `web-server` binary when running the service
- Creates a random, unique user account for the service (see
[here](http://0pointer.net/blog/dynamic-users-with-systemd.html) for more
information on how/why this works)
- Creates a random user for the service
- Creates temporary, home and cache directories for the service, makes sure that
random user has permission to use them (with the specified directory modes
too)
@ -346,11 +291,6 @@ nixosModule = { config, lib, pkgs, ... }:
};
```
[The service name is overly defensive. It's intended to avoid conflicting with
any other unit on the system named `gohello.service`. Feel free to remove this
part, it is really just defensive devops by design to avoid name
conflicts.](conversation://Mara/hacker)
Then you can add it to the container by importing our new module in its
configuration and activating the gohello service:
@ -388,21 +328,11 @@ $ curl http://10.233.1.2 -H "Host: gohello.local.cetacean.club"
hello world :)
```
<xeblog-conv name="Mara" mood="hacker">
Exercises for the reader:
Try adding a [nixos
option](https://nixos.org/manual/nixos/stable/index.html#sec-writing-modules)
that correlates to the `--bind` flag that `gohello` uses as the TCP
[As an exercise for the reader, try adding a <a
href="https://nixos.org/manual/nixos/stable/index.html#sec-writing-modules">nixos
option</a> that correlates to the `--bind` flag that `gohello` uses as the TCP
address to serve HTTP from. You will want to have the type be
`types.port`. If you are stuck, see
[here](https://github.com/Xe/nixos-configs/tree/master/common/services) for inspiration.
Also try adding `AmbientCapabilities = "CAP_NET_BIND_SERVICE"` and
`CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"` to your `serviceConfig` and
bind `gohello` to port 80 without nginx involved at all.
</xeblog-conv>
`types.port`.](conversation://Mara/hacker)
You can delete this container with `sudo nixos-container destroy gohello` when
you are done with it.
@ -432,12 +362,3 @@ Next time I will cover how to install NixOS to a server and deploy system
configurations using [deploy-rs](https://github.com/serokell/deploy-rs). This
will allow you to have your workstation build configuration for your servers and
push out all the changes from there.
---
Many thanks to Open Skies for being my fearless editor that helps make these
things shine.
In part of this post I use my new Xeact-powered HTML component for some of the
conversation fragments, but the sizing was off on my iPhone when I tested it. If
you know what I am doing wrong, please [get in touch](/contact).

View File

@ -1,53 +0,0 @@
---
title: Stop Using Politics As A Cudgel To Discourage Experimentation
date: 2022-04-21
tags:
- rant
- systemd
- communityhealth
---
So let's say you get bored one day and you decide you want to do things that god
and man have decreed impossible. Let's also say that this exact thing involves a
tool that just happens to rustle all of the jimmies (for reasons that are not
entirely clear). Then you get it all to a point where you want to submit it
upstream so you can get help experimenting with this tool.
So you submit it to upstream in the experimental branch, expecting very little
pushback so you can get help tinkering with things. But once you submit it
upstream, [all hell breaks
loose](https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/33329).
Stop using politics as a cudgel to discourage experimentation. Yes it involves
systemd. Just because you think that the tool is overcomplicated doesn't mean
that other people don't find it useful. Trying to shut down experimentation is
how you get people to leave the community or give up participating in open
source altogether.
The reactions in that thread are both disappointing and somewhat to be expected.
I don't know why people have such a negative reaction to systemd. It's just an
init system, not a religion. It wouldn't have become a good choice for so much
of the Linux ecosystem without it having solid technical merits. If it is really
that bad then the mantle of responsibility is on you for coming up with a better
option.
[No, OpenRC is not that option. It can be PART OF an option, but it is not a
competitor by itself.](conversation://Cadey/coffee)
I know I said I'd stop ranting on this blog as much, but really this stuff
grinds my gears and I feel that I should use my platform for good in this
regard. This is inexcusable. I want to reiterate that I have _no_ power in this
regard. I am just some random person on a blog that got frustrated at the
reactions to this contribution. Some pushback is acceptable. Accusing a
contributor of ignorance is inexcusable. Comments like this have no place in
open source contributions:
> SysTemD is the STD of operating systems. There is no "one little poke", you
> can't be a little bit pregnant.
Jake, if you're out there reading this: keep doing this thing. It is a fantastic
creation that I thought was impossible. You may have to soft-fork the
distribution to get this to work reliably, but I really want to see where this
rabbit-hole goes.
Keep hacking.

View File

@ -1,118 +0,0 @@
---
title: "What To Do As A Recruiter When A Gender-diverse Person Asks You To Update Their Name"
date: 2022-04-01
---
[I really wish this was an April Fool's post. I had a few ideas planned, but
maybe you will get to see them next year.<br /><br />As a reminder, I am
speaking for myself and not for my employer.](conversation://Cadey/coffee)
This post is directed at all of the recruiters that are reading this blog. This
is a scenario that many of you may not have dealt with. After having an example
of this with a recruiter recently I figure it's a teaching moment.
I am speaking up about this because I know many others who have gone through the
same kinds of problems and have not felt safe to speak up about them. I am not
speaking for those people in this post, but I want to use my platform as a
blogger to amplify the sentiment of what I have heard over the years.
## To Recruiters
As a recruiter, if you are cold-emailing someone, please do the research to get
their name correct. If you do not and someone asks you to correct it, do it.
When gender-diverse people like me get an email that references an out of date
name, it is seen as a sign that the person sending that email has not done their
research before sending that email out into the void.
When you correct that name in your system also make sure to cancel all outgoing
automated emails to that person. The caching layer of the recruiting system may
have already drafted those emails based on a template. If they go out, this will
be seen as a _massive sign of disrespect_. It will also make the person
receiving that email question if you _actually corrected_ the name in that
system or not. It may make the recipient also question if you are just giving
them lip service to save face instead of making a genuine effort to ensure that
the recruiting system has accurate information in it.
This is not a good way to foster the kind of trust needed for a gender-diverse
person to want to choose your employer as the single point of failure for access
to medication, food and regular medical checkups. For many gender-diverse
people, changing jobs can mean an interruption of access to life-saving
medication.
You may get a slightly angry reply if you send out emails with incorrect
information. This can happen because gender-diverse people are likely to feel
like society really doesn't care about them and that they are not being
respected to have agency over their identity. To some this is a fact and not
a feeling. And with
[all](https://www.theguardian.com/us-news/2022/mar/10/idaho-bill-trans-youth-treatment-ban-passes-house)
[of](https://www.washingtonpost.com/dc-md-va/2022/03/17/texas-trans-child-abuse-investigations/)
[the](https://www.nbcnews.com/nbc-out/out-politics-and-policy/alabama-bill-seeks-ban-hormone-treatments-trans-youth-rcna18512)
[actions](https://www.hrc.org/press-releases/breaking-2021-becomes-record-year-for-anti-transgender-legislation)
governments have been taking to directly attack the freedoms and rights to
self-determination that gender-diverse people like me rely on, you can't blame
them for being fed up with the situation. It is not fun to feel like your very
existence is made out to be some black mark of doom on Western civilization. It
is even less fun to be reminded of that when reading your email inbox. Please
understand that we mean well, society is just broken in general.
The least you can do is ensure that you do _any amount of research_ to ensure
that you are using the correct name. It may be a good idea to add the following
text to your recruiting emails (before you brag about fundraising is probably
best, I tune out about then):
> If I got your name incorrect, please let me know what name/pronouns you would
> like me to update our system to use. I got this name from $SOURCE.
Adding the source of where you got that name from can help make this less
stressful for gender-diverse people. People's names are spattered everywhere
across the internet. Letting people know where you got that information from can
help them know what to fix if a fix is needed.
Some chosen names may seem "weird" due to societal biases that serve to ensure
that the primary way that people use to refer to eachother in particular are not
chosen by the people being referred to. Trust that the person on the other end
is being honest about their identity. The truth requires no belief.
If they ask you to update their pronouns, respect that and ensure you use them
without failure. Using the wrong pronouns can be seen as an even worse
disrespect than using the wrong name. You do not want this to happen if your
goal is to find people to hire.
## To Gender-diverse People
Yeah, this situation sucks. I can't disagree. You really do need to assume good
faith as much as you can. Most of these recruiter systems rely on ["data
enrichment" APIs](https://clearbit.com/) and potentially outdated mass scraping
of LinkedIn and people's blogs.
It can help if you make publicly available posts like
[this](/blog/xe-2021-08-07) that unambiguously say what you want people to call
you by. Keep it updated in case journalists decide to compare your chosen name
to mercenary groups.
Try to be as polite and direct as possible. Here is an example of how I have
asked recruiters to update their information in the past:
> Please update your files with the name "Xe Iaso" (capital I). I am
> slowly moving away from "Christine Dodrill" as the name I use to
> represent myself professionally.
If you are moving away from a "dead name", you may want to use something like
this:
> I have no record of a "Christine Dodrill" at this email address. You may want
> to look elsewhere. If you would like to proceed with me instead, here is
> information about me: https://christine.website.
Throw in your pronouns too to be safe.
[I really need to change this blog's domain, but I have such amazing SEO that I
really don't want to break it.](conversation://Cadey/coffee)
Also consider deleting the email and not replying to them. That's totally valid
too unless you are in desperate need for a new employer.
You do not need to justify speaking up about an employer having the wrong name
for you. The truth requires no belief. Speaking the truth to power is the
essence of valor, which is one of the highest forms of love.

View File

@ -1,16 +1,7 @@
let Person =
{ Type =
{ name : Text
, tags : List Text
, gitLink : Optional Text
, twitter : Optional Text
}
{ Type = { name : Text, tags : List Text, gitLink : Text, twitter : Text }
, default =
{ name = ""
, tags = [] : List Text
, gitLink = None Text
, twitter = None Text
}
{ name = "", tags = [] : List Text, gitLink = "", twitter = "" }
}
let Author =

View File

@ -36,7 +36,6 @@ img {
.conversation-chat {
align-self: center;
min-width: 0;
}
.gruvbox-dark pre, pre {

View File

@ -1,75 +0,0 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1649676176,
"narHash": "sha256-OWKJratjt2RW151VUlJPRALb7OU2S5s+f0vLj4o1bHM=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "a4b154ebbdc88c8498a5c7b01589addc9e9cb678",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"naersk": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1650265945,
"narHash": "sha256-SO8+1db4jTOjnwP++29vVgImLIfETSXyoz0FuLkiikE=",
"owner": "nix-community",
"repo": "naersk",
"rev": "e8f9f8d037774becd82fce2781e1abdb7836d7df",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "naersk",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1650647760,
"narHash": "sha256-Ng8CGYLSTxeI+oEux0x+tSRA6K7ydoyfJNQf56ld+Uo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b80f570a92d04e8ace67ff09c34aa48708a5c88c",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1650161686,
"narHash": "sha256-70ZWAlOQ9nAZ08OU6WY7n4Ij2kOO199dLfNlvO/+pf8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1ffba9f2f683063c2b14c9f4d12c55ad5f4ed887",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"naersk": "naersk",
"nixpkgs": "nixpkgs_2"
}
}
},
"root": "root",
"version": 7
}

215
flake.nix
View File

@ -1,215 +0,0 @@
{
description = "A very basic flake";
inputs = {
nixpkgs.url = "nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
naersk.url = "github:nix-community/naersk";
};
outputs = { self, nixpkgs, flake-utils, naersk }:
flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ] (system:
let
pkgs = import nixpkgs { inherit system; };
naersk-lib = naersk.lib."${system}";
src = ./.;
in rec {
packages = rec {
bin = naersk-lib.buildPackage {
pname = "xesite-bin";
root = src;
buildInputs = with pkgs; [ pkg-config openssl git ];
};
config = pkgs.stdenv.mkDerivation {
pname = "xesite-config";
inherit (bin) version;
inherit src;
buildInputs = with pkgs; [ dhall ];
phases = "installPhase";
installPhase = ''
cd $src
mkdir -p $out
dhall resolve < $src/config.dhall >> $out/config.dhall
'';
};
static = pkgs.stdenv.mkDerivation {
pname = "xesite-static";
inherit (bin) version;
inherit src;
phases = "installPhase";
installPhase = ''
mkdir -p $out
cp -vrf $src/static $out
cp -vrf $src/css $out
'';
};
posts = pkgs.stdenv.mkDerivation {
pname = "xesite-posts";
inherit (bin) version;
inherit src;
phases = "installPhase";
installPhase = ''
mkdir -p $out
cp -vrf $src/blog $out
cp -vrf $src/gallery $out
cp -vrf $src/talks $out
'';
};
default = pkgs.symlinkJoin {
name = "xesite-${bin.version}";
paths = [ config posts static bin ];
};
};
devShell = pkgs.mkShell {
buildInputs = with pkgs; [
# Rust
rustc
cargo
rust-analyzer
cargo-watch
# system dependencies
openssl
pkg-config
# kubernetes deployment
dhall
dhall-json
# dependency manager
niv
# tools
ispell
];
SITE_PREFIX = "devel.";
CLACK_SET = "Ashlynn,Terry Davis,Dennis Ritchie";
RUST_LOG = "debug";
RUST_BACKTRACE = "1";
GITHUB_SHA = "devel";
};
nixosModules.bot = { config, lib, ... }:
with lib;
let cfg = config.xeserv.services.xesite;
in {
options.within.services.xesite = {
enable = mkEnableOption "Activates my personal website";
useACME = mkEnableOption "Enables ACME for cert stuff";
port = mkOption {
type = types.port;
default = 32837;
example = 9001;
description =
"The port number xesite should listen on for HTTP traffic";
};
domain = mkOption {
type = types.str;
default = "xesite.akua";
example = "christine.website";
description =
"The domain name that nginx should check against for HTTP hostnames";
};
sockPath = mkOption rec {
type = types.str;
default = "/srv/within/run/xesite.sock";
example = default;
description =
"The unix domain socket that xesite should listen on";
};
};
config = mkIf cfg.enable {
users.users.xesite = {
createHome = true;
description = "github.com/Xe/site";
isSystemUser = true;
group = "within";
home = "/srv/within/xesite";
extraGroups = [ "keys" ];
};
systemd.services.xesite = {
wantedBy = [ "multi-user.target" ];
serviceConfig = {
User = "xesite";
Group = "within";
Restart = "on-failure";
WorkingDirectory = "/srv/within/xesite";
RestartSec = "30s";
Type = "notify";
# Security
CapabilityBoundingSet = "";
DeviceAllow = [ ];
NoNewPrivileges = "true";
ProtectControlGroups = "true";
ProtectClock = "true";
PrivateDevices = "true";
PrivateUsers = "true";
ProtectHome = "true";
ProtectHostname = "true";
ProtectKernelLogs = "true";
ProtectKernelModules = "true";
ProtectKernelTunables = "true";
ProtectSystem = "true";
ProtectProc = "invisible";
RemoveIPC = "true";
RestrictSUIDSGID = "true";
RestrictRealtime = "true";
SystemCallArchitectures = "native";
SystemCallFilter = [
"~@reboot"
"~@module"
"~@mount"
"~@swap"
"~@resources"
"~@cpu-emulation"
"~@obsolete"
"~@debug"
"~@privileged"
];
UMask = "007";
};
script = let site = packages.default;
in ''
export SOCKPATH=${cfg.sockPath}
export DOMAIN=${toString cfg.domain}
cd ${site}
exec ${site}/bin/xesite
'';
};
services.nginx.virtualHosts."xesite" = {
serverName = "${cfg.domain}";
locations."/" = {
proxyPass = "http://unix:${toString cfg.sockPath}";
proxyWebsockets = true;
};
forceSSL = cfg.useACME;
useACMEHost = "christine.website";
extraConfig = ''
access_log /var/log/nginx/xesite.access.log;
'';
};
};
};
});
}

View File

@ -14,7 +14,6 @@ serde = { version = "1", features = ["derive"] }
thiserror = "1"
tracing = "0.1"
tracing-futures = "0.2"
url = "2"
[dev-dependencies]
tokio = { version = "1", features = ["full"] }

View File

@ -1,10 +1,7 @@
use std::{fs, io, path::Path};
use chrono::prelude::*;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tracing::{debug, error, instrument};
use url::Url;
pub type Campaigns = Vec<Object<Campaign>>;
pub type Pledges = Vec<Object<Pledge>>;
@ -64,33 +61,17 @@ pub struct User {
pub url: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RefreshGrant {
pub access_token: String,
pub refresh_token: String,
pub expires_in: serde_json::Value,
pub scope: serde_json::Value,
pub token_type: String,
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug)]
pub enum Error {
#[error("json error: {0}")]
#[error("json error: {0:?}")]
Json(#[from] serde_json::Error),
#[error("request error: {0}")]
#[error("request error: {0:?}")]
Request(#[from] reqwest::Error),
#[error("{0}")]
IO(#[from] io::Error),
#[error("url parse error: {0}")]
URLParse(#[from] url::ParseError),
}
#[derive(Debug, Serialize, Deserialize, Clone, Default, Eq, PartialEq)]
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct Credentials {
pub client_id: String,
pub client_secret: String,
@ -124,18 +105,12 @@ pub struct Links {
}
impl Client {
pub fn new() -> Result<Self> {
let mut creds = Credentials::default();
let p = Path::new(".patreon.json");
let config = fs::read_to_string(p)?;
creds = serde_json::from_str(&config)?;
Ok(Self {
pub fn new(creds: Credentials) -> Self {
Self {
cli: reqwest::Client::new(),
base_url: "https://api.patreon.com".into(),
creds: creds,
})
}
}
#[instrument(skip(self))]
@ -182,57 +157,4 @@ impl Client {
let data: Data<Vec<Object<Pledge>>, Object<User>> = serde_json::from_str(&data)?;
Ok(data.included.unwrap())
}
/*
POST www.patreon.com/api/oauth2/token
?grant_type=refresh_token
&refresh_token=<the users refresh_token>
&client_id=<your client id>
&client_secret=<your client secret>
1. grab new creds
2. serialize new creds to disk
3. reload current creds in ram
4. ???
5. profit!
*/
#[instrument(skip(self))]
pub async fn refresh_token(&mut self) -> Result<()> {
let mut u = Url::parse(&self.base_url)?;
u.set_path("/api/oauth2/token");
u.query_pairs_mut()
.append_pair("grant_type", "refresh_token")
.append_pair("refresh_token", &self.creds.refresh_token)
.append_pair("client_id", &self.creds.client_id)
.append_pair("client_secret", &self.creds.client_secret);
let rg: RefreshGrant = self
.cli
.post(&u.to_string())
.header(
"Authorization",
format!("Bearer {}", self.creds.access_token),
)
.send()
.await?
.error_for_status()?
.json()
.await?;
let mut creds = self.creds.clone();
creds.access_token = rg.access_token;
creds.refresh_token = rg.refresh_token;
let p = Path::new(".patreon.json");
if p.exists() {
fs::remove_file(p)?;
}
let mut fout = fs::File::create(p)?;
serde_json::to_writer(&mut fout, &creds)?;
self.creds = creds;
Ok(())
}
}

40
shell.nix Normal file
View File

@ -0,0 +1,40 @@
let
sources = import ./nix/sources.nix;
pkgs =
import sources.nixpkgs { overlays = [ (import sources.nixpkgs-mozilla) ]; };
dhallpkgs = import sources.easy-dhall-nix { inherit pkgs; };
dhall-yaml = dhallpkgs.dhall-yaml-simple;
dhall = dhallpkgs.dhall-simple;
xepkgs = import sources.xepkgs { inherit pkgs; };
rust = pkgs.callPackage ./nix/rust.nix { };
in with pkgs;
with xepkgs;
mkShell {
buildInputs = [
# Rust
rust
cargo-watch
# system dependencies
openssl
pkg-config
# kubernetes deployment
dhall
dhall-yaml
# dependency manager
niv
# tools
ispell
];
SITE_PREFIX = "devel.";
CLACK_SET = "Ashlynn,Terry Davis,Dennis Ritchie";
RUST_LOG = "debug";
RUST_BACKTRACE = "1";
RUST_SRC_PATH =
"${pkgs.latest.rustChannels.nightly.rust-src}/lib/rustlib/src/rust/library";
GITHUB_SHA = "devel";
}

View File

@ -1,16 +1,7 @@
let Person =
{ Type =
{ name : Text
, tags : List Text
, gitLink : Optional Text
, twitter : Optional Text
}
{ Type = { name : Text, tags : List Text, gitLink : Text, twitter : Text }
, default =
{ name = ""
, tags = [] : List Text
, gitLink = None Text
, twitter = None Text
}
{ name = "", tags = [] : List Text, gitLink = "", twitter = "" }
}
in [ Person::{
@ -29,8 +20,8 @@ in [ Person::{
, "istio"
, "typescript"
]
, gitLink = Some "https://github.com/euforic"
, twitter = Some "https://twitter.com/euforic"
, gitLink = "https://github.com/euforic"
, twitter = "https://twitter.com/euforic"
}
, Person::{
, name = "David Roberts"
@ -50,8 +41,8 @@ in [ Person::{
, "embedded"
, "sql"
]
, gitLink = Some "https://github.com/ddr0"
, twitter = Some "https://twitter.com/DDR_4"
, gitLink = "https://github.com/ddr0"
, twitter = "https://twitter.com/DDR_4"
}
, Person::{
, name = "Faizan Jamil"
@ -74,7 +65,8 @@ in [ Person::{
, "full-stack"
, "linux"
]
, gitLink = Some "https://github.com/faizjamil"
, gitLink = "https://github.com/faizjamil"
, twitter = "N/A"
}
, Person::{
, name = "Joseph Crawley"
@ -88,8 +80,8 @@ in [ Person::{
, "bash"
, "linux"
]
, gitLink = Some "https://github.com/espe-on"
, twitter = Some "https://twitter.com/espe_on_"
, gitLink = "https://github.com/espe-on"
, twitter = "https://twitter.com/espe_on_"
}
, Person::{
, name = "nicoo"
@ -104,7 +96,7 @@ in [ Person::{
, "security"
, "SDR"
]
, gitLink = Some "https://github.com/nbraud"
, gitLink = "https://github.com/nbraud"
}
, Person::{
, name = "Prajjwal Singh"
@ -120,8 +112,8 @@ in [ Person::{
, "google-cloud"
, "typescript"
]
, gitLink = Some "https://github.com/Prajjwal"
, twitter = Some "https://twitter.com/prajjwalsin"
, gitLink = "https://github.com/Prajjwal"
, twitter = "https://twitter.com/prajjwalsin"
}
, Person::{
, name = "Piyushh Bhutoria"
@ -133,8 +125,8 @@ in [ Person::{
, "php"
, "google-cloud"
]
, gitLink = Some "https://github.com/Piyushhbhutoria"
, twitter = Some "https://twitter.com/PiyushhB"
, gitLink = "https://github.com/Piyushhbhutoria"
, twitter = "https://twitter.com/PiyushhB"
}
, Person::{
, name = "Ryan Casalino"
@ -151,7 +143,8 @@ in [ Person::{
, "flask"
, "unix"
]
, gitLink = Some "https://github.com/rjpcasalino"
, gitLink = "https://github.com/rjpcasalino"
, twitter = "N/A"
}
, Person::{
, name = "Jeremy White"
@ -170,8 +163,8 @@ in [ Person::{
, "google-cloud"
, "azure"
]
, gitLink = Some "https://github.com/dudymas"
, twitter = Some "https://twitter.com/dudymas"
, gitLink = "https://github.com/dudymas"
, twitter = "https://twitter.com/dudymas"
}
, Person::{
, name = "Zachary McKee"
@ -188,12 +181,14 @@ in [ Person::{
, "nginx"
, "gunicorn"
]
, gitLink = Some "https://github.com/ZacharyRMcKee"
, gitLink = "https://github.com/ZacharyRMcKee"
, twitter = "N/A"
}
, Person::{
, name = "Muazzam Kazmi"
, tags = [ "Rust", "C++", "x86assembly", "WinAPI", "Node.js", "React.js" ]
, gitLink = Some "https://github.com/muazzamalikazmi"
, gitLink = "https://github.com/muazzamalikazmi"
, twitter = "N/A"
}
, Person::{
, name = "Jeffin Mathew"
@ -207,8 +202,8 @@ in [ Person::{
, "javascript"
, "iot"
]
, gitLink = Some "https://github.com/mjeffin"
, twitter = Some "https://twitter.com/mpjeffin"
, gitLink = "https://github.com/mjeffin"
, twitter = "https://twitter.com/mpjeffin"
}
, Person::{
, name = "Nasir Hussain"
@ -223,17 +218,24 @@ in [ Person::{
, "golang"
, "rpm packaging"
]
, gitLink = Some "https://github.com/nasirhm"
, twitter = Some "https://twitter.com/_nasirhm_"
, gitLink = "https://github.com/nasirhm"
, twitter = "https://twitter.com/_nasirhm_"
}
, Person::{
, name = "Avi Parshan"
, tags =
[ "python", "windows", "javascript", "html", "android", "java", "C#" ]
, gitLink = Some "https://github.com/avipars"
, twitter = Some "https://twitter.com/aviinfinity"
[ "python"
, "windows"
, "javascript"
, "html"
, "android"
, "java"
, "C#"
]
, gitLink = "https://github.com/avipars"
, twitter = "https://twitter.com/aviinfinity"
}
, Person::{
, Person:: {
, name = "Tommy Nguyen"
, tags =
[ "c++"
@ -244,13 +246,6 @@ in [ Person::{
, "web"
, "google-cloud-platform"
]
, gitLink = Some "https://github.com/remyabel"
}
, Person::{
, name = "Krish Jain"
, tags =
[ "c++", "linux", "c", "python", "ios", "nlp", "machine learning" ]
, gitLink = Some "https://github.com/Krish-sysadmin"
, twitter = Some "https://twitter.com/krishjain02"
, gitLink = "https://github.com/remyabel"
}
]

View File

@ -1,10 +1,7 @@
use crate::{post::Post, signalboost::Person};
use color_eyre::eyre::Result;
use serde::Deserialize;
use std::{
fs,
path::{Path, PathBuf},
};
use std::{fs, path::PathBuf};
use tracing::{error, instrument};
pub mod markdown;
@ -12,26 +9,25 @@ pub mod poke;
#[derive(Clone, Deserialize)]
pub struct Config {
#[serde(rename = "clackSet")]
pub(crate) clack_set: Vec<String>,
pub(crate) signalboost: Vec<Person>,
pub(crate) port: u16,
#[serde(rename = "resumeFname")]
pub(crate) resume_fname: PathBuf,
#[serde(rename = "webMentionEndpoint")]
pub(crate) webmention_url: String,
#[serde(rename = "miToken")]
pub(crate) mi_token: String,
}
#[instrument]
async fn patrons() -> Result<Option<patreon::Users>> {
let p = Path::new(".patreon.json");
if !p.exists() {
info!("{:?} does not exist", p);
return Ok(None);
}
let mut cli = patreon::Client::new()?;
if let Err(why) = cli.refresh_token().await {
error!("error getting refresh token: {}", why);
}
use patreon::*;
let creds: Credentials = envy::prefixed("PATREON_")
.from_env()
.unwrap_or(Credentials::default());
let cli = Client::new(creds);
match cli.campaign().await {
Ok(camp) => {

View File

@ -232,7 +232,7 @@ async fn main() -> Result<()> {
let _ = std::fs::remove_file(&sockpath);
let uds = UnixListener::bind(&sockpath)?;
axum::Server::builder(ServerAccept { uds })
.serve(app.into_make_service_with_connect_info::<UdsConnectInfo>())
.serve(app.into_make_service_with_connect_info::<UdsConnectInfo, _>())
.await?;
}
Err(_) => {

View File

@ -36,7 +36,7 @@ impl Into<jsonfeed::Item> for Post {
.date_published(self.date.to_rfc3339())
.author(
jsonfeed::Author::new()
.name("Xe Iaso")
.name("Christine Dodrill")
.url("https://christine.website")
.avatar("https://christine.website/static/img/avatar.png"),
);
@ -83,7 +83,8 @@ impl Post {
async fn read_post(dir: &str, fname: PathBuf, cli: &Option<mi::Client>) -> Result<Post> {
debug!(
"loading {}",
"loading {}/{}",
dir,
fname.clone().into_os_string().into_string().unwrap()
);

View File

@ -6,9 +6,9 @@ pub struct Person {
pub tags: Vec<String>,
#[serde(rename = "gitLink")]
pub git_link: Option<String>,
pub git_link: String,
pub twitter: Option<String>,
pub twitter: String,
}
#[cfg(test)]

View File

@ -21,12 +21,7 @@
<div class="cell -4of12 content">
<big>@person.name</big>
<p>@for tag in person.tags { @tag }</p>
@if person.git_link.is_some() {
<a href="@person.git_link.unwrap()">GitHub</a>
}
@if person.twitter.is_some() {
<a href="@person.twitter.unwrap()">Twitter</a>
}
<a href="@person.git_link">GitHub</a> - <a href="@person.twitter">Twitter</a>
</div>
}
</div>