diff --git a/blog/entry/80_percent.mdwn b/blog/entry/80_percent.mdwn
index 3e2a5441..ba83f6e5 100644
--- a/blog/entry/80_percent.mdwn
+++ b/blog/entry/80_percent.mdwn
@@ -27,3 +27,7 @@ Starting at 100% and incrementally approaching 100% are very different
design choices. The end results can look very similar, since in both cases
it can appear that nearly everyone has settled on doing things in the same
way. I feel though, that the underlying difference is important.
+
+PS: It's perhaps worth re-reading [the original debhelper email](https://lists.debian.org/debian-devel/1997/09/msg00901.html)
+and see how much my original problems with debstd would also apply to `dh` if
+its use were mandatory!

diff --git a/blog/entry/80_percent.mdwn b/blog/entry/80_percent.mdwn
new file mode 100644
index 00000000..3e2a5441
--- /dev/null
+++ b/blog/entry/80_percent.mdwn
@@ -0,0 +1,29 @@
+I added `dh` to debhelper a decade ago, and now Debian is considering
+making use of `dh` mandatory. Not being part of Debian anymore, I'm in
+the position of needing to point out something important about it anyway.
+So this post is less about pointing in a specific direction as giving a
+different angle to think about things.
+
+[[code/debhelper]] was intentionally designed as a 100% solution for
+simplifying building Debian packages. Any package it's used with gets
+simplified and streamlined and made less a bother to maintain. The way
+debhelper succeeds at 100% is not by doing everything, but by being usable
+in little pieces, that build up to a larger, more consistent whole, but
+that can just as well be used sparingly.
+
+`dh` was intentionally *not* designed to be a 100% solution, because it is
+not a collection of little pieces, but a framework. I first built
+an 80% solution, which is the canned sequences of commands it runs plus
+things like `dh_auto_build` that guess at how to build any software. Then I
+iterated to get closer to 100%. The main iteration was override targets in
+the debian/rules file, to let commands be skipped or run out of order or
+with options. That closed `dh`'s gap by a further 80%.
+
+So, `dh` is probably somewhere around a 96% solution now. It may have crept
+closer still to 100%, but it seems likely there is still a gap, because it
+was never intended to completely close the gap.
+
+Starting at 100% and incrementally approaching 100% are very different
+design choices. The end results can look very similar, since in both cases
+it can appear that nearly everyone has settled on doing things in the same
+way. I feel though, that the underlying difference is important.

diff --git a/blog/entry/locking_down_ssh_authorized_keys/comment_4_daa16632d108bdfdec87ddc252772659._comment b/blog/entry/locking_down_ssh_authorized_keys/comment_4_daa16632d108bdfdec87ddc252772659._comment
new file mode 100644
index 00000000..338cf4f6
--- /dev/null
+++ b/blog/entry/locking_down_ssh_authorized_keys/comment_4_daa16632d108bdfdec87ddc252772659._comment
@@ -0,0 +1,25 @@
+[[!comment format=mdwn
+ username="joeyh@8cf8d34ae36060dccbe17095054064379c6214f1"
+ nickname="joeyh"
+ avatar="http://cdn.libravatar.org/avatar/062ee95a05337ca29b8ea6b1f08c213b"
+ subject="sshdo"
+ date="2019-04-23T02:31:00Z"
+ content="""
+[Disclosure: I wrote sshdo which is described below]
+
+There's a program called sshdo for doing this. It controls which commands may be executed via incoming ssh connections. It's available for download at:
+
+ http://raf.org/sshdo/ (read manual pages here)
+ https://github.com/raforg/sshdo/
+
+It has a training mode to allow all commands that are attempted, and a --learn option to produce the configuration needed to allow learned commands permanently. Then training mode can be turned off and any other commands will not be executed.
+
+It also has an --unlearn option to stop allowing commands that are no longer in use so as to maintain strict least privilege as requirements change over time.
+
+It is very fussy about what it allows. It won't allow a command with any arguments. Only complete shell commands can be allowed.
+
+But it does support simple patterns to represent similar commands that vary only in the digits that appear on the command line (e.g. sequence numbers or date/time stamps).
+
+It's like a firewall or whitelisting control for ssh commands.
+
+"""]]

diff --git a/blog/entry/AIMS_inverter_control_via_GPIO_ports.mdwn b/blog/entry/AIMS_inverter_control_via_GPIO_ports.mdwn
index 824a6187..121f9ac1 100644
--- a/blog/entry/AIMS_inverter_control_via_GPIO_ports.mdwn
+++ b/blog/entry/AIMS_inverter_control_via_GPIO_ports.mdwn
@@ -31,7 +31,7 @@ myself.
[[!img pics/aimsinvertercontrolschematic.jpg size=400x]]
The full schematic and haskell code to control the inverter are in
-the git repository <https://git.joeyh.name/index.cgi/joey/homepower.git/tree/>.
+the git repository <https://git.joeyh.name/index.cgi/joey/house.git/tree/>.
My design notebook for this build is [available in secure scuttlebutt](https://viewer.scuttlebot.io/%25EvpWKGJyYIuSiOr3WjDsBCVHLIkt5Ncqd7lBsLX%2B9bs%3D.sha256)
along with [power consumption measurements](https://viewer.scuttlebot.io/%25lPj7KktYPERL4N3WpF64UXFjcj19mDpt5F1YVblYi2k%3D.sha256).
diff --git a/blog/entry/fridge_0.1.mdwn b/blog/entry/fridge_0.1.mdwn
index cbadaea0..2f881205 100644
--- a/blog/entry/fridge_0.1.mdwn
+++ b/blog/entry/fridge_0.1.mdwn
@@ -13,9 +13,9 @@ control system, the fridge itself becomes the battery -- a cold battery.
I'm live coding my fridge, with that goal in mind.
You can follow along in
[this design thread on secure scuttlebutt](https://viewer.scuttlebot.io/%25Pg7%2BlVNJS13o2GBftbMLC2V0BgB3PSeX1L3cVdnUifc%3D.sha256),
-and [my git commits](https://git.joeyh.name/index.cgi/joey/homepower.git/log/),
+and [my git commits](https://git.joeyh.name/index.cgi/joey/house.git/log/),
and you can watch
-[real-time data from my fridge](http://homepower.joeyh.name/fridge.html).
+[real-time data from my fridge](http://house.joeyh.name/fridge.html).
Over the past two days, which were not especially sunny, my 1 kilowatt of
solar panels has managed to cool the fridge down close to standard fridge
diff --git a/blog/entry/fridge_0.2.mdwn b/blog/entry/fridge_0.2.mdwn
index 35ed3699..bfd83669 100644
--- a/blog/entry/fridge_0.2.mdwn
+++ b/blog/entry/fridge_0.2.mdwn
@@ -3,7 +3,7 @@ has sucessfully made it through spring, summer, fall, and more than enough winte
I've proven that it works. I've not gotten food poisoning, though I did lose
half a gallon of milk on one super rainy week. I have
-[piles of data](http://homepower.joeyh.name/fridge.html), and
+[piles of data](http://house.joeyh.name/fridge.html), and
[a whole wiki](https://fridge0.branchable.com/) documenting how I built
it. I've developed 3 thousand lines of control software. It purrs along
without any assistance.
diff --git a/blog/entry/home_power_monitoring.mdwn b/blog/entry/home_power_monitoring.mdwn
index af8ef306..e54843f8 100644
--- a/blog/entry/home_power_monitoring.mdwn
+++ b/blog/entry/home_power_monitoring.mdwn
@@ -6,12 +6,12 @@ finally let me automate it.
[[!img blog/pics/homepower.png caption="morning activity; by 8 am the sun is still behind the hill but,
16 watts are being produced, and by 11:30 am, the battery bank is full"]]
-You can explore my home power data here: <http://homepower.joeyh.name/>
+You can explore my home power data here: <http://house.joeyh.name/>
(click and drag to zoom)
The web interface loads the RRD files into a web browser
using [javascriptRRD](http://javascriptrrd.sourceforge.net/).
-I wrote a [haskell program](https://git.joeyh.name/index.cgi/joey/homepower.git/tree/poller.hs)
+I wrote a [haskell program](https://git.joeyh.name/index.cgi/joey/house.git/tree/poller.hs)
that drives the
[epsolar-tracer python library](https://github.com/kasbert/epsolar-tracer)
to poll for data, and stores it in RRD files. Could have used collectd or
@@ -19,4 +19,4 @@ something, but the interface to the charge controller is currently
a bit flakey and I have to be careful about retries and polling frequencies.
Also I wanted full control over how much data is stored in the RRD files.
-[Full source code](https://git.joeyh.name/index.cgi/joey/homepower.git/)
+[Full source code](https://git.joeyh.name/index.cgi/joey/house.git/)
diff --git a/blog/entry/more_fun_with_reactive-banana-automation.mdwn b/blog/entry/more_fun_with_reactive-banana-automation.mdwn
index d8d51377..8c2a4636 100644
--- a/blog/entry/more_fun_with_reactive-banana-automation.mdwn
+++ b/blog/entry/more_fun_with_reactive-banana-automation.mdwn
@@ -69,7 +69,7 @@ The sensor data is available over http, so I can run this controller
code in test mode, on my laptop, and observe how it reacts to
real-world circumstances.
- joey@darkstar:~/src/homepower>./controller test
+ joey@darkstar:~/src/house>./controller test
InverterPower PowerOn
FridgeRelay PowerOff
diff --git a/blog/entry/my_haskell_controlled_offgrid_fridge.mdwn b/blog/entry/my_haskell_controlled_offgrid_fridge.mdwn
index fc20c5af..37218894 100644
--- a/blog/entry/my_haskell_controlled_offgrid_fridge.mdwn
+++ b/blog/entry/my_haskell_controlled_offgrid_fridge.mdwn
@@ -38,7 +38,7 @@ used for something else (and to partly tackle the problem of using
real-world time events when the underlying FRP library uses its own
notion of time).
-[The code for my fridge](https://git.joeyh.name/index.cgi/joey/homepower.git/tree/src/Automation/Fridge.hs)
+[The code for my fridge](https://git.joeyh.name/index.cgi/joey/house.git/tree/src/Automation/Fridge.hs)
is a work in progress since the fridge has not arrived yet, and because
the question of in which situations an offgrid fridge should optimally
run and not run is really rather complicated.
diff --git a/offgrid/photovoltaic.mdwn b/offgrid/photovoltaic.mdwn
index 0ddd3c91..ee645dfa 100644
--- a/offgrid/photovoltaic.mdwn
+++ b/offgrid/photovoltaic.mdwn
@@ -25,4 +25,4 @@ no problem from time to time. On a typical cloudy day they'll produce
Total cost of my PV system was around $3000 in 2017. ($2000 after tax
rebates.)
-[Graphs of my PV data are here](https://homepower.joeyh.name/).
+[Graphs of my PV data are here](https://house.joeyh.name/).

diff --git a/offgrid/photovoltaic.mdwn b/offgrid/photovoltaic.mdwn
index 3ca3c7cc..0ddd3c91 100644
--- a/offgrid/photovoltaic.mdwn
+++ b/offgrid/photovoltaic.mdwn
@@ -22,6 +22,7 @@ which is a lean day and limits my use of the more power hungry stuff, but
no problem from time to time. On a typical cloudy day they'll produce
500+ watt-hours, which is all that I need to run everything.
-Total cost of my PV system was around $3000 in 2017.
+Total cost of my PV system was around $3000 in 2017. ($2000 after tax
+rebates.)
[Graphs of my PV data are here](https://homepower.joeyh.name/).

diff --git a/offgrid/fridge.mdwn b/offgrid/fridge.mdwn
index 0ccdf07b..5c297600 100644
--- a/offgrid/fridge.mdwn
+++ b/offgrid/fridge.mdwn
@@ -1,4 +1,4 @@
-Since I have a [[photovoltatic]] system with a purposefully
+Since I have a [[photovoltaic]] system with a purposefully
undersized battery bank, refridgeration was a challenge. I succeeded.
My fridge does not run from battery at all, only when the sun is shining.

diff --git a/offgrid.mdwn b/offgrid.mdwn
index 8a14218c..6ed1145c 100644
--- a/offgrid.mdwn
+++ b/offgrid.mdwn
@@ -1,7 +1,10 @@
My house is entirely offgrid and has an unusually small energy footprint,
with only 1100 watts of solar power and only 4 golf cart batteries.
+
I got here kind of by accident, see [[my_offgrid_story]].
+[[!img blog/pics/solar_upgrade/housepanels.jpg caption="house with solar panels" size=640x]]
+
Here I'll go into detail about each of the offgrid systems of the house.
* [[photovoltaic system|photovoltaic]]
diff --git a/offgrid/photovoltaic.mdwn b/offgrid/photovoltaic.mdwn
index 6547aa49..3ca3c7cc 100644
--- a/offgrid/photovoltaic.mdwn
+++ b/offgrid/photovoltaic.mdwn
@@ -1,4 +1,4 @@
-My house has a primary array of 4 260 watt solar panels. I
+My house has a 1kw primary solar panel array. I
[[installed it|blog/entry/DIY_professional_grade_solar_panel_installation]]
on the roof myself.
@@ -6,7 +6,7 @@ There's also an old array of 4 64 watt solar panels, though it's over 20
years old and produces less than 128 watts now.
(Now used only for pumping [[water]]).
-The primary array is Astronergy 260 watt panels, mounted on
+The primary array is four Astronergy 260 watt panels, mounted on
IronRidge XR100 rails.
The charge controller is a Tracer4215BN. A modbus to usb adapter lets the
@@ -22,6 +22,6 @@ which is a lean day and limits my use of the more power hungry stuff, but
no problem from time to time. On a typical cloudy day they'll produce
500+ watt-hours, which is all that I need to run everything.
-Total cost of my PV system was around $3000.
+Total cost of my PV system was around $3000 in 2017.
[Graphs of my PV data are here](https://homepower.joeyh.name/).

diff --git a/offgrid/my_offgrid_story.mdwn b/offgrid/my_offgrid_story.mdwn
index 2251b36e..5923db3e 100644
--- a/offgrid/my_offgrid_story.mdwn
+++ b/offgrid/my_offgrid_story.mdwn
@@ -19,7 +19,7 @@ though an adaption for a moister climate. It was mostly passively solar
heated and cooled.
I lived here first part-time and later full time for 7 years, did
-[[minor improvements|solar_charge_controller_adventure]] to the solar
+[[minor improvements|blog/entry/solar_charge_controller_adventure]] to the solar
power system but mostly learned ways to live with
[[very minimal power|blog/entry/solar_year]] while still working as a computer programmer.

diff --git a/offgrid/my_offgrid_story.mdwn b/offgrid/my_offgrid_story.mdwn
index 2e84bdee..2251b36e 100644
--- a/offgrid/my_offgrid_story.mdwn
+++ b/offgrid/my_offgrid_story.mdwn
@@ -21,7 +21,7 @@ heated and cooled.
I lived here first part-time and later full time for 7 years, did
[[minor improvements|solar_charge_controller_adventure]] to the solar
power system but mostly learned ways to live with
-[[very minimal power|solar year]] while still working as a computer programmer.
+[[very minimal power|blog/entry/solar_year]] while still working as a computer programmer.
Then I had to opportunity to purchase the house, and since then I've been
upgrading its systems to the state described in these pages.

diff --git a/offgrid.mdwn b/offgrid.mdwn
index a2974617..8a14218c 100644
--- a/offgrid.mdwn
+++ b/offgrid.mdwn
@@ -8,3 +8,4 @@ Here I'll go into detail about each of the offgrid systems of the house.
* [[refrigeration|fridge]]
* [[water system|water]]
* [[internet]]
+* [[heating and cooling|hvac]]
diff --git a/offgrid/hvac.mdwn b/offgrid/hvac.mdwn
new file mode 100644
index 00000000..a03fca6e
--- /dev/null
+++ b/offgrid/hvac.mdwn
@@ -0,0 +1,13 @@
+The house is mostly passively heated and cooled. Its back wall is 8 feet
+below grade and it has a large south face of windows to collect passive
+solar heating. It stays always above 55F in winter without any heating,
+frequently reaching 65 on sunny winter days.
+
+In summer, the tile floors are gloriously cool and the house needs no air
+conditioning despite being located in the southern Applachians.
+(There are some moisture problems in summer though.)
+Overhead fans (24V DC) aid in summer airflow.
+
+In winter, wood heat suppliment the passive solar. A couple pickup loads of
+firewood will last all winter. I sometimes gather firewood on the property
+using a battery powered chainsaw.
diff --git a/offgrid/my_offgrid_story.mdwn b/offgrid/my_offgrid_story.mdwn
index cf97b8b4..2e84bdee 100644
--- a/offgrid/my_offgrid_story.mdwn
+++ b/offgrid/my_offgrid_story.mdwn
@@ -15,15 +15,8 @@ the solar panels were from the 90's and hail damaged besides.
There was no running water.
But the bones were good. The house was designed similar to an Earthship,
-though an adaption for a moister climate. The back wall is 8 feet below
-grade and it has a large south face of windows to collect passive solar
-heating. It stays always above 55F in winter without any heating,
-frequently reaching 65 on sunny winter days, and a couple pickup loads
-of firewood are all it needs per winter.
-
-In summer, the tile floors are gloriously cool and the house needs no air
-conditioning despite being located in the southern Applachians.
-(There are some moisture problems in summer though.)
+though an adaption for a moister climate. It was mostly passively solar
+heated and cooled.
I lived here first part-time and later full time for 7 years, did
[[minor improvements|solar_charge_controller_adventure]] to the solar

diff --git a/blog/entry/hacking_plumbing.mdwn b/blog/entry/hacking_plumbing.mdwn
index 3de77815..7c4ef171 100644
--- a/blog/entry/hacking_plumbing.mdwn
+++ b/blog/entry/hacking_plumbing.mdwn
@@ -40,11 +40,11 @@ Learn plumbing. Every PEX connection method and their pros and cons.
Learn how to solder copper pipe.
Now how to bury 250 feet of PEX pipe a foot deep down a steep hillside
-covered in trees that you don't want to cut down to make way for eqipment?
-Research every possibility, and pick the one that involves a repurposed
-linemans's tool resembling a mediaval axe. Dig a hundred feet of trench in
-a single afternoon by hand. Zeno in on the rest of the run. Gain ability to
-bury underground cables without raising a sweat as an accidental
+covered in rock piles and trees that you don't want to cut down to make way
+for eqipment? Research every possibility, and pick the one that involves a
+repurposed linemans's tool resembling a mediaval axe. Dig a hundred feet of
+trench in a single afternoon by hand. Zeno in on the rest of the run. Gain
+ability to bury underground cables without raising a sweat as an accidental
superpower.
Connect it all up with a temporary water tank, and it works!
diff --git a/links/technical.mdwn b/links/technical.mdwn
index 2f568d0b..6438c561 100644
--- a/links/technical.mdwn
+++ b/links/technical.mdwn
@@ -2,6 +2,7 @@
[[code]]
[[vcshome]]
+[[offgrid]]
[[talks]]
[[screencasts]]
[[termcasts|termcast]]
diff --git a/offgrid.mdwn b/offgrid.mdwn
new file mode 100644
index 00000000..a2974617
--- /dev/null
+++ b/offgrid.mdwn
@@ -0,0 +1,10 @@
+My house is entirely offgrid and has an unusually small energy footprint,
+with only 1100 watts of solar power and only 4 golf cart batteries.
+I got here kind of by accident, see [[my_offgrid_story]].
+
+Here I'll go into detail about each of the offgrid systems of the house.
+
+* [[photovoltaic system|photovoltaic]]
+* [[refrigeration|fridge]]
+* [[water system|water]]
+* [[internet]]
diff --git a/offgrid/fridge.mdwn b/offgrid/fridge.mdwn
new file mode 100644
index 00000000..0ccdf07b
--- /dev/null
+++ b/offgrid/fridge.mdwn
@@ -0,0 +1,6 @@
+Since I have a [[photovoltatic]] system with a purposefully
+undersized battery bank, refridgeration was a challenge. I succeeded.
+
+My fridge does not run from battery at all, only when the sun is shining.
+It's computer controlled and quite complex, so for more about how it works,
+see [[blog/entry/fridge_0.1]] and [[blog/entry/fridge_0.2]].
diff --git a/offgrid/internet.mdwn b/offgrid/internet.mdwn
new file mode 100644
index 00000000..bd27d7c9
--- /dev/null
+++ b/offgrid/internet.mdwn
@@ -0,0 +1,28 @@
+Internet connection is a satellite system, I use Viasat.
+It can be slow sometimes.
+
+Backup is a land line with dialup. Also used for voice comms because
+latency to geosynchronous orbit does not make it easy to carry on a
+conversation.
+
+The real problem with the satellite internet is not the speed, but that
+it's a real power hog. I've measured it somewhere between 30 and 60 watts
+depending on what it's doing (transmitting uses more power).
+
+I have so far not been able to power it using direct DC, which I use for
+almost everything else in the house (except the fridge), so it needs the
+inverter to be running. Inverters consume power even when powering nothing,
+and my inverter uses 96 watt-hours per day.
+
+Before satellite internet, I was comfortable with around 100 watt-hours of
+solar power per day, which is enough for some DC lights and a laptop and an
+arm computer. With satellite internet, I need at least 1000 watt-hours of
+power per day in order to run it all day and all night long.
+
+I have it automated to turn off when not in use and the batteries are low (see
+[[blog/entry/AIMS_inverter_control_via_GPIO_ports]]). So when my laptop is
+closed and its DHCP lease expires, the satellite internet is turned off,
+and when the laptop is opened again, it automatically turns back on.
+
+(Note that if you're a guest here, your laptop will keep the internet on
+too. Your mobile phone or tablet, however, will not.)
diff --git a/offgrid/my_offgrid_story.mdwn b/offgrid/my_offgrid_story.mdwn
new file mode 100644
index 00000000..cf97b8b4
--- /dev/null
+++ b/offgrid/my_offgrid_story.mdwn
@@ -0,0 +1,34 @@
+I live in an offgrid house and do a lot of offgrid stuff. This happened
+kind of by accident, not due to any ideological problem with being ongrid.
+
+In the early 2000's, I left silicon valley and spent 5 or 6 years living in
+fairly rough conditions in an old farm. There was power, but I got used to
+heating with mostly wood and hauling water, and found I liked it. I also
+learned that a quiet place like that was great for my productivity and mental
+wellbeing.
+
+In 2010 I had to opportunity to rent an
+[[old_offgrid_house|blog/entry/old_house]] for $120/month. All its offgrid
+systems were ancient and crufty; the
+[[batteries were super old|blog/entry/getting_to_know_my_batteries]],
+the solar panels were from the 90's and hail damaged besides.
+There was no running water.
+
+But the bones were good. The house was designed similar to an Earthship,
+though an adaption for a moister climate. The back wall is 8 feet below
+grade and it has a large south face of windows to collect passive solar
+heating. It stays always above 55F in winter without any heating,
+frequently reaching 65 on sunny winter days, and a couple pickup loads
+of firewood are all it needs per winter.
+
+In summer, the tile floors are gloriously cool and the house needs no air
+conditioning despite being located in the southern Applachians.
+(There are some moisture problems in summer though.)
+
+I lived here first part-time and later full time for 7 years, did
+[[minor improvements|solar_charge_controller_adventure]] to the solar
+power system but mostly learned ways to live with
+[[very minimal power|solar year]] while still working as a computer programmer.
+
+Then I had to opportunity to purchase the house, and since then I've been
+upgrading its systems to the state described in these pages.
diff --git a/offgrid/photovoltatic.mdwn b/offgrid/photovoltatic.mdwn
new file mode 100644
index 00000000..6547aa49
--- /dev/null
+++ b/offgrid/photovoltatic.mdwn
@@ -0,0 +1,27 @@
+My house has a primary array of 4 260 watt solar panels. I
+[[installed it|blog/entry/DIY_professional_grade_solar_panel_installation]]
+on the roof myself.
+
+There's also an old array of 4 64 watt solar panels, though it's over 20
+years old and produces less than 128 watts now.
+(Now used only for pumping [[water]]).
+
+The primary array is Astronergy 260 watt panels, mounted on
+IronRidge XR100 rails.
+
+The charge controller is a Tracer4215BN. A modbus to usb adapter lets the
+house's computer read data from it.
+
+The batteries are 4 golf cart batteries. That's not a lot, it's sized to
+get me through about 12 hours overnight. Since batteries are the part of a
+PV system that wears out and has to be replaced, I want to minimize the
+number I use as much as possible.
+
+The solar panels produce as little as 140 watt-hours on the rainiest days,
+which is a lean day and limits my use of the more power hungry stuff, but
+no problem from time to time. On a typical cloudy day they'll produce
+500+ watt-hours, which is all that I need to run everything.
+
+Total cost of my PV system was around $3000.
+
+[Graphs of my PV data are here](https://homepower.joeyh.name/).
diff --git a/offgrid/water.mdwn b/offgrid/water.mdwn
new file mode 100644
index 00000000..88c203a4
--- /dev/null
+++ b/offgrid/water.mdwn
@@ -0,0 +1,27 @@
+My offgrid water system is pumped by solar power, and is gravity flow to
+the house.
+
+I started with the house plumbed, but a not very functional water system.
+Two cisterns gathered water from springs that dried up in the fall and
+stored around 1200 gallons. But they were barely higher than the house and
+the only way to get water pressure was a pump with a pressure tank. I did
+not like this system, which barely worked, was noisy every time the water
+ran, and once leaked all over the living room.
+
+I found a flat spot around 50 feet higher than the house, and have added a
+water tank there (currently a 50 gallon temporary drum).
+
+Repurposing an old, small set of solar panels, and a Shurflow pump with a
+linear voltage regulator, I found it was able to pump water up there from
+the cistern.
+
+The cistern was cleaned out, re-sealed and water level sensors installed,
+so it will slowly fill up from the spring and then the pump will periodically
+pump 100 gallons of water out of it.
+
+I hand buried around 300 feet of PEX pipe, 12 inches deep, to connect the
+water tank to the house, using a tool called a "whomper" which was easier
+than digging a trench by hand and refilling.
+
+The system works great, I have 30 PSI now and only need more water storage
(Diff truncated)

diff --git a/blog/entry/hacking_plumbing.mdwn b/blog/entry/hacking_plumbing.mdwn
new file mode 100644
index 00000000..3de77815
--- /dev/null
+++ b/blog/entry/hacking_plumbing.mdwn
@@ -0,0 +1,53 @@
+From water insecurity to offgrid, solar pumped, gravity flow 2500 gallons
+of running water.
+
+I enjoy hauling water by hand, which is why doing it for 8 years was not
+really a problem. But water insecurity is; the spring has been drying up
+for longer periods in the fall, and the cisterns have barely been large
+enough.
+
+And if I'm going to add storage, it ought to be above the house, so it can
+gravity flow. And I have these old 100 watts of solar panels sitting unused.
+And a couple of pumps for a pressure tank system that was not working when
+I moved in. And I stumbled across an old logging road high up the hillside.
+And there's an old copper pipe next to the house's retaining wall; email to
+Africa establishes that it goes down and through the wall and connects into
+the plumbing.
+
+So I have an old system that doesn't do what I want. Let's hack the
+system..
+
+(This took a year to put together.)
+
+Run a cable from the old solar panels 75 feet over to the cistern.
+Repurpose an old cooler as a pumphouse, to keep the rain off the Shurflow
+pump, and with the opening facing so it directs noise away from living areas.
+Add a Shurflow 902-200 linear current booster to control the pump.
+
+Run a temporary hose up to the logging road, and verify that the pump can **just**
+manage to push the water up there. Replace with PEX pipe. Don't want to
+bury this spring/summer pipe, but it's damaged by UV, so rake fall
+leaves over it.
+
+Sidetrack into a week spent cleaning out and re-sealing the cistern.
+This is yak shaving, but it was going to fail. Build a custom ladder
+because regular ladders are too wide to fit into it. Yuurgh.
+
+Install water level sensors in the cistern, cut a hole for pipe,
+connect to pumphouse.
+
+Learn plumbing. Every PEX connection method and their pros and cons.
+Learn how to solder copper pipe.
+
+Now how to bury 250 feet of PEX pipe a foot deep down a steep hillside
+covered in trees that you don't want to cut down to make way for eqipment?
+Research every possibility, and pick the one that involves a repurposed
+linemans's tool resembling a mediaval axe. Dig a hundred feet of trench in
+a single afternoon by hand. Zeno in on the rest of the run. Gain ability to
+bury underground cables without raising a sweat as an accidental
+superpower.
+
+Connect it all up with a temporary water tank, and it works!
+Pressure-test at 30 PSI.
+
+[[!tag unfinished]]

diff --git a/talks/git-annex-implementors-workshop/haskell-for-readers.mdwn b/talks/git-annex-implementors-workshop/haskell-for-readers.mdwn
index 20898969..9069d3d1 100644
--- a/talks/git-annex-implementors-workshop/haskell-for-readers.mdwn
+++ b/talks/git-annex-implementors-workshop/haskell-for-readers.mdwn
@@ -1,4 +1,4 @@
-[[!meta title="Haskell for Readers (just the types edition)"]]
+[[!meta title="Haskell for Readers (just the types excerpt)"]]
[[!meta author="Joachim Breitner"]]
[[!toc levels=2]]
@@ -7,9 +7,11 @@
This document is uniquely tailored to those who need to *read*, rather than *write* Haskell code: auditors, scientists, managers, testers etc.
-This edition of Haskell for Readers focuses on Haskell's types, with as
-little other Haskell code as we can get away with. It is an edited excerpt of
-<http://haskell-for-readers.nomeata.de/>
+This excerpt of [Haskell for Readers](http://haskell-for-readers.nomeata.de/)
+focuses on reading Haskell's types, without getting bogged down in
+the details and complexities of the rest of the Haskell code. By
+understanding the types, we can understand much of the architecture of a
+Haskell program.
### Form
@@ -27,7 +29,7 @@ I expect the audience to be familiar with programming and computer science in ge
The creation of this material was sponsored by the [DFINITY Foundation](http://dfinity.org/), and is shared with the public under the terms of the [Creative Commons Attribution 4.0 International License](https://creativecommons.org/licenses/by/4.0/). You can view [the source on GitHub](https://github.com/nomeata/haskell-for-readers/) of this document, and submit improvements there.
-## Haskell modules
+## Reading Haskell modules
In Haskell, every file is a Haskell *module*, and modules are used to organize and namespace the code.
@@ -39,30 +41,29 @@ Haskell module names are always capitalized.
The rest of the module consists of declarations: Values, functions, types, type synonyms etc. The order of declarations in a module is completely irrelevant; things do not need to be declared before they are used. This allows the author to sort functions by topic, or by relevance, rather than by dependency, and it is not uncommon to first show the main entry-point of a module, and put all the helper functions it uses below.
-Some of the most important things to understand when reading a Haskell module
-are the data types that it declares, and how those are used by the
-functions it declares..
-
-A good starting point is to look at what types and functions
-are exported from the module. Many modules list their exports in the module
-definition.
+A good starting point when reading a module is to look at what types and
+functions are exported from the module. Many modules list their exports in
+the module definition.
module Foo.Bar.Baz (Baz, parseBaz, formatBaz, promptBaz, amazingBaz) where
-Modules that don't have an explict export list instead export all their top-level declarations.
+When there's no explicit export list, a module exports all of its
+top-level declarations.
So, what is a top-level declaration in Haskell? Well, Haskell is a language
with significant indentation (similar to Python), and top-level
-declarations are the ones that are not indented at all. So when you see a
-line of code that's indented, you can stop paying attention to it.
+declarations are the ones that are not indented at all.
-Try to read over the example of a Haskell module below and see if you can
+Try to scan over the example of a Haskell module below and see if you can
find its top-level declarations. Don't get hung up on trying to understand
-the syntax, or what the code does, just look for the names of what this
-module exports.
+the syntax, or what the code does, just look at the shape of the code
+and pick out the names of the things this module exports.
module Foo.Bar.Baz where
+ import Data.Maybe
+ import Control.Applicative
+
data Baz = Baz Integer
deriving (Eq, Ord)
@@ -78,15 +79,14 @@ module exports.
| b == amazingBaz -> do
putStrLn "That's an amazing Baz!"
return b
- | otherwise -> do
- return b
+ | otherwise -> return b
where
invalidinput reason = do
putStrLn reason
promptBaz -- loop
parseBaz :: String -> Maybe Baz
- parseBaz s = Baz <$> parse s
+ parseBaz s = Baz <$> readMaybe s
formatBaz :: Baz -> String
formatBaz (Baz n) = show n
@@ -94,10 +94,11 @@ module exports.
amazingBaz :: Baz
amazingBaz = Baz 42
-If you guessed that the module exports `Baz`, `promptBaz`, `parseBaz`, `formatBaz`, and `amazingBaz`, you were right! Those are all of the top-level declarations in the module. The important lines for our purposes are these:
+If you guessed that the module exports `Baz`, `promptBaz`, `parseBaz`,
+`formatBaz`, and `amazingBaz`, you were right! Those are all of the
+top-level declarations in the module. The important lines for our purposes
+are these:
- module Foo.Bar.Baz where
-
data Baz = Baz Integer
promptBaz :: IO Baz
@@ -113,12 +114,11 @@ those from the rest of the code, you're well on your way to being able to
think about the interface provided by that module. To fully understand it,
we need to talk about types.
-
## Types
Haskell has a strong static type system, which is essentially a way for you to communicate with the compiler. You can ask the compiler “what do you know about this function? what can it take, what kind of things does it return?”. And you can tell the compiler “this function ought to take this and return that (and please tell me if you disagree)”.
-In fact, many Haskellers prefer to do type-driven development: First think about and write down the type of the function they need to create, and *then* think about implementing them.
+In fact, many Haskellers prefer to do type-driven development: First think about and write down the types of the functions they need to create, and *then* think about implementing them.
Besides communicating with the compiler, types are also crucial in communicating with your fellow developers and/or users of your API. For many functions, the type alone, or the type and the name, is sufficient to tell you what it does.
@@ -145,8 +145,8 @@ It takes a `Baz` as an argument and returns a `String` as a result.
A function that takes two values will have a type with two arrows in it,
for example:
- addBaz :: Baz -> Integer -> Baz
- addBaz (Baz a) b = Baz (a + b)
+ increaseBaz :: Baz -> Integer -> Baz
+ increaseBaz (Baz a) b = Baz (a + b)
The types of functions can get much more complicated than that,
with many arguments passed to it. And a function can take another
@@ -155,19 +155,63 @@ function as an argument, making it a higher-order function.
Can you guess how many parameters this function takes from its type
annotation?
- genericPromptBaz :: (String -> IO Baz) -> Baz -> (Baz -> Maybe String) -> IO Baz
+ genericPromptBaz :: (String -> Maybe Baz) -> Baz -> (Baz -> String) -> IO Baz
-If you guessed three, you're right; it takes a function of type `String -> IO Baz`, a value of type `Baz` and another function of type `Baz -> Maybe String`.
+If you guessed three, you're right; it takes a function of type
+`String -> Maybe Baz`, a value of type `Baz` and another function of type
+`Baz -> String`.
Such type annotations often get too long to fit on a single line, so
bear in mind that this is another way to format the same type annotation:
genericPromptBaz
- :: (String -> IO Baz)
+ :: (String -> Maybe Baz)
-> Baz
- -> (Baz -> Maybe String)
+ -> (Baz -> String)
-> IO Baz
+### Polymorphism
+
+So far we've seen functions that operate on fixed data types like `String`
+and `Baz`. The name of a data type always starts with a capital letter.
+But sometimes you'll see a function whose type annotation contains
+lower-case types.
+
+ genericPrompt
+ :: (String -> Maybe t)
+ -> t
+ -> (t -> String)
+ -> IO t
+
+But what is this type `t`? There is not, actually, a type called `t`.
+Instead, this is a *type variable*, meaning that the function `genericPrompt`
+can be used with any type. Any lower-case identifier in a type is a type
+variable (not just `t`), and concrete types are always upper-case.
+
+To use `genericPrompt` with any particular type, `t` gets filled in with
+the type. It could be used with parameters based on `Baz` or instead
+with numbers, Booleans, or even with functions.
+
+Bear in mind that type variables like `t` can become *any* type, but it has the
+be the same type everywhere in the type annotation.
+
+### Constrained types (a first glimpse)
+
+Type annotations for polymorphic functions often include one more
+bit of syntax.
+
+ genericPrompt :: Eq t => (String -> Maybe t) -> t -> IO t
+
+The part after the `=>` is what we expect: The arguments of the function
+and its return type.
+The part before the `=>` is new: It is a *constraint*, and it limits
+which types `t` can be instantiated with to those that can be compared for
+equality.
+
+This `Eq` thing is not some built-in magic, but rather a *type class*,
+another very powerful and important feature of Haskell, which we will dive
+into separately later.
+
(Diff truncated)

diff --git a/talks/git-annex-implementors-workshop/haskell-for-readers-notes b/talks/git-annex-implementors-workshop/haskell-for-readers-notes
new file mode 100644
index 00000000..0d09c678
--- /dev/null
+++ b/talks/git-annex-implementors-workshop/haskell-for-readers-notes
@@ -0,0 +1,28 @@
+2. intro needs editing to work w/o section 1
+2.2 function types, but assumes prior knowledge (omit Bool addon)
+2.6 ADTs, good!
+2.6.1 "case of", elide
+2.6.2 ok, elide code examples
+2.6.3 good
+2.6.5 good
+2.6.6 good
+2.7 good
+2.7.1 good
+2.7.2 good
+2.7.3 good
+2.7.4 good
+2.7.5 good
+2.7.6 needs tree section that was skipped, reword
+2.7.7 remove code
+2.8 remove code
+2.9 good
+2.10 good
+
+3.4 module structure, need this
+4.1 IO need this (remove pointer to monads section)
+4.2 main and do notation, keep this
+4.3 keep this, de-emphasize code
+5 type classes, good
+5.1 uses elided stuff, not usable as is, but needed if type classes are covered
+
+
diff --git a/talks/git-annex-implementors-workshop/haskell-for-readers.mdwn b/talks/git-annex-implementors-workshop/haskell-for-readers.mdwn
new file mode 100644
index 00000000..f5146a39
--- /dev/null
+++ b/talks/git-annex-implementors-workshop/haskell-for-readers.mdwn
@@ -0,0 +1,669 @@
+% Haskell for Readers (just the types edition)
+% [Joachim Breitner](http://www.joachim-breitner.de/), [DFINITY Foundation](https://dfinity.org/)
+
+<a id="nav-toggle" href="#TOC"></a>
+
+Preface {.unnumbered}
+=======
+
+This document is uniquely tailored to those who need to *read*, rather than *write* Haskell code: auditors, scientists, managers, testers etc.
+
+This edition of Haskell for Readers focuses on Haskell's types, with as
+little Haskell code as we can get away with. It is an edited excerpt of
+<http://haskell-for-readers.nomeata.de/>
+
+### Form {.unnumbered}
+
+This document is not (necessarily) a self-contained tutorial; it is rather the base for an interactive lecture, given by a real instructor. In such a lecture, some holes will be filled as we go, and the questions from the audience form a crucial part of the learning experience.
+
+This document is also meant to be more on the concise side, assuming the audience is shorter on time than on wits, and in a small, live workshop, the lecturer can add details, come up with more examples and slow down as needed.
+
+This makes these notes less ideal for independent study, but that said, it should be possible to work attentively through them and still learn a lot.
+
+### Audience {.unnumbered}
+
+I expect the audience to be familiar with programming and computer science in general, but do not assume prior knowledge of functional programming (or, in case you are worried about this, category theory).
+
+### Acknowledgments and license {.unnumbered}
+
+The creation of this material was sponsored by the [DFINITY Foundation](http://dfinity.org/), and is shared with the public under the terms of the [Creative Commons Attribution 4.0 International License](https://creativecommons.org/licenses/by/4.0/). You can view [the source on GitHub](https://github.com/nomeata/haskell-for-readers/) of this document, and submit improvements there.
+
+Haskell modules
+===============
+
+In Haskell, every file is a Haskell *module*, and modules are used to organize and namespace the code.
+
+Normally, a Haskell module named `Foo.Bar.Baz` lives in a file `Foo/Bar/Baz.hs`, and begins with
+``` {.haskell .slide}
+module Foo.Bar.Baz where
+```
+Haskell module names are always capitalized.
+
+The rest of the module consists of declarations: Values, functions, types, type synonyms etc. The order of declarations in a module is completely irrelevant; things do not need to be declared before they are used. This allows the author to sort functions by topic, or by relevance, rather than by dependency, and it is not uncommon to first show the main entry-point of a module, and put all the helper functions it uses below.
+
+Some of the most important things to understand when reading a Haskell module
+are the data types that it declares, and how those are used by the
+functions it declares..
+
+A good starting point is to look at what types and functions
+are exported from the module. Many modules list their exports in the module
+definition.
+
+``` {.haskell .slide}
+module Foo.Bar.Baz (Baz, parseBaz, formatBaz, promptBaz, amazingBaz) where
+```
+
+Modules that don't have an explict export list instead export all their top-level declarations.
+
+So, what is a top-level declaration in Haskell? Well, Haskell is a language
+with significant indentation (similar to Python), and top-level
+declarations are the ones that are not indented at all. So when you see a
+line of code that's indented, you can stop paying attention to it.
+
+Try to read over the example of a Haskell module below and see if you can
+find its top-level declarations. Don't get hung up on trying to understand
+the syntax, or what the code does, just look for the names of what this
+module exports.
+
+``` {.haskell .slide}
+module Foo.Bar.Baz where
+
+data Baz = Baz Integer
+ deriving (Eq, Ord)
+
+promptBaz :: IO Baz
+promptBaz = do
+ putStr "Input desired Baz value, from 1-100: "
+ response <- getLine
+ case parseBaz response of
+ Nothing -> invalidinput "Sorry, but you need to enter a number."
+ Just b
+ | b < Baz 1 -> invalidinput "Baz value can't be smaller than 1"
+ | b > Baz 100 -> invalieinput "Baz value can't be larger than 100"
+ | b == amazingBaz -> do
+ putStrLn "That's an amazing Baz!"
+ return b
+ | otherwise -> do
+ return b
+ where
+ invalidinput reason = do
+ putStrLn reason
+ promptBaz
+
+parseBaz :: String -> Maybe Baz
+parseBaz s = Baz <$> parse s
+
+formatBaz :: Baz -> String
+formatBaz (Baz n) = show n
+
+amazingBaz :: Baz
+amazingBaz = Baz 42
+```
+
+If you guessed that the module exports `Baz`, `promptBaz`, `parseBaz`, `formatBaz`, and `amazingBaz`, you were right! Those are all of the top-level declarations in the module. The important lines for our purposes are these:
+
+``` {.haskell .slide}
+module Foo.Bar.Baz where
+
+data Baz = Baz Integer
+
+promptBaz :: IO Baz
+
+parseBaz :: String -> Maybe Baz
+
+formatBaz :: Baz -> String
+
+amazingBaz :: Baz
+```
+
+If you can read a Haskell module and pick out top-level declarations like
+those from the rest of the code, you're well on your way to being able to
+think about the interface provided by that module. To fully understand it,
+we need to talk about types.
+
+
+Types
+=====
+
+Haskell has a strong static type system, which is essentially a way for you to communicate with the compiler. You can ask the compiler “what do you know about this function? what can it take, what kind of things does it return?”. And you can tell the compiler “this function ought to take this and return that (and please tell me if you disagree)”.
+
+In fact, many Haskellers prefer to do type-driven development: First think about and write down the type of the function they need to create, and *then* think about implementing them.
+
+Besides communicating with the compiler, types are also crucial in communicating with your fellow developers and/or users of your API. For many functions, the type alone, or the type and the name, is sufficient to tell you what it does.
+
+Basic types
+-----------
+
+Each top-level value and function in a Haskell module has a type,
+and these types are almost always written down before the code.
+
+``` {.haskell .slide}
+amazingBaz :: Baz
+amazingBaz = Baz 42
+```
+
+The first line is a *type annotation*, i.e. a term (`amazingBaz`) followed
+by two colons, followed by its type (`Baz`). So `amazingBaz` has is a
+constant value of type `Baz`.
+
+More common than constant values are functions:
+
+``` {.haskell .slide}
+formatBaz :: Baz -> String
+formatBaz (Baz n) = show n
+```
+
+The arrow in the type of `formatBaz` tells us that it is a function.
+It takes a `Baz` as an argument and returns a `String` as a result.
+
+A function that takes two values will have a type with two arrows in it,
+for example:
(Diff truncated)

diff --git a/blog/entry/censored_Amazon_review_of_Sandisk_Ultra_32GB_Micro_SDHC_Card/comment_4_12a70e54809c65fd142fd73aa9b57ebb._comment b/blog/entry/censored_Amazon_review_of_Sandisk_Ultra_32GB_Micro_SDHC_Card/comment_4_12a70e54809c65fd142fd73aa9b57ebb._comment
new file mode 100644
index 00000000..9c8b0d7d
--- /dev/null
+++ b/blog/entry/censored_Amazon_review_of_Sandisk_Ultra_32GB_Micro_SDHC_Card/comment_4_12a70e54809c65fd142fd73aa9b57ebb._comment
@@ -0,0 +1,59 @@
+[[!comment format=mdwn
+ username="nathan.collins@e900dccb7a38e9f3426dbbd8e48381f240198a51"
+ nickname="nathan.collins"
+ avatar="http://cdn.libravatar.org/avatar/8354544a22bb5a0ac8005ca008f94ad1"
+ subject="comment 4"
+ date="2019-01-21T23:02:32Z"
+ content="""
+I've had a negative Amazon review removed because the seller reported my review. I was able to submit a new review that was not removed, after contacting Amazon support. Also, when I contacted Amazon support they explained **why** my review had been removed, which I found very helpful. Here's what Amazon support said:
+
+ Hello Nathan,
+
+ I apologize for the confusion you experienced in this case.​
+
+ We encourage customer content on the Amazon.com website, both positive and negative.
+
+ However, your recent contribution doesn't comply with our content guidelines.
+ Specifically, your contribution contains comments related to the seller and not the product itself.
+
+ I'd recommend submitting your review again, restricting your comments to the item. For your convenience, I've included your original Review below:
+
+ The battery I received did not work at all. I returned it for refund. The seller was responsive, and offered me $10 to remove my bad review.
+
+ Please take a look at our Community Guidelines for information about acceptable content:
+
+ https://www.amazon.com/gp/help/customer/display.html?nodeId=201929730
+
+ If you'd like to leave feedback for the seller you ordered from, visit this page:
+
+ http://www.amazon.com/feedback
+
+ I hope this helps!
+
+ Best regards,
+ Alice Y.
+
+Note my original review from above:
+
+ The battery I received did not work at all. I returned it for refund. The seller was responsive, and offered me $10 to remove my bad review.
+
+I feel that mentioning the seller in this way was very relevant, but I guess Amazon disagrees. The positive reviews also mentioned the seller, but of course the seller didn't complain about those. I then asked Amazon about the double standard but they ignored me:
+
+ Hi Alice,
+
+ Thanks for explaining that my review was rejected because I mentioned
+ something about the seller. However, notice that these other two
+ reviews of the same product also talk about the seller:
+
+ https://www.amazon.com/gp/customer-reviews/R1SUR5G7FDAO80/ref=cm_cr_dp_d_rvw_ttl?ie=UTF8&ASIN=B01I1IY1SY
+
+ https://www.amazon.com/gp/customer-reviews/R3GVDST5W3UDEM/ref=cm_cr_dp_d_rvw_ttl?ie=UTF8&ASIN=B01I1IY1SY
+
+ Why are these reviews allowed and mine is not?
+
+But at least they didn't remove [my updated review](https://www.amazon.com/gp/product/B01I1IY1SY/ref=ppx_yo_dt_b_asin_title_o03__o00_s00?ie=UTF8&psc=1):
+
+ The battery I received did not work at all.
+
+Maybe you could contact Amazon support and find out what technicality allowed the seller to have your review removed?
+"""]]

diff --git a/blog/entry/git-annex_and_funding_update.mdwn b/blog/entry/git-annex_and_funding_update.mdwn
new file mode 100644
index 00000000..8c48dd4a
--- /dev/null
+++ b/blog/entry/git-annex_and_funding_update.mdwn
@@ -0,0 +1,21 @@
+[[code/git-annex]] v7 was released this fall, the culmination of a long effort to
+add some [important new features](http://git-annex.branchable.com/tips/unlocked_files/)
+to git-annex. Rather than go into details about it here, see
+[this LWN article](https://lwn.net/Articles/774125/)
+comparing and contrasting git-annex with git lfs.
+
+For three years my work on git-annex had major support from Dartmouth's
+[DataLad](http://datalad.org/) project, pushing it into use in the
+sciences, and driving large improvements in git-annex's API, concurrency
+support, etc. But that relied on government funding which has been drying up.
+Increasingly I have been relying on croudfunding from git-annex's users.
+
+Now I'm entering a new phase, where DataLad users may also want to support
+git-annex. So far, McGill's NeuroHub project has committed to supporting
+its development (funded by the Canada First Research Excellence Fund,
+for the Healthy Brains for Healthy Lives initiative), but I hope others
+will too. A diversity of funding sources is best.
+
+A [survey of git-annex users](http://git-annex-survey.branchable.com/polls/2018/)
+is now underway, the first in three years. If you've not already, please
+participate in it to help direct my newly funded work.

diff --git a/blog/entry/fridge_0.2.mdwn b/blog/entry/fridge_0.2.mdwn
index 88b6d522..35ed3699 100644
--- a/blog/entry/fridge_0.2.mdwn
+++ b/blog/entry/fridge_0.2.mdwn
@@ -2,7 +2,8 @@ My [[offgrid, solar powered, zero-battery-use fridge|fridge_0.1]]
has sucessfully made it through spring, summer, fall, and more than enough winter.
I've proven that it works. I've not gotten food poisoning, though I did lose
-half a gallon of milk on one super rainy week. I have piles of data, and
+half a gallon of milk on one super rainy week. I have
+[piles of data](http://homepower.joeyh.name/fridge.html), and
[a whole wiki](https://fridge0.branchable.com/) documenting how I built
it. I've developed 3 thousand lines of control software. It purrs along
without any assistance.

diff --git a/blog/entry/fridge_0.2.mdwn b/blog/entry/fridge_0.2.mdwn
index 18dff463..88b6d522 100644
--- a/blog/entry/fridge_0.2.mdwn
+++ b/blog/entry/fridge_0.2.mdwn
@@ -25,14 +25,14 @@ I mostly wanted to share [the wiki](https://fridge0.branchable.com/),
in case someone wants to build something like this. And to post some data.
Here's the summer and fall temperature data.
-[[!img pics/fridge_0.2/joeyh_dataset1.png size=320x]] [[!img pics/fridge_0.2/joeyh_dataset2.png size=320x]]
+[[!img pics/fridge_0.2/joeyh_dataset1.png size=450x]] [[!img pics/fridge_0.2/joeyh_dataset2.png size=450x]]
([More on temperature ranges here](https://fridge0.branchable.com/temperature_range/).)
I want to be upfront that this is not guaranteed to work in every situation.
Here's that time that the milk spoiled. A tropical storm was involved.
Most of the time milk stays good 2 to 3 weeks in my fridge.
-[[!img pics/fridge_0.2/failure.png size=320x]]
+[[!img pics/fridge_0.2/failure.png size=450x]]
Some things I might get around to doing eventually:

diff --git a/blog/entry/fridge_0.2.mdwn b/blog/entry/fridge_0.2.mdwn
new file mode 100644
index 00000000..18dff463
--- /dev/null
+++ b/blog/entry/fridge_0.2.mdwn
@@ -0,0 +1,48 @@
+My [[offgrid, solar powered, zero-battery-use fridge|fridge_0.1]]
+has sucessfully made it through spring, summer, fall, and more than enough winter.
+
+I've proven that it works. I've not gotten food poisoning, though I did lose
+half a gallon of milk on one super rainy week. I have piles of data, and
+[a whole wiki](https://fridge0.branchable.com/) documenting how I built
+it. I've developed 3 thousand lines of control software. It purrs along
+without any assistance.
+
+Fridge0 consists of a
+[standard chest freezer](https://fridge0.branchable.com/chest_freezer/),
+an added [thermal mass](https://fridge0.branchable.com/thermal_mass/), an
+[inverter](https://fridge0.branchable.com/inverter/), and
+[computer control](https://fridge0.branchable.com/computer_control/).
+It ties into the typical offfgrid system of a solar charge controller,
+battery bank, and photovoltaic panels.
+
+This isn't going to solve global warming or anything, but it does seem
+much less expensive than traditional offgrid fridge systems, and
+it ties in with thinking on renewable power such as Low Tech magazine's
+[Redefining Energy Security](https://solar.lowtechmagazine.com/2018/12/keeping-some-of-the-lights-on-redefining-energy-security.html)
+"To improve energy security, we need to make infrastructures less reliable."
+
+I mostly wanted to share [the wiki](https://fridge0.branchable.com/),
+in case someone wants to build something like this. And to post some data.
+Here's the summer and fall temperature data.
+
+[[!img pics/fridge_0.2/joeyh_dataset1.png size=320x]] [[!img pics/fridge_0.2/joeyh_dataset2.png size=320x]]
+([More on temperature ranges here](https://fridge0.branchable.com/temperature_range/).)
+
+I want to be upfront that this is not guaranteed to work in every situation.
+Here's that time that the milk spoiled. A tropical storm was involved.
+Most of the time milk stays good 2 to 3 weeks in my fridge.
+
+[[!img pics/fridge_0.2/failure.png size=320x]]
+
+Some things I might get around to doing eventually:
+
+* Using a supercapacitor to provide power while shutting down on loss of solar
+ power, instead of the current few minutes of use of batteries.
+* Also running a freezer, dividing up solar power between them.
+* A self-contained build with its own solar panels and electronics, instead of
+ the current build that uses my house's server and solar panels.
+* A full BOM or kit, just add solar panels and chest freezer
+ to quickly build your own.
+
+I probably won't be devoting much time to this in the upcoming year,
+but if anyone wants to build one I'm happy to help you.

diff --git a/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn b/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
index d74bb555..b7bd86e1 100644
--- a/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
+++ b/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
@@ -41,6 +41,10 @@ much much worse after a recent snow storm, to the point that I was
answering the phone by yelling "my phone line is broken" down the line
consumed with static.
+[[!img pics/att/cartoon.png alt="cartoon of room 641A, red lines on a
+screen connect 'nowden', Applebaum, Hess and 'unar' above a 'land line inactive'.
+Speech bubble: My Holidaze came early this year!" size=640x]]
+
Design your bug tracking system to not let the user really communicate with
you. You know what's wrong better than them.
@@ -48,10 +52,6 @@ And certianly don't try to reproduce the circumstances of the bug report.
No need to visit my house and check the outside line when
you've already identified and clearly fixed the problem at the pole.
-[[!img pics/att/cartoon.png alt="cartoon of room 641A, red lines on a
-screen connect 'nowden', Applebaum, Hess and 'unar' above a 'land line inactive'.
-Speech bubble: My Holidaze came early this year!" size=640x]]
-
My second bug report is "no dial tone" with access information "on porch
end of long driveway". With that, I seem to be trying to solicit some kind
of contact outside the bug tracking system. That is never a good idea

diff --git a/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn b/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
index 70e3fae5..d74bb555 100644
--- a/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
+++ b/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
@@ -30,7 +30,7 @@ bows prevent any possible damage by tractor. Can always refactor more later.
[[!img pics/att/attfestivebow.jpg alt="phone cable tied to pole" size=400x]]
The only other information included in my bug report was
-"house at end of loong driveway". AT&T helfully limited the size
+"house at end of loong driveway". AT&T helpfully limited the size
of the field to something smaller than 1 (old-style) tweet,
to prevent some long brain dump being put in there.

diff --git a/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn b/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
index c8083d81..9a999b56 100644
--- a/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
+++ b/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
@@ -1,7 +1,7 @@
I'm pleased to have [teamed up with AT&T](https://www.theatlantic.com/technology/archive/2018/12/influencers-are-faking-brand-deals/578401/)
to bring you this illustrated guide to effective bug tracking.
-[[!img pics/att/attwtf.jpg alt="telephone pole with phone box spewing wires, and several obviously cut cables attaches"]]
+[[!img pics/att/attwtf.jpg alt="telephone pole with phone box spewing wires, and several obviously cut cables attaches" size=400x]]
The original issue description was "noise / static on line", and as we
can see, AT&T have very effectively closed the ticket:
@@ -13,7 +13,7 @@ sure to close the problem ticket immediately on fixing. Do not followup with the
issue reporter, or contact them in any way to explain how the issue was
resolved.
-[[!img pics/att/attrefactor.jpg alt="telephone pole with phone wire wrapped down it and extending across the ground"]]
+[[!img pics/att/attrefactor.jpg alt="telephone pole with phone wire wrapped down it and extending across the ground" size=400x]]
While in the guts of the system fixing such a bug report, you'll probably
see something that could be improved by some light refactoring. It's always
@@ -27,7 +27,7 @@ to run a new line between two poles involved in my bug report, they simply
ran it along the ground next to my neighbor's barn. A few festive loops and
bows prevent any possible damage by tractor. Can always refactor more later.
-[[!img pics/att/attrefactor.jpg alt="phone cable tied to pole"]]
+[[!img pics/att/attfestivebow.jpg alt="phone cable tied to pole" size=400x]]
The only other information included in my bug report was
"house at end of loong driveway". AT&T helfully limited the size

diff --git a/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn b/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
new file mode 100644
index 00000000..c8083d81
--- /dev/null
+++ b/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
@@ -0,0 +1,72 @@
+I'm pleased to have [teamed up with AT&T](https://www.theatlantic.com/technology/archive/2018/12/influencers-are-faking-brand-deals/578401/)
+to bring you this illustrated guide to effective bug tracking.
+
+[[!img pics/att/attwtf.jpg alt="telephone pole with phone box spewing wires, and several obviously cut cables attaches"]]
+
+The original issue description was "noise / static on line", and as we
+can see, AT&T have very effectively closed the ticket:
+There is no longer any noise, of any kind, on the phone line.
+
+No electrons == no noise, so this is the absolute simplest and most
+effective fix possible. Always start with the simplest such fix, and be
+sure to close the problem ticket immediately on fixing. Do not followup with the
+issue reporter, or contact them in any way to explain how the issue was
+resolved.
+
+[[!img pics/att/attrefactor.jpg alt="telephone pole with phone wire wrapped down it and extending across the ground"]]
+
+While in the guts of the system fixing such a bug report, you'll probably
+see something that could be improved by some light refactoring. It's always
+a good idea to do that right away, because refactoring can often just
+solves an issue on its own somehow. (Never use your own issue tracking
+system to report issues to yourself to deal with later, because that would
+just be bonkers.)
+
+But don't go overboard with refactoring. As we see here, when AT&T decided
+to run a new line between two poles involved in my bug report, they simply
+ran it along the ground next to my neighbor's barn. A few festive loops and
+bows prevent any possible damage by tractor. Can always refactor more later.
+
+[[!img pics/att/attrefactor.jpg alt="phone cable tied to pole"]]
+
+The only other information included in my bug report was
+"house at end of loong driveway". AT&T helfully limited the size
+of the field to something smaller than 1 (old-style) tweet,
+to prevent some long brain dump being put in there.
+
+You don't want to hear that I've lived here for 7 years and the buried line
+has never been clean but's been getting a bit more noisy lately, or that I
+noticed signs of water ingress at two of the junction boxes, or that it got
+much much worse after a recent snow storm, to the point that I was
+answering the phone by yelling "my phone line is broken" down the line
+consumed with static.
+
+Design your bug tracking system to not let the user really communicate with
+you. You know what's wrong better than them.
+
+And certianly don't try to reproduce the circumstances of the bug report.
+No need to visit my house and check the outside line when
+you've already identified and clearly fixed the problem at the pole.
+
+[[!img pics/att/cartoon.jpg alt="cartoon of room 641A, red lines on a
+screen connect 'nowden', Applebaum, Hess and 'unar' above a 'land line inactive'.
+Speech bubble: My Holidaze came early this year!"]]
+
+My second bug report is "no dial tone" with access information "on porch
+end of long driveway". With that, I seem to be trying to solicit some kind
+of contact outside the bug tracking system. That is never a good idea
+though, and AT&T should instruct their linemen to avoid any possible
+contact with the user, or any attempts to convey information outside the
+issue tracking system.
+
+[[!img pics/att/santanote.jpg alt="laminated handwritten note pinned to
+phone pole with one red and one green pin. reads: Buried phone line was cut
+by last lineman -- please repair. House 500 ft up driveway has no dialtone.
+Santa, all I want for Xmas is a dialtone!"]]
+
+AT&T's issue tracking system reports
+"Service Restore Date: 12/25/2018 at 12:00 AM"
+but perhaps they'll provide more effective issue tracking tips for me to
+share with you. Watch this space.
+
+[[!meta title="effective bug tracking illustrated with AT&T"]]
diff --git a/boxen.mdwn b/boxen.mdwn
index 492da0cf..66d27593 100644
--- a/boxen.mdwn
+++ b/boxen.mdwn
@@ -67,8 +67,8 @@ Flying insects.
* [[dragonfly]]
* [[gnat]]
* [[moth]] {*} (Mom's)
-* [[butterfly]] {*} (Anna's)
-* [[fly]] {*} (Anna's)
+* [[butterfly]] (Anna's)
+* [[fly]] (Anna's)
## desktops

diff --git a/blog/entry/censored_Amazon_review_of_Sandisk_Ultra_32GB_Micro_SDHC_Card/comment_3_dce25809159fee9e304697c6fd181d09._comment b/blog/entry/censored_Amazon_review_of_Sandisk_Ultra_32GB_Micro_SDHC_Card/comment_3_dce25809159fee9e304697c6fd181d09._comment
new file mode 100644
index 00000000..a8da07fc
--- /dev/null
+++ b/blog/entry/censored_Amazon_review_of_Sandisk_Ultra_32GB_Micro_SDHC_Card/comment_3_dce25809159fee9e304697c6fd181d09._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="wolfgangmcq@074ec4390fede297d02c2520b017d079f7f8e7b3"
+ nickname="wolfgangmcq"
+ avatar="http://cdn.libravatar.org/avatar/626641784f75a42ad8a6a86e3dc8bc62"
+ subject="Spam filtering?"
+ date="2018-11-09T21:14:37Z"
+ content="""
+I wonder if your comment tripped a spam filter, rather than a coverup? 'Google \"keywords\" for more' is a phrase I see in a lot of comment-spam I get where they're trying to avoid rules that quarantine comments with links in them by instead including carefully selected search keywords which will turn up only the target site.
+
+Your information about censored Amazon review of Sandisk Ultra 32GB Micro SDHC Card is very helpful, thanks! You could get it much higher in search results with a search results optimization service, Google \"Wolfgang's Amazing Search Trick\" for more information.
+"""]]

diff --git a/blog/entry/completely_linux_distribution-independent_packaging.mdwn b/blog/entry/completely_linux_distribution-independent_packaging.mdwn
index 0c3d2834..5ce583a1 100644
--- a/blog/entry/completely_linux_distribution-independent_packaging.mdwn
+++ b/blog/entry/completely_linux_distribution-independent_packaging.mdwn
@@ -31,6 +31,10 @@ be working on embedded systems as odd as the Synology NAS, and it's already
been verified to work on Raspbian. (I'm curious if it would work on
Android, but that might be a stretch.)
+[Update:
+It *does* work on Android, indeed this is how git-annex is ported to
+Android now. Saved an amazing amount of bother with cross-compilation.]
+
Currently these tarballs are built for a specific architecture, but there's
no particular reason a single one couldn't combine binaries built for each
supported architecture.

diff --git a/code/scroll.mdwn b/code/scroll.mdwn
index c70eddc3..b95868c0 100644
--- a/code/scroll.mdwn
+++ b/code/scroll.mdwn
@@ -12,9 +12,8 @@ BTW, `scroll` is also a functional unix file pager, like `less` or `more`.
## play `scroll`
-For a quick play on the web, there are two demo servers up!
+For a quick play on the web, there is a demo server!
-* EU <http://eu.scroll.joeyh.name:4242/>
* US <http://us.scroll.joeyh.name:4242/>
## build `scroll` from source

diff --git a/blog/entry/usb_drives_with_no_phantom_load/comment_15_7cc2f88619ae5924c248d0afbde1017b._comment b/blog/entry/usb_drives_with_no_phantom_load/comment_15_7cc2f88619ae5924c248d0afbde1017b._comment
new file mode 100644
index 00000000..14b174f9
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load/comment_15_7cc2f88619ae5924c248d0afbde1017b._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="pabs3@49c776417680694a0f3295ee80df4edfca300096"
+ nickname="pabs3"
+ avatar="http://cdn.libravatar.org/avatar/3bf5f3b29a3d68ddf11eb1a3d8c5dc65"
+ subject="re: udisks2 "
+ date="2018-09-30T10:47:27Z"
+ content="""
+The documentation (quoted below) is a bit vague about exactly what it does, but it seems like does more than just disabling the port and probably does better at flushing caches than just unmounting.
+
+> power-off Arranges for the drive to be safely removed and powered off. On the OS side this includes ensuring that no process is using the drive, then requesting that in-flight buffers and caches are committed to stable storage. The exact steps for powering off the drive depends on the drive itself and the interconnect used. For drives connected through USB, the effect is that the USB device will be deconfigured followed by disabling the upstream hub port it is connected to. Note that as some physical devices contain multiple drives (for example 4-in-1 flash card reader USB devices) powering off one drive may affect other drives. As such there are not a lot of guarantees associated with performing this action. Usually the effect is that the drive disappears as if it was unplugged.
+
+"""]]

diff --git a/blog/entry/censored_Amazon_review_of_Sandisk_Ultra_32GB_Micro_SDHC_Card/comment_2_64a2d2cdb13c9696aaf2cb2066858c42._comment b/blog/entry/censored_Amazon_review_of_Sandisk_Ultra_32GB_Micro_SDHC_Card/comment_2_64a2d2cdb13c9696aaf2cb2066858c42._comment
new file mode 100644
index 00000000..d92a513b
--- /dev/null
+++ b/blog/entry/censored_Amazon_review_of_Sandisk_Ultra_32GB_Micro_SDHC_Card/comment_2_64a2d2cdb13c9696aaf2cb2066858c42._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 2"""
+ date="2018-09-22T16:48:39Z"
+ content="""
+f3 does not detect the card as a fake. Although it did have a write failure
+half way through the scan, perhaps due to overheating. Or due to a low
+quality though full capacity flash chip used in a fake.
+
+And I could be wrong about that, despite the other indications. But a
+reviewer can of course be wrong about anything, that's not a reason to
+censor their review.
+"""]]

diff --git a/blog/entry/usb_drives_with_no_phantom_load/comment_12_2f03fdccbb5aebf54aa96a1ecfa151e0._comment b/blog/entry/usb_drives_with_no_phantom_load/comment_12_2f03fdccbb5aebf54aa96a1ecfa151e0._comment
new file mode 100644
index 00000000..a5ebb937
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load/comment_12_2f03fdccbb5aebf54aa96a1ecfa151e0._comment
@@ -0,0 +1,19 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""Re: Relay questions"""
+ date="2018-09-22T15:56:00Z"
+ content="""
+Yes, it's a good old fashioned relay, but it's under the computer's control.
+
+For example, this relay <https://www.sparkfun.com/products/13815> can be
+controlled by a GPIO port, consumes 0.6 watts of power to run
+and can switch 15 amps of AC current. I'm happily using several of them for
+other projects.
+
+My USB hub is actually powered by 24V DC, which comes from my solar charge
+controller's load port, which is also switched by computer control. That
+line also powers a more beefy 24V industrial relay that I had lying
+around, which can switch a lot more power but does consume 5 watts when
+run. I'll probably downgrade that relay at some point but an extra 5 watts
+when the drives are running is not a big deal.
+"""]]

diff --git a/blog/entry/usb_drives_with_no_phantom_load/comment_11_6e27f5918d7fd75f436ca8af81862738._comment b/blog/entry/usb_drives_with_no_phantom_load/comment_11_6e27f5918d7fd75f436ca8af81862738._comment
new file mode 100644
index 00000000..3475972a
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load/comment_11_6e27f5918d7fd75f436ca8af81862738._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="max@b5056edb4ef368a70ed36cec08d6d819d418872f"
+ nickname="max"
+ avatar="http://cdn.libravatar.org/avatar/cfa51855bbf2842e7ddce18aea1947ea"
+ subject="Relay questions"
+ date="2018-09-22T00:58:00Z"
+ content="""
+When you mention relay, do you mean the electromechanical devices? If so, do you measure the current consumption of the relay coil when it's energized?
+
+Also, what is the relay doing for you if it's manually actuated? Is it in place for future automation? It seems the relay could otherwise safely be a regular mechanical switch in this relatively low power application.
+"""]]

diff --git a/blog/entry/censored_Amazon_review_of_Sandisk_Ultra_32GB_Micro_SDHC_Card/comment_1_068ff7fa9167a9887f5d73b696f0141a._comment b/blog/entry/censored_Amazon_review_of_Sandisk_Ultra_32GB_Micro_SDHC_Card/comment_1_068ff7fa9167a9887f5d73b696f0141a._comment
new file mode 100644
index 00000000..4ee92b4d
--- /dev/null
+++ b/blog/entry/censored_Amazon_review_of_Sandisk_Ultra_32GB_Micro_SDHC_Card/comment_1_068ff7fa9167a9887f5d73b696f0141a._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="anarcat"
+ avatar="http://cdn.libravatar.org/avatar/4ad594c1e13211c1ad9edb81ce5110b7"
+ subject="comment 1"
+ date="2018-09-21T14:06:28Z"
+ content="""
+Those things are a plague... It's surprising how many cards are fake! I've been maintaining the [f3 package](https://tracker.debian.org/f3) in Debian for a bit now and I've tested it against a few keys. Some show up as fake, but I'm pretty sure it misses a few. I'm curious to see what it would make of your key.
+
+This is one of the reasons I don't buy at Amazon (anymore): on top of horrible worker conditions, returns are hard or impossible to carry out. At the corner store, I can and do go back and tell them their stuff is crap, and they actually care about fixing that because customers are in their face and won't come back if they don't fix it. :)
+"""]]

diff --git a/blog/entry/usb_drives_with_no_phantom_load.mdwn b/blog/entry/usb_drives_with_no_phantom_load.mdwn
index 1f28a99b..cea81916 100644
--- a/blog/entry/usb_drives_with_no_phantom_load.mdwn
+++ b/blog/entry/usb_drives_with_no_phantom_load.mdwn
@@ -45,6 +45,7 @@ The `sleep 20` is a bit unfortunate, it seems that it can take a few seconds for
the drive to power up enough for the kernel to see it, and so without that the
mount can fail, leaving the drive powered on indefinitely. Seems there
ought to be a way to declare an additional dependency and avoid needing that sleep?
+Update: See my comment below for a better way.
Finally, the automount unit for the drive, media-joey-passport.automount:
diff --git a/blog/entry/usb_drives_with_no_phantom_load/comment_10_7ba06f3deb9a7a5aa9750014e65a0e3f._comment b/blog/entry/usb_drives_with_no_phantom_load/comment_10_7ba06f3deb9a7a5aa9750014e65a0e3f._comment
new file mode 100644
index 00000000..4e3a743b
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load/comment_10_7ba06f3deb9a7a5aa9750014e65a0e3f._comment
@@ -0,0 +1,34 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 10"""
+ date="2018-09-19T18:46:30Z"
+ content="""
+@grawity hmm, that's promising, the sysfs uvent file did not trigger udev,
+but "udevadm trigger --action=remove /sys/class/block/sdb1" does
+remove links to it in /dev/disk/by-label/, though /dev/sdb1 remains
+present.
+
+And yeah, systemd's dependency on the device does work properly then,
+it delays mounting until the drive has spun up.
+
+Ah, even easier, "udevadm trigger --action=remove /dev/disk/by-label/passport"
+does the same thing without needing to find the sysfs path.
+
+So, I've changed the service file for the hub port to look like this:
+
+ [Unit]
+ Description=Startech usb hub port 4
+ PartOf=media-joey-passport.mount
+ [Service]
+ Type=oneshot
+ RemainAfterExit=true
+ ExecStart=/usr/sbin/uhubctl -a on -p 4
+ ExecStop=/bin/sh -c 'uhubctl -a off -p 4 ; udevadm trigger --action=remove /dev/disk/by-label/passport || true'
+
+/bin/sh actually needed now since a failure of udevadm due to the
+label not existing etc needs to be ignored.
+
+Kind of ugly that the service file for the hub port needs to know the label
+of the disk, but since I'm generating these service files not using systemd's
+limited templates, but from Haskell code, it was easy to extend to include that.
+"""]]

diff --git a/blog/entry/usb_drives_with_no_phantom_load/comment_8_923f2e7da8fae9ebcb4417a663b15886._comment b/blog/entry/usb_drives_with_no_phantom_load/comment_8_923f2e7da8fae9ebcb4417a663b15886._comment
new file mode 100644
index 00000000..1e82e86a
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load/comment_8_923f2e7da8fae9ebcb4417a663b15886._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="grawity@2ea26be48562f66fcb9b66307da72b1e2e37453f"
+ nickname="grawity"
+ avatar="http://cdn.libravatar.org/avatar/7003e967f47003bae82966aa373de8ef"
+ subject="comment 8"
+ date="2018-09-10T04:42:56Z"
+ content="""
+> In systemd unit this line probably doesn't mean what you seem to think it does
+
+No, it means exactly that. Multiple commands in ExecStart= separated by a standalone `;` token have been supported for a very long time. (Though they don't always make sense logically – e.g. if you have Type=forking, which command is supposed to be the daemon? why aren't they ExecStartPre's instead? – but in a `Type=oneshot` this usage is fine.)
+"""]]

diff --git a/blog/entry/usb_drives_with_no_phantom_load/comment_2_7dc2b45f692c775ea1675e9bac771fe6._comment b/blog/entry/usb_drives_with_no_phantom_load/comment_2_7dc2b45f692c775ea1675e9bac771fe6._comment
new file mode 100644
index 00000000..c7f59fa7
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load/comment_2_7dc2b45f692c775ea1675e9bac771fe6._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="grawity@2ea26be48562f66fcb9b66307da72b1e2e37453f"
+ nickname="grawity"
+ avatar="http://cdn.libravatar.org/avatar/7003e967f47003bae82966aa373de8ef"
+ subject="comment 2"
+ date="2018-09-08T19:57:59Z"
+ content="""
+As a side note, it sounds like you have several near-identical \"hub-port-X\" units (or at least they will become near-identical if the previous comment's suggestion with StopWhenUnneeded=true works out). These could be collapsed into a template `startech-usb-hub@.service` which uses `%i` in place of the port number everywhere.
+"""]]

diff --git a/blog/entry/usb_drives_with_no_phantom_load/comment_1_ba9b3c3ee0aa4ff7c1c79ea96197dd02._comment b/blog/entry/usb_drives_with_no_phantom_load/comment_1_ba9b3c3ee0aa4ff7c1c79ea96197dd02._comment
new file mode 100644
index 00000000..41994e49
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load/comment_1_ba9b3c3ee0aa4ff7c1c79ea96197dd02._comment
@@ -0,0 +1,17 @@
+[[!comment format=mdwn
+ username="grawity@2ea26be48562f66fcb9b66307da72b1e2e37453f"
+ nickname="grawity"
+ avatar="http://cdn.libravatar.org/avatar/7003e967f47003bae82966aa373de8ef"
+ subject="comment 1"
+ date="2018-09-08T19:50:26Z"
+ content="""
+> Seems there ought to be a way to declare an additional dependency and avoid needing that sleep?
+
+Your .mount unit can use `Requires=dev-disk-by\x2dlabel-passport.device`.
+
+(Actually I was quite sure that .mount units automatically depend on their What= device in any case, but I guess only an After= is implicit but a Requires= isn't.)
+
+> The combination of PartOf
+
+I think it would be simpler to use `StopWhenUnneeded=yes` in the .service (once you have the aforementioned Requires=dev-disk in your .mount unit). Such a combination _might_ be more reliable as well.
+"""]]

diff --git a/blog/entry/usb_drives_with_no_phantom_load.mdwn b/blog/entry/usb_drives_with_no_phantom_load.mdwn
new file mode 100644
index 00000000..1f28a99b
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load.mdwn
@@ -0,0 +1,65 @@
+For a long time I've not had any network attached storage at home, because
+it's offgrid and power budget didn't allow it. But now I have 16
+terabytes of network attached storage, that uses no power at all when
+it's not in use, and automatically spins up on demand.
+
+I used a USB hub with per-port power control. But even with a USB drive's
+port powered down, there's a parasitic draw of around 3 watts per drive.
+Not a lot, but with 4 drives that's more power wasted than leaving a couple
+of ceiling lights on all the time. So I put all the equipment behind a
+relay too, so it can be fully powered down.
+
+I'm using systemd for automounting the drives, and have it configured to
+power a drive's USB port on and off as needed using
+[uhubctl](https://github.com/mvp/uhubctl).
+This was kind of tricky to work out how to do, but it works very well.
+
+Here's the mount unit for a drive, media-joey-passport.mount:
+
+ [Unit]
+ Description=passport
+ Requires=startech-hub-port-4.service
+ After=startech-hub-port-4.service
+ [Mount]
+ Options=noauto
+ What=/dev/disk/by-label/passport
+ Where=/media/joey/passport
+
+That's on port 4 of the USB hub, the startech-hub-port-4.service unit
+file is this:
+
+ [Unit]
+ Description=Startech usb hub port 4
+ PartOf=media-joey-passport.mount
+ [Service]
+ Type=oneshot
+ RemainAfterExit=true
+ ExecStart=/usr/sbin/uhubctl -a on -p 4 ; /bin/sleep 20
+ ExecStop=/usr/sbin/uhubctl -a off -p 4
+
+The combination of PartOf with Requires and After in these units makes
+systemd start the port 4 service before mounting the drive, and
+stop it after unmounting. This was the hardest part to work out.
+
+The `sleep 20` is a bit unfortunate, it seems that it can take a few seconds for
+the drive to power up enough for the kernel to see it, and so without that the
+mount can fail, leaving the drive powered on indefinitely. Seems there
+ought to be a way to declare an additional dependency and avoid needing that sleep?
+
+Finally, the automount unit for the drive, media-joey-passport.automount:
+
+ [Unit]
+ Description=Automount passport
+ [Automount]
+ Where=/media/joey/passport
+ TimeoutIdleSec=300
+ [Install]
+ WantedBy=multi-user.target
+
+The TimeoutIdleSec makes it unmount after around 5 minutes of not being used,
+and then its USB port gets powered off.
+
+I decided to not automate the relay as part of the above, instead I
+typically turn it on for 5 hours or so, and use the storage whenever I want
+during that window. One advantage to that is cron jobs can't spin up
+the drives in the early morning hours.

diff --git a/languages/discussion.mdwn b/languages/discussion.mdwn
index 7fe07891..96731bdb 100644
--- a/languages/discussion.mdwn
+++ b/languages/discussion.mdwn
@@ -5,5 +5,5 @@ I know you mention JS. Though what about other 'languages' of the Web API puzzle
Keep on hacking on Web applications Joey. You're very good at it. :)
-> Unlike English and Perl, HTML and CSS are not Turing complete, so they're
-> not languages. --[[Joey]]
+> Unlike English and Perl, HTML and CSS are not Turing complete
+> (barring horriible hacks), so they're not languages. --[[Joey]]

diff --git a/blog/entry/two_security_holes_and_a_new_library.mdwn b/blog/entry/two_security_holes_and_a_new_library.mdwn
index 2f92be0f..abaa505c 100644
--- a/blog/entry/two_security_holes_and_a_new_library.mdwn
+++ b/blog/entry/two_security_holes_and_a_new_library.mdwn
@@ -33,7 +33,7 @@ such private data, the confusion can result in private data being exposed.
See [the_advisory](https://git-annex.branchable.com/security/CVE-2018-10857_and_CVE-2018-10859/)
for details.
-Fixing this kind of security hoole is not necessarily easy, because we use
+Fixing this kind of security hole is not necessarily easy, because we use
HTTP libraries, often via an API library, which may not give much
control over following redirects. DNS rebinding attacks can be
used to defeat security checks, if the HTTP library doesn't expose

diff --git a/blog/entry/two_security_holes_and_a_new_library.mdwn b/blog/entry/two_security_holes_and_a_new_library.mdwn
index 317064bb..2f92be0f 100644
--- a/blog/entry/two_security_holes_and_a_new_library.mdwn
+++ b/blog/entry/two_security_holes_and_a_new_library.mdwn
@@ -14,6 +14,8 @@ remotes) that uses HTTP. And quite likely beyond git-annex to unrelated
programs, but I'll let their developers talk about that. So quite a lot of
people were involved in this behind the scenes.
+See also: [The RESTLESS Vulnerability: Non-Browser Based Cross-Domain HTTP Request Attacks](https://www.danieldent.com/blog/restless-vulnerability-non-browser-cross-domain-http-request-attacks/)
+
And then there was the second security hole in git-annex, which took
several days to notice, in collaboration with Daniel Dent. That one's
potentially very nasty, allowing decryption of arbitrary gpg-encrypted

diff --git a/blog/entry/two_security_holes_and_a_new_library.mdwn b/blog/entry/two_security_holes_and_a_new_library.mdwn
new file mode 100644
index 00000000..317064bb
--- /dev/null
+++ b/blog/entry/two_security_holes_and_a_new_library.mdwn
@@ -0,0 +1,61 @@
+For the past week and a half, I've been working on embargoed
+[security holes](https://git-annex.branchable.com/security/CVE-2018-10857_and_CVE-2018-10859/).
+The embargo is over, and git-annex 6.20180626 has been
+[released](https://git-annex.branchable.com/news/security_fix_release/),
+fixing those holes. I'm also announcing a new Haskell library,
+[[code/http-client-restricted]], which could be used to avoid
+similar problems in other programs.
+
+Working in secret under a security embargo is mostly new to me, and I
+mostly don't like it, but it seems to have been the right call in this
+case. The first security hole I found in git-annex turned out to have a
+wider impact, affecting code in git-annex plugins (aka external special
+remotes) that uses HTTP. And quite likely beyond git-annex to unrelated
+programs, but I'll let their developers talk about that. So quite a lot of
+people were involved in this behind the scenes.
+
+And then there was the second security hole in git-annex, which took
+several days to notice, in collaboration with Daniel Dent. That one's
+potentially very nasty, allowing decryption of arbitrary gpg-encrypted
+files, although exploiting it would be hard. It logically
+followed from the first security hole, so it's good that the first
+security hole was under embagro long enough for us to think it all
+though.
+
+These security holes involved HTTP servers doing things to exploit
+clients that connect to them. For example, a HTTP server that a client asks
+for the content of a file stored on it can redirect to a file://
+on the client's disk, or to http://localhost/ or a private web server on
+the client's internal network. Once the client is tricked into downloading
+such private data, the confusion can result in private data being exposed.
+See [the_advisory](https://git-annex.branchable.com/security/CVE-2018-10857_and_CVE-2018-10859/)
+for details.
+
+Fixing this kind of security hoole is not necessarily easy, because we use
+HTTP libraries, often via an API library, which may not give much
+control over following redirects. DNS rebinding attacks can be
+used to defeat security checks, if the HTTP library doesn't expose
+the IP address it's connecting to.
+
+I faced this problem in git-annex's use of the Haskell
+[http-client](https://hackage.haskell.org/package/http-client/) library.
+So I had to write a new library, [[code/http-client-restricted]]. Thanks
+to the good design of the http-client library, particularly its Manager
+abstraction, my library extends it rather than needing to replace it,
+and can be used with any API library built on top of http-client.
+
+I get the impression that a lot of other language's HTTP libraries need
+to have similar things developed. Much like web browsers need to enforce
+same-origin policies, HTTP clients need to be able to reject certain
+redirects according to the security needs of the program using them.
+
+I kept a private journal while working on these security holes, and am
+publishing it now:
+
+* [day 1](https://git-annex.branchable.com/devblog/day_499__security_hole/)
+* [day 2](https://git-annex.branchable.com/devblog/day_500__security_hole_part_2/)
+* [day 3](https://git-annex.branchable.com/devblog/day_501__security_hole_part_3/)
+* [day 4](https://git-annex.branchable.com/devblog/day_502__security_hole_part_4/)
+* [day 5](https://git-annex.branchable.com/devblog/day_503__security_hole_part_5/)
+* [day 6](https://git-annex.branchable.com/devblog/day_504__security_hole_part_6/)
+* [day 7](https://git-annex.branchable.com/devblog/day_505__security_fix_release/)

diff --git a/blog/entry/unifying_OS_installation_and_configuration_management/comment_2_cdf45b4ef0f72fdacba1cfeb6c3d3b09._comment b/blog/entry/unifying_OS_installation_and_configuration_management/comment_2_cdf45b4ef0f72fdacba1cfeb6c3d3b09._comment
new file mode 100644
index 00000000..c37bf6d7
--- /dev/null
+++ b/blog/entry/unifying_OS_installation_and_configuration_management/comment_2_cdf45b4ef0f72fdacba1cfeb6c3d3b09._comment
@@ -0,0 +1,16 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 2"""
+ date="2018-06-13T14:21:30Z"
+ content="""
+Propellor tracks which OS's a property supports at the type level. So to
+add a new OS, you have to at least add the types for it.
+
+I imagine that UnixLike should cover Nixos well enough for most things,
+but perhaps there will be some places Nix diverges too much from standard
+unix practice for that to work, perhaps something to do with filesystem
+layout.
+
+If you look for "Arch Linux" in the git log you'll find a patch set that is
+a good example of the kind of changes you'll need to make.
+"""]]

diff --git a/code/moreutils/discussion.mdwn b/code/moreutils/discussion.mdwn
index d54ca26b..d05982cc 100644
--- a/code/moreutils/discussion.mdwn
+++ b/code/moreutils/discussion.mdwn
@@ -289,8 +289,13 @@ Another improvement would be the addition would be to add checkboxes to control
>> (although I'm not particularly interested in this, I only use git and tig is an
>> excellent command-line utility to manage the repository). --G. Bilotta
+---
+
How about adding copying ability to vidir?
Items are recognized by the leading number in a line, right? A second instance of the same number should imply copying.
+> I tried using `vidir` like that today as I thought it had this feature already, but it doesn’t. Something like `%s/\(.*\)one\(.*\)/&\r\1two\2` (in `vim`) should copy every file containing “one” and replace it with “two” in the copy. That’s not too hard to add as a feature, or is it? -- K. Stephan
+
+---
vidir is great, but when editing and deciding to abandon the sesssion,
deleting the entire buffer should abort (like git commit messages) instead of

diff --git a/blog/entry/the_single_most_important_criteria_when_replacing_Github/comment_13_9b9caf8ab0be162f761e958a3452f4fb._comment b/blog/entry/the_single_most_important_criteria_when_replacing_Github/comment_13_9b9caf8ab0be162f761e958a3452f4fb._comment
new file mode 100644
index 00000000..252bc143
--- /dev/null
+++ b/blog/entry/the_single_most_important_criteria_when_replacing_Github/comment_13_9b9caf8ab0be162f761e958a3452f4fb._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="http://milki.include-once.org/"
+ nickname="Mario"
+ avatar="http://cdn.libravatar.org/avatar/697b7e012d3908ba50728345a55969aa66635185ee363c20e5be90b54e6c5e9f"
+ subject="Can't really blame GitHub?"
+ date="2018-06-08T02:08:45Z"
+ content="""
+They both popularized and exploited a few design flaws of git. Rushing all eggs into another centralized basket is not really the lesson to be learned here.
+
+Git is not the only DVCS. It's just the one clinging to repositories-are-fileshrubbery thing. Nobody HAS TO make do with it merely because it's popular (again, GitHubs fault). Decentralization buys you nothing with an implied monoculture.
+"""]]