Recent changes to this wiki:

Added a comment: Robust water solutions
diff --git a/blog/entry/hacking_water/comment_6_834b316d78faccab55f3ae5079f87e5d._comment b/blog/entry/hacking_water/comment_6_834b316d78faccab55f3ae5079f87e5d._comment
new file mode 100644
index 00000000..fa7f27c1
--- /dev/null
+++ b/blog/entry/hacking_water/comment_6_834b316d78faccab55f3ae5079f87e5d._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="dgray@c271d419d641bd22709a8e53c8b644231a99fa0c"
+ nickname="dgray"
+ avatar="http://cdn.libravatar.org/avatar/e17da7ffdecec6c96edd2d4a69879bd8"
+ subject="Robust water solutions"
+ date="2019-07-15T10:46:21Z"
+ content="""
+I read this with interest as it's pretty relevant for my situation, but with a totally different climate. In Crete we have a problem with power cuts, 
+but even though it's a dry climate we have a stable water supply. Also we run a co-op of a few of us to pump well water for irrigation, into our own tanks. This seems a ideal case to intermittently pump using solar power, since we get so much sunshine. Also the lowest recorded temperature here is 0.8C so no worries about frost damage. 
+
+Dave
+"""]]

layout
diff --git a/blog/entry/custom_type_checker_errors_for_propellor.mdwn b/blog/entry/custom_type_checker_errors_for_propellor.mdwn
index 14ad1410..20804db0 100644
--- a/blog/entry/custom_type_checker_errors_for_propellor.mdwn
+++ b/blog/entry/custom_type_checker_errors_for_propellor.mdwn
@@ -103,7 +103,7 @@ let's see if we can make it better.
 [[!tag propellor devblog]]
 [[!tag haskell]]
 
---
+----
 
 This was sponsored by Jake Vosloo and Trenton Cronholm
 <a href="https://patreon.com/joeyh">on Patreon</a>.

layout
diff --git a/blog/entry/custom_type_checker_errors_for_propellor.mdwn b/blog/entry/custom_type_checker_errors_for_propellor.mdwn
index 81f51a43..14ad1410 100644
--- a/blog/entry/custom_type_checker_errors_for_propellor.mdwn
+++ b/blog/entry/custom_type_checker_errors_for_propellor.mdwn
@@ -63,7 +63,7 @@ error, like this one:
 	    • Cannot combine properties:
 	        Property <unknown>
 	        Property HasInfo + Debian + Buntish + ArchLinux + FreeBSD
-		(Property <unknown> is often caused by applying a Property constructor to the wrong number of arguments.)
+	        (Property <unknown> is often caused by applying a Property constructor to the wrong number of arguments.)
 	    • In the first argument of ‘(&)’, namely
 	        ‘props & osDebian Unstable’
 

layout
diff --git a/blog/entry/custom_type_checker_errors_for_propellor.mdwn b/blog/entry/custom_type_checker_errors_for_propellor.mdwn
index c353634b..81f51a43 100644
--- a/blog/entry/custom_type_checker_errors_for_propellor.mdwn
+++ b/blog/entry/custom_type_checker_errors_for_propellor.mdwn
@@ -13,15 +13,15 @@ Most of the early wins came from using ghc's TypeError class, not
 the new library. I wanted custom type errors that were able to talk about
 problems with Property targets, like these:
     
-    • ensureProperty inner Property is missing support for: 
-	FreeBSD
+	    • ensureProperty inner Property is missing support for: 
+		FreeBSD
 
-    • This use of tightenTargets would widen, not narrow, adding: 
-        ArchLinux + FreeBSD
+	    • This use of tightenTargets would widen, not narrow, adding: 
+	        ArchLinux + FreeBSD
 
-    • Cannot combine properties:
-        Property FreeBSD
-        Property HasInfo + Debian + Buntish + ArchLinux
+	    • Cannot combine properties:
+	        Property FreeBSD
+	        Property HasInfo + Debian + Buntish + ArchLinux
 
 So I wrote a type-level pretty-printer for propellor's MetaType lists. One
 interesting thing about it is that it rewrites types such as `Targeting
@@ -40,43 +40,43 @@ Often a property constructor in propellor gets a new argument added to it.
 A propellor config that has not been updated to include the new argument
 used to result in this kind of enormous and useless error message:
 
-    • Couldn't match type ‘Propellor.Types.MetaTypes.CheckCombinable
-                             (Propellor.Types.MetaTypes.Concat
-                                (Propellor.Types.MetaTypes.NonTargets y0)
-                                (Data.Type.Bool.If
-                                   (Propellor.Types.MetaTypes.Elem
-                                      ('Propellor.Types.MetaTypes.Targeting 'OSDebian)
-                                      (Propellor.Types.MetaTypes.Targets y0))
-                                   ('Propellor.Types.MetaTypes.Targeting 'OSDebian
-                                      : Data.Type.Bool.If
-                                          (Propellor.Types.MetaTypes.Elem
-                                             ('Propellor.Types.MetaTypes.Targeting 'OSBuntish)
-    -- many, many lines elided
-    • In the first argument of ‘(&)’, namely
-        ‘props & osDebian Unstable’
+	    • Couldn't match type ‘Propellor.Types.MetaTypes.CheckCombinable
+	                             (Propellor.Types.MetaTypes.Concat
+	                                (Propellor.Types.MetaTypes.NonTargets y0)
+	                                (Data.Type.Bool.If
+	                                   (Propellor.Types.MetaTypes.Elem
+	                                      ('Propellor.Types.MetaTypes.Targeting 'OSDebian)
+	                                      (Propellor.Types.MetaTypes.Targets y0))
+	                                   ('Propellor.Types.MetaTypes.Targeting 'OSDebian
+	                                      : Data.Type.Bool.If
+	                                          (Propellor.Types.MetaTypes.Elem
+	                                             ('Propellor.Types.MetaTypes.Targeting 'OSBuntish)
+	    -- many, many lines elided
+	    • In the first argument of ‘(&)’, namely
+	        ‘props & osDebian Unstable’
 
 The type-errors library was a big help. It's able to detect when the type
 checker gets "stuck" reducing a type function, and is going to
 dump it all out to the user. And you can replace that with a custom type
 error, like this one:
 
-    • Cannot combine properties:
-        Property <unknown>
-        Property HasInfo + Debian + Buntish + ArchLinux + FreeBSD
-	(Property <unknown> is often caused by applying a Property constructor to the wrong number of arguments.)
-    • In the first argument of ‘(&)’, namely
-        ‘props & osDebian Unstable’
+	    • Cannot combine properties:
+	        Property <unknown>
+	        Property HasInfo + Debian + Buntish + ArchLinux + FreeBSD
+		(Property <unknown> is often caused by applying a Property constructor to the wrong number of arguments.)
+	    • In the first argument of ‘(&)’, namely
+	        ‘props & osDebian Unstable’
 
 Detecting when the type checker is "stuck" also let me add some custom
 type errors to handle cases where type inference has failed:
 
-    • ensureProperty outer Property type is not able to be inferred here.
-      Consider adding a type annotation.
-    • When checking the inferred type
-        writeConfig :: forall (outer :: [Propellor.Types.MetaTypes.MetaType]) t.
+	    • ensureProperty outer Property type is not able to be inferred here.
+	      Consider adding a type annotation.
+	    • When checking the inferred type
+	        writeConfig :: forall (outer :: [Propellor.Types.MetaTypes.MetaType]) t.
 
-    • Unable to infer desired Property type in this use of tightenTargets.
-      Consider adding a type annotation.
+	    • Unable to infer desired Property type in this use of tightenTargets.
+	      Consider adding a type annotation.
 
 Unfortunately, the use of TypeError caused one problem. When too many
 arguments are passed to a property constructor that's being combined with

blog update
diff --git a/blog/entry/custom_type_checker_errors_for_propellor.mdwn b/blog/entry/custom_type_checker_errors_for_propellor.mdwn
new file mode 100644
index 00000000..c353634b
--- /dev/null
+++ b/blog/entry/custom_type_checker_errors_for_propellor.mdwn
@@ -0,0 +1,109 @@
+Since [[code/propellor]] is configured by writing Haskell, type errors are
+an important part of its interface. As more type level machinery has been
+added to propellor, it's become more common for type errors to refer to
+hard to understand constraints. And sometimes simple mistakes in a
+propellor config result in the type checker getting confused and spewing an
+error that is thousands of lines of gobbledygook.
+
+Yesterday's release of the new 
+[type-errors](https://hackage.haskell.org/package/type-errors)
+library got me excited to improve propellor's type errors.
+
+Most of the early wins came from using ghc's TypeError class, not
+the new library. I wanted custom type errors that were able to talk about
+problems with Property targets, like these:
+    
+    • ensureProperty inner Property is missing support for: 
+	FreeBSD
+
+    • This use of tightenTargets would widen, not narrow, adding: 
+        ArchLinux + FreeBSD
+
+    • Cannot combine properties:
+        Property FreeBSD
+        Property HasInfo + Debian + Buntish + ArchLinux
+
+So I wrote a type-level pretty-printer for propellor's MetaType lists. One
+interesting thing about it is that it rewrites types such as `Targeting
+OSDebian` back to the `Debian` type alias that the user expects to see.
+
+To generate the first error message above, I used the pretty-printer
+like this:
+
+	(TypeError
+		('Text "ensureProperty inner Property is missing support for: "
+			':$$: PrettyPrintMetaTypes (Difference (Targets outer) (Targets inner))
+		)
+	)
+
+Often a property constructor in propellor gets a new argument added to it.
+A propellor config that has not been updated to include the new argument
+used to result in this kind of enormous and useless error message:
+
+    • Couldn't match type ‘Propellor.Types.MetaTypes.CheckCombinable
+                             (Propellor.Types.MetaTypes.Concat
+                                (Propellor.Types.MetaTypes.NonTargets y0)
+                                (Data.Type.Bool.If
+                                   (Propellor.Types.MetaTypes.Elem
+                                      ('Propellor.Types.MetaTypes.Targeting 'OSDebian)
+                                      (Propellor.Types.MetaTypes.Targets y0))
+                                   ('Propellor.Types.MetaTypes.Targeting 'OSDebian
+                                      : Data.Type.Bool.If
+                                          (Propellor.Types.MetaTypes.Elem
+                                             ('Propellor.Types.MetaTypes.Targeting 'OSBuntish)
+    -- many, many lines elided
+    • In the first argument of ‘(&)’, namely
+        ‘props & osDebian Unstable’
+
+The type-errors library was a big help. It's able to detect when the type
+checker gets "stuck" reducing a type function, and is going to
+dump it all out to the user. And you can replace that with a custom type
+error, like this one:
+
+    • Cannot combine properties:
+        Property <unknown>
+        Property HasInfo + Debian + Buntish + ArchLinux + FreeBSD
+	(Property <unknown> is often caused by applying a Property constructor to the wrong number of arguments.)
+    • In the first argument of ‘(&)’, namely
+        ‘props & osDebian Unstable’
+
+Detecting when the type checker is "stuck" also let me add some custom
+type errors to handle cases where type inference has failed:
+
+    • ensureProperty outer Property type is not able to be inferred here.
+      Consider adding a type annotation.
+    • When checking the inferred type
+        writeConfig :: forall (outer :: [Propellor.Types.MetaTypes.MetaType]) t.
+
+    • Unable to infer desired Property type in this use of tightenTargets.
+      Consider adding a type annotation.
+
+Unfortunately, the use of TypeError caused one problem. When too many
+arguments are passed to a property constructor that's being combined with
+other properties, ghc used to give its usual error message about too many
+arguments, but now it gives the custom "Cannot combine properties" type
+error, which is not as useful.
+
+[Seems likely that's a ghc bug](https://gitlab.haskell.org/ghc/ghc/issues/16894)
+but I need a better test case to make progress on that front. Anyway,
+I decided I can live with this problem for now, to get all the other
+nice custom type errors.
+
+The only other known problem with propellor's type errors is that,
+when there is a long list of properties being combined together, a single
+problem can result in a cascade of many errors. Sometimes that
+also causes ghc to use a lot of memory. While custom error messages don't
+help with this, at least the error cascade is nicer and individual messages
+are not as long.
+
+Propellor 5.9.0 has all the custom type error messages discussed here. If
+you see a hard to understand error message when using it, get in touch and
+let's see if we can make it better.
+
+[[!tag propellor devblog]]
+[[!tag haskell]]
+
+--
+
+This was sponsored by Jake Vosloo and Trenton Cronholm
+<a href="https://patreon.com/joeyh">on Patreon</a>.

Added a comment: re: udisks2
diff --git a/blog/entry/usb_drives_with_no_phantom_load/comment_16_cb53a97ad84573781050e59d71b6ac88._comment b/blog/entry/usb_drives_with_no_phantom_load/comment_16_cb53a97ad84573781050e59d71b6ac88._comment
new file mode 100644
index 00000000..dc657f72
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load/comment_16_cb53a97ad84573781050e59d71b6ac88._comment
@@ -0,0 +1,18 @@
+[[!comment format=mdwn
+ username="te36"
+ avatar="http://cdn.libravatar.org/avatar/7eebce89b2d86b0a9ed16d75fe7f978e"
+ subject="re: udisks2"
+ date="2019-06-24T06:32:37Z"
+ content="""
+If i just switch off ports via uhubctl and not bother removing the /dev nodes for it, then something in the system will later access the /dev node, which leads to I/O error, removal of the dev node from the file system, but also in powering on the port again and then recreation of the dev nodes. Aka: not good to ensure that ports will be powered off reliably. For example on my system, udisksd itself wakes up disks something like every 10 minutes for SMART check.
+
+When using udevadm or any other tool to just remove the /dev nodes, then the risk of unintentionally powering up the port/disk still exists when there are any appliation with still open files to the dev nodes i think. Worse yet, when all dev nodes are removed and one wants to power the port up again with uhubctl, then the system will not recreate the dev nodes - because the kernel never saw the disk to have gone away. Only the dev nodes where removed.
+
+udiskctl actually does the correct job: It removes the kernel notion of the disk, which then removes the dev nodes. Example:
+
+udisks-Message: 22:56:40.318: Successfully sent SCSI command SYNCHRONIZE CACHE to /dev/sdd
+udisks-Message: 22:56:41.088: Successfully sent SCSI command START STOP UNIT to /dev/sdd
+udisks-Message: 22:56:41.174: Powered off /dev/sdd - successfully wrote to sysfs path /sys/devices/pci0000:00/0000:00:08.1/0000:38:00.4/usb6/6-1/6-1.3/6-1.3.1/remove
+
+So the correct solution for me is to use udiskctl to remove dev nodes, then power down port with uhubctl. After powering up with uhubctl the kernel will correctly recreate the dev nodes. And in between, the only remaining risk for unintentionally waking up the disk is anything in the kernel resestting the whole hub.
+"""]]

comment
diff --git a/blog/entry/hacking_water/comment_5_c7bf44ead51288de3f255053307b70ca._comment b/blog/entry/hacking_water/comment_5_c7bf44ead51288de3f255053307b70ca._comment
new file mode 100644
index 00000000..7b71337d
--- /dev/null
+++ b/blog/entry/hacking_water/comment_5_c7bf44ead51288de3f255053307b70ca._comment
@@ -0,0 +1,16 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""Re: winter"""
+ date="2019-06-18T16:11:27Z"
+ content="""
+The water pump will be shut off and drained to winterize and probably only
+run a couple of times over the winter to refill tanks. I may eventually
+automate that with an arduino, temp sensor, and a couple of servos.
+
+The water tanks are unlikely to get more than a few inches of ice in them
+over a winter, which will not damage them, and I'm also going to paint them
+black to maximize solar heating. (And prevent algae growth.)
+
+My main concern is the buried line back to the house, which is on the
+marginal side of deep enough for this area, but probably deep enough.
+"""]]

Added a comment
diff --git a/blog/entry/hacking_water/comment_4_eb17d94ee65255913050e2c199a3374f._comment b/blog/entry/hacking_water/comment_4_eb17d94ee65255913050e2c199a3374f._comment
new file mode 100644
index 00000000..e59e3ebd
--- /dev/null
+++ b/blog/entry/hacking_water/comment_4_eb17d94ee65255913050e2c199a3374f._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="anthony@ad39673d230d75cbfd19d2757d754030049c7673"
+ nickname="anthony"
+ avatar="http://cdn.libravatar.org/avatar/05b48b72766177b3b0a6ff4afdb70790"
+ subject="comment 4"
+ date="2019-06-18T06:32:43Z"
+ content="""
+That's an impressive project! 
+
+Curious, what happens in the winter? Is it warm enough there that the tanks & lines don't freeze, or does it all have to be drained?
+"""]]

Added a comment: very interesting project
diff --git a/blog/entry/hacking_water/comment_3_ee711e07e3ee2e5c1b1721546b72321b._comment b/blog/entry/hacking_water/comment_3_ee711e07e3ee2e5c1b1721546b72321b._comment
new file mode 100644
index 00000000..76dd0de1
--- /dev/null
+++ b/blog/entry/hacking_water/comment_3_ee711e07e3ee2e5c1b1721546b72321b._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="mudflap"
+ avatar="http://cdn.libravatar.org/avatar/b06245a6c156973d6ca8279307f737af"
+ subject="very interesting project"
+ date="2019-06-17T19:13:14Z"
+ content="""
+I will not be able to do this on my property- it's completely flat. But I sure enjoy all the pics. Keep it up!
+"""]]

clarify solar panels
diff --git a/blog/entry/hacking_water.mdwn b/blog/entry/hacking_water.mdwn
index ea2a2b2d..aeac5aa1 100644
--- a/blog/entry/hacking_water.mdwn
+++ b/blog/entry/hacking_water.mdwn
@@ -7,12 +7,13 @@ for longer periods in the fall, and the cisterns have barely been large
 enough to get through.
 
 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
-after my solar upgrade. And a couple of pumps for a pressure tank system
-that was not working when I moved in. And I stumbled across an odd little
-flat spot halfway up the hillside. And there's an exposed 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.
+gravity flow. And I have this 100 watt array of 20 year old solar
+panels sitting unused after my solar upgrade. And a couple of pumps for a
+pressure tank system that was not working when I moved in. And I stumbled
+across an odd little flat spot halfway up the hillside. And there's an
+exposed 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.
 
 [[!img blog/pics/hacking_water/oldsolarpanel.jpg size=x300 alt="a solar
 panel with a large impact crater; the glass has cracked into thousands of

Added a comment: pv panels
diff --git a/blog/entry/hacking_water/comment_2_a564768e0c41453a956a442322f98ede._comment b/blog/entry/hacking_water/comment_2_a564768e0c41453a956a442322f98ede._comment
new file mode 100644
index 00000000..fbd71146
--- /dev/null
+++ b/blog/entry/hacking_water/comment_2_a564768e0c41453a956a442322f98ede._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="val"
+ avatar="http://cdn.libravatar.org/avatar/53c3129da7459a10fa233e782dde5ed8"
+ subject="pv panels"
+ date="2019-06-16T04:34:07Z"
+ content="""
+out of curiosity how much solar do you have installed (how many 100w panels)
+
+also I noticed that the panel looked cracked, are you putting this in series with the other panels? This might reduce your yield by quite a lot.
+
+Nice write-up by the way.
+"""]]

Added a comment: Hey Joe
diff --git a/blog/entry/hacking_water/comment_1_ee4ad394abf97efff43dd3f33fa9a87b._comment b/blog/entry/hacking_water/comment_1_ee4ad394abf97efff43dd3f33fa9a87b._comment
new file mode 100644
index 00000000..cf4152c0
--- /dev/null
+++ b/blog/entry/hacking_water/comment_1_ee4ad394abf97efff43dd3f33fa9a87b._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="notjoey"
+ avatar="http://cdn.libravatar.org/avatar/a0071fb2ba33b0815fa61863b6e9d1bb"
+ subject="Hey Joe"
+ date="2019-06-16T01:24:09Z"
+ content="""
+where you goin' with that gun in your hand?
+"""]]

add pic
diff --git a/blog/entry/hacking_water.mdwn b/blog/entry/hacking_water.mdwn
index 3224eb1d..ea2a2b2d 100644
--- a/blog/entry/hacking_water.mdwn
+++ b/blog/entry/hacking_water.mdwn
@@ -113,6 +113,8 @@ the ground between two water tanks is a complex assembly of blue pipes and
 brass fittings, with several valves"]]
 [[!img blog/pics/hacking_water/oldsolarpaneldetail.jpg size=x300
 alt="close-up of old cracked solar panel"]]
+[[!img blog/pics/hacking_water/tanksinstalled.jpg size=x300
+alt="the two tanks installed, high on a hillside"]]
 
 Begin filling the tanks, unsure how long it will take as the pump balances
 available sunlight and spring flow.
diff --git a/blog/pics/hacking_water/tanksinstalled.jpg b/blog/pics/hacking_water/tanksinstalled.jpg
new file mode 100644
index 00000000..3b11d410
Binary files /dev/null and b/blog/pics/hacking_water/tanksinstalled.jpg differ

publish
diff --git a/blog/entry/hacking_plumbing.mdwn b/blog/entry/hacking_water.mdwn
similarity index 90%
rename from blog/entry/hacking_plumbing.mdwn
rename to blog/entry/hacking_water.mdwn
index df77b72e..3224eb1d 100644
--- a/blog/entry/hacking_plumbing.mdwn
+++ b/blog/entry/hacking_water.mdwn
@@ -9,10 +9,10 @@ enough to get through.
 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
 after my solar upgrade. 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.
+that was not working when I moved in. And I stumbled across an odd little
+flat spot halfway up the hillside. And there's an exposed 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.
 
 [[!img blog/pics/hacking_water/oldsolarpanel.jpg size=x300 alt="a solar
 panel with a large impact crater; the glass has cracked into thousands of
@@ -25,7 +25,8 @@ black plastic curves out of the ground next to a wall"]]
 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.)
+(This took a year to research and put together, including learning a lot
+about plumbing.)
 
 Run a cable from the old solar panels 75 feet over to the spring.
 Repurpose an old cooler as a pumphouse, to keep the rain off the Shurflow 
@@ -39,10 +40,8 @@ labeled PUMP, PV, HIGH, LOW, GND, FLOAT SWITCH"]]
 [[!img blog/pics/hacking_water/temporary_water_barrel.jpeg size=x300 alt="50 gallon water barrel
 perched on a hillside with some hoses connected to it"]]
 
-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.
+Run a temporary pipe up to the logging road, and verify that the pump can
+**just** manage to push the water up there.
 
 Sidetrack into a week spent cleaning out and re-sealing the spring's
 settling tank. This was yak shaving, but it was going to fail. Build a
@@ -60,9 +59,6 @@ tank with walls bright and new, water level sensors and pipe to pump"]]
 Install water level sensors in the settling tank, 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 up a steep hillside
 covered in rock piles and trees that you don't want to cut down to make way
 for equipment? Research every possibility, and pick the one that involves a
@@ -72,18 +68,17 @@ repurposed linemans's tool resembling a medieval axe.
 with trees wherever there are not rocks"]]
 [[!img blog/pics/hacking_water/wilton_thinline_trenching_tool.jpeg size=x300 alt="just unboxed
 trenching tool looks like a large black metal spatula"]]
-
-Dig 100 feet of 1 inch wide trench in a single afternoon by hand. Zeno in
-on the rest of the 300 foot run. Gain ability to bury underground cables
-without raising a sweat as an accidental superpower. Arms ache for a full
-month afterwards.
-
 [[!img blog/pics/hacking_water/wilton_trench.jpeg size=x300 alt="30 feet of very narrow trench
 comes out of the woods and along the side of the house past the satellite
 internet dish"]]
 [[!img blog/pics/hacking_water/pipe_route.jpeg size=x300 alt="lines drawn
 over a photo of the hillside show the pipe's curving route to the top"]]
 
+Dig 100 feet of 1 inch wide trench in a single afternoon by hand. Zeno in
+on the rest of the 300 foot run. Gain ability to bury underground cables
+without raising a sweat as an accidental superpower. Arms ache for a full
+month afterwards.
+
 Connect it all up with a temporary water barrel, and it works! Gravity flow
 yields 30 PSI!
 
@@ -121,5 +116,3 @@ alt="close-up of old cracked solar panel"]]
 
 Begin filling the tanks, unsure how long it will take as the pump balances
 available sunlight and spring flow.
-
-[[!tag unfinished]]

add
diff --git a/blog/entry/hacking_plumbing.mdwn b/blog/entry/hacking_plumbing.mdwn
index f99c61d5..df77b72e 100644
--- a/blog/entry/hacking_plumbing.mdwn
+++ b/blog/entry/hacking_plumbing.mdwn
@@ -14,7 +14,9 @@ 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.
 
-TODO solar panel picture
+[[!img blog/pics/hacking_water/oldsolarpanel.jpg size=x300 alt="a solar
+panel with a large impact crater; the glass has cracked into thousands of
+peices but is still hanging together"]]
 [[!img blog/pics/hacking_water/loggingroad.jpeg size=x300 alt="barely perceptable flat spot on tree
 covered hillside"]]
 [[!img blog/pics/hacking_water/copper_pipe_to_house.jpeg size=x300 alt="a copper pipe sheathed in
@@ -70,17 +72,18 @@ repurposed linemans's tool resembling a medieval axe.
 with trees wherever there are not rocks"]]
 [[!img blog/pics/hacking_water/wilton_thinline_trenching_tool.jpeg size=x300 alt="just unboxed
 trenching tool looks like a large black metal spatula"]]
-[[!img blog/pics/hacking_water/wilton_trench.jpeg size=x300 alt="30 feet of very narrow trench
-comes out of the woods and along the side of the house past the satellite
-internet dish"]]
-[[!img blog/pics/hacking_water/pipe_route.jpeg size=x300 alt="lines drawn
-over a photo of the hillside show the pipe's curving route to the top"]]
 
 Dig 100 feet of 1 inch wide trench in a single afternoon by hand. Zeno in
 on the rest of the 300 foot run. Gain ability to bury underground cables
 without raising a sweat as an accidental superpower. Arms ache for a full
 month afterwards.
 
+[[!img blog/pics/hacking_water/wilton_trench.jpeg size=x300 alt="30 feet of very narrow trench
+comes out of the woods and along the side of the house past the satellite
+internet dish"]]
+[[!img blog/pics/hacking_water/pipe_route.jpeg size=x300 alt="lines drawn
+over a photo of the hillside show the pipe's curving route to the top"]]
+
 Connect it all up with a temporary water barrel, and it works! Gravity flow
 yields 30 PSI!
 
@@ -95,7 +98,8 @@ house"]]
 faucet with water flowing into the kitchen sink"]]
 
 Clear a 6 foot wide path through the woods up the hill and roll up two 550
-gallon Norwesco water tanks. Level and prepare two 6 foot diameter pads.
+gallon Norwesco water tanks. Haul 650 pounds of sand up the hill, by hand,
+one 5 gallon bucket at a time. Level and prepare two 6 foot diameter pads.
 
 [[!img blog/pics/hacking_water/tanktruck.jpeg size=x300 alt="Joey standing in front of a black 4x4
 pickup truck with a large white 550 gallon water tank on its side in the
@@ -109,25 +113,13 @@ strewn around the cramped worksite"]]
 Build a buried manifold with valves turned by water meter key. Include a
 fire hose outlet just in case.
 
+[[!img blog/pics/hacking_water/tankconnect.jpg size=x300 alt="in a hole in
+the ground between two water tanks is a complex assembly of blue pipes and
+brass fittings, with several valves"]]
+[[!img blog/pics/hacking_water/oldsolarpaneldetail.jpg size=x300
+alt="close-up of old cracked solar panel"]]
+
 Begin filling the tanks, unsure how long it will take as the pump balances
 available sunlight and spring flow.
 
-Done for now. 
-
-----
-
-As with any project, this has a continuing todo list:
-
-* Paint tanks black to prevent algae growth and freezing.
-* Run overflow down hill to 1500 gallon cistern by the house.
-* Install water pressure sensor in utility room, read by ADC and fed into
-  house computer to alert when water is low.
-* Solar powered hot water heater.
-* Set up aux pump back up from lower cistern to water tanks for use during
-  drought.
-* Direct final overflow onto rocks so a waterfall sounds when the system is
-  full.
-* Automatic winterization of the pump house with an arduino, temp sensor,
-  and some servos.
-
 [[!tag unfinished]]
diff --git a/blog/pics/hacking_water/oldsolarpanel.jpg b/blog/pics/hacking_water/oldsolarpanel.jpg
new file mode 100644
index 00000000..626158de
Binary files /dev/null and b/blog/pics/hacking_water/oldsolarpanel.jpg differ
diff --git a/blog/pics/hacking_water/oldsolarpaneldetail.jpg b/blog/pics/hacking_water/oldsolarpaneldetail.jpg
new file mode 100644
index 00000000..dc09afd3
Binary files /dev/null and b/blog/pics/hacking_water/oldsolarpaneldetail.jpg differ
diff --git a/blog/pics/hacking_water/tankconnect.jpg b/blog/pics/hacking_water/tankconnect.jpg
new file mode 100644
index 00000000..54b2f4e6
Binary files /dev/null and b/blog/pics/hacking_water/tankconnect.jpg differ

add
diff --git a/blog/entry/hacking_plumbing.mdwn b/blog/entry/hacking_plumbing.mdwn
index 20616be6..f99c61d5 100644
--- a/blog/entry/hacking_plumbing.mdwn
+++ b/blog/entry/hacking_plumbing.mdwn
@@ -14,6 +14,12 @@ 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.
 
+TODO solar panel picture
+[[!img blog/pics/hacking_water/loggingroad.jpeg size=x300 alt="barely perceptable flat spot on tree
+covered hillside"]]
+[[!img blog/pics/hacking_water/copper_pipe_to_house.jpeg size=x300 alt="a copper pipe sheathed in
+black plastic curves out of the ground next to a wall"]]
+
 So I have an old system that doesn't do what I want. Let's hack the
 system..
 
@@ -24,6 +30,13 @@ 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.
 
+[[!img blog/pics/hacking_water/pump_house.jpeg size=x300 alt="red cooler attached to a tree with a
+pump in it. water is streaming out of one of the two pipes attached to it"]]
+[[!img blog/pics/hacking_water/pump_controller.jpeg size=x300 alt="circuit board with terminals
+labeled PUMP, PV, HIGH, LOW, GND, FLOAT SWITCH"]]
+[[!img blog/pics/hacking_water/temporary_water_barrel.jpeg size=x300 alt="50 gallon water barrel
+perched on a hillside with some hoses connected to it"]]
+
 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
@@ -34,6 +47,14 @@ settling tank. This was yak shaving, but it was going to fail. Build a
 custom ladder because regular ladders are too wide to fit into it.
 Flashback to my tightest squeezes from caving. Yuurgh.
 
+[[!img blog/pics/hacking_water/settling_tank_ladder.jpeg size=x300 alt="very narrow concrete
+water tank with its concrete lid opened and a rough wooden ladder sticking
+out of it"]]
+[[!img blog/pics/hacking_water/settling_tank_original.jpeg size=x300 alt="interior of water tank
+drained with muck covering the bottom and plaster flaking from walls"]]
+[[!img blog/pics/hacking_water/settling_tank_reconditioned.jpeg size=x300 alt="interior of water
+tank with walls bright and new, water level sensors and pipe to pump"]]
+
 Install water level sensors in the settling tank, cut a hole for pipe, 
 connect to pumphouse.
 
@@ -43,10 +64,20 @@ Learn how to solder copper pipe.
 Now how to bury 250 feet of PEX pipe a foot deep up a steep hillside
 covered in rock piles and trees that you don't want to cut down to make way
 for equipment? Research every possibility, and pick the one that involves a
-repurposed linemans's tool resembling a mediaval axe. 
-
-Dig a hundred feet of 1 inch wide trench in a single afternoon by hand.
-Zeno in on the rest of the run. Gain ability to bury underground cables
+repurposed linemans's tool resembling a medieval axe. 
+
+[[!img blog/pics/hacking_water/rocky_path.jpeg size=x300 alt="hillside strewn in large rocks
+with trees wherever there are not rocks"]]
+[[!img blog/pics/hacking_water/wilton_thinline_trenching_tool.jpeg size=x300 alt="just unboxed
+trenching tool looks like a large black metal spatula"]]
+[[!img blog/pics/hacking_water/wilton_trench.jpeg size=x300 alt="30 feet of very narrow trench
+comes out of the woods and along the side of the house past the satellite
+internet dish"]]
+[[!img blog/pics/hacking_water/pipe_route.jpeg size=x300 alt="lines drawn
+over a photo of the hillside show the pipe's curving route to the top"]]
+
+Dig 100 feet of 1 inch wide trench in a single afternoon by hand. Zeno in
+on the rest of the 300 foot run. Gain ability to bury underground cables
 without raising a sweat as an accidental superpower. Arms ache for a full
 month afterwards.
 
@@ -57,22 +88,46 @@ Pressure-test the copper pipe going into the house to make sure it's not
 leaking behind the retaining wall. Fix all the old leaky plumbing and
 fixtures in the house.
 
+[[!img blog/pics/hacking_water/pressure_test.jpeg size=x300 alt="water pressure guage connected to
+PEX pipe coming out of trench and connecting to copper pipe that goes into
+house"]]
+[[!img blog/pics/hacking_water/faucet.jpeg size=x300 alt="modern restaurant-style sprung arched
+faucet with water flowing into the kitchen sink"]]
+
 Clear a 6 foot wide path through the woods up the hill and roll up two 550
 gallon Norwesco water tanks. Level and prepare two 6 foot diameter pads.
-Build an buried manifold with valves turned by water meter key. Include a
+
+[[!img blog/pics/hacking_water/tanktruck.jpeg size=x300 alt="Joey standing in front of a black 4x4
+pickup truck with a large white 550 gallon water tank on its side in the
+bed and arching high above"]]
+[[!img blog/pics/hacking_water/tankroll.jpeg size=x300 alt="Joey from the back as he rolls water
+tank up a forested hill"]]
+[[!img blog/pics/hacking_water/tankpad.jpeg size=x300 alt="six foot circle marked off with rope
+and filled with sand; a water tank is in the background and tools are
+strewn around the cramped worksite"]]
+
+Build a buried manifold with valves turned by water meter key. Include a
 fire hose outlet just in case.
 
 Begin filling the tanks, unsure how long it will take as the pump balances
-available sunlight and spring flow. Meanwhile, run an overflow pipe back
-down the hill to the 1500 gallon cistern by the house. Arrange it so the
-same pipe can be used to pump back from there up the hill if necessary.
+available sunlight and spring flow.
+
+Done for now. 
+
+----
 
-Direct the final overflow onto some rocks, so the sound of a waterfall
-indicates when all storage is full.
+As with any project, this has a continuing todo list:
 
-Paint the tanks black to prevent algae growth in summer and freezing in
-winter. Install a water pressure sensor in the utility room, to eventually
-be read by a ADC and fed into the house computer, to alert when the water
-is running low.
+* Paint tanks black to prevent algae growth and freezing.
+* Run overflow down hill to 1500 gallon cistern by the house.
+* Install water pressure sensor in utility room, read by ADC and fed into
+  house computer to alert when water is low.
+* Solar powered hot water heater.
+* Set up aux pump back up from lower cistern to water tanks for use during
+  drought.
+* Direct final overflow onto rocks so a waterfall sounds when the system is
+  full.
+* Automatic winterization of the pump house with an arduino, temp sensor,
+  and some servos.
 
 [[!tag unfinished]]
diff --git a/blog/pics/hacking_water/copper_pipe_to_house.jpeg b/blog/pics/hacking_water/copper_pipe_to_house.jpeg
new file mode 100644
index 00000000..95471c79
Binary files /dev/null and b/blog/pics/hacking_water/copper_pipe_to_house.jpeg differ
diff --git a/blog/pics/hacking_water/faucet.jpeg b/blog/pics/hacking_water/faucet.jpeg
new file mode 100644
index 00000000..22e32d5e
Binary files /dev/null and b/blog/pics/hacking_water/faucet.jpeg differ
diff --git a/blog/pics/hacking_water/loggingroad.jpeg b/blog/pics/hacking_water/loggingroad.jpeg
new file mode 100644
index 00000000..e8d8c45a
Binary files /dev/null and b/blog/pics/hacking_water/loggingroad.jpeg differ
diff --git a/blog/pics/hacking_water/pipe_route.jpeg b/blog/pics/hacking_water/pipe_route.jpeg
new file mode 100644
index 00000000..84b8198d
Binary files /dev/null and b/blog/pics/hacking_water/pipe_route.jpeg differ
diff --git a/blog/pics/hacking_water/pressure_test.jpeg b/blog/pics/hacking_water/pressure_test.jpeg
new file mode 100644
index 00000000..dd156439
Binary files /dev/null and b/blog/pics/hacking_water/pressure_test.jpeg differ
diff --git a/blog/pics/hacking_water/pump_controller.jpeg b/blog/pics/hacking_water/pump_controller.jpeg
new file mode 100644
index 00000000..9d86c358
Binary files /dev/null and b/blog/pics/hacking_water/pump_controller.jpeg differ
diff --git a/blog/pics/hacking_water/pump_house.jpeg b/blog/pics/hacking_water/pump_house.jpeg
new file mode 100644
index 00000000..d586970b
Binary files /dev/null and b/blog/pics/hacking_water/pump_house.jpeg differ
diff --git a/blog/pics/hacking_water/rocky_path.jpeg b/blog/pics/hacking_water/rocky_path.jpeg
new file mode 100644
index 00000000..6e3bc6af
Binary files /dev/null and b/blog/pics/hacking_water/rocky_path.jpeg differ
diff --git a/blog/pics/hacking_water/rough_elevation.png b/blog/pics/hacking_water/rough_elevation.png
new file mode 100644
index 00000000..f029d916
Binary files /dev/null and b/blog/pics/hacking_water/rough_elevation.png differ
diff --git a/blog/pics/hacking_water/settling_tank_ladder.jpeg b/blog/pics/hacking_water/settling_tank_ladder.jpeg
new file mode 100644
index 00000000..12100a01
Binary files /dev/null and b/blog/pics/hacking_water/settling_tank_ladder.jpeg differ
diff --git a/blog/pics/hacking_water/settling_tank_original.jpeg b/blog/pics/hacking_water/settling_tank_original.jpeg
new file mode 100644
index 00000000..c1baa08b
Binary files /dev/null and b/blog/pics/hacking_water/settling_tank_original.jpeg differ
diff --git a/blog/pics/hacking_water/settling_tank_reconditioned.jpeg b/blog/pics/hacking_water/settling_tank_reconditioned.jpeg
new file mode 100644
index 00000000..044f5221
Binary files /dev/null and b/blog/pics/hacking_water/settling_tank_reconditioned.jpeg differ
diff --git a/blog/pics/hacking_water/tankpad.jpeg b/blog/pics/hacking_water/tankpad.jpeg
new file mode 100644
index 00000000..849a9cf2
Binary files /dev/null and b/blog/pics/hacking_water/tankpad.jpeg differ
diff --git a/blog/pics/hacking_water/tankroll.jpeg b/blog/pics/hacking_water/tankroll.jpeg
new file mode 100644
index 00000000..462321f7
Binary files /dev/null and b/blog/pics/hacking_water/tankroll.jpeg differ
diff --git a/blog/pics/hacking_water/tanktruck.jpeg b/blog/pics/hacking_water/tanktruck.jpeg
new file mode 100644
index 00000000..5863e3f5
Binary files /dev/null and b/blog/pics/hacking_water/tanktruck.jpeg differ
diff --git a/blog/pics/hacking_water/temporary_water_barrel.jpeg b/blog/pics/hacking_water/temporary_water_barrel.jpeg
new file mode 100644
index 00000000..d680e62c
Binary files /dev/null and b/blog/pics/hacking_water/temporary_water_barrel.jpeg differ
diff --git a/blog/pics/hacking_water/wilton_thinline_trenching_tool.jpeg b/blog/pics/hacking_water/wilton_thinline_trenching_tool.jpeg
new file mode 100644
index 00000000..aa3e2b2a
Binary files /dev/null and b/blog/pics/hacking_water/wilton_thinline_trenching_tool.jpeg differ
diff --git a/blog/pics/hacking_water/wilton_trench.jpeg b/blog/pics/hacking_water/wilton_trench.jpeg
new file mode 100644
index 00000000..af19fd06
Binary files /dev/null and b/blog/pics/hacking_water/wilton_trench.jpeg differ

update
diff --git a/blog/entry/hacking_plumbing.mdwn b/blog/entry/hacking_plumbing.mdwn
index 7c4ef171..20616be6 100644
--- a/blog/entry/hacking_plumbing.mdwn
+++ b/blog/entry/hacking_plumbing.mdwn
@@ -1,53 +1,78 @@
-From water insecurity to offgrid, solar pumped, gravity flow 2500 gallons
+From water insecurity to offgrid, solar pumped, gravity flow 1000 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.
+enough to get through.
 
 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.
+gravity flow. And I have these old 100 watts of solar panels sitting unused
+after my solar upgrade. 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.
+Run a cable from the old solar panels 75 feet over to the spring.
 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
+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.
+Sidetrack into a week spent cleaning out and re-sealing the spring's
+settling tank. This was yak shaving, but it was going to fail. Build a
+custom ladder because regular ladders are too wide to fit into it.
+Flashback to my tightest squeezes from caving. Yuurgh.
 
-Install water level sensors in the cistern, cut a hole for pipe, 
+Install water level sensors in the settling tank, 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
+Now how to bury 250 feet of PEX pipe a foot deep up a steep hillside
 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!
-Pressure-test at 30 PSI.
+for equipment? Research every possibility, and pick the one that involves a
+repurposed linemans's tool resembling a mediaval axe. 
+
+Dig a hundred feet of 1 inch wide 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. Arms ache for a full
+month afterwards.
+
+Connect it all up with a temporary water barrel, and it works! Gravity flow
+yields 30 PSI!
+
+Pressure-test the copper pipe going into the house to make sure it's not
+leaking behind the retaining wall. Fix all the old leaky plumbing and
+fixtures in the house.
+
+Clear a 6 foot wide path through the woods up the hill and roll up two 550
+gallon Norwesco water tanks. Level and prepare two 6 foot diameter pads.
+Build an buried manifold with valves turned by water meter key. Include a
+fire hose outlet just in case.
+
+Begin filling the tanks, unsure how long it will take as the pump balances
+available sunlight and spring flow. Meanwhile, run an overflow pipe back
+down the hill to the 1500 gallon cistern by the house. Arrange it so the
+same pipe can be used to pump back from there up the hill if necessary.
+
+Direct the final overflow onto some rocks, so the sound of a waterfall
+indicates when all storage is full.
+
+Paint the tanks black to prevent algae growth in summer and freezing in
+winter. Install a water pressure sensor in the utility room, to eventually
+be read by a ADC and fed into the house computer, to alert when the water
+is running low.
 
 [[!tag unfinished]]

added two suggestions: even and wol
diff --git a/code/moreutils/discussion.mdwn b/code/moreutils/discussion.mdwn
index d05982cc..2a9a6571 100644
--- a/code/moreutils/discussion.mdwn
+++ b/code/moreutils/discussion.mdwn
@@ -1,6 +1,15 @@
 Feel free to edit this page to suggest tools to add, or make any other
 comments --[[Joey]]
 
+## tool suggestion: "even" and "wol"
+
+Written these two tiny tools, but figure they're useful to others:
+
+*even* was originally written to filter unicode files for use with grep/rg: https://gist.github.com/lionello/9166502
+
+*wol* is a portable wake-on-lan tool: https://gist.github.com/lionello/6481448
+
+
 ## tool suggestion: "fork after grep"
 
 I've written this tool for tzap (tuning DVB-T cards). Iit runs a process as a child, waits for a string to appear on its stdout/stderr, then daemonizes it.     

update
diff --git a/blog/entry/the_most_important_missing_unicode_extension.mdwn b/blog/entry/the_most_important_missing_unicode_extension.mdwn
index aaa4236a..aaa06cb5 100644
--- a/blog/entry/the_most_important_missing_unicode_extension.mdwn
+++ b/blog/entry/the_most_important_missing_unicode_extension.mdwn
@@ -6,7 +6,8 @@ Here's a symbol we all know well: &#65535;
 Aka, "box with 4 small F's in it".
 
 But that's not the only such symbol, there was a whole class of them in
-common use in the early 2000's, so common that they were called "tofu".
+common use in the early 2000's, so common that they were called "tofu" when
+grouped together.
 
 Modern unicode systems cannot display those historical characters!
 

blog update
diff --git a/blog/entry/the_most_important_missing_unicode_extension.mdwn b/blog/entry/the_most_important_missing_unicode_extension.mdwn
new file mode 100644
index 00000000..aaa4236a
--- /dev/null
+++ b/blog/entry/the_most_important_missing_unicode_extension.mdwn
@@ -0,0 +1,18 @@
+Unicode allows you to write any symbol in use in the world, and if one is
+somehow missing from Unicode, it'll get added in a future version of the
+specification. Right?
+
+Here's a symbol we all know well: &#65535;  
+Aka, "box with 4 small F's in it".
+
+But that's not the only such symbol, there was a whole class of them in
+common use in the early 2000's, so common that they were called "tofu".
+
+Modern unicode systems cannot display those historical characters!
+
+Clearly this needs to be fixed with an extension to the Unicode standard.
+I propose TOFU, a combining character that makes whatever it's combined
+with be displayed as a font fallback box glyph containing its Unicode code
+point.
+
+[[!tag unicode]]

Added a comment: Thank you!
diff --git a/blog/entry/80_percent/comment_1_6fb2810a54f7eecc08449f230a90c963._comment b/blog/entry/80_percent/comment_1_6fb2810a54f7eecc08449f230a90c963._comment
new file mode 100644
index 00000000..d18ab7c5
--- /dev/null
+++ b/blog/entry/80_percent/comment_1_6fb2810a54f7eecc08449f230a90c963._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ username="http://www.mirbsd.org/"
+ nickname="mirabilos"
+ avatar="http://cdn.libravatar.org/avatar/a023e7289de2880cf8d275a41e90c321ed7a5f5db9b3eef00ded69b878a1d367"
+ subject="Thank you!"
+ date="2019-05-27T15:31:17Z"
+ content="""
+Hi joeyh,
+
+thanks for expressing that; maybe this will have some weight.
+
+Despite initial reluctance, I adopted dh for some of my packages, but I still wish to have the flexibility to not use it (sometimes, a rules file with dh is longer and more complex than one without, due to the need for too many overrides; sometimes, buildsystem detection works against you, etc.), let alone the idea of being forced to use something turns me off of things.
+
+Being able to choose is **good**. (No matter what _some_ people say about this not being about choice, I disagree there.) Monocultures have inherent problems.
+"""]]

title
diff --git a/blog/entry/hacking_water_teaser.mdwn b/blog/entry/hacking_water_teaser.mdwn
index ff9c881a..b137a53d 100644
--- a/blog/entry/hacking_water_teaser.mdwn
+++ b/blog/entry/hacking_water_teaser.mdwn
@@ -1,3 +1,5 @@
 [[!img pics/trucktank.jpg alt="Joey standing in front of a black 4x4 pickup
 truck with a large white 550 gallon water tank on its side in the bed and
 arching high above"]]
+
+[[!meta title="hacking water (teaser)"]]

post
diff --git a/blog/entry/hacking_water_teaser.mdwn b/blog/entry/hacking_water_teaser.mdwn
new file mode 100644
index 00000000..ff9c881a
--- /dev/null
+++ b/blog/entry/hacking_water_teaser.mdwn
@@ -0,0 +1,3 @@
+[[!img pics/trucktank.jpg alt="Joey standing in front of a black 4x4 pickup
+truck with a large white 550 gallon water tank on its side in the bed and
+arching high above"]]
diff --git a/blog/pics/trucktank.jpg b/blog/pics/trucktank.jpg
new file mode 100644
index 00000000..5863e3f5
Binary files /dev/null and b/blog/pics/trucktank.jpg differ

ps
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!

blog update
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.

update
diff --git a/contact.mdwn b/contact.mdwn
index cb322fe6..0ba240a2 100644
--- a/contact.mdwn
+++ b/contact.mdwn
@@ -1,5 +1,6 @@
 * email: <id@joeyh.name>
 * gpg key: [E85A 5F63 B31D 24C1 EBF0  D81C C910 D922 2512 E3C7](http://pgp.cs.uu.nl/stats/2512E3C7.html)
-* pump: <http://identi.ca/joeyh>
 * irc: joeyh (irc.oftc.net or irc.freenode.net)
+* mastodon: <a href="https://octodon.social/@joeyh">@joeyh@mastodon.social</a>
+* scuttlebutt: @BCM6DHYJvWzwWi1lFl2tjDXjaqyZAEmJH5ZONSpXhtc=.ed2551
 * some ways to send me a "thank you" for my work, if you're so inclined: [[thanks]]

Added a comment: sshdo
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.
+
+"""]]

renamed homepower to house
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/).

remove old pump feed
diff --git a/grep.mdwn b/grep.mdwn
index 7c2fb83d..c1e0db29 100644
--- a/grep.mdwn
+++ b/grep.mdwn
@@ -8,6 +8,5 @@ on the net.
 List of feeds:
 
 * [[!aggregate expirecount=25 name="music" feedurl="http://libre.fm/rdf.php?fmt=rss&page=%2Fuser%2Fjoeyhess%2Frecent-tracks" url="http://libre.fm/user/joeyhess"]]
-* [[!aggregate expirecount=25 name="identi.ca posts" feedurl="https://tmp.joeyh.name/pump.atom" url="http://identi.ca/joeyh"]]
 * [[!aggregate expirecount=25 name="mastadon posts" feedurl="https://octodon.social/users/joeyh.atom" url="https://octodon.social/users/joeyh"]]
 * [[!aggregate expirecount=25 name="books" feedurl="http://www.goodreads.com/review/list_rss/2159448?key=afd7e8432b3f9e33edab442a7c94e95849af4527&shelf=currently-reading" url="http://www.goodreads.com/user/show/2159448"]]

add
diff --git a/talks/git-annex-developer-workshop/notes.otl b/talks/git-annex-developer-workshop/notes.otl
new file mode 100644
index 00000000..11a12e8e
--- /dev/null
+++ b/talks/git-annex-developer-workshop/notes.otl
@@ -0,0 +1,167 @@
+Day 1 (4 hours)
+	12:15-4:30 pm
+	with lunch and coffee breaks
+intro (20 minutes)
+	bus factor
+	introductions
+		what's your git-annex skill level?
+		your Haskell skill level?
+	Haskell for Readers
+		http://joeyh.name/talks/git-annex-developer-workshop/haskell-for-readers
+	schedule overview
+building git-annex from source (10 minutes)
+	git clone git://git-annex.branchable.com/ git-annex
+	sudo apt-get install haskell-stack  /  http://haskellstack.org/
+	stack build
+		(older OS: stack build --stack-yaml stack-lts-9.9.yaml)
+git-annex core concepts and types (60 minutes)
+	Key
+		Types/Key.hs (aka Types.Key)
+		key/value storage
+			not encryption key
+		annex symlinks and links
+		show examples
+	UUID
+		Types.UUID
+		unique identifier for git repository / special remote
+			does a normal git repository have an identifier?
+				no
+				any clone is much like any other
+				git repositories may contain unique data
+					but then no other repo knows about it
+			why does git-annex need a repository identifier?
+				each clone may contain a different set of contents of files
+				needs to know where the contents of a file is located
+		NoUUID
+			ugly and hides problems
+			thought exercise
+				imagine getting rid of NoUUID constructor
+				use Maybe UUID instead
+				eliminate the Maybe where possible
+	Remote
+		Types.Remote
+		git remote
+		special remote
+		important fields of the Remote data type
+			uuid
+			cost
+			storeKey
+			retrieveKeyFile
+			removeKey
+			checkPresent
+		compare Remote with external special remote protocol
+			http://git-annex.branchable.com/design/external_special_remote_protocol/
+interlude: how we use git-annex (30-60 minutes)
+	me
+		I built it for my own personal use
+		glimpse inside some of my git-annex repos and workflows
+	how datalad uses git-annex
+		over to yarik and michael
+	how others here use git-annex
+git-annex core concepts continued (30 minutes)
+	recap
+		Key
+		UUID
+		Remote
+	git-annex branch
+		http://git-annex.branchable.com/internals/
+		union merging
+		CRDTs & vector clocks
+			Annex.VectorClock
+		example
+			location tracking
+			Logs.Presence.Pure
+				LogLine
+				LogStatus
+				logParser
+				buildLog
+		exercise
+			design a new git-annex branch file format
+			how does this interact with union merging?
+			how are old values removed from the file?
+		space efficiency
+			repeated uuids, timestamps, etc		
+			git gc
+	what core git-annex concepts *don't* have types?
+		git-annex branch files
+		git-annex object files
+		would adding types for these improve the code?
+case study: adding a new, major feature to git-annex (60 minutes)
+	git-annex import tree
+		http://git-annex.branchable.com/design/importing_trees_from_special_remotes/
+	high level design
+		dual of export tree
+			after importing, exporting the same tree is a no-op
+			after exporting, importing yields the same tree
+		lists content of special remote
+		downloads new content from special remote
+			(necessary to generate keys?)
+		builds a git tree of its contents
+		potential for export tree conflict
+			important sticking point in design
+			mitigations
+				race safety via good ContentIdentifier
+				S3 versioning
+	UI
+		analogy to git fetch / git push
+			import tree and git fetch both update remote tracking branch
+				refs/remotes/$name/master 
+			git push and export tree also update remote tracking branch
+				to reflect their changes to the remote
+		import/export special remote becomes similar to a git working tree
+			without .git
+			but with files that may be modified there and later imported
+	api design
+		ImportActions
+	data types
+		ImportLocation
+		ContentIdentifier
+			storage
+				git-annex branch
+				Logs.ContentIdentifier
+			mappings between ContentIdentifier and Key
+				local sqlite database
+					Database.ContentIdentifier
+		ImportableContents
+		RemoteTrackingBranch
+		ImportTreeConfig
+		ImportCommitConfig
+	option parsing
+		Parser ImportOptions
+			added RemoteImportOptions
+			optparse-applicative
+planning for tomorrow (10 minutes)
+	start thinking about a feature you'd like to see in git-annex
+	or a part of git-annex implementation you want to explore
+	to discuss tomorrow morning
+
+Day 2 (4 hours)
+	9 am-1pm with coffee and lunch breaks
+git-annex implementation details (60 minutes)
+	not as core as Key, UUID, Remote, but all over the code
+	Git
+		Repo
+		Ref
+		Branch
+		Sha
+		Tag
+		exercise
+			Ref, Branch, Sha, Tag are all aliases
+			not type safe!
+			no distinction at all between Commit, Tree, Object
+			split into separate types for type safety
+				perhaps Ref Tag, Ref Commit, Ref Tree, Ref Object
+				and Sha Tag, Sha Commit, Sha Tree, Sha Object
+		why is Git interface in git-annex at all?
+	GitConfig
+		exercise
+			add a new git config value to it
+	Annex
+			monad
+			"global" state
+				gitRepo
+				getGitConfig
+				remoteList
+	Types.Command
+discussion: designing new git-annex features (120 minutes)
+	discuss participants' feature ideas and think about designs

remove termcast
diff --git a/links/technical.mdwn b/links/technical.mdwn
index 6438c561..8a8ecd2e 100644
--- a/links/technical.mdwn
+++ b/links/technical.mdwn
@@ -5,6 +5,5 @@
 [[offgrid]]  
 [[talks]]  
 [[screencasts]]  
-[[termcasts|termcast]]  
 [[rfcs|rfc]]  
 [[boxen]]  

openid port
diff --git a/index.mdwn b/index.mdwn
index fe978e51..3c618ab0 100644
--- a/index.mdwn
+++ b/index.mdwn
@@ -52,4 +52,4 @@ show=5 feeds=no archive=yes]]
 """]]
 
 [[!meta openid="http://joeyh.name/"
-server="http://openid.kitenet.net:8081/simpleid/"]]
+server="http://openid.kitenet.net:8086/simpleid/"]]

rename
diff --git a/talks/git-annex-implementors-workshop/haskell-for-readers.mdwn b/talks/git-annex-developer-workshop/haskell-for-readers.mdwn
similarity index 100%
rename from talks/git-annex-implementors-workshop/haskell-for-readers.mdwn
rename to talks/git-annex-developer-workshop/haskell-for-readers.mdwn

update
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/).

link
diff --git a/offgrid.mdwn b/offgrid.mdwn
index bafd7921..3bbd4466 100644
--- a/offgrid.mdwn
+++ b/offgrid.mdwn
@@ -12,3 +12,5 @@ Here I'll go into detail about each of the offgrid systems of the house.
 * [[water system|water]]
 * [[internet]]
 * [[heating and cooling|hvac]]
+
+Also, see my blog posts tagged [[blog/solar]].

link
diff --git a/blog/solar.mdwn b/blog/solar.mdwn
index d81458ca..ee53e0b2 100644
--- a/blog/solar.mdwn
+++ b/blog/solar.mdwn
@@ -1,4 +1,4 @@
-Posts related to solar power and off the grid living.
+Posts related to solar power and [[offgrid]] living.
 
 [[!inline pages="blog/entry/* and link(solar) and !*/Discussion"
 actions=yes show=0]]

tweak
diff --git a/offgrid.mdwn b/offgrid.mdwn
index 6ed1145c..bafd7921 100644
--- a/offgrid.mdwn
+++ b/offgrid.mdwn
@@ -3,7 +3,7 @@ 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]]
+[[!img blog/pics/solar_upgrade/housepanels.jpg alt="house with solar panels" size=320x]]
 
 Here I'll go into detail about each of the offgrid systems of the house.
 

fix link
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.

updates
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/).

fix link
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.
 

fix link
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.

typo
diff --git a/offgrid/photovoltatic.mdwn b/offgrid/photovoltaic.mdwn
similarity index 100%
rename from offgrid/photovoltatic.mdwn
rename to offgrid/photovoltaic.mdwn

reorg
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

added
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)
add
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]]

expand
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)
convert from github flavored markdown
diff --git a/talks/git-annex-implementors-workshop/haskell-for-readers-notes b/talks/git-annex-implementors-workshop/haskell-for-readers-notes
deleted file mode 100644
index 0d09c678..00000000
--- a/talks/git-annex-implementors-workshop/haskell-for-readers-notes
+++ /dev/null
@@ -1,28 +0,0 @@
-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
index f5146a39..20898969 100644
--- a/talks/git-annex-implementors-workshop/haskell-for-readers.mdwn
+++ b/talks/git-annex-implementors-workshop/haskell-for-readers.mdwn
@@ -1,18 +1,17 @@
-% Haskell for Readers (just the types edition)
-% [Joachim Breitner](http://www.joachim-breitner.de/), [DFINITY Foundation](https://dfinity.org/)
+[[!meta title="Haskell for Readers (just the types edition)"]]
+[[!meta author="Joachim Breitner"]]
 
-<a id="nav-toggle" href="#TOC"></a>
+[[!toc levels=2]]
 
-Preface {.unnumbered}
-=======
+## Preface
 
 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
+little other Haskell code as we can get away with. It is an edited excerpt of
 <http://haskell-for-readers.nomeata.de/>
 
-### Form {.unnumbered}
+### Form
 
 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.
 
@@ -20,23 +19,22 @@ This document is also meant to be more on the concise side, assuming the audienc
 
 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}
+### Audience
 
 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}
+### Acknowledgments and license
 
 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
-===============
+## 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
-```
+
+	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.
@@ -49,9 +47,7 @@ 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
-```
+	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.
 
@@ -65,56 +61,52 @@ 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
-```
+	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 -- loop
+	
+	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
-```
+	module Foo.Bar.Baz where
+	
+	data Baz = Baz Integer
+	
+	promptBaz :: IO Baz
+	
+	parseBaz :: String -> Maybe Baz
+	
+	formatBaz :: Baz -> String
+	

(Diff truncated)
add
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)
Added a comment
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?
+"""]]

add news item for moreutils 0.63
diff --git a/code/moreutils/news/version_0.58.mdwn b/code/moreutils/news/version_0.58.mdwn
deleted file mode 100644
index 6d5ada3d..00000000
--- a/code/moreutils/news/version_0.58.mdwn
+++ /dev/null
@@ -1,6 +0,0 @@
-moreutils 0.58 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * OpenBSD compile fix.
-     Thanks, Michael Reed.
-   * ts: Quiet perl's complaints about utf-8. Closes: #[812143](http://bugs.debian.org/812143)
-     Thanks, Nicolas Schier."""]]
\ No newline at end of file
diff --git a/code/moreutils/news/version_0.63.mdwn b/code/moreutils/news/version_0.63.mdwn
new file mode 100644
index 00000000..a1ece662
--- /dev/null
+++ b/code/moreutils/news/version_0.63.mdwn
@@ -0,0 +1,8 @@
+moreutils 0.63 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * vipe: Clean up temp file even when it exits with an error.
+     Thanks, Stig Palmquist.
+   * ts: Fix ts -m %.s to not output negative microseconds.
+     Thanks, Dima Kogan
+   * sponge: Fix bug in -a mode that doubled original content of file when
+     the temp file is located on a different filesystem."""]]
\ No newline at end of file

calendar update
diff --git a/blog/archives/2019.mdwn b/blog/archives/2019.mdwn
new file mode 100644
index 00000000..88e86900
--- /dev/null
+++ b/blog/archives/2019.mdwn
@@ -0,0 +1 @@
+[[!calendar type=year year=2019 pages="blog/entry/* and !*/Discussion"]]
diff --git a/blog/archives/2019/01.mdwn b/blog/archives/2019/01.mdwn
new file mode 100644
index 00000000..fee381ea
--- /dev/null
+++ b/blog/archives/2019/01.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=01 year=2019 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(01) and creation_year(2019) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2019/02.mdwn b/blog/archives/2019/02.mdwn
new file mode 100644
index 00000000..2cce0fab
--- /dev/null
+++ b/blog/archives/2019/02.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=02 year=2019 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(02) and creation_year(2019) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2019/03.mdwn b/blog/archives/2019/03.mdwn
new file mode 100644
index 00000000..f8d8824c
--- /dev/null
+++ b/blog/archives/2019/03.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=03 year=2019 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(03) and creation_year(2019) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2019/04.mdwn b/blog/archives/2019/04.mdwn
new file mode 100644
index 00000000..8f2eb65e
--- /dev/null
+++ b/blog/archives/2019/04.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=04 year=2019 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(04) and creation_year(2019) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2019/05.mdwn b/blog/archives/2019/05.mdwn
new file mode 100644
index 00000000..d9267d07
--- /dev/null
+++ b/blog/archives/2019/05.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=05 year=2019 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(05) and creation_year(2019) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2019/06.mdwn b/blog/archives/2019/06.mdwn
new file mode 100644
index 00000000..7b763b5a
--- /dev/null
+++ b/blog/archives/2019/06.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=06 year=2019 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(06) and creation_year(2019) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2019/07.mdwn b/blog/archives/2019/07.mdwn
new file mode 100644
index 00000000..316c4025
--- /dev/null
+++ b/blog/archives/2019/07.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=07 year=2019 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(07) and creation_year(2019) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2019/08.mdwn b/blog/archives/2019/08.mdwn
new file mode 100644
index 00000000..3e675f3b
--- /dev/null
+++ b/blog/archives/2019/08.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=08 year=2019 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(08) and creation_year(2019) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2019/09.mdwn b/blog/archives/2019/09.mdwn
new file mode 100644
index 00000000..e4c497dc
--- /dev/null
+++ b/blog/archives/2019/09.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=09 year=2019 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(09) and creation_year(2019) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2019/10.mdwn b/blog/archives/2019/10.mdwn
new file mode 100644
index 00000000..d0fcf9bf
--- /dev/null
+++ b/blog/archives/2019/10.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=10 year=2019 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(10) and creation_year(2019) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2019/11.mdwn b/blog/archives/2019/11.mdwn
new file mode 100644
index 00000000..4cc830e9
--- /dev/null
+++ b/blog/archives/2019/11.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=11 year=2019 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(11) and creation_year(2019) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/blog/archives/2019/12.mdwn b/blog/archives/2019/12.mdwn
new file mode 100644
index 00000000..69401057
--- /dev/null
+++ b/blog/archives/2019/12.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=12 year=2019 pages="blog/entry/* and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(12) and creation_year(2019) and blog/entry/* and !*/Discussion" show=0 feeds=no reverse=yes]]

add
will hotlink on git-annex/thanks
diff --git a/blog/pics/neurohub.png b/blog/pics/neurohub.png
new file mode 100644
index 00000000..cc023138
Binary files /dev/null and b/blog/pics/neurohub.png differ

Added a comment: would use battery after you open fridge
diff --git a/blog/entry/fridge_0.2/comment_2_1dd3a8f65335f20c262efe1316cc858c._comment b/blog/entry/fridge_0.2/comment_2_1dd3a8f65335f20c262efe1316cc858c._comment
new file mode 100644
index 00000000..9777950e
--- /dev/null
+++ b/blog/entry/fridge_0.2/comment_2_1dd3a8f65335f20c262efe1316cc858c._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="flot"
+ avatar="http://cdn.libravatar.org/avatar/b57c3b7ad3347a234b26d779a5ccd31b"
+ subject="would use battery after you open fridge"
+ date="2018-12-28T10:30:22Z"
+ content="""
+I would allow for a battery on a timer that would run the refrigerator for a minute after opening and closing it.
+Or a mechanical timer that one'd manually set after use.
+
+Would at least deal with humidity, and stabilize temperature somewhat.
+"""]]

fridge0
diff --git a/code.mdwn b/code.mdwn
index 612043a0..c31824ba 100644
--- a/code.mdwn
+++ b/code.mdwn
@@ -17,9 +17,8 @@ The stuff that's swapped into my local cache at the moment.
 [[ikiwiki-hosting]]
 [[shell-monad]]
 [[reactive-banana-automation]]
-[[http-client-restricted]]
 [[easy-peasy-devicetree-squeezy]]
-[[scuttlebutt-types]]
+[[fridge0]]
 
 ## Less active projects
 
@@ -34,6 +33,8 @@ In maintenance mode mostly, but I still have my hands in it somewhat.
 [[zxcvbn-c]]
 [[scroll]]
 [[github-backup]]
+[[http-client-restricted]]
+[[scuttlebutt-types]]
 
 ## Past projects
 

add fridge0
diff --git a/code/fridge0.mdwn b/code/fridge0.mdwn
new file mode 100644
index 00000000..5544a54e
--- /dev/null
+++ b/code/fridge0.mdwn
@@ -0,0 +1,6 @@
+Fridge0 is a design for an offgrid, solar powered fridge
+
+[[blog/entry/fridge_0.1]]  
+[[blog/entry/fridge_0.2]]  
+
+<https://fridge0.branchable.com>

Added a comment: Radiative cooling for off-grid fridge
diff --git a/blog/entry/fridge_0.2/comment_1_4d06b1c362aaaebea4f076371f77cbe8._comment b/blog/entry/fridge_0.2/comment_1_4d06b1c362aaaebea4f076371f77cbe8._comment
new file mode 100644
index 00000000..0e602a2a
--- /dev/null
+++ b/blog/entry/fridge_0.2/comment_1_4d06b1c362aaaebea4f076371f77cbe8._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="thomas.robbins.15@4531f7ca2945620158e47204700a8d9d5214349b"
+ nickname="thomas.robbins.15"
+ avatar="http://cdn.libravatar.org/avatar/bddbe3a962bbfce80a1c248692484240"
+ subject="Radiative cooling for off-grid fridge"
+ date="2018-12-27T23:03:25Z"
+ content="""
+Hi Joey,
+
+For your next off-grid fridge project, consider:
+
+http://www.sciencemag.org/news/2017/02/cheap-plastic-film-cools-whatever-it-touches-10-c
+"""]]

blog update
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.

link to data
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.

bigger
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:
 

blog update
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.

add
diff --git a/blog/pics/fridge_0.2/failure.png b/blog/pics/fridge_0.2/failure.png
new file mode 100644
index 00000000..f7f1b625
Binary files /dev/null and b/blog/pics/fridge_0.2/failure.png differ
diff --git a/blog/pics/fridge_0.2/peak.jpeg b/blog/pics/fridge_0.2/peak.jpeg
new file mode 100644
index 00000000..44ba9643
Binary files /dev/null and b/blog/pics/fridge_0.2/peak.jpeg differ

add
diff --git a/blog/pics/fridge_0.2/joeyh_dataset1.png b/blog/pics/fridge_0.2/joeyh_dataset1.png
new file mode 100644
index 00000000..fae706c2
Binary files /dev/null and b/blog/pics/fridge_0.2/joeyh_dataset1.png differ
diff --git a/blog/pics/fridge_0.2/joeyh_dataset2.png b/blog/pics/fridge_0.2/joeyh_dataset2.png
new file mode 100644
index 00000000..35e05689
Binary files /dev/null and b/blog/pics/fridge_0.2/joeyh_dataset2.png differ

flow
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

freudian typo
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.
 

scale
diff --git a/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn b/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
index 65e288e2..70e3fae5 100644
--- a/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
+++ b/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
@@ -50,7 +50,7 @@ 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!"]]
+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

oop
diff --git a/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn b/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
index 9a999b56..65e288e2 100644
--- a/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
+++ b/blog/entry/effective_bug_tracking_illustrated_with_ATT.mdwn
@@ -48,7 +48,7 @@ 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
+[[!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!"]]
 

oop
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

add
diff --git a/blog/pics/att/attfestivebow.jpg b/blog/pics/att/attfestivebow.jpg
new file mode 100644
index 00000000..b9c29b53
Binary files /dev/null and b/blog/pics/att/attfestivebow.jpg differ
diff --git a/blog/pics/att/attrefactor.jpg b/blog/pics/att/attrefactor.jpg
new file mode 100644
index 00000000..6d1ff8ed
Binary files /dev/null and b/blog/pics/att/attrefactor.jpg differ
diff --git a/blog/pics/att/attwtf.jpg b/blog/pics/att/attwtf.jpg
new file mode 100644
index 00000000..9d8df840
Binary files /dev/null and b/blog/pics/att/attwtf.jpg differ
diff --git a/blog/pics/att/cartoon.png b/blog/pics/att/cartoon.png
new file mode 100644
index 00000000..49927ba9
Binary files /dev/null and b/blog/pics/att/cartoon.png differ
diff --git a/blog/pics/att/santanote.jpg b/blog/pics/att/santanote.jpg
new file mode 100644
index 00000000..df196ad4
Binary files /dev/null and b/blog/pics/att/santanote.jpg differ

blog update
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
 

Added a comment: Chroots and disk images are properties of hosts
diff --git a/blog/entry/propelling_disk_images/comment_4_1d5d318ffa6e70c5f90f2e57909819eb._comment b/blog/entry/propelling_disk_images/comment_4_1d5d318ffa6e70c5f90f2e57909819eb._comment
new file mode 100644
index 00000000..0e8024f2
--- /dev/null
+++ b/blog/entry/propelling_disk_images/comment_4_1d5d318ffa6e70c5f90f2e57909819eb._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="diane@d02701b3d70f84d667f39e4b05008c653d390ff3"
+ nickname="diane"
+ avatar="http://cdn.libravatar.org/avatar/cc4527dbd49491e829732a2a8f5ee041"
+ subject="Chroots and disk images are properties of hosts"
+ date="2018-12-04T22:32:57Z"
+ content="""
+It wasn't obvious when I was reading this page, but chroots and containers need to be attached to real hosts that run the commands and host the resulting containers. It's a bit clearer at <a href=\"https://joeyh.name/blog/entry/propelling_containers/\">/blog/entry/propeling_containers/</a>, but I read this page first.
+"""]]

removed
diff --git a/blog/entry/propelling_containers/comment_1_b4939ce3944b3e157817b966c672ef68._comment b/blog/entry/propelling_containers/comment_1_b4939ce3944b3e157817b966c672ef68._comment
deleted file mode 100644
index 641d6b03..00000000
--- a/blog/entry/propelling_containers/comment_1_b4939ce3944b3e157817b966c672ef68._comment
+++ /dev/null
@@ -1,10 +0,0 @@
-[[!comment format=mdwn
- username="diane@d02701b3d70f84d667f39e4b05008c653d390ff3"
- nickname="diane"
- avatar="http://cdn.libravatar.org/avatar/cc4527dbd49491e829732a2a8f5ee041"
- subject="Containers are properties of real hosts"
- date="2018-12-04T22:27:27Z"
- content="""
-It wasn't immediately obvious to me when I started looking at propellor today, but as far as I can tell containers (or images) need to be attached to a real host that will actually be building the chroot, container, or image.
-
-"""]]

Added a comment: Containers are properties of real hosts
diff --git a/blog/entry/propelling_containers/comment_1_b4939ce3944b3e157817b966c672ef68._comment b/blog/entry/propelling_containers/comment_1_b4939ce3944b3e157817b966c672ef68._comment
new file mode 100644
index 00000000..641d6b03
--- /dev/null
+++ b/blog/entry/propelling_containers/comment_1_b4939ce3944b3e157817b966c672ef68._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="diane@d02701b3d70f84d667f39e4b05008c653d390ff3"
+ nickname="diane"
+ avatar="http://cdn.libravatar.org/avatar/cc4527dbd49491e829732a2a8f5ee041"
+ subject="Containers are properties of real hosts"
+ date="2018-12-04T22:27:27Z"
+ content="""
+It wasn't immediately obvious to me when I started looking at propellor today, but as far as I can tell containers (or images) need to be attached to a real host that will actually be building the chroot, container, or image.
+
+"""]]

fix indent
diff --git a/blog/entry/7drl_2015_day_5_type_directed_spell_system_development.mdwn b/blog/entry/7drl_2015_day_5_type_directed_spell_system_development.mdwn
index 24c0a632..8757454b 100644
--- a/blog/entry/7drl_2015_day_5_type_directed_spell_system_development.mdwn
+++ b/blog/entry/7drl_2015_day_5_type_directed_spell_system_development.mdwn
@@ -67,7 +67,7 @@ runDream :: M NextStep -> M NextStep -> (S -> S) -> M NextStep
 runDream sleepcont wakecont wakeupstate = go =<< sleepcont
    where
          go (NextStep v ms) = return $ NextStep v $ Just $
-	 	maybe wake (go <=<) ms
+                 maybe wake (go <=<) ms
          wake _evt = do
                  modify wakeupstate
                  wakecont

update for android
diff --git a/blog/entry/howto_create_your_own_time_zone.mdwn b/blog/entry/howto_create_your_own_time_zone.mdwn
index 6759a153..b0adecc1 100644
--- a/blog/entry/howto_create_your_own_time_zone.mdwn
+++ b/blog/entry/howto_create_your_own_time_zone.mdwn
@@ -48,6 +48,12 @@ Previously:
 * [[daylight_savings_crime]]
 * [[fall_back_day]]
 
+Also, for Android:
+
+Android does not seem to have an way to define your own time zone.
+I select Barbados from the time zone list, since it is on GMT-4 year round
+with no daylight savings.
+
 [[!tag time lay linux shell]]
 
 [[!meta title="howto: create your own time zone"]]

Added a comment: Spam filtering?
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.
+"""]]

update
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.

co2
diff --git a/thanks.mdwn b/thanks.mdwn
index c27092cd..c2ac1124 100644
--- a/thanks.mdwn
+++ b/thanks.mdwn
@@ -5,4 +5,6 @@ more tangible options:
 * paypal joey@kitenet.net
 * [support me on Patreon](https://patreon.com/joeyh) or [Liberapay](https://liberapay.com/joeyh/)
 * [My Amazon wishlist](http://www.amazon.com/gp/registry/registry.html/104-5960215-8415137?ie=UTF8&type=wishlist&id=H9MGKNPCYVS2)
-* bitcoin address: <a href="bitcoin:[[!inline raw=yes pages=bitcoin]]">[[!inline raw=yes pages=bitcoin]]</a>
+* bitcoin address: <a href="bitcoin:[[!inline raw=yes pages=bitcoin]]">[[!inline raw=yes pages=bitcoin]]</a>  
+  (Due to the excessive CO2 use of bitcoin transactions, I currently prefer
+  not to use bitcoin though.)

update
diff --git a/boxen.mdwn b/boxen.mdwn
index 6b191d69..492da0cf 100644
--- a/boxen.mdwn
+++ b/boxen.mdwn
@@ -14,6 +14,7 @@ Mostly mythical creatures.
 * kiwi {*} (Anna's)
 * aquamiser2 {*} (Mark's)
 * peregrine {*} (Mom's)
+* sow {*} (Dad's)
 * [[kodama]]
 * [[phoenix]]
 * [[dragon]]

more
diff --git a/boxen.mdwn b/boxen.mdwn
index 1c5a95a2..6b191d69 100644
--- a/boxen.mdwn
+++ b/boxen.mdwn
@@ -40,7 +40,7 @@ Mostly birds.
 * [[pell]] {*}
 * orca
 * [[diatom]]
-* [[elephant]] {*}
+* [[elephant]]
 * [[clam]] {*}
 * [[mayfly]] {*}
 * [[oyster]] {*}

elephant decomissioning
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

Added a comment: re: udisks2
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.
+
+"""]]

comment
diff --git a/blog/entry/usb_drives_with_no_phantom_load/comment_14_d05fdc21e49b9690de6d8d44eaa871c7._comment b/blog/entry/usb_drives_with_no_phantom_load/comment_14_d05fdc21e49b9690de6d8d44eaa871c7._comment
new file mode 100644
index 00000000..d2fa0a3b
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load/comment_14_d05fdc21e49b9690de6d8d44eaa871c7._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""re: udisks2"""
+ date="2018-09-29T17:06:40Z"
+ content="""
+@pabs3, `udisksctl power-off --block-device /dev/disk/by-label/passport`
+does cause udev to remove the device file, it does not seem any better than
+`udevadm trigger --action=remove` in this situation though, because systemd
+has already unmounted the disk before that runs.
+
+The only difference I notice is that it disconnects the hub port, but that
+leaves it powered on, so uhubctl is still needed to power off.
+
+Unless it's better at getting caches flushed to the disk or something like that?
+"""]]

Added a comment: udisks2?
diff --git a/blog/entry/usb_drives_with_no_phantom_load/comment_13_e99ffb613ff1c3620bfa37b8a65c7a91._comment b/blog/entry/usb_drives_with_no_phantom_load/comment_13_e99ffb613ff1c3620bfa37b8a65c7a91._comment
new file mode 100644
index 00000000..087a8709
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load/comment_13_e99ffb613ff1c3620bfa37b8a65c7a91._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="pabs3@49c776417680694a0f3295ee80df4edfca300096"
+ nickname="pabs3"
+ avatar="http://cdn.libravatar.org/avatar/3bf5f3b29a3d68ddf11eb1a3d8c5dc65"
+ subject="udisks2?"
+ date="2018-09-29T07:48:25Z"
+ content="""
+Have you considered using `udisksctl power-off` to turn off the USB drives safely before shutting off power to their ports? That uses the same mechanisms as the \"safely eject\" GUI options and I think that would safer and might solve the timing issues.
+"""]]

response
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.
+"""]]

response
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.
+"""]]

Added a comment: Relay questions
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.
+"""]]

Added a comment
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. :)
+"""]]

updated
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.
+"""]]

blog update
diff --git a/blog/entry/censored_Amazon_review_of_Sandisk_Ultra_32GB_Micro_SDHC_Card.mdwn b/blog/entry/censored_Amazon_review_of_Sandisk_Ultra_32GB_Micro_SDHC_Card.mdwn
new file mode 100644
index 00000000..801bfad2
--- /dev/null
+++ b/blog/entry/censored_Amazon_review_of_Sandisk_Ultra_32GB_Micro_SDHC_Card.mdwn
@@ -0,0 +1,23 @@
+> ★ counterfeits in amazon pipeline
+> 
+> The 32 gb card I bought here at Amazon turned out to be fake. Within days I was
+> getting read errors, even though the card was still mostly empty.
+> 
+> The logo is noticably blurry compared with a 32 gb card purchased elsewhere.
+> Also, the color of the grey half of the card is subtly wrong, and the lettering
+> is subtly wrong.
+> 
+> Amazon apparently has counterfiet stock in their pipeline, google "amazon
+> counterfiet" for more.
+
+You will not find this review on 
+[Sandisk Ultra 32GB Micro SDHC UHS-I Card with Adapter - 98MB/s U1 A1 - SDSQUAR-032G-GN6MA ](https://www.amazon.com/gp/product/B073JWXGNT/ref=oh_aui_detailpage_o02_s01?ie=UTF8&psc=1)
+because it was rejected. As far as I can tell my review violates none of Amazon's 
+[posted guidelines](http://www.amazon.com/review-guidelines).
+But it's specific about how to tell this card is counterfeit, and it mentions 
+[a](https://petapixel.com/2018/05/31/beware-amazon-still-sells-counterfeit-memory-cards/)
+[real](https://www.theatlantic.com/technology/archive/2018/04/amazon-may-have-a-counterfeit-problem/558482/) 
+[and](https://www.engadget.com/2018/05/31/fulfilled-by-amazon-counterfeit-fake/)
+[ongoing](https://www.axios.com/amazon-counterfeit-fake-products-8dedb8f9-4828-4c80-b7f7-76a724ad117e.html)
+[issue](https://www.diyphotography.net/psa-fake-sandisk-memory-cards-are-everywhere-including-amazon/)
+that Amazon clearly wants to cover up.

Added a comment: With unison
diff --git a/blog/entry/locking_down_ssh_authorized_keys/comment_3_0a9e26f4882326f822388833ea8e1d49._comment b/blog/entry/locking_down_ssh_authorized_keys/comment_3_0a9e26f4882326f822388833ea8e1d49._comment
new file mode 100644
index 00000000..9eab623b
--- /dev/null
+++ b/blog/entry/locking_down_ssh_authorized_keys/comment_3_0a9e26f4882326f822388833ea8e1d49._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="https://launchpad.net/~cassou"
+ nickname="cassou"
+ avatar="http://cdn.libravatar.org/avatar/b393bcb0caac9d23ede8fbacf2990f69469c22bd4a6a60fa4ff7bf51e7273c1f"
+ subject="With unison"
+ date="2018-09-12T08:56:25Z"
+ content="""
+Add that to your authorized_keys file on the host to restrict usage of the key to unison:
+
+    # Look at manpage sshd(8) for more information on options
+    command=\"unison -server\",restrict ssh-rsa ...the key...
+
+"""]]

Added a comment
diff --git a/blog/entry/usb_drives_with_no_phantom_load/comment_9_c4bdf81496c5f507821a26aa5a8e98ea._comment b/blog/entry/usb_drives_with_no_phantom_load/comment_9_c4bdf81496c5f507821a26aa5a8e98ea._comment
new file mode 100644
index 00000000..ff68d62e
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load/comment_9_c4bdf81496c5f507821a26aa5a8e98ea._comment
@@ -0,0 +1,19 @@
+[[!comment format=mdwn
+ username="grawity@2ea26be48562f66fcb9b66307da72b1e2e37453f"
+ nickname="grawity"
+ avatar="http://cdn.libravatar.org/avatar/7003e967f47003bae82966aa373de8ef"
+ subject="comment 9"
+ date="2018-09-10T04:44:37Z"
+ content="""
+> udev keeps the disk device files in place. Attempting to access the device then leads to an IO error, and then udev notices the device is gone and removes its files
+
+Udev maintains `/dev/disk/*` symlinks and systemd's .device units, but it relies 100% on receiving the kernel's uevents to trigger the maintenance. (For that matter, the actual `/dev/sd*` nodes are created by the kernel itself via devtmpfs, not by udev anymore.)
+
+Therefore that's a kernel problem. (Or... maybe just the way uhubctl works? Does the kernel even _know_ that the port is now powered off? I think it doesn't, because uhubctl sort of bypasses the whole USB stack.)
+
+I wonder what happens if you try to fake a kernel uevent using `udevadm trigger --action=remove /sys/<sysfs_path>` after the poweroff. (Or `echo remove > /sys/<sysfs_path>/uevent`.) That won't remove the device from kernel, just poke udev about its supposed removal.
+
+> https://www.spinics.net/lists/linux-usb/msg157413.html
+
+Aha. That's unfortunate. (Even though \"the API can always be enlarged\" I wouldn't hold my breath...) 
+"""]]

Added a comment
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.)
+"""]]

comment
diff --git a/blog/entry/usb_drives_with_no_phantom_load/comment_7_cd5572bcc465858890cf129f0b218844._comment b/blog/entry/usb_drives_with_no_phantom_load/comment_7_cd5572bcc465858890cf129f0b218844._comment
new file mode 100644
index 00000000..2c40ea0e
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load/comment_7_cd5572bcc465858890cf129f0b218844._comment
@@ -0,0 +1,7 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 7"""
+ date="2018-09-09T22:28:23Z"
+ content="""
+<https://www.spinics.net/lists/linux-usb/msg157413.html>
+"""]]

comments
diff --git a/blog/entry/usb_drives_with_no_phantom_load/comment_5_15393a74da8d1fd466a65a11ad284023._comment b/blog/entry/usb_drives_with_no_phantom_load/comment_5_15393a74da8d1fd466a65a11ad284023._comment
new file mode 100644
index 00000000..b456a4dc
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load/comment_5_15393a74da8d1fd466a65a11ad284023._comment
@@ -0,0 +1,27 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""re: systemd unit syntax error"""
+ date="2018-09-09T16:24:47Z"
+ content="""
+No, my syntax is correct and works. According to systemd.syntax(7):
+
+	Empty lines and lines starting with "#" or ";" are ignored, which 
+	may be used for commenting.
+
+Nothing about use of those characters further into a line.
+
+(Some of systemd's docs do contain examples of using sh -c and escaping
+semicolons, so perhaps this has changed since earlier versions. Multiple
+ExecStart lines is another way to run multiple commands.)
+
+But in fact, the example I gave works. You can see the 20 second delay in the
+journal:
+
+	Sep 09 12:32:24 honeybee systemd[1]: Starting Startech usb hub port 4...
+	Sep 09 12:32:26 honeybee uhubctl[18819]: Current status for hub 2-1 [0409:005a, USB 2.00, 4 ports]
+	Sep 09 12:32:26 honeybee uhubctl[18819]:   Port 4: 0000 off
+	Sep 09 12:32:26 honeybee uhubctl[18819]: Sent power on request
+	Sep 09 12:32:26 honeybee uhubctl[18819]: New status for hub 2-1 [0409:005a, USB 2.00, 4 ports]
+	Sep 09 12:32:26 honeybee uhubctl[18819]:   Port 4: 0101 power connect [0480:0200]
+	Sep 09 12:32:46 honeybee systemd[1]: Started Startech usb hub port 4.
+"""]]
diff --git a/blog/entry/usb_drives_with_no_phantom_load/comment_6_c6dbe656e101c3097d73b1aeed76c173._comment b/blog/entry/usb_drives_with_no_phantom_load/comment_6_c6dbe656e101c3097d73b1aeed76c173._comment
new file mode 100644
index 00000000..bfc300d1
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load/comment_6_c6dbe656e101c3097d73b1aeed76c173._comment
@@ -0,0 +1,37 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 6"""
+ date="2018-09-09T16:49:36Z"
+ content="""
+@grawity, I tried the Requires on the device label,
+but it does not avoid the problem:
+
+	root@honeybee:/etc/systemd/system>ls /media/joey/archive-12
+	ls: cannot open directory '/media/joey/archive-12': No such device
+
+	Sep 09 13:33:50 honeybee systemd[1]: Starting Startech usb hub port 3...
+	Sep 09 13:33:50 honeybee uhubctl[23224]: Current status for hub 2-1 [0409:005a, USB 2.00, 4 ports]
+	Sep 09 13:33:50 honeybee uhubctl[23224]:   Port 3: 0000 off
+	Sep 09 13:33:50 honeybee uhubctl[23224]: Sent power on request
+	Sep 09 13:33:50 honeybee uhubctl[23224]: New status for hub 2-1 [0409:005a, USB 2.00, 4 ports]
+	Sep 09 13:33:50 honeybee uhubctl[23224]:   Port 3: 0101 power connect [1058:25ee]
+	Sep 09 13:33:50 honeybee systemd[1]: Started Startech usb hub port 3.
+	Sep 09 13:33:50 honeybee systemd[1]: Mounting archive-12...
+	Sep 09 13:33:50 honeybee systemd[1]: media-joey-archive\x2d12.mount: Mount process exited, code=exited status=32
+	Sep 09 13:33:50 honeybee systemd[1]: media-joey-archive\x2d12.mount: Failed with result 'exit-code'.
+	Sep 09 13:33:50 honeybee systemd[1]: Failed to mount archive-12.
+
+Ah, I think I see why. After the usb port is shut down, udev keeps the disk
+device files in place. Attempting to access the device then leads to an IO error,
+and *then* udev notices the device is gone and removes its files. So
+systemd's dependencies are working, but it's seeing stale information.
+
+Perhaps this is a bug in udev, that it's not noticing the usb port powered
+down. It seems to not get any kernel event for it at all, according to
+`udevadm monitor`.
+
+I thought maybe a different workaround would be to dd one byte from the
+disk after powering it down, or `udevadm trigger` the device manually.
+Unfortunately, the kernel auto-powers the usb port back up when either of
+those is done. So I'll stick with the sleep for now..
+"""]]

Added a comment: systemd unit syntax error
diff --git a/blog/entry/usb_drives_with_no_phantom_load/comment_4_f514a67a0e4e84e5a9ca647bbc8c0b19._comment b/blog/entry/usb_drives_with_no_phantom_load/comment_4_f514a67a0e4e84e5a9ca647bbc8c0b19._comment
new file mode 100644
index 00000000..a650d0b2
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load/comment_4_f514a67a0e4e84e5a9ca647bbc8c0b19._comment
@@ -0,0 +1,20 @@
+[[!comment format=mdwn
+ username="mk.fraggod@a2e97043e01bcf7237d068482b83a6cb9e179ca0"
+ nickname="mk.fraggod"
+ avatar="http://cdn.libravatar.org/avatar/db0d5b15da76246bed0ba3c253b5ea82"
+ subject="systemd unit syntax error"
+ date="2018-09-09T13:40:08Z"
+ content="""
+In systemd unit this line probably doesn't mean what you seem to think it does:
+
+    ExecStart=/usr/sbin/uhubctl -a on -p 4 ; /bin/sleep 20
+
+\"/bin/sleep 20\" there is probably irrelevant - a comment in an ini file.
+
+Unless debian patches systemd that way, whole ExecStart= line isn't passed to shell anyway, so if that part was passed to command, it'd probably just exit on parsing options.
+
+Something like this should work though:
+
+    ExecStart=/bin/bash -c \"uhubctl -a on -p 4 ; /bin/sleep 20\"
+
+"""]]

Added a comment: Monitoring USB drive health in Linux
diff --git a/blog/entry/usb_drives_with_no_phantom_load/comment_3_3078a8364227e91bd33e78acd75cd079._comment b/blog/entry/usb_drives_with_no_phantom_load/comment_3_3078a8364227e91bd33e78acd75cd079._comment
new file mode 100644
index 00000000..23950f45
--- /dev/null
+++ b/blog/entry/usb_drives_with_no_phantom_load/comment_3_3078a8364227e91bd33e78acd75cd079._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="Abishek_Muthian"
+ avatar="http://cdn.libravatar.org/avatar/1fe2acd0e7273426630dc7f02d05c9ea"
+ subject="Monitoring USB drive health in Linux"
+ date="2018-09-09T05:48:07Z"
+ content="""
+Hi Joey,
+
+Could you also elaborate on how you monitor your USB drive health, considering kernel doesn't seem to allow SAT ATA passthrough commands when using 'uas' driver.
+"""]]