Elizafox's Ramblings

The blag/dumping ground of Elizabeth Ōkami

I haven't found a comprehensive guide online to being safe in electronics, other than giving out fragments of tips. There's obvious things like “don't touch the hot part of the soldering iron,” but there are a lot of other things not mentioned.

This will be a post I will update as I find more information on the subject, and find things people may not be aware of when doing electronics work.

Failure to abide by safety guidelines may result in grave injury or death. Electronics is largely a safe hobby, when safe procedures are followed. Make safety a habit. You don't want to end up like this (content warning: graphic PSA).

This post is not comprehensive. Use common sense. If you don't know better than, say, don't touch the hot end of an iron, you should probably have a few more birthdays before you do electronics work.

This all may seem like a lot, but trust me: it becomes automatic with practise.

Feedback is welcome for this post. Contact me for any additions, clarifications, or fixes you would like to see!

Solder safety

  • Never put solder in your mouth, not even for a moment, not even to hold it
  • Do not eat or drink anything whilst soldering
  • Wash your hands after soldering thoroughly
  • Only solder in well-ventilated areas
    • Lead is not in solder smoke as it is not hot enough to vaporise the lead; however, tin may be present and is an inhalation hazard
    • Note: tin is always present in lead-free solders
  • Do not allow pets to chew on or eat solder
  • Keep solder out of reach of children

Flux safety

  • Many fluxes are corrosive or irritating to skin; do not allow flux to contact your hands for long periods
    • Wash off all flux thoroughly if it gets on your hands
  • Clean all excess flux off your boards and work area with isopropanol (rubbing alcohol)
  • Do not allow pets to consume flux
  • Keep flux out of reach of children

Soldering iron / hot equipment safety

  • Never grab the soldering iron or hot solder sucker by the end
  • Do not leave hot equipment unattended for any period
  • Have a non-flammable holder for the equipment around at all times
  • If using a sponge, ensure it is wet
    • Do not hold the equipment to any point on a sponge for long periods
  • Never use temperatures above what you need for the work at hand
  • If a hot piece of equipment falls, let it fall; under no circumstances should you ever try to grab it
  • Assume hot equipment is always hot; verify the equipment is not hot before servicing in any way (tip change, etc.)
  • Use care around hot equipment; do not rush or try to hurry
  • Work should never be performed on an energised circuit
  • Wear only cotton or wool garments when working with hot equipment; other garments may melt and burn the skin upon contact
    • Don't work with hot equipment without wearing jeans and a shirt; I know it sounds cute to solder in a skirt, but for safety's sake, please don't

Isopropanol (rubbing alcohol) safety

  • Isopropanol is highly flammable; keep away from all hot equipment and sparks of any kind
  • Do not ingest isopropanol; I'm not sure I have to include this one, but I've been asked if it's toxic...
  • Never use isopropanol on any energised circuit
  • Remove all excess isopropanol from surfaces and allow to dry before energising a circuit
  • Prolonged contact with skin may result in dry skin; try to avoid skin contact with isopropanol

PCB safety

  • Work should never be performed on an energised circuit
  • High voltage and low voltage must be separated in any design, with isolation at all connection points
    • Drilled gaps in the board between high and low voltage are best when possible to avoid arcing
    • Traces, contacts, and pins must be spaced sufficiently apart to avoid arcing
    • Traces, contacts, and pins must be sized appropriately to the voltage and amperage; more voltage and/or more amperage means you need larger traces
    • Higher voltages always require wider traces, even at low current
  • PCB edges can be sharp; use caution when handling PCB's
  • Wash your hands after handling any PCB
  • Discharge any higher-value capacitors in your circuit before work, especially those charged at higher voltages and above a millifarad
  • Wear respiratory protection when sanding PCB's, and remove all dust wifh HEPA filter vacuums
  • Do not store PCB's in bulk in bags; this can result in toxic and dangerous dust from PCB friction collecting inside the bag

Voltage/amperage safety

  • Only work around energised circuits with dry hands
  • The breakdown voltage of dry skin is around 20-40 volts (although can be as high as 50 volts for very dry skin); above this, power can begin to conduct through the skin; use care around voltages above 20 volts,
    • The threshold amperage at which you can still let go of a current source is around 6 milliamps (however, this will vary by person); use special care when this much amperage is involved with 20 or more volts
    • As a general rule, to set good habits, use caution with any voltage over 20 volts, and treat any voltage over 50 volts as inherently dangerous, regardless of current
  • Do not attempt to directly use mains voltage in your project without knowing exactly what you are doing
  • Use only components rated for the voltage your circuit will run at
  • Ensure your wire gauge is sufficiently thick for the voltage and current going through it; here is how to size your wire correctly

Battery safety

  • Do not heat, puncture, or incinerate any battery; fire may result
  • If a battery will not charge, and it is not the charger at fault, do not attempt to force it to charge; discard the battery
  • Do not attempt to charge a battery run that has completely discharged (below 2 volts for a Lithium-ion battery)
  • If a battery begins to smoke, or emit a smell like acetone, metallic, or sweet, carefully remove the battery place immediately into a flame-resistant container and remove the battery to the outdoors; allow symptoms to dissipate before disposal
  • If a battery has begun to swell, carefully remove the battery and place immediately into a flame-resistant container (preferably with sand); dispose of it at an e-waste site
  • Dispose of all batteries at e-waste sites; never throw away a battery, or place them into a recycling bin
  • Battery fires should be extinguished with an ABC fire extinguisher or sand; remove the electronics outdoors immediately after extinguishing, as the battery may reignite
  • Use only insulated tools on equipment with large capacitors or higher voltages
  • Always assume if the voltage is high, the amperage is high

Other safety

  • Never work on energised equipment
  • Do not attempt to disassemble a microwave unless you know what you are doing; there is a capacitor inside that may be charged to lethal levels
  • Do not attempt to service a CRT unless you know what you are doing; there is a flyback capacitor inside charged at potentially lethal levels; always ground the CRT before use
  • If dealing with lasers, always wear eye protection
  • When checking for smells, waft, do not smell directly

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

Wow, two blog posts in one day? Gasp!

I didn't find many good guides on how to do this online other than some very poor quality YouTube videos, and had to figure all this out myself.

I hope by sharing my experience, I can help others who may be struggling with this.

Crash course on RS-485

RS-485 is a protocol often used for long-distance communication in industrial environments. It offers relatively high datarates at very long distances. It's remarkably robust and simple. The jist of it is that it uses differential signalling over twisted pair (although at low baud, it'll work over wet spaghetti noodles) to help with noise immunity and robustness.

If you need to transmit data over a couple of metres with an Arduino, just use RS-485 and some 4-connector cable. You'll thank me later.

This protocol is not the same as RS-232. The possible cable runs are much shorter, the signalling speed is much slower, no multipoint, limited multidrop, larger voltage swing, and RS-232 has standard connectors.

It is also not the same as RS-422. RS-422 has more in common with RS-232 than RS-485. RS-485 can have multiple commanding devices and multiple listening devices; RS-422 can only have one commanding device. RS-422 is also a 4-wire system, whereas RS-485 is a two-wire system. RS-422 is also full duplex, whereas RS-485 is half duplex. It also has shorter possible cable runs, like RS-232, but unlike RS-485.

Actually using RS-485 with the Arduino

RS-485 works with voltages of -7 to +12V range, i.e. ±7V with 0-5V signalling.

These voltage ranges will damage or destroy your Arduino. The pins are not tolerant of negative voltage, nor are they tolerant of voltage above the board's Vout (either 3.3V or 5V).

But there are solutions.

Enter MAX485 and MAX3485

Note: The MAX485 family is for 5 volt operation, whereas the MAX3485 is for 3.3 volt operation; otherwise they are morally equivalent. When in doubt, just get the MAX3483 or MAX3485; either is fine, though the MAX3483 has better noise immunity. Make sure to buy DIP packages if you're working with breadboards or perfboard! You can buy them from Mouser.

The MAX485 and MAX3485 are chips by Maxim that can drive RS-485 for us. They essentially isolate your Arduino from the voltages of RS-485 and also generate the correct voltages we need on the wires.

A word about noise

You may notice reading the datasheets on these chips that some of them are slower than others, and it talks about slew-rate limiting.

You might be thinking, “what the Heck does that mean? And why the Heck would I want a slower chip?”

Slew rate is how fast the voltage actually changes. We ideally want nice little square waves, but we rarely get this in reality. Slew-rate limited drivers are slower because they are limited by this rate in how fast they transmit signals to and from the Arduino (waiting for voltage to go to the correct levels at the correct times rather than just transmitting noise), providing cleaner outputs and and helping provide immunity from reflections and noise (which is ubiquitous in our environment). It's about tradeoffs. If you don't need more than 250 kpbs operation (and over the Arduino's UART, you probably don't), slew-rate limited chips are a great option to provide just a bit of extra insurance. But if you need that speed, and have good cables, then slew-rate limited IC's may not be the best choice.


It's pretty simple to wire up this chip to the Arduino. Be sure to refer to the datasheet for the exact pinouts.

Just connect VCC to the 3.3/5V output of the Arduino, GND to ground, A to your transmit line on your cable, and B to your receive line on your cable. If you're using peripherals, consult your documentation as to which is A or B (A is generally positive, B is generally negative).

Connect RO to your RX pin on your board, and DI to your TX pin. The other two pins, DE and RE, should be connected to whatever spare pins you have, but take note which is which and where they're connected; this will become important later.

Driver enable and receive enable (negative)

RS-485 is a half-duplex protocol. We can't transmit and receive at the same time without an additional pair. We need to set when we transmit and when we receive. This is where the DE and RE pins come in.

The DE and RE pins control the driver and receiver. The receiver is negated (that's what that little bar on top means), to make it easier to just connect it to DE and have it work as you expect. It can however be convenient to connect them separately.

Essentially, when DE is driven high, the chip is in transmit mode unconditionally. When both are low, the chip is in receive mode. We can set a low-power shutdown mode of the chip by driving RE high and DE low, which may be useful in some applications.


It is a good idea to terminate both ends of your cables with a 120Ω resistor (connecting A and B together with said resistor), but for short runs at low baud, it is not strictly necessary. Longer runs should absolutely terminate their cables to minimise reflections on the line.

Multiple devices

There can be more than two devices on an RS-485 bus. The official limit is 32, but I've heard some data sheets speak about 128 devices on the same bus. If you use multiple devices, you should use a protocol like Modbus, which is the most common option anyway.

Connections on the bus should be made with short stub connections off the main line. These connections to the bus should be kept as short as possible. A termination resistor is not needed between the two connections of the stub.

For short runs under 10 metres with low baud, wiring it any way that's convenient is probably fine.

A warning about ground

It is important that the ground potential between two RS-485 stations be the same. A simple connection between all of the grounds with a large-value resistor for current limiting (in case of large ground potential differences) is enough to ensure reliable transmission and avoid problems.

It is a common myth repeated online that differential signalling means you don't need to worry about ground potentials. That is absolutely untrue, and this myth can destroy your transceivers or even destroy your Arduino (or at best, just distort your signal). Just make ground common, it alleviates so many potential headaches and sadness.

For example: the difference in ground potential between my Arduino connected to my laptop, and my weather vane and anemometer, is actually several volts. I had a lot of transmit and receive errors, until I connected the grounds through a 5.6MΩ resistor (for current limiting, although it would have probably been better to use something like a 1MΩ resistor or less, to reduce impedance and lessen noise on ground).


Programming the Arduino with these chips is a snap:

// Our receive enable (negative) and driver enable pins
// Set these to where you connected them, as noted above
const int RE_NEG_PIN = 2;
const int DE_PIN = 3;

void read_enable() {
	digitalWrite(RE_NEG_PIN, 0);
	digitalWrite(DE_PIN, 0);

void write_enable() {
	digitalWrite(RE_NEG_PIN, 1);
	digitalWrite(DE_PIN, 1);

void lopower_shutdown() {
	digitalWrite(RE_NEG_PIN, 1);
	digitalWrite(DE_PIN, 0);

void loop() {

	pinMode(DE_PIN, OUTPUT);

	// Choose what baud you want or need here

void loop() {
	// Enable transmit

	// We came out of low-power mode. Wait a few milliseconds before transmit.


	// Ensure we are flushed before we disable writing

	// Read a response back
	char ch = Serial1.read();

	// Go into low-power mode

	// Wait one second


I don't know why it was so hard to figure out how to do this with just the chip online. In hindsight, this was all incredibly obvious and also right there in the data sheet if I'd known where to look and what all the jargon meant. But everything is working well for my application (weather station components).

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

I found I needed more serial connections than provided for by the Arduino Nano 33 BLE. SoftwareSerial doesn't exist on many Arduino platforms, but I couldn't figure out why. It's also a slow and suboptimal solution even in the best of times.

I found others online in the same boat, but the documentation on how to achieve this is sparse.

I will attempt to document everything I've found so far, providing example code where relevant.

A word of caution

Trying to use too many serial ports will certainly lead to sadness, confusion, and use a lot of pins.

For the Arduino Nano 33 BLE, if you absolutely need buffering and need more than two serial ports, you're out of luck. You will basically have to use another solution, or switch to a different Arduino with more pins. I offer some solutions below.

For other Arduinos: there basically is no solution, at least not a good one, other than the generic ones I outline below.

Without further ado...

Various Arduino models

How to achieve this varies based on Arduino model. There is no “one way” to do this.

A generic solution for everything

If you need more than 4 serial ports, you should probably consider using a multiplexer, or a microcontroller that has multiple UARTs and an SPI/I2C interface. You can find one with a basic search.

For just a spare port or two, DFRobot sells an I2C to dual UART module. If you need it sooner, Mouser sells them also.

Beware that more than four of these cannot be put on the I2C bus without an I2C multiplexer, but it would be inadvisable to do so anyway. I2C just doesn't have enough bandwidth for all these ports.

Arduino Nano 33 BLE

This platform is actually not the same as the Arduino Nano 33 IoT. It uses a different processor entirely, and the method used here is not applicable to that. More on that in a moment.

The Arduino Nano 33 BLE internally has a spare hardware serial connection that can be tied to (almost) any two pins. Here is an example (no headers required):

// Choose whatever digital/analog pins you want here
const int tx_pin = 2;
const int rx_pin = 3;

UART Serial2(tx_pin, rx_pin, NC, NC);

void setup() {
	// Wait for USB serial port

	// Init all the pins we are going to use
	pinMode(tx_pin, OUTPUT);
	pinMode(rx_pin, INPUT);

	// Start our new serial port

void loop() {

	char buf[8] = {0};
	size_t count = Serial2.readBytes(buf, sizeof(buf));
	Serial.print("Count = "); Serial.println(count);
	Serial.print("Data = "); Serial.println(buf);

But if you need more than two serial connections, you have to get clever:

// Choose whatever digital/analog pins you want here
const int tx_pin_s2 = 2;
const int rx_pin_s2 = 3;
UART Serial2(tx_pin_s2, rx_pin_s2, NC, NC);

const int tx_pin_s3 = 4;
const int rx_pin_s3 = 5;
UART Serial3(tx_pin_s3, rx_pin_s3, NC, NC);

void setup() {
	// Wait for USB serial port

	// Init all the pins we are going to use
	pinMode(tx_pin_s2, OUTPUT);
	pinMode(tx_pin_s3, OUTPUT);
	pinMode(rx_pin_s2, INPUT);
	pinMode(rx_pin_s3, INPUT);

	// Here we do not initalise serial, because we only have one spare hardware serial port.
	// We can partially overcome this limitation; more on that in a bit.

void loop() {
	char buf[8] = {0};


	// Start up Serial2 before use


	size_t count = Serial2.readBytes(buf, sizeof(buf));
	Serial.print("Count = "); Serial.println(count);
	Serial.print("Data = "); Serial.println(buf);

	// Ensure everything is completely flushed before we close it
	// Not strictly necessary, but done "just in case."

	// When we're done, shut down the port; we temporarily become unable to use it.
	// WARNING: nothing is buffered after this point on the port.
	// Anything transmitted to the port will be *LOST*. You have been warned.



	// When we want to use Serial3, we do the same thing


	// Clear buffer of previous contents
	memset(buf, 0, sizeof(buf));

	size_t count = Serial3.readBytes(buf, sizeof(buf));
	Serial.print("Count = "); Serial.println(count);
	Serial.print("Data = "); Serial.println(buf);

	// Same as Serial2

	// Same thing as Serial2, and same caveats apply.

As noted in the example code, an important limitation applies: when we use one port, we can't use the other. Nothing will be buffered or saved when we are using one port. If this limitation is important to you, consider using another board like the Due, Mega, or Giga. These boards have four UART ports.

Note that each serial pair will consume two pins.

Arduino Nano 33 IoT and Arduino Uno

The Arduino Nano 33 IoT and the Arduino Uno are different from the BLE. They use what are called SERCOMs, which is a considerably more involved process.

I defer to Adafruit's guide on it all for this, as they do a better job explaining it all than I can.

Arduino Due/Mega/Giga

On boards such as the Due, Mega, or Giga, there are four serial ports. If you need more than that, you should probably defer to using one of the generic solutions I outlined above.


Although a lot of people think UART has gone the way of disco with peripherals that use SPI or I2C, it certainly has not. It is not outmoded or obsolete by any means. Many useful modules only use UART, and do not use SPI or I2C at all, not even optionally. GPS modules and many air quality sensors come to mind.

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

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.treehouse.systems 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.treehouse.systems 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.treehouse.systems 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.treehouse.systems 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.treehouse.systems 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.treehouse.systems 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.treehouse.systems Tip jar: PayPal || CashApp || LiberaPay