Elizafox's Ramblings

The blag/dumping ground of Elizabeth Myers

There I said it.

The benefit to anyone is questionable and could cause untold harm to millions.

I say this as an advocate of open source software, but also as someone who has been in FOSS a very long time as well as the professional software industry. I know how the sausage is made and I don't want that on anything critical to safety of life without rigorous standards.


Most open source takes the attitude that bugs are to be fixed by the end user, or at least reported, and the user is the QA department.

If your medical device firmware goes wrong, there may not be a user to report anything anymore.

Without rigorous testing and QA, a device's firmware would never be allowed to be on a device used for safety of life. For good reason too.


The lack of warranty or accountability is a huge obstacle.

It's fine to have no warranty for say, a calendar app. If it breaks in two, that's your problem.

Now imagine if someone updates the software on their insulin pump with something they downloaded off GitHub, and an integer overflow causes a massive overdose in insulin and results in death of the person.

Who is held to account for this?

Now let's say a well-intentioned 16 year old modifies Grandma's pacemaker to close a vulnerability with its near-field communications, telling their grandmother this could be used to hack it and kill them, but the fix inadvertently introduces another issue meaning some arrythmias aren't detected.

Who is held to account for this, too?

Again, these issues don't come up in non-safety of life applications, but open source is not an accountable medium.

Will anyone actually give a shit?

Open sourcing a project does not imply anyone will step up to the plate.

If a pacemaker only has a few thousand implantations, mostly amongst the retired elderly, who largely don't know how to use computers... the odds are remarkably low that you will find someone who has had it implanted and has the requisite experience to maintain it. And ideally, you would want more than this for reviews.

Willingness to follow industry safety-of-life standards

Standards such as P7774 and the JSF air vehicle coding rules are standards designed to ensure safety-critical systems don't fail.

I can't imagine many FOSS devs willing to follow these standards. We can't even get them all to agree a code of conduct is a good thing whilst sexual assault in the industry happens daily.

But it's worth noting these standards are written in blood. The Therac-25 killed people. I am not convinced FOSS methodologies as commonly practised could have prevented this tragedy, especially at the time. After all, with FOSS, the user is the QA department.

Regulatory hurdles

For reasons stated above, I am not convinced a regulator would allow anything developed with classical FOSS methodologies to be allowed anywhere near a human.

I think it may be possible to develop this software in the open, but “homebrew” software on devices critical to safety-of-life is a terrifying proposition to me.

Dunning-Kruger effect

The Dunning-Kruger effect is the observation that people often perceive themselves to be more capable than they are.

This is especially true of programmers.

I like the idea of hacking implants, but I don't like the reality of someone killing me with bad code and lax standards.

I don't think most programmers have requisite knowledge to maintain these devices. They often require specialist knowledge and working hand-in-hand with medical professionals.

Getting your average programmer to even go to the doctor is a nightmare.

Now imagine getting these people together to volunteer.

So, what's the answer?

I honestly think companies need to be legally held responsible for the maintenance of their product software until the last one is no longer in use.

In addition, we need industry protocols and standards for adjusting these devices so things don't become “obsolete” in short order.

I think in extreme cases, the government should arrange a contractor to maintain the devices if the previous one goes out of business.

We already make oil well drillers pay to plug their wells (though there are problems there too, like stripper well scams and the plugging bonds being laughably too low), we should do the same for device manufacturers.


I am not against FOSS or open-source in any way. I think a FOSS model can work, with company backing, for safety-critical code. But without said backing, someone to hold accountable for the software, I think it's too risky to have these devices open to user modification.

— Elizabeth Myers (Elizafox) Fedi (elsewhere): @Elizafox@social.naln1.ca Tip jar: PayPal || CashApp || LiberaPay

Someone mentioned that there should be a spec for #nobot, so I'm going to write one.

It's going to be the simplest thing that does what everyone expects it to do.

I'm also going to include #noindex and any other bot tags.

Without further ado...


This document specifies a proposed standard for consensual interactions with bots on the Fediverse and other social media platforms.

This document is licensed under the CC0-1.0 or later.

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

This document is a living specification and is versioned in accordance with semantic versioning.

This version of the specification is 0.2.5.


Many users of the Fediverse and various social media platforms do not wish to be followed by bots, or interact with bots in any way, or do not want interactions they did not initiate.

A de facto standard has arisen, the “#nobot” and “#noindex” hashtags in profiles.

Most bots already respect these hashtags. This specification simply clarifies what bots should do when and where they encounter the tags, and what bots should check for. This also gives a semantic outline of how opt-in and opt-out bots are generally expected to behave.

Bot actions

There are two classes of bot actions for the purpose of this specification: direct and indirect bot actions. When the term “bot action” is used alone, it SHALL be construed to mean both indirect and direct bot actions.

Indirect bot actions

An indirect bot action for the purposes of this specification is defined as one of the following:

  • Retrieving a user's metadata for purposes aside from checking for tags
  • Retrieving a user's post history
  • Boosting, reblogging, or reposting the post, or a related action
  • Favouriting, liking, or reacting to the post, or a related action

Direct bot actions

A direct bot action is defined as any of the following:

  • Replying to a thread
  • Directly or privately messaging a user

Opt-in and opt-out

A bot is classified as opt-in or opt-out based on its behaviour and method of consent for bot actions.


A bot is defined as “opt-in” if the user MUST follow or directly interact with the bot in some form, such as in a private message, to perform a bot action. A specific tag MAY also be used. An enrollment system MAY also be used.

Opt-in bots SHOULD NOT perform direct bot actions in any way without prior interaction, and MUST NOT perform any indirect bot actions without consent.


A bot is defined as “opt-out” if the bot will perform bot actions without prior opting-in by the user.

Bots SHOULD NOT perform direct bot actions on an opt-out basis.

There is the possibility of privacy concerns with non-consensual indexing of users. Please consider privacy and legal ramifications very carefully before indexing users on an opt-out basis.

If a user has opted out through one of the mechanisms below, then an opt-out bot MUST NOT directly interact with the user. Indirect bot actions MAY be permissible, provided the user has interacted with the bot first.

Following an opt-out bot MUST NOT override the semantics of any of the tags mentioned below.


Bots SHOULD be opt-in, not opt-out. It it strongly RECOMMENDED that new bots SHOULD be opt-in.

However, an unfortunate reality is that many bots exist which are not opt-in. Therefore, this specification will deal with both.

Non-conforming bots MAY be blocked by users and admins, so adherence to the specification is strongly RECOMMENDED.


The following tags are defined, which MUST have the leading octothorpe on the Fediverse and platforms which use “hashtags”, but MAY NOT on platforms that use another tagging mechanism:

  • #nobot
  • #noindex
  • #bot
  • #index
  • #yesbot
  • #yesindex

Valid locations for tags

When a mechanism for opt-in or opt-out uses a tag, any of the described tags MUST be put in the biographical or about page of a user who wishes to opt in or out of specific functionality to take effect. Bots MUST parse this information and search for any tags relevant to its operation as outlined further in this specification.

A user MAY place the tags in a specific “tags” field. Bots SHOULD check this field as well, if applicable.

Tags MUST NOT be parsed from any other source.


If two tags conflict with each other, it is unspecified what a bot may do. Regardless, bots SHOULD respect the most restrictive policy specified by the tags. For example, #nobot SHOULD take precedence over #noindex.


The #nobot tag SHALL opt the user out of indirect interactions with bots. All opt-out bots MUST respect this tag, regardless of functionality.

The #nobot tag SHOULD imply #noindex. Users are nonetheless encouraged to use both tags if they do not wish to be indexed nor have any non-consentual bot interactions.


The #noindex tag SHALL opt the user out of indexing by bots. All opt-out bots which perform indexing MUST respect this tag.


The #bot tag SHALL indicate the user in question is a bot. It is RECOMMENDED all bots put this in their profile or biographical information or tags section or equivalent, if there is no other means on the platform to identify a bot.


The #index tag SHALL indicate the user in question is an indexing bot. It is RECOMMENDED all indexing bots place this tag in their profile or biographical information or tags section or equivalent.


The #yesbot tag SHALL indicate the user in question opts into all bot actions, but MAY NOT have opted into indexing.


The #yesindex tag SHALL indictate the user in question opts into being indexed. The user in question MAY NOT have opted into any other bot actions.


If a user follows a bot, they SHALL be deeemed to have opted into bot actions, unless a conflicting tag is present. In such cases, the tag takes precedence.

Following a bot MUST NOT be construed as permission to index the user.


If a user blocks a bot, they SHALL be deemed to have opted out of all bot interactions. Bots MUST NOT attempt block evasion. However, such bots are usually malicious and wouldn't follow the specification regardless.


A bot that caches consent information such as tags MUST refresh that data upon request, or SHOULD refresh it at least once per hour per any bot action with the user in question.

Revocation and granting

Consent MAY be revoked or granted at any time by users. See the section on “Caching” for more information.

Authorship and conflicts of interest

This specification has been authored by Elizabeth Myers.

The author(s) of this specification assert they have no conflicts of interest related to this specification.

Version history

  • 0.1: initial revision
  • 0.1.1: minor corrections
  • 0.1.2: formatting fixes
  • 0.1.3: clarify opt-in and opt-out slightly
  • 0.1.4: change “ban evasion” to “block evasion”
  • 0.1.5: clarify what opt-out bots should do
  • 0.2.0: add section on caching and revocation
  • 0.2.1: clarification and fix wording on opt-in
  • 0.2.2: notes about behaviour with conflicting tags and specify that #nobot should include #noindex.
  • 0.2.3: strengthen wording about opt-out being not recommended.
  • 0.2.4: add enrollment as part of opt-in
  • 0.2.5: disallow parsing of tags from any other source

— Elizabeth Myers (Elizafox) Fedi (elsewhere): @Elizafox@social.naln1.ca Tip jar: PayPal || CashApp || LiberaPay

I am indeed employed now. I won't disclose where I work in public, because I have literal stalkers from Fedi (truly the sign of a great, very healthy, very excellent community, with no problems with toxicity at all).

Just a standard disclaimer: all views expressed on this account are my own. They do not reflect the views of my employer or any other organisations I am affiliated with.

I will disclose that I work for a security company, and what I do is related to that.

— Elizabeth Myers (Elizafox) Fedi (elsewhere): @Elizafox@social.naln1.ca Tip jar: PayPal || CashApp || LiberaPay

I realise I'm going to be throwing a Molotov into the Fediverse right now, but I think it's time for me to put thoughts to paper about what I really want out of a social media network.

I have a feeling I'm not alone in many of these goals, either.

My qualifications in this space? I've been on Fedi since the beginning, have hacked on Mastodon before, I have abortively made an ActivityPub server, I run an instance, and I was formerly an IRC daemon developer. I certainly am not 100% right on everything, but I at least know more or less what I'm talking about. At least I think I do.

WARNING: I am going to get very political, very opinionated, I am going to talk about the worst people on Fedi a little, there may be some adult language ahead, I assume you all are grown-ups and can deal with it.

This is also going to be a 10,000-mile overhead view and more of an outline than a concrete specification. I believe the best battle-testing of a specification will be in the creation of a piece of prototype software to accompany it.

The problem

Fedi is a toxic environment. It is utterly filled with racists, pedophiles, libertarians (but I repeat myself), tankies, and assorted grifter scum.

And that is because literally anyone can join, and there is little you can do about it.

People are often confused why PoC would choose BlueSky over Fedi. Do you know why?

It's because Fedi has a racism problem no one is willing to confront in a meaningful way. Instead, we'd rather fight amongst ourselves and attack the actors who don't really matter in the grand scheme of things (or who are totally innocent), whilst leaving the real, bona-fide, actual Nazis alone.

I want to change that.

It also doesn't help that ActivityPub is a terrible protocol. It uses JSON-LD, which is incredibly complex on its own, as well as ActivityStreams, which contains so many activities that no one will ever use.

Goal one: it should be fun

I want whatever replaces the Fediverse to be fun. Fedi as it stands is not very fun. There is way too much drama constantly for it to be fun. It feels more like Twitter than it does anything else, and the worst parts of it to boot.

This needs to change. And I believe changing the way it all works is part of it.

It must not just be a fun environment, but a welcoming environment with community spirit. Nazis and assorted scum should never be able to come on the platform. Of course people won't like other people, but I want the bullshit to be kept to a minimum.

I realise zero-drama is a difficult goal to achieve, and perhaps impossible, but I believe if discourse and drama can be kept to a minimum by the way the protocol works, then it will be a net win.

Ending open-world federation

Open-world federation is a fine experiment, but in reality it's a disaster.

Who the Hell thought it was a good idea to allow anyone to just show up to the whole network with a server? Even IRC figured out this model was untenable decades ago. There was once a single IRC network, and then a server linked that allowed anyone to link, and EFNet and separate IRC networks as we know them were born.

We cannot allow servers who are not trusted to join, or at least keep them contained to their own little Hellholes.

We cannot allow Nazis to join. We cannot allow tankies to join. We cannot allow pedophiles to join. We cannot allow freeze-peach people to join. We cannot allow assholes to join. We cannot allow racists to join. You must either respect others or get the fuck out.

We must exclude these people, by design. And the way to do that is to end the ability for them to join in the first place.

At the same time, balance must be struck. People must be allowed to federate. They have to be able to join the network to begin with.


A semi-open world system where someone must vouch for you to join is what I am proposing here. Using some sort of cryptographic attestation should be required saying that “I am foo.org, I am vouched for by bar.social”. If multiple people attest your server, that's fine too.

Attestations should expire and be revocable. They should be automatically renewed every 90 days, unless the server falls off the network or its keys change.

This brings accountability to the system. If someone repeatedly vouches for Nazis, their attestations can be revoked in the control panel by those who have previously vouched for them. This means if no one is willing to vouch for them, then they are unable to join. Obviously, instances can still be blocked as well. Since the attestations of vouching are signed, they cannot be forged.

It's possible to even go further, and require a certain number of attestations, or refuse to federate with servers who don't have a certain number of attestations. Attesting a server means federating by default, however.

This could fragment the network. But is this such a bad thing? People worry about network fragmentation, but I don't think it's such a bad idea. This idea that we should all be under a big tent singing songs together is ridiculous liberal nonsense. We live in the real world, and not everyone is everyone's friend.

Consensus-based blocking

I believe we can resolve many issues with a consensus-based blocking system. No more blocklists. The community gets to decide what it wants and doesn't want. Obviously, a server can reject a block that has been implemented widely, but there should at least be a queue. Server blocks should be optionally shared with federating servers, and those which reach a certain threshold should be put in a queue for admin review.

Blocking obviously revokes any previous attestations.


Identity should be transferrable, revocable, and owned by the person.

All posts should belong to the person. They need to be transferrable too. Everything about an account should be transferrable, importable, and exportable, on demand, no questions asked.

They should be allowed to use a server like foo.org and use that account everywhere on other servers that allow it.

Their identity should be tied to something client-side, like a key, which attests that they are who they say they are, letting them transfer their identity or have multiple accounts with multiple authentication providers, with proof that they are who they say they are.

Obviously, there can be services to back up said identity as well, although then you're worried about trusting them, although, you kind of have to trust any server you're on not to fuck with you anyway.

Said proof could also include signed statements and other counter-signatures, replacing the “blue checkmark.”

To make things simpler and decouple identity providing fully from the act of using a server, I propose assigning the user a unique ID number. This means they'll look like @foo#1292@bar.org.


The thing about social media is that it is inherently a non-private medium. Nonetheless, a better balance can be struck.

I think direct messages should be encrypted with E2E using a scheme similar to Signal's.

Privacy scope should be limited to an actor list with wildcards allowed, and an unlisted tag. *@* is public, but could have the unlisted tag set.

This allows for features like circles as well.

Local groups

Communities are not just global.

There should be local community as well. I visualise a sort of section where it's similar to a federated forum in addition to the usual feed. Maybe something like a federated Reddit? I'm still working this part out.

The protocol

As stated above, ActivityPub has a lot of issues. It's impossible to implement to-spec either, without doing what Mastodon does. Refusing to do what Mastodon does means you're automatically not compatible with it. This is absolutely unacceptable.

What I want to do is likely not achievable within the bounds of the protocol anyway.

I propose using a system based on JSON schema instead. Servers can describe the schema version they use, and any extensions, via a .well-known URI endpoint.

Everything will be JSON. It will be ActivityStreams flavoured, but considerably simplified. YAGNI principles should apply everywhere here.


I don't think any of these problems are insurmountable. I haven't come up with a concrete specification yet, this is all just a 10,000-mile overhead view. I have other priorities at the moment. But I think something similar to this could be viable.

I will likely edit and update this document as time goes on, to incorporate suggested changes, or new ideas I like. Watch this space.

— Elizabeth Myers (Elizafox) Fedi (elsewhere): @Elizafox@social.naln1.ca Tip jar: PayPal || CashApp || LiberaPay

I think about map projections sometimes.

I'm not a geographer, and I'm not a mapmaker. I just have a passing interest in map projections.

So I'm gonna share some thoughts on them.

The problem

Geographers will likely want to kill me for simplifying too much here. So be it. I'm going to give the very simplified, lots of detail and specific definitions left out version of the problem.

When trying to turn the 3D sphereoblate spheroid that is mother Earth into a flat map, there will always be distortion. This cannot be avoided. There's actually a remarkable theorem about this, but I won't get into it.

The various methods of distorting the map to make it fit on a 2D surface are called projections.

One might think an approach like Google Earth showing the Earth as a sphere can solve it, but that's actually just the Orthographic projection, and actually has distortion, too.

So, with that out of the way, let's discuss the properties of projections.

The properties of projections

Projections can be classified by the properties they preserve, which illustrates that a perfect map projection does not exist. Quoth Wikipedia: – Preserving direction (azimuthal or zenithal) – Preserving shape locally (conformal or orthomorphic) – Preserving area (equal-area or equiareal or equivalent or authalic) – Preserving distance (equidistant), a trait possible only between one or two points and every other point – Preserving shortest route, a trait preserved only by the gnomonic projection

Not all properties can be preserved every time, as mentioned above. There are compromise projections, but they're still a compromise. It's a matter of choosing what properties you wish to preserve.

Various projections

I could wax poetic about the many types of projections and their merits for days. I will show a sample of a few below.


The projection shown in the emoji 🌎🌍🌏

Orthographic projection


Robinson projection


Mollweide projection

OkayGoode homolsine

Goode homolsine projection

Alber's conic

Alber's conic projection

Azimuthal equidistant

This one may remind you of the UN flag, and indeed, that is the projection it uses. This is also the projection that Flat Earthers think truly represents the Earth. Flat Earthers are, of course completely wrong. The Earth is thicc, not flat.

Azimuthal equidistant projection


My personal favourite. Of course I have a favourite map projection, doesn't everyone?

Dymaxion projection

The Mercator projection

This is the crux of the controversy here and what I'm looking to get at.

The Mercator projection looks like this:

Mercator Projection with Tissot's indicatrix

(Those dots are Tissot's Indicatrix, a measure of a projection's relative distortion).

You've most definitely seen it. This is the projection Google Maps uses (well, strictly speaking, it uses Web Mercator).

Why do people think it's racist?

Well, as you can see by the dots, northern latitudes are shown to be far too large, whilst Africa is shown to be relatively small. The reality is Africa is very, very large.

How large?

Here is an image of Africa with Russia, Germany, the UK, Spain, France, and India overlayed.

Quite big

(That green bit in the Atlantic is French Guiana, and yes indeed it is part of France.)

The Mercator projection has lied to you.

Okay, but is there anything to the whole “it's racist” thing?

There is nuance here.

The thing about the Mercator projection is that it has some unique properties: – North is always up, south is always down (or down on an inverted map), this may seem obvious but many projections do not preserve this property – Although sizes are distorted towards the poles, shapes are preserved – Most mid-range latitudes are not exceptionally distorted – Lines of latitude and longitude are perfectly straight

Which is why it's used in global navigation. There really aren't many projections which can do a better job for this purpose. This is why Google Maps uses it.

For local navigation, there may be better choices, but it's not as important on a local level anyway. More on that in a bit.

But for general use, there's no reason to use Mercator, and I do suspect an element of racism and colonialist mindset is there as for its continued use.

Proposed replacements

The reality is, most of us aren't Magellan, and we won't be circumnavigating the globe.

Mercator became the main map projection displayed in classrooms, likely due to its convenient property of “north is always up” and showing shape. But the distortion of European countries and America's size likely played at least a subconscious role in it as well, if I had to guess. This is also why “north is up” as well, when there's actually no reason whatsoever it has to be that way.

Unfortunately, there aren't really good replacements for Mercator for navigation. It's the only projection that preserves size, shape, and north pointing upwards, even with its unfortunate size distortion.

Displaying the Earth for educational purposes

There are better projections for display. This is why Robinson (as shown above) is becoming more common. I argue the Dymaxion projection is also a good projection, if only because it looks cool and has a minimal amount of size and shape distortion (at the cost of distance being difficult to visualise).

Local navigation

For local navigation, it's actually not that important what projection is in use, most of the time. The distortion is just not visible on that scale. It really doesn't make a bit of difference. Personally I like equal-area projections, but again... it doesn't matter on the local level.

Reasoning about relative sizes of countries, continents, and oceans

For this purpose, there is no replacement for an equal-area projection. Even if shape is distorted (although a compromise projection can help with this), preserving size can still give an idea the true size of something.


Most of the time, orthogonal projections are used for global weather, such as earth.nullschool.net. Though if you tinker with that site, you'll see other projections are available.


Overall, I don't think replacing Mercator in every context is worthwhile. However, I do think exposure to other map projections is needed in schools. There are so many different ways to see the Earth, and each projection offers a unique perspective on the planet. Teaching more projections can help students and by extension society reason about the true nature of our planet.

It may also help students reason about the true scale of colonialism, and help them realise just how much of the planet Western powers took over in their bid for dominance. It's truly sobering to think that a continent as large as Africa came under the dominance of such small countries.

— Elizabeth Myers (Elizafox) Fedi (elsewhere): @Elizafox@social.naln1.ca Tip jar: PayPal || CashApp || LiberaPay

Once upon a time I built a weather station. I don't have any photos lying around, but I could ask Anna if she has any.

I named the project Weatherbox, after a song by Mission of Burma (see here).

By built, I don't mean I bought one. I mean I literally built it myself, from raw components.

I'll tell you how you can, too, along with some things I will do differently in the future.


Full disclosure: I am an Amazon Associate and I may earn commission on these links; you won't be charged extra for this. I know it sounds scummy or that I'm selling out, but I need the money. Please consider using the links below to buy the materials in question!

Here are some of the things I used. I wouldn't necessarily use all these components now, but they worked at the time:

Electrical components

Mounting hardware

  • This mount, although I'd probably consider a different one, this one I know works
  • This junction box, I drilled some holes in the side and used some O-rings to keep water out
  • I modified this LaCrosse temperature sensor housing as a housing for the BME680 and such by sawing off the internal mount and enlarging the holes for wires; but there are better options
  • U-bolts I got from Lowe's, it isn't worth it to buy them online, I used them to mount the junction box to the pole
  • Hose clamps to attach the mount to my railing at my apartment, although you don't have to

Prototyping and assembly equipment

  • 22 AWG wire
  • If you don't have a soldering station, the X-Tronic 3020-XTS is good value for money, although I found the helping hands of questionable value, the rest is great
  • Jumper wire for prototyping
  • Decent breadboards; Elegoo's are okay
  • A long MicroUSB cable; I never got around to adding a solar panel, my apartment area was too shaded
  • Some 1% tolerance or better resistors, these “BOJACK” ones (whatever that means) are okay; 0.25 watt resistors are fine, although 0.5 watt is fine too, it doesn't really matter, we aren't putting that much current through them
  • Heat shrink, I used this and it was fine
  • A stripboard track cutter, similar to this although perhaps you may not want to get it off Amazon because there don't seem to be many and the one I could find had bad reviews; I don't remember where I got mine, it wasn't Amazon though
  • Some pin headers, any will do, for breakout boards that don't have them, although you can also just use wire
  • A lighter for the heat shrink

Miscellaneous components


I didn't keep photos of the assembly process, though I wish I had. It took me a lot of trial and error to get it right and settle on a design I was happy with.

In the end, I settled on a design where my Arduino 33 IoT would connect to the network, and my Raspberry Pi Zero would poll it for data and upload it to my site.


The Arduino itself lived in the junction box. All the ports were on the bottom of the box, with grommets to prevent water ingress. I added some extra grommets to ensure at least some airflow.

I broke out the BME680 onto its own daughter board, and put it in the aforementioned LaCrosse housing. I found the junction box could get quite warm and would cause inaccurate readings, plus we want plenty of airflow anyway for the air quality measurement.

The UV sensor was covered in the aforementioned conformal coating and attached with epoxy to the top of the junction box. I just used some wires and wired it to the Arduino in the box. I added a drip loop to the wire to ensure it wouldn't cause issues.

For the anemometer and rain gauge, I used pull-up resistors (2K resistors worked best I found, but you could probably get away with a 5K or even 10K resistor) and a 2pF capacitor in series for filtering. I did most of my debouncing in software, however. I just hooked up the connectors to little pieces of stripboard I cut out for the purpose to mount the resistors and capacitors.

The wind vane was trickiest. Here is the datasheet. As the PDF says, “it has eight switches, each connected to a different resistor.” I used a 10K ohm resistor like it suggested to create a voltage divider, which I hooked up to the ADC on the Arduino. I ran a binary search to find the correct voltage value. I find that the “in-between” measurements are fiddly and only work in certain directions. One should expect to have 8 indicated positions total and no more, and anything else is a bonus. This is fine for a hobbyist project, but do keep that in mind. I would definitely experiment and see how your wind vane reacts. This is the point where good tolerance resistors are helpful.

Most of the discrete breakout board components speak I2C, so I just hooked them all up to the I2C line. You may want to add I2C buffers if you're getting lots of interference or too much capacitance. Something like this may help.


I cannot emphasise this enough, you must prototype, prototype, prototype. Do everything on breadboards before you put it on stripboard. They have their limitations, but they'll help you get a feel for how the components work. I found the fiddliest part was not the I2C bus, but the weather vane/anemometer/rain gauge. It took me ages to figure out I needed a pull-up on them, and even longer to debug the circuit for the wind vane.


I have some code here that you can adapt to this project or just use. You'll want to swap out the Si1145 UV sensor code if you use the S12SD. It gives a simple analog reading, documented here.

What I'd do different

I did mention a few things I'd change, like swapping out the Si1145 for the S12SD and maybe a different mount.

I think a big change I'd make, perhaps the biggest of all, would be to swap out the anemometer, wind vane, and rain gauge, for discrete components that didn't all just come together in a kit. Something far more professional would be in order. This will require fabricating some mounts, but I think that's within my abilities. I plan to do that for the second iteration of this project.

I would definitely spend more time on the breadboard working things out than I would otherwise.

I found the WiFi fiddly and annoying. I would probably switch to LoRa communications instead of WiFi, this would also enable me to use the cheaper Arduino 33 BLE. I'd just have to get it working with the Pi Zero.

I had a lot of stuck I2C bus problems, likely due to capacitance issues. I would definitely use a buffer as aforementioned, and perhaps a simple transistor to fully shut down all devices on the bus and restart all devices.

In the future, I would like to make this solar powered. I'll need to find a cooling/heating solution for the battery, however, to keep it within temperature ranges (especially as the PNW gets hotter and with our sometimes cold winters).


I didn't need any 5V components for this project. 3.3V is increasingly the standard for breakouts and sensors. Not even the anemometer, wind vane, or rain gauge needed 5V. If you use an Arduino 33 Nano series board and want to use 5V components, you'll have to bridge the 5V connector to enable the 5V regulator. You'll likely want logic level converters to avoid damaging the Arduino, or voltage dividers, as it can't take more than 3.3V on its pins except ground.

— Elizabeth Myers (Elizafox) Fedi (elsewhere): @Elizafox@social.naln1.ca Tip jar: PayPal || CashApp || LiberaPay

I noticed something peculiar whilst tinkering with my jail. The impact of this oversight is probably minimal to none (it certainly leads to nothing exploitable), but it's IMO a bug that should be fixed.

UPDATE: I have filed a bug

Note to CVE clout chasers

Before you go running out and getting a CVE:


You will make a total ass of yourself, and I will point back to this blog post, and I will laugh at you.

Things this can do

  • Allow you to see how the jail is configured on the host to a very limited extent (at least the filesystem layout, in part)

Things this cannot do

  • Help you escape the jail
  • Help you obtain elevated privileges outside the jail
  • Help you obtain privileges inside the jail
  • Exploit the system in any way
  • Let you do anything that you couldn't do with a real kernel exploit, which you would probably need to successfully escape the jail


Jails in FreeBSD are a container mechanism, albeit not quite as comprehensive as Linux namespace containers like LXC. They've been around about 23 years, being introduced in March 2000. I think it may be best to think of them more as a replacement for jailing things via chroot (long since known to be insecure, because chroot is not a security mechanism) that has sort of evolved into a container.

The problem

If you run procstat vm $PID in a jail:

root@z6a.info:/ # procstat vm 989
  PID              START                END PRT  RES PRES REF SHD FLAG  TP PATH
  989      0x20210af3000      0x20210af7000 r--    4   10  22   2 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/usr/sbin/rtsold
  989      0x20210af7000      0x20210afe000 r-x    7   10  22   2 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/usr/sbin/rtsold
  989      0x20210afe000      0x20210aff000 rw-    1    0   5   0 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/usr/sbin/rtsold
  989      0x20210aff000      0x20210b01000 rw-    2    0   1   0 C---- sw
  989      0x20a11ab8000      0x20a31a98000 ---    0    0   0   0 ----- gd
  989      0x20a31a98000      0x20a31ab8000 rw-    3    0   1   0 C--D- sw
  989      0x20a32148000      0x20a32169000 rw-    7    0   1   0 C---- sw
  989      0x20a3290c000      0x20a3290f000 r--    3    6  32  12 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libcasper.so.1
  989      0x20a3290f000      0x20a32912000 r-x    3    6  32  12 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libcasper.so.1
  989      0x20a32912000      0x20a32913000 r--    1    0   5   0 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libcasper.so.1
  989      0x20a32913000      0x20a32914000 rw-    1    0   5   0 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libcasper.so.1
  989      0x20a32914000      0x20a32915000 rw-    1    0   1   0 C---- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libcasper.so.1
  989      0x20a3335c000      0x20a33364000 r--    7   19  52  18 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libutil.so.9
  989      0x20a33364000      0x20a3336f000 r-x   11   19  52  18 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libutil.so.9
  989      0x20a3336f000      0x20a33370000 rw-    1    0   5   0 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libutil.so.9
  989      0x20a33370000      0x20a33371000 rw-    1    0   1   0 C---- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libutil.so.9
  989      0x20a33371000      0x20a33373000 rw-    0    0   0   0 ----- --
  989      0x20a33855000      0x20a33856000 r--    1    2  24   4 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/casper/libcap_syslog.so.1
  989      0x20a33856000      0x20a33858000 r-x    2    2  24   4 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/casper/libcap_syslog.so.1
  989      0x20a33858000      0x20a33859000 rw-    1    0   5   0 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/casper/libcap_syslog.so.1
  989      0x20a33859000      0x20a3385a000 rw-    1    0   1   0 C---- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/casper/libcap_syslog.so.1
  989      0x20a34523000      0x20a345a8000 r--   78  321  89  41 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libc.so.7
  989      0x20a345a8000      0x20a346f3000 r-x  219  321  89  41 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libc.so.7
  989      0x20a346f3000      0x20a346fc000 r--    9    0   5   0 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libc.so.7
  989      0x20a346fc000      0x20a346fd000 rw-    1    0   5   0 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libc.so.7
  989      0x20a346fd000      0x20a34704000 rw-    7    0   1   0 C---- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libc.so.7
  989      0x20a34704000      0x20a34926000 rw-    7    0   1   0 C---- sw
  989      0x20a355cd000      0x20a355d7000 r--    8   20  30  10 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libnv.so.0
  989      0x20a355d7000      0x20a355e4000 r-x   12   20  30  10 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libnv.so.0
  989      0x20a355e4000      0x20a355e5000 rw-    1    0   5   0 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libnv.so.0
  989      0x20a355e5000      0x20a355e7000 rw-    2    0   1   0 C---- vn /usr/jails/basejail/base_amd64_amd64_13.2/lib/libnv.so.0
  989      0x20a36200000      0x20a36400000 rw-    2    0   1   0 C---- sw
  989      0x20a366f9000      0x20a368f9000 rw-   18    0   1   0 C---- sw
  989      0x20a37800000      0x20a37c00000 rw-    1    1   1   0 C---- sw
  989      0x20a38947000      0x20a38948000 rw-    0    0   1   0 ----- sw
  989     0x31ae14de4000     0x31ae14deb000 r--    7   29  76  28 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/libexec/ld-elf.so.1
  989     0x31ae14deb000     0x31ae14e01000 r-x   22   29  76  28 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/libexec/ld-elf.so.1
  989     0x31ae14e01000     0x31ae14e02000 r--    1    0   5   0 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/libexec/ld-elf.so.1
  989     0x31ae14e02000     0x31ae14e03000 rw-    1    0   5   0 CN--- vn /usr/jails/basejail/base_amd64_amd64_13.2/libexec/ld-elf.so.1
  989     0x31ae14e03000     0x31ae14e04000 rw-    0    0   1   0 C---- sw
  989     0x7fffffffe000     0x7ffffffff000 r-x    1    1  59   0 ----- ph

Whoops! The paths arguably aren't supposed to leak into the jail like this (and with other commands I've tested, they don't).

The cause

I haven't probed very deeply into the cause due to a lack of time, but I have a suspicion it's caused by the kernel not sanitising paths somehow.


Although I use the term “mitigation” very, very loosely, I figure it may be helpful to document what can help if you care about this.

Things that do not help: – Disallowing kmem and /dev/io in the jail – Disallowing procfs in the jail – Setting enforce_statfs to 1 or 2 – sysctl security.bsd.unprivileged_proc_debug=0 in the host


Things that do help: – This only works if the user is root in the jail; disallowing root access in your jails may help (although this does make management a pain), and securing your jail against root exploits


This probably isn't a real vulnerability or problem in practise. I doubt it even deserves a CVE number. I don't think this could lead to any effective compromise or escape from the jail.

Nonetheless, it's a bug that should be fixed, IMO. If anything else, at least fixing it would follow the principle of least astonishment.

— Elizabeth Myers (Elizafox) Fedi (elsewhere): @Elizafox@social.naln1.ca Tip jar: PayPal || CashApp || LiberaPay

I spent literally all day yesterday trying to figure out how to get a jail to route an IPv4 IP given to me by Vultr using vnet (I was having bizarre issues with IPv6 and the host stack; vnet fixed them).

It sounds like an easy problem, and to people who are super experienced... it probably is, and the answer is obvious in hindsight. But even 16 years of BSD experience couldn't help me here.

The problem in detail

The problem was routing an additional IPv4 IP that Vultr gave me, that wasn't in the same subnet as my main IP. It works fine on the host without issue:

ifconfig <external_interface> inet <second_address> alias

That's because the default route is already configured.

You'd think that in the jail, it'd be a matter of doing the same and it'd all Just Work™. Nope!

(Note with the below: not all attempts are logged, just the major ones I remember... I tried a lot of things, for 8 hours)

Adding the route: try 1

When you try it in the jail, like so:

route add default <host_gateway> -iface eth0

You get an error like:

route: writing to routing socket: Network is unreachable
add net default: gateway via fib 0: Network is unreachable


So I tried this in the jail:

route add <host_gateway> -iface eth0
route add default <host_gateway>

But nothing would route out of the jail.

Adding the route: try 2

I even tried this in the jail:

route add -net <host_gateway>/<host_subnet> -iface eth0
route add -host default <host_gateway>

And that was even worse. Now everything said “no route to host.” Gah.

I also kept getting this weird issue where I'd add a default route, and it would get corrupted into some gobbledygook in netstat -4rn like so:

Destination        Gateway        Flags Netif Expire  US    eth0   link#2         US    eth0

Maybe some kind of bug?

Adding the route: try 3

I went back to the former solution, and did tcpdump on the host. I was seeing traffic coming out of the jail, but it wasn't coming back. I could arping the host, and the gateway, but still couldn't route things.

So I figured it was a host issue.

I tried this on the host (not the jail):

sysctl net.inet.ip.forwarding=1
route add -net <second_ip> -inet bridge1


Adding the route: try 4

At this point I was super confused. So in desperation, I even tried this on the host:

route add -net <second_ip> -inet epair1a

Still nothing.

Adding the route: try 5

I was pretty much despondent at this point. I had spun my wheels on this problem for 8 hours, brainstorming answers, trying literally everything I could think of, picking other's brains who were just as confused as I was.

So in an act of extreme desperation, I tried the one thing I hadn't tried on the host:

route add -net <second_ip> -inet <external_interface>

And then...

It worked.

Holy crap, it worked. I bowed down, thanked the heavens, and felt so much relief, I went and made Taco Hamburger Helper (I'm poor, okay?).

Making it all work

So here's how you can make this work in your environment, too, on Vultr and probably other hosting providers, if you're in the same situation:


cat >>/etc/rc.conf <<EOF
route_default_router="-host <host_gateway> -iface eth0"
ifconfig_eth0="inet <host_ip>/<host_subnet>"  # For me it was /32
route -host <host_gateway> -iface eth0
route default <host_gateway>


cat >>/etc/rc.conf <<EOF
route_jail0="-host <second_ip> -iface <external_interface>"
sysctl net.inet.ip.forwarding=1  # This just does what the line in rc.conf does on reboot
route add -host <second_ip> -iface <external_interface>


If you have a provider that restricts the original MAC where something came from, you can replace <host_gateway> above with your host machine's IP. I tested that, and it all Just Works™. It should then route through your host machine to the outside world, and the jail's MAC in theory shouldn't be visible.


I hate IPv4 more than I hate IPv6 at this point. It's always been like this, whereas IPv6 has mostly been pretty good (minus ISP/modem issues... I once had a horrible Siemens DSL modem that wouldn't pass 6to4, but that's a story for another day).

— Elizabeth Myers (Elizafox) Fedi (elsewhere): @Elizafox@social.naln1.ca Tip jar: PayPal || CashApp || LiberaPay

I get lonely a lot.

I mean, it's natural. I live alone.

I sort of got alienated from most of my friends here due to my own mistakes and missteps (although I cut off a lot of toxic people too). Those are mine to live with. But I have a few left here, and they've stuck by me.

It's probably for the better. A lot of them were just dead weight to me.

I know I need to rebuild my friend circle.

Being comfortable with yourself

I think something I've learnt how to do throughout all this is how to be comfortable with just me. I haven't lived alone ever in my life, until now. It's definitely an interesting experience.

I feel like it's a little isolating, but it's fine. It's teaching me valuable lessons about myself, and helping me grow more comfortable in my own skin. I really needed this, even if I didn't want it.

Will I ever live with someone again?


I don't know what the future holds. I know right now, I'm not looking for new roommates, especially because the last one was an utter disaster, for me and for the people I was close to (and this is partially how I lost them).

I do know this: living alone means I don't have to live up to anyone's expectations or anything.

— Elizabeth Myers (Elizafox) Fedi (elsewhere): @Elizafox@social.naln1.ca Tip jar: PayPal || CashApp || LiberaPay

I wanna talk a bit about something I discussed in therapy.

Guilt alone changes nothing. It doesn't help you, nor the people you've hurt.

What matters is empathy.

Guilt leads to self-pity.

The only thing guilt leads to, is self-pity.

Self-pity is not constructive. In fact, it is incredibly destructive, and leads to fatalism. All you do when you pity yourself is proclaim, “it cannot be helped; I clearly cannot change, because I am a bad person.”

This is false and leads you to repeating the same patterns over and over.

Empathy leads to change

Put yourself in the shoes of the people you hurt. Do you genuinely think they want you to punish yourself repeatedly as penance, and feel endless remorse, or do you think they want you to change and not do it again?

I'm willing to bet it's the latter.

Wallowing in guilt will only make things worse. What good does self-punishment do if you've made no commitment to avoid repeating the mistake?

You're probably not a bad person

You have much to offer others. You may do bad things, but so do we all. The mark of a good person is one who learns from their mistakes and puts in the effort to change their behaviour.

Guilt alone is neutral. Most people experience it. It isn't anything special, and as I said above, it doesn't help.

Nothing can substitute actions.

As my Latin teacher said: results, not excuses.

— Elizabeth Myers (Elizafox) Fedi (elsewhere): @Elizafox@social.naln1.ca Tip jar: PayPal || CashApp || LiberaPay