Recent changes to this wiki:

creating tag page tags/announcement
diff --git a/tags/announcement.mdwn b/tags/announcement.mdwn
new file mode 100644
index 0000000..066ab20
--- /dev/null
+++ b/tags/announcement.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged announcement"]]
+
+[[!inline pages="tagged(announcement)" actions="no" archive="yes"
+feedshow=10]]

creating tag page tags/meta
diff --git a/tags/meta.mdwn b/tags/meta.mdwn
new file mode 100644
index 0000000..1d01a73
--- /dev/null
+++ b/tags/meta.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged meta"]]
+
+[[!inline pages="tagged(meta)" actions="no" archive="yes"
+feedshow=10]]

publishing
diff --git a/posts/on-hold.mdwn b/posts/on-hold.mdwn
new file mode 100644
index 0000000..00a96b3
--- /dev/null
+++ b/posts/on-hold.mdwn
@@ -0,0 +1,18 @@
+[[!meta date="Tue, 15 May 2018 12:00:10 +0000"]]
+[[!meta title="Yakking is on hold for now"]]
+[[!meta author="The Yakking Staff"]]
+[[!tag announcement meta]]
+
+After 242 articles and almost five years of almost weekly posts,
+we've run out of steam. Yakking is going to take an indefinite
+vacation. There will be no new articles, at least for now.
+
+We might start again later. There might be occasional articles at
+irregular intervals, if we feel energetic. Or not.
+
+We've enjoyed writing Yakking. We hope you've enjoyed reading Yakking.
+The site will remain, and the archives are still there for reading.
+
+Happy hacking. May the farce be with you. Be well.
+
+Daniel, Richard, and Lars.

publishing
diff --git a/posts/launcher-scripts.mdwn b/posts/launcher-scripts.mdwn
new file mode 100644
index 0000000..31c5792
--- /dev/null
+++ b/posts/launcher-scripts.mdwn
@@ -0,0 +1,65 @@
+[[!meta date="Wed, 11 Apr 2018 12:00:08 +0000"]]
+[[!meta title="Writing launcher scripts"]]
+[[!meta author="Richard Maw"]]
+[[!tag shell]]
+
+Unless your program is compiled into one big binary lump
+it will typically need to load other assets on program start.
+
+This is usually libraries, though other assets may also be required.
+
+Your programming environment will define some standard locations
+(see [hier(7)][] for some examples),
+but will normally have a way to specify more.
+
+*   C programs will look for libraries in directories listed
+    as : separated strings in the `LD_LIBRARY_PATH` environment variable.
+*   Python programs will look in `PYTHONPATH`.
+*   Lua programs will look in `LUA_PATH`, `LUA_CPATH`
+    and other environment variables
+    based on the version of the Lua interpreter.
+*   Java will look in its class path,
+    which can be set with the `-classpath` option.
+*   Executables will be sought in the `PATH` environment variable.
+
+If you only need assets in the standard locations
+then you wouldn't normally need to change anything.
+
+However you're not always able to stick to only distribution provided software.
+
+In this case you need to use software which has "bundled" its dependencies
+alongside its own software.
+
+Linux ELF binaries can make use of its "RPATH" to add extra paths,
+but most executable formats don't have a direct equivalent.
+
+In which case we can instead specify the new locations with a wrapper script.
+The standard trick is to use `$0` for the name of the script,
+[dirname(1)][] to get the directory the script is located in,
+and [readlink(1)][] `-f` on the to turn it into an absolute path.
+
+[[!format sh """
+#!/bin/sh
+D="$(dirname "$(readlink -f "$0")")"
+cp="$(set -- "$D/support/jars/"*.jar; IFS=:; printf %s "$*")"
+exec java -classpath "$cp" com.myapplication.Main "$@"
+"""]]
+
+This works for running the script in the directory the assets are stored in,
+but it can be convenient to add the program to a directory in `PATH`.
+
+If written as a bash script you can use `$BASH_SOURCE`
+which is guaranteed to be the path of the script,
+and in circumstances I can now no longer reproduce
+I needed to use it instead of `$0`.
+
+[[!format sh """
+#!/bin/bash
+D="$(dirname "$(readlink -f "${BASH_SOURCE}")")"
+cp="$(set -- "$D/support/jars/"*.jar; IFS=:; printf %s "$*")"
+exec java -classpath "$cp" com.myapplication.Main "$@"
+"""]]
+
+[hier(7)]: https://linux.die.net/man/7/hier
+[dirname(1)]: http://man7.org/linux/man-pages/man1/dirname.1.html
+[readlink(1)]: http://man7.org/linux/man-pages/man1/readlink.1.html

Added a comment: More obscure paths
diff --git a/posts/falsehoods-programmers-believe-about-file-paths/comment_2_7501b5b20b7ce7659879e852fd3bf6ad._comment b/posts/falsehoods-programmers-believe-about-file-paths/comment_2_7501b5b20b7ce7659879e852fd3bf6ad._comment
new file mode 100644
index 0000000..28087bd
--- /dev/null
+++ b/posts/falsehoods-programmers-believe-about-file-paths/comment_2_7501b5b20b7ce7659879e852fd3bf6ad._comment
@@ -0,0 +1,43 @@
+[[!comment format=mdwn
+ username="hlandau@b7933f40478b2aff43d3c2b041a348c97fae0856"
+ nickname="hlandau"
+ avatar="http://cdn.libravatar.org/avatar/03d7065dea8bff2b7727522f03db3ea6"
+ subject="More obscure paths"
+ date="2018-04-07T08:21:16Z"
+ content="""
+Some things to add:
+
+- On MacOS 9 the path component separator was :. Falsehood: The path component separator is always / or \.
+- You should add a list of all the reserved characters on Windows, as well as the COM*, etc. already listed. From https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#:
+
+    < (less than)
+    > (greater than)
+    : (colon)
+    \" (double quote)
+    / (forward slash)
+    \ (backslash)
+    | (vertical bar or pipe)
+    ? (question mark)
+    * (asterisk)
+
+- Falsehood: Windows UNC paths \\FOO refer to a network server. Counterpoint: \\.\COM1.
+- Falsehood: Absolute Windows paths always begin with DRIVELETTER:\. Counterpoint: \\?\C:\Foo. This is the long path support, the \\?\ prefix is used to indicate the application can support long paths.
+- Falsehood: Windows UNC paths begin with \\SERVERNAME. Counterpoint: \\?\UNC\SERVERNAME. \\?\UNC\ is the long path UNC prefix.
+- Falsehood: A subdirectory of a directory is on the same physical storage on Windows. Counterpoint: Drives can be mounted at arbitrary directories, not just at drive letters.
+- Falsehood: Barring symlinks and hardlinks, a file has only one path which can access it. Counterpoint: File paths can contain redundancies like /./, leading to an arbitrary number of strings accessing the same file.
+- Falsehood: Barring symlinks and hardlinks and with paths normalised to remove redundancies, a file has only one path which can access it. Counterpoint: The same filesystem can appear at different mountpoints on *nix using bind mounts. On Windows, junctions can be used, which function similarly to directory symlinks, but which are not symlinks and which are substantially more transparent.
+- Falsehood: If a Windows path has a drive letter, it must be an absolute path. Counterpoint: C:foo.txt (or C:bar\foo.txt or C:..\bar\foo.txt) is relative to the current working directory which was last set for C:, which may be different to the current working directory for the current drive letter.
+- Falsehood: Barring symlinks or hardlinks, a file has only one name. Counterpoint: Short names on Windows. C:\LongFilename.txt is reduced to C:\LongFi~1.txt for legacy access.
+- Falsehood: A short name will always end in ~1.EXT. Counterpoint: If C:\LongFilenameX.txt and C:\LongFilenameY.txt both exist, one will be C:\LongFi~1.txt and one will be C:\LongFi~2.txt. Which is which is indeterminate.
+- Falsehood: Opening a path successfully means you've opened a file. Counterpoint: Directories (and sockets, and so on) can be opened on *nix. On Windows, alternate file streams are addressed with the colon, which are subcomponents of files.
+- Falsehood: A file only has one stream of data associated with it. Counterpoint: Windows has alternate file streams. MacOS has resource forks.
+- Falsehood: A filesystem supports filenames longer than 8+3 characters. Counterpoint: DOS is limited to 8 characters before the file extension and 3 after.
+- Falsehood: If you write to a file with provided normalised path X and then delete normalised path Y, where Y != X, X will still exist. Counterpoint: On Windows, if X is an alternate file stream path (C:\Foo.txt:sub1), and Y is the file path (C:\Foo.txt), deleting Y will delete X. Also if Y != X is a case sensitive comparison and the filesystem is case insensitive.
+- Falsehood: The types of objects which can appear on a filesystem is limited to files and directories. Counterpoint: Windows has files (including hardlinks), directories, symlinks, junctions. \*nix has files (including hardlinks), directories, symlinks, sockets, FIFOs, character devices, block devices. Some *nixes may have other object types, like Solaris doors.
+- Falsehood: A platform provides or doesn't provide mandatory locking. Counterpoint: Windows does and it is used by default. Linux doesn't provide mandatory file locking.
+- Falsehood: A filesystem mounted on *nix is always case sensitive. Counterpoint: Linux can mount FAT32, NTFS, etc.
+- Falsehood: A filesystem mounted on Windows is always case insensitive. Counterpoint: Windows can be configured to make its filesystems case sensitive.
+- Falsehood: The separators between multiple directory components are the same as that used to separate the directory components and the filename. Counterpoint: OpenVMS paths look like this: SYS$SYSDEVICE:[DIR1.DIR2.DIR3]FILENAME.EXT;FILEVER.
+
+Feel free to use the above.
+"""]]

creating tag page tags/pulseaudio
diff --git a/tags/pulseaudio.mdwn b/tags/pulseaudio.mdwn
new file mode 100644
index 0000000..35f6460
--- /dev/null
+++ b/tags/pulseaudio.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged pulseaudio"]]
+
+[[!inline pages="tagged(pulseaudio)" actions="no" archive="yes"
+feedshow=10]]

creating tag page tags/text-to-speech
diff --git a/tags/text-to-speech.mdwn b/tags/text-to-speech.mdwn
new file mode 100644
index 0000000..7d1c219
--- /dev/null
+++ b/tags/text-to-speech.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged text-to-speech"]]
+
+[[!inline pages="tagged(text-to-speech)" actions="no" archive="yes"
+feedshow=10]]

creating tag page tags/festival
diff --git a/tags/festival.mdwn b/tags/festival.mdwn
new file mode 100644
index 0000000..c1894db
--- /dev/null
+++ b/tags/festival.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged festival"]]
+
+[[!inline pages="tagged(festival)" actions="no" archive="yes"
+feedshow=10]]

publishing
diff --git a/posts/text-to-speech.mdwn b/posts/text-to-speech.mdwn
new file mode 100644
index 0000000..5d804ab
--- /dev/null
+++ b/posts/text-to-speech.mdwn
@@ -0,0 +1,147 @@
+[[!meta date="Wed, 04 Apr 2018 15:23:39 +0000"]]
+[[!meta title="Text To Speech"]]
+[[!meta author="Richard Maw"]]
+[[!tag pulseaudio festival text-to-speech shell]]
+
+What do you do if you need to be present on a call but you've lost your voice?
+Why write an 11 line shell script to replace it of course!
+
+Our first port of call is of course to Google!
+So what is the first result for "linux text to speech"?
+(well for me it's [RPi Text to Speech (Speech Synthesis)][])
+and deep down there after Cepstral it's) Festival of course!
+
+So how do we use that?
+
+First we need to install it. Since I wrote this on an Ubuntu system I do:
+
+[[!format sh """
+$ apt install festival
+"""]]
+
+This installs the `festival` command,
+which has a convenient `--tts` option!
+
+[[!format sh """
+$ echo hello world | festival --tts
+"""]]
+
+This however has two problems:
+
+1.  It is fatiguing on the fingers to tweak the parameters and run the command.
+2.  The output of the command is to the speakers rather than a microphone.
+
+We can fix problem 1 with a trivial shell script
+to produce output after every line instead.
+
+[[!format sh """
+#!/bin/sh
+while read -r REPLY; do
+        printf %s "$REPLY" | festival --tts
+done
+"""]]
+
+The problem of outputting to a microphone is somewhat more involved.
+
+It's possible to loop your speaker output through a recording device
+in Pulse Audio by setting the recording device to a "Monitor" device.
+
+It's no doubt possible to drive this from the command-line,
+but since my chat software is graphical I've no problem using `pavucontrol`.
+
+Once the chat software is running the "Recording" tab
+and change the input device of the application.
+
+This works but is unsatisfying because you need a second output device
+otherwise other sounds will be broadcast
+and is prone to causing feedback.
+
+What we need is some kind of virtual microphone.
+As usual, the likes of Google and StackOverflow come to hand,
+and a virtual microphone is what Pulse Audio calls a "null sink".
+
+We can create a null sink and give it a recognisable name by running:
+
+[[!format sh """
+pacmd load-module module-null-sink sink_name=Festival
+pacmd update-sink-proplist Festival device.description=Festival
+"""]]
+
+Then we can remove it again by running:
+
+[[!format sh """
+pacmd unload-module module-null-sink
+"""]]
+
+So how do we get festival to play its output to that?
+
+We can't start the command, then tweak the parameters in `pavucontrol`
+because it doesn't run long enough to change that before it starts playing.
+
+We can play audio to a specified device with the `paplay` command,
+but how do we get Festival to output?
+
+Fortunately Festival lets you set some parameters in its scripting language.
+
+We need to pick a common audio format that `paplay` can read
+and `festival` can produce.
+We can set this with:
+
+[[!format lisp """
+(Parameter.set 'Audio_Required_Format 'aiff)
+"""]]
+
+We need to tell festival to play audio through a specified Pulse Audio device.
+The best way I could find to do this
+was setting `Audio_Method` to `Audio_Command`
+and `Audio_Command` to a `paplay` command.
+
+[[!format lisp """
+(Parameter.set 'Audio_Method 'Audio_Command)
+(Parameter.set 'Audio_Command "paplay $FILE --client-name=Festival --stream-name=Speech --device=Festival")
+"""]]
+
+Festival lets us run commands on its command-line so the final script we get is:
+
+[[!format sh """
+#!/bin/sh
+pacmd load-module module-null-sink sink_name=Festival
+pacmd update-sink-proplist Festival device.description=Festival
+while read -r REPLY; do
+        festival --batch \
+                '(Parameter.set '\''Audio_Required_Format '\''aiff)' \
+                '(Parameter.set '\''Audio_Method '\''Audio_Command)' \
+                '(Parameter.set '\''Audio_Command "paplay $FILE --client-name=Festival --stream-name=Speech --device=Festival")' \
+                '(SayText "'"$REPLY"'")'
+done
+pacmd unload-module module-null-sink
+"""]]
+
+1.  Run that in a terminal window.
+2.  Start your chat program.
+3.  Start `pavucontrol` and change the input device of your program to Festival.
+4.  Type lines of text into the terminal to speak.
+
+---
+
+Since this was a project to achieve the goal of being able to participate
+in a group chat without being able to speak, development stopped there.
+
+Should further development be warranted other changes could include:
+
+1.  The module load and unload process is pretty fragile.
+    Would need to use an API that is tied to process lifetime
+    or at least unload by ID rather than name.
+2.  No escaping mechanism for `$REPLY`.
+    Would need to learn string escaping in lisp.
+3.  Lots of work done per line of text.
+    Festival has a server mode which could reduce the amount of work per line.
+4.  Investigate a way to pipe audio directly between Festival and Pulse Audio.
+    `text2wave` exists to write to a file, possibly standard output,
+    and `pacat` exists to take audio from standard input and put it to speakers,
+    but I couldn't get it to work at the time.
+5.  Replace festival entirely.
+    It is in need of maintainership, and has been broken in Fedora releases,
+    so replacing the voice generation with pyttsx, espeak or flite could help.
+
+[RPi Text to Speech (Speech Synthesis)]: https://elinux.org/RPi_Text_to_Speech_(Speech_Synthesis)

publishing
diff --git a/posts/coming-back-to-a-project.mdwn b/posts/coming-back-to-a-project.mdwn
new file mode 100644
index 0000000..ffc8205
--- /dev/null
+++ b/posts/coming-back-to-a-project.mdwn
@@ -0,0 +1,51 @@
+[[!meta date="Wed, 28 Mar 2018 13:17:44 +0000"]]
+[[!meta title="Coming back to a project"]]
+[[!meta author="Daniel Silverstone"]]
+
+We've spoken about how [you are not finished yet][notfinished] with your
+project, how to avoid [burning bridges][bridges] in conversation, how to tell if a
+project is [dead, or merely resting][deador], and on knowing
+[when to retire][retire] from a project.  Today I'd like to talk about coming
+back to a project which you have taken a hiatus from.
+
+Whether your break from a project was planned or unplanned, whether you were
+fundamental to a project's progress, or "just" a helper, your absence will
+have been felt by others associated with the project, even if they've not said
+anything.  It's always a nice thing to let anyone who ought to know, know that
+you have returned.  Depending on your level of integration into that project's
+community and the particulars of your absence, your own sense of privacy around
+any reasons, etc., it can be worth letting the community know a little of why
+you were away, and why that means that now you are back.
+
+If your time away was unannounced, unplanned, abrupt, or otherwise disruptive
+to the community, it can also help to mend things if you are prepared to
+apologise for your absence.  **NOTE** I am not saying you have to apologise or
+excuse the reasons why you were absent, merely to note to the community that
+you are sorry to have been away and that you look forward to reintegrating.
+
+Just like I'd recommend when you join a community, make a point of discussing
+what you intend to achieve in the short term, in case there's someone who wants
+to assist you or has been working on something similar.  Also make it clear if
+you've kept up passively with the community in your time away, or if you've
+essentially a blank for the period of your absence.  That can help others to
+decide if they need to tell you about things which might be important, or
+otherwise relevant, to your interests and plans.
+
+If you were specifically, and solely, responsible for certain things when you
+were previously part of the project, you should, ideally, go back and check if
+anyone has picked it up in your absence.  If so, make a point of thanking them,
+and asking them if they wish to continue with the work without you, share the
+burden with you, or pass the work back to you entirely.  If that responsibility
+was part of why you had a hiatus, ensure that you don't end up in a similar
+position again.
+
+There are many more suggestions I might make, but I'll leave you with one final
+one.  Don't expect the project you re-join to be the same project you left.  In
+your absence things will have progressed with respect to the project, others in
+the community, and yourself.  Don't be saddened by this, instead rejoice in the
+diversity of life and dig back in with gusto.
+
+[notfinished]: /posts/so-you-think-you-are-finished/
+[bridges]: /posts/bridge-fires/
+[deador]: /posts/dead-or-resting/
+[retire]: /posts/when-to-retire/

publishing
diff --git a/posts/famous-bugs.mdwn b/posts/famous-bugs.mdwn
new file mode 100644
index 0000000..32dc770
--- /dev/null
+++ b/posts/famous-bugs.mdwn
@@ -0,0 +1,28 @@
+[[!meta date="Wed, 21 Mar 2018 12:00:06 +0000"]]
+[[!meta title="Famous bugs"]]
+[[!meta author="Lars Wirzenius"]]
+[[!tag bug]]
+
+The history of computing has a number of famous bugs. It can be
+amusing to read about them. Here's a start:
+
+* The very [first recorded bug][] (1945)
+* The [Mariner 1][] crash (1962)
+* The [Therac-25][] radiation overdose (1985)
+* The [AT&T network][] failure (1990)
+* The [Pentium bug][] (1994)
+* The [Ariane 5][] rocket explosion (1996)
+* The [other Pentium bug][] (1997)
+* The [Mars climate orbiter][] crash (1999)
+
+[first recorded bug]: https://thenextweb.com/shareables/2013/09/18/the-very-first-computer-bug/
+[Mariner 1]: https://en.wikipedia.org/wiki/Mariner_1
+[Therac-25]: https://en.wikipedia.org/wiki/Therac-25
+[AT&T network]: http://www.phworld.org/history/attcrash.htm
+[Ariane 5]: http://www-users.math.umn.edu/~arnold/disasters/ariane.html
+[Pentium bug]: https://en.wikipedia.org/wiki/Pentium_FDIV_bug
+[other Pentium bug]: https://en.wikipedia.org/wiki/Pentium_FDIV_bug
+[Mars climate orbiter]: https://en.wikipedia.org/wiki/Mars_Climate_Orbiter
+
+What's your favourite bug? Please tell us in comments.
+

publishing
diff --git a/posts/so-you-think-you-are-finished.mdwn b/posts/so-you-think-you-are-finished.mdwn
new file mode 100644
index 0000000..135f832
--- /dev/null
+++ b/posts/so-you-think-you-are-finished.mdwn
@@ -0,0 +1,19 @@
+[[!meta date="Wed, 14 Mar 2018 12:00:07 +0000"]]
+[[!meta title="So you think you are finished?"]]
+[[!meta author="Daniel Silverstone"]]
+
+It's not often that we just link to a single article, but sometimes that
+article is simply "worth it".  In this instance I recently read a posting
+by Loup Vaillant which made me think "This needs to be distributed to the
+yakking community" and so here we go…
+
+Loup wrote about what needs to happen when you think you're finished writing
+your free/open source project's code, and I think you should run over there
+right now and read it:
+[After your project is done — Loup Vaillant](http://loup-vaillant.fr/articles/after-your-project-is-done)
+
+If you're still here afterwards, well done.  Now go and look over any projects
+of your own which you consider "finished" and see if there's anything Loup
+hilighted which still needs to be done to them.  Maybe make a
+[next-action](https://www.brevedy.com/2014/02/19/organizing-next-actions/) to
+do something about it.

publishing
diff --git a/posts/cycles.mdwn b/posts/cycles.mdwn
new file mode 100644
index 0000000..5008245
--- /dev/null
+++ b/posts/cycles.mdwn
@@ -0,0 +1,51 @@
+[[!meta date="Wed, 07 Mar 2018 12:03:07 +0000"]]
+[[!meta title="Cycles in development"]]
+[[!meta author="Lars Wirzenius"]]
+[[!tag workflow]]
+
+Software development tends to happen in cycles and it's good to be
+aware of this.
+
+The innermost cycle is the edit-build-test-debug loop. You write or
+change some code, you build the software (assuming a compiled
+language), and you run the software to test it, either manually or by
+using automated tests.
+
+Being the innermost cycle, it's probably where most of development
+time happens. It tends to pay to optimise it to make it as fast as
+possible. Each of the parts can be optimised. For editing, use an
+editor you like and are comforable with. Also, a keyboard you can type
+efficiently with. For building, use incremental building, and maybe
+turn off optimisation to make the build got faster. For testing, maybe
+run only the relevant tests, and make those run quickly.
+
+Other cycles are:
+
+* The TDD cycle: add a new test, add or change code to make test pass,
+  refactor. This tends to embed the edit-build-test-debug loop.
+  Usually at most minutes in length.
+
+* Adding a minimal user-visible change: a new feature is broken into
+  the smallest increment that makes the user's life better, and this
+  is then developed, often using a number of TDD cycles.
+
+* A "sprint" in Agile development: often a week or two or three, often
+  adds an entire user-visible feature or other change, or several.
+
+* A release cycle: often many weeks or months, adds significant new
+  features. The set of added features or other changes, and the length
+  of the release cycle are all often determined by business interests,
+  business strategy, and often ridiculed by developers. This sometimes
+  happens for open source projects too, however. For example, a Linux
+  distribution might synchronise its own release schedule with
+  multiple major, critical components it includes.
+
+* The maintenance cycle: after the software has been "finished", and
+  put into production, bugs and other misfeatures get fixed and once
+  there's enough of them, a new release is made and put into production.
+
+In each case, it is useful to know the intended and expected length of
+the cycle, and what needs to happen during the cycle, and what the
+intended and expected result should be. It is also useful to try to
+identify and remove unnecessary clutter from the cycles, to make
+things go smoothly.

Added a comment: I don't use Windows
diff --git a/posts/git-workflow/comment_2_97f03f3b17a05d5350002547717ffb6a._comment b/posts/git-workflow/comment_2_97f03f3b17a05d5350002547717ffb6a._comment
new file mode 100644
index 0000000..9adaa68
--- /dev/null
+++ b/posts/git-workflow/comment_2_97f03f3b17a05d5350002547717ffb6a._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="https://liw.fi/"
+ avatar="http://cdn.libravatar.org/avatar/c9632d33d21a2acc25cd320c2dcbce25cb21ad42117dc19426c51ce1cdf042dc"
+ subject="I don't use Windows"
+ date="2018-02-26T19:29:49Z"
+ content="""
+I wouldn't modify these tips for Windows, since I don't ever use Windows, and haven't ever used Windows for professional software development. I am completely ignorant of how anything works in Windows. Sorry.
+"""]]

Added a comment: for a windows user
diff --git a/posts/git-workflow/comment_1_5a0a925b08a8a9860024f650563f65c7._comment b/posts/git-workflow/comment_1_5a0a925b08a8a9860024f650563f65c7._comment
new file mode 100644
index 0000000..8d2bf5a
--- /dev/null
+++ b/posts/git-workflow/comment_1_5a0a925b08a8a9860024f650563f65c7._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="dave@b2d908db837e32f8db83379ab60228d9c000b1e2"
+ nickname="dave"
+ avatar="http://cdn.libravatar.org/avatar/c29af380256fbe512313cc722c972920"
+ subject="for a windows user"
+ date="2018-02-26T15:09:55Z"
+ content="""
+How would you modify these tips for someone using Git on Windows? You make several mentions of Debian here. Thanks much!
+"""]]

publishing
diff --git a/posts/git-workflow.mdwn b/posts/git-workflow.mdwn
new file mode 100644
index 0000000..5ed2d61
--- /dev/null
+++ b/posts/git-workflow.mdwn
@@ -0,0 +1,58 @@
+[[!meta date="Wed, 21 Feb 2018 12:00:11 +0000"]]
+[[!meta title="The only proper git workflow, use nothing else"]]
+[[!meta author="Lars Wirzenius"]]
+[[!tag git workflow]]
+
+The title of this article is intentionally provocative.
+
+Git is a flexible tool that allows many kinds of workflow for using
+it. Here is the workflow I favour for teams:
+
+* The `master` branch is meant to be always releasable.
+
+* Every commit in `master` MUST pass the full test suite, though not
+  all commits in merged change sets need to do that.
+
+* Changes are done in dedicated branches, which get merged to `master`
+  frequently - avoid long-lived branches, since they tend to result in
+  much effort having to be spent on resolving merge conflicts.
+
+    * If frequent merging is, for some reason, not an option, at least
+      rebase the branch onto current master frequently: at least
+      daily. This keeps conflicts fairly small.
+
+* Before merging a branch into `master`, rebase it onto `master` and
+  resolve any conflicts - also rebase the branch so it tells a clean
+  story of the change.
+
+    * `git rebase -i master` is a very powerful tool. Learn it.
+
+    * A clean story doesn't have commits that fix mistakes earlier in
+      the branch-to-be-merged, and introduces changes within the
+      branch in chunks of a suitable size, and in an order that makes
+      sense to the reader. Clean up "Fix typo in previous commit" type
+      of commits.
+
+* Update the `NEWS` file when merging into `master`. Also Debian
+  packaging files, if those are included in the source tree.
+
+[bumper]: http://git.liw.fi/bumper/
+
+* Tag releases using PGP signed, annotated tags. I use a tool called
+  [bumper][], which updates `NEWS`, `version.py`, `debian/changelog`,
+  tags a release, and updates the files again with with `+git`
+  appended to version number.
+
+    * Review, update `NEWS`, `debian/changelog` before running bumper
+      to make sure they're up to date.
+
+* Name branches and tags with a prefix `foo/` where `foo` is your
+  username, handle, or other identifier.
+
+* If `master` is broken, fixing it has highest priority for the
+  project.
+
+* If there is a need for the project to support older releases, create
+  a branch for each such, when needed, starting from the release's
+  tag. Treat release branches as `master` for that release.
+

creating tag page tags/calm
diff --git a/tags/calm.mdwn b/tags/calm.mdwn
new file mode 100644
index 0000000..eff4c85
--- /dev/null
+++ b/tags/calm.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged calm"]]
+
+[[!inline pages="tagged(calm)" actions="no" archive="yes"
+feedshow=10]]

creating tag page tags/bridge
diff --git a/tags/bridge.mdwn b/tags/bridge.mdwn
new file mode 100644
index 0000000..7864a68
--- /dev/null
+++ b/tags/bridge.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged bridge"]]
+
+[[!inline pages="tagged(bridge)" actions="no" archive="yes"
+feedshow=10]]

publishing
diff --git a/posts/bridge-fires.mdwn b/posts/bridge-fires.mdwn
new file mode 100644
index 0000000..bd8be78
--- /dev/null
+++ b/posts/bridge-fires.mdwn
@@ -0,0 +1,97 @@
+[[!meta date="Wed, 07 Feb 2018 12:00:09 +0000"]]
+[[!meta title="Don't burn that bridge!"]]
+[[!meta author="Lars Wirzenius"]]
+[[!tag bridge calm]]
+
+You may be familiar with some variant of this scenario:
+
+> You're on a mailing list (or web forum or Google group or whatever),
+> where some topic you're interested in is being discussed. You see
+> someone saying something you think is wrong. You fire off a quick
+> reply telling them they're wrong, and move on to the next topic.
+>
+> Later on, you get a reply, and for some reason they are upset at you
+> telling them they're wrong, and you get upset at how rude they are,
+> so you send another quick reply, putting them in their place. Who do
+> they think they are, spouting off falsehoods and being rude about
+> it.
+>
+> The disagreement spirals and becomes hotter and more vicious each
+> iteration. What common ground there was in the beginning is soon
+> ruined by trenches, bomb craters, and barbed wire. Any bridges
+> between the parties are on fire. There's no hope for peace.
+
+This is called a flame war. It's not a good thing, but it's not
+uncommon in technical discussions on the Internet. Why does it happen
+and how can you avoid it?
+
+As someone covered in scars of many a flame war, here are my
+observations (entirely unsubstantiated by sources):
+
+* Flame wars happen because people try to be seen as being more
+  correct than others, or to be seen to win a disagreement. This often
+  happens online because the communication medium lacks emotional
+  bandwidth. It is difficult to express subtle emotions and cues over
+  a text-only channel, especially, or any one-way channel.
+
+  Disagreements spiral away more rarely in person, because in-person
+  communication contains a lot of unspoken parts, which signal things
+  like someone being upset, before the thing blows up entirely. In
+  text-only communication, one needs to express such cues more
+  explicitly, and be careful when reading to spot the more subtle
+  cues.
+
+* In online discussions around free software there are also often no
+  prior personal bonds between participants. Basically, they don't
+  know each other. This makes it harder to understand each other.
+
+* The hottest flame wars tend to happen in contexts where the
+  participants have the least to lose.
+
+Some advice (again, no sources):
+
+* Try hard to understand the other parties in a disagreement. The
+  technical term is empathy. You don't need to agree with them, but
+  you need to try to understand why they say what they say and how
+  they feel. As an example, I was once in a meeting where a co-worker
+  arrived badly late, and the boss was quite angry. It was quickly
+  spiralling into a real-life flame war, until someone pointed out that
+  the boss was upset because he needed to get us developers do certain
+  things, and people being late was making that harder to achieve, and
+  at the same time the co-worker who was late was mourning his dog
+  who'd been poorly for years and had recently committed suicide by
+  forcing open a 6th floor window and jumping out.
+
+* Try even harder to not express anger and other unconstructive
+  feelings, especially by attacking the other parties. Instead of
+  "you're wrong, and you're so stupid that the only reason you don't
+  suffocate is because breathing is an autonomous action that doesn't
+  require the brain, go jump into a frozen lake", say something like
+  "I don't agree with you, and I'm upset about this discussion so I'm
+  going to stop participating, at least for a while". And then don't
+  participate further.
+
+* Do express your emotions explicitly, if you think that'll mean
+  others will understand you better.
+
+* Try to find at least something constructive to say, and some common
+  ground. Just because someone is wrong about what the colour of the
+  bike shed should be, doesn't mean you have to disagree whether a
+  bike shed is useful.
+
+* Realise that shutting up doesn't mean you agree with the other
+  parties in a disagreement, and it doesn't mean you "lose" the
+  argument.
+
+* Apply [rule 6][] vigorously: write angry responses if it helps you
+  deal with your emotions, but don't send them. You can then spend the
+  rest of you life being smug about how badly other people have been
+  humiliated and shown to be wrong.
+
+[rule 6]: https://liw.fi/rules/
+
+Your homework for this week, should you choose to accept it, is to
+find an old flame war and read through it and see where the
+participants could've said something different and defuse the
+situation. You get bonus points if it's one which you've participated
+in yourself.

publishing
diff --git a/posts/processing-input.mdwn b/posts/processing-input.mdwn
new file mode 100644
index 0000000..4567f58
--- /dev/null
+++ b/posts/processing-input.mdwn
@@ -0,0 +1,87 @@
+[[!meta date="Thu, 01 Feb 2018 12:00:09 +0000"]]
+[[!meta title="Processing input"]]
+[[!meta author="Daniel Silverstone"]]
+
+Computer programs typically need some input on which to perform their
+purpose.  In order to ascribe meaning to the input, programs will perform a
+process called parsing.  Depending on exactly how the author chooses to
+develop their program, there are a number of fundamentally different ways to
+convert a byte sequence to something with more semantic information layered
+on top.
+
+Lexing and Parsing
+==================
+
+[Lexical analysis][] is the process by which a program takes a stream of bytes
+and converts it to a stream of [tokens][].  Tokens have a little more meaning,
+such as taking the byte sequence `"Hello"` and representing it as a token of
+the form `STRING` whose value is `Hello`.  Once a byte stream has been turned
+into a token stream, the program can then parse the token stream.
+
+Typically, the [parsing][] process consumes the token stream and produces as
+its output something like an [abstract syntax tree][].  This AST layers enough
+semantic meaning onto the input to allow the program to make use of the input
+properly.  As an example, in the right context, a parser might take a token
+stream of the form `STRING(println) '(' STRING(Hello) ')' ';'` and turn it into
+an AST node of the form `FunctionInvocation("println", [ "Hello" ])`.  As you
+can see, that would be far more useful if the program in question is a
+compiler.
+
+Parsing in this way is commonly applied when the language [grammar][] in
+question meets certain rules which allow it to be expressed in such a way that
+a token stream can be unambiguously converted to the AST with no more than one
+"look-ahead" token.  Such languages can convert "left-to-right"
+i.e. unidirectionally along the token stream and usually we call those
+languages [LALR(1)][].
+
+To facilitate easy lexical analysis and the generation of `LALR(1)` parsers,
+there exist a number of generator programs such as `flex` and `bison`, or
+`re2c` and `lemon`.  Indeed such generators are available for non-C languages
+such as `alex` and `happy` for Haskell, or `PLY` for Python.
+
+Parsing Expression Grammars
+===========================
+
+[PEGs][] are a type of parser which typically end up represented as a
+[recursive descent][] parser.  PEGs sometimes allow for a parser to be
+represented in a way which is more natural for the language definer.  Further, 
+there is effectively infinite capability for look-ahead when using PEGs, allowing
+them to parse grammars which a more traditional `LALR(1)` would be unable to.
+
+Combinatory Parsing
+===================
+
+[Parser combinators][] take advantage of higher order functions in programming
+languages to allow a parser to be built up by combining smaller parsers
+together into more complex parsers, until a full parser for the input can be
+built.  The lowest level building blocks of such parsers are often called
+terminal recognisers and they recognise the smallest possible building block of
+the input (which could be a token from a lexical analyser or could be a byte or
+unicode character).  Most parser combinator libraries offer a number of
+standard combinators, such as one which will recognise one or more of the
+passed in parser, returning the recognised elements as a list.
+
+Sadly, due to the strong functional programming nature of combinators, it's
+often very hard to statically analyse the parser to check for ambiguities or
+inconsistencies in the grammar.  These issues only tend to become obvious at
+runtime, meaning that if you're using parser combinators to build your parser,
+it's recommended that you carefully write your grammar first, and convert it
+to code second.
+
+Homework
+========
+
+Find a program which you use, which consumes input in a form specific to the
+program itself.  (Or find a library which is meant to parse some format) and
+take a deep look at how it performs lexical analysis and parsing.
+
+
+[parsing]: https://en.wikipedia.org/wiki/Parsing
+[Lexical analysis]: https://en.wikipedia.org/wiki/Lexical_analysis
+[tokens]: https://en.wikipedia.org/wiki/Lexical_analysis#Token
+[abstract syntax tree]: https://en.wikipedia.org/wiki/Abstract_syntax_tree
+[grammar]: https://en.wikipedia.org/wiki/Formal_grammar
+[LALR(1)]: https://en.wikipedia.org/wiki/LALR_parser
+[PEGs]: https://en.wikipedia.org/wiki/Parsing_expression_grammar
+[recursive descent]: https://en.wikipedia.org/wiki/Recursive_descent_parser
+[Parser combinators]: https://en.wikipedia.org/wiki/Parser_combinator

Added a comment: screen -D -m $SOMETHING
diff --git a/posts/screen-advanced/comment_1_9460f7225742e9044770b64265aa6359._comment b/posts/screen-advanced/comment_1_9460f7225742e9044770b64265aa6359._comment
new file mode 100644
index 0000000..074f5eb
--- /dev/null
+++ b/posts/screen-advanced/comment_1_9460f7225742e9044770b64265aa6359._comment
@@ -0,0 +1,14 @@
+[[!comment format=mdwn
+ username="yeti"
+ avatar="http://cdn.libravatar.org/avatar/874987fa76129f3ab4f6799fc6597f06"
+ subject="screen -D -m $SOMETHING"
+ date="2018-01-28T08:31:20Z"
+ content="""
+Your example starts `screen` with a script containing `screen -t irssi irssi`.
+
+This script is not really a config file like the ones you would use via screen's `-c configfilename` option.
+
+`script`'s \"real\" config files can contain commands which would be not recognised by your way of doing it.
+
+Your way is starting `screen -t ...` in an already running outer `screen` what makes the inner screen recognise running in screen and therefore delegating its commands to the outer screen.
+"""]]

removed
diff --git a/posts/screen-advanced/comment_1_d4bad05ddb080ed3865086b33564ecb8._comment b/posts/screen-advanced/comment_1_d4bad05ddb080ed3865086b33564ecb8._comment
deleted file mode 100644
index 652101e..0000000
--- a/posts/screen-advanced/comment_1_d4bad05ddb080ed3865086b33564ecb8._comment
+++ /dev/null
@@ -1,14 +0,0 @@
-[[!comment format=mdwn
- username="yeti"
- avatar="http://cdn.libravatar.org/avatar/874987fa76129f3ab4f6799fc6597f06"
- subject="screen -D -m $SOMETHING"
- date="2018-01-28T08:30:01Z"
- content="""
-Your example starts `screen` with a script containing `screen -t irssi irssi`.
-
-This script is not really a config file like the ones you would use via screen's `-c configfilename` option.
-
-`script`'s \"real\" can contain commands which would be not recognised by your way of doing it.
-
-Your way is starting `screen -t ...` in an already running outer `screen` what makes the inner screen recognise running in screen and therefore delegating its commands to the outer screen.
-"""]]

Added a comment: screen -D -m $SOMETHING
diff --git a/posts/screen-advanced/comment_1_d4bad05ddb080ed3865086b33564ecb8._comment b/posts/screen-advanced/comment_1_d4bad05ddb080ed3865086b33564ecb8._comment
new file mode 100644
index 0000000..652101e
--- /dev/null
+++ b/posts/screen-advanced/comment_1_d4bad05ddb080ed3865086b33564ecb8._comment
@@ -0,0 +1,14 @@
+[[!comment format=mdwn
+ username="yeti"
+ avatar="http://cdn.libravatar.org/avatar/874987fa76129f3ab4f6799fc6597f06"
+ subject="screen -D -m $SOMETHING"
+ date="2018-01-28T08:30:01Z"
+ content="""
+Your example starts `screen` with a script containing `screen -t irssi irssi`.
+
+This script is not really a config file like the ones you would use via screen's `-c configfilename` option.
+
+`script`'s \"real\" can contain commands which would be not recognised by your way of doing it.
+
+Your way is starting `screen -t ...` in an already running outer `screen` what makes the inner screen recognise running in screen and therefore delegating its commands to the outer screen.
+"""]]

publishing
diff --git a/posts/dead-or-resting.mdwn b/posts/dead-or-resting.mdwn
new file mode 100644
index 0000000..1a8b1ed
--- /dev/null
+++ b/posts/dead-or-resting.mdwn
@@ -0,0 +1,53 @@
+[[!meta date="Wed, 24 Jan 2018 12:00:07 +0000"]]
+[[!meta title="Is it dead, or is it just resting?"]]
+[[!meta author="Daniel Silverstone"]]
+
+There are any number of metrics, heuristics, entrail-reading techniques, etc.
+which can be used to determine whether or not a project is "alive" for a
+variety of meanings of that word.  Depending on what you need to use a project
+for, you might need to make such an assessment and so this week I'd like to
+give you a few ways to assess a software project's "liveness".  In addition,
+you might be a project developer who needs to assess the "liveness" of
+something you're working on yourself. (For example, to check it's well organised
+for your expected user-base).
+
+If you're trying to use a project in a production situation, then you likely
+want to look to whether the codebase is relatively stable, whether it makes
+sensibly structured releases with some kind of semantic versioning, what its
+tracker looks like (relatively few open *BUGS*, short times between bug reports
+and fixes), and what its documentation looks like.  You may also look for
+evidence that others rely on the project, but that's perhaps less important.
+It's fine for the codebase to have a lot of churn providing there seems to be
+some commitment to a stable/reliable interface (be that a CLI or an API).
+
+If, on the other hand, you're looking for a project to use in a fairly
+experimental situation, you may be looking for a project whose developers are
+very active and responding well to feature requests, bug reports, etc., however,
+here the number of open bugs is much less relevant than the way in which the
+developers react to new uses of their codebase.
+
+If you're looking for a project to contribute to, then assuming you've already
+determined your own interest in the project to be high, I'd suggest that you
+look to the developers/community: how active (or otherwise) they are, how
+well they respond to comments/queries/issues/bugs etc..  A project which is
+beautifully written but whose developers take months over responding to simple
+queries might not be suitable for you to join.  On the other hand, a nicely
+written project whose developers are essentially absent is very much ripe for
+an offer of adoption and/or simple forking.
+
+(As a corollary, if you're a project developer and someone turns up looking at
+your project and you know it's not right for them,
+[consider pointing them somewhere else](http://blog.petdance.com/2018/01/02/the-best-open-source-project-for-someone-might-not-be-yours-and-thats-ok/).)
+
+So, in summary, useful things to look at include:
+
+* The churn in the project codebase
+* Whether the project has adopted semantic versioning
+* Whether the project makes lots of bugfix releases or seems stable
+* How well the project team responds to new issues/bugs
+* How well the project accepts contributions.
+
+This week your homework is to assess a project you're part of, with the eyes of
+someone who might be coming to it to use/contribute-to.  See if you can find
+ways you could improve your own projects, and give it a go.
+

publishing
diff --git a/posts/msg-trunc.mdwn b/posts/msg-trunc.mdwn
new file mode 100644
index 0000000..3e83a5c
--- /dev/null
+++ b/posts/msg-trunc.mdwn
@@ -0,0 +1,127 @@
+[[!meta date="Wed, 17 Jan 2018 12:21:03 +0000"]]
+[[!meta title="MSG_TRUNC is poorly supported"]]
+[[!meta author="Richard Maw"]]
+[[!tag C Linux syscall python]]
+
+`ssize = recv(fd, NULL, 0, MSG_PEEK|MSG_TRUNC)`
+lets you see how big the next message in a UDP or UNIX socket's buffer is.
+
+This can be important
+if your application-level communications can support variable message sizes,
+since you need to be able to provide a buffer large enough for the data,
+but preferrably not too much larger that it wastes memory.
+
+Unfortunately, a lot of programming languages' bindings don't make this easy.
+
+---
+
+Python's approach is to allocate a buffer of the provided size,
+and then reallocate to the returned size afterwards,
+and return the buffer.
+
+This behaviour is intended to permit a larger allocation to begin with,
+but as a side-effect it also permits a smaller one to not break horribly.
+
+Well, mostly. In Python 2 it breaks horribly.
+
+[[!format sh """
+$ python -c 'import socket
+s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
+s.bind("/tmp/testsock")
+s.recv(0, socket.MSG_PEEK|socket.MSG_TRUNC)'
+Traceback (most recent call last):
+  File "<string>", line 4, in <module>
+SystemError: ../Objects/stringobject.c:3909: bad argument to internal function
+"""]]
+
+Python 3 instead returns an empty buffer immediately before reading the socket.
+
+[[!format sh """
+$ python3 -c 'import socket
+s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
+s.bind("/tmp/testsock")
+m = s.recv(0, socket.MSG_PEEK|socket.MSG_TRUNC)
+print(len(m), m)'
+0 b''
+"""]]
+
+You can work around this by receiving a minimum length of 1.
+
+[[!format sh """
+$ python -c 'import socket
+s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
+s.bind("/tmp/testsock")
+m = s.recv(1, socket.MSG_PEEK|socket.MSG_TRUNC)
+print(len(m), m)'
+(4, 'a\x00n\x00')
+"""]]
+
+The returned buffer's length is that of the message,
+though most of the buffer's contents is junk.
+
+The reason these interfaces aren't great
+is that they return an object rather than using a provided one,
+and it would be unpleasant for it to return a different type based on its flags.
+
+Python has an alternative interface in the form of [socket.recv_into][],
+which should fare better, since it can return the size separately,
+it should be able to translate a `None` buffer into a `NULL` pointer.
+
+[[!format sh """
+$ python -c 'import socket
+s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
+s.bind("/tmp/testsock")
+m = s.recv_into(None, 0, socket.MSG_PEEK|socket.MSG_TRUNC)
+print(m)'
+Traceback (most recent call last):
+  File "<string>", line 4, in <module>
+TypeError: recv_into() argument 1 must be read-write buffer, not None
+"""]]
+
+Unfortunately, this proves not to be the case.
+
+In Python 2 we can re-use a "null byte array" for this purpose.
+
+[[!format sh """
+$ python -c 'import socket
+s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
+s.bind("/tmp/testsock")
+nullbytearray = bytearray()
+m = s.recv_into(nullbytearray, 0, socket.MSG_PEEK|socket.MSG_TRUNC)
+print(m)'
+4
+"""]]
+
+Unfortunately, Python 3 decided to be clever.
+
+[[!format sh """
+$ python3 -c 'import socket
+s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
+s.bind("/tmp/testsock")
+nullbytearray = bytearray()
+m = s.recv_into(nullbytearray, 0, socket.MSG_PEEK|socket.MSG_TRUNC)
+print(m)'
+0
+"""]]
+
+Like with plain `recv` it returns early without waiting for the message.
+
+I had hoped to provide a counter-example of a programming language
+that provided a way to expose this as part of its standard library.
+
+Distressingly, the best behaved standard libraries
+were the ones that exposed the system calls directly
+with all the lack of safety guarantees that implies.
+
+---
+
+In conclusion, `MSG_TRUNC` is a thing you can do on Linux.
+
+If you want to use it from a higher-level language than C
+you won't find a good interface for it in the standard library.
+
+If you find yourself in a position to design a language,
+please bear in mind people may want to do this on Linux,
+so at least provide access to the un-mangled system call interface.
+
+[socket.recv_into]: https://docs.python.org/3/library/socket.html#socket.socket.recv_into

publishing
diff --git a/posts/when-to-retire.mdwn b/posts/when-to-retire.mdwn
new file mode 100644
index 0000000..8548dab
--- /dev/null
+++ b/posts/when-to-retire.mdwn
@@ -0,0 +1,32 @@
+[[!meta date="Wed, 10 Jan 2018 12:00:09 +0000"]]
+[[!meta title="Knowing when to retire from a project"]]
+[[!meta author="Lars Wirzenius"]]
+
+A few months ago we published an article on
+[[how to retire|/posts/retiring/]]. It was followed by my decision to
+[retire Obnam](https://blog.liw.fi/posts/2017/08/13/retiring_obnam).
+Today, we will discuss how you know you should retire from a free
+software project.
+
+Here are some possible reasons:
+
+* It's no longer fun.
+* You no longer have time for it.
+* It's no longer a useful project.
+* It requires hardware you no longer have.
+* You don't like the technical decisions made in the project and
+  changing them now would be too much work.
+* The project has no users anymore.
+* You have a falling out with the other contributors.
+* You just don't want to work on the project anymore.
+* You win the lottery and can't be bothered to do anything anymore.
+* You become enlightened and realise that computers are preventing you
+  from becoming happy, so you give away everything you have, and move
+  into a cave to meditate.
+* The project is finished and requires no more work: it has all the
+  features its users need, and all bugs have been fixed.
+
+You may also like the talk [Consensually doing things together?][] by
+Enrico Zini from the 2017 Debconf.
+
+[Consensually doing things together?]: https://debconf17.debconf.org/talks/92/

Update: liw's bio page
diff --git a/bio/liw.mdwn b/bio/liw.mdwn
index 690acf3..963408f 100644
--- a/bio/liw.mdwn
+++ b/bio/liw.mdwn
@@ -1,9 +1,8 @@
 # Lars Wirzenius
 
-Lars Wirzenius is a Senior Software Developer at Codethink, a Linux
-Foundation and GENIVI Alliance associate member providing advanced
-software engineering primarily for mobile, embedded, IVI and cloud
-infrastructure projects.
+Lars Wirzenius is CTO at QvarnLabs Ab, a company developing Qvarn,
+software for storing structured data and providing secure, controlled
+access to it.
 
 Lars studied Computer Science at Helsinki University with Linus
 Torvalds, made the first patch to the Linux kernel, co-founded the Linux

publishing
diff --git a/posts/isolation.mdwn b/posts/isolation.mdwn
new file mode 100644
index 0000000..cd018dc
--- /dev/null
+++ b/posts/isolation.mdwn
@@ -0,0 +1,98 @@
+[[!meta date="Wed, 03 Jan 2018 12:00:10 +0000"]]
+[[!meta title="Isolation — when it's good to be alone…"]]
+[[!meta author="Daniel Silverstone"]]
+
+Today I'd like to talk to you about isolation.  Not, as some of you might
+expect given the topics of my more recent ramblings, about personal isolation -
+but about software isolation.
+
+Software isolation means different things to different people; from something
+as common as
+[process isolation](https://en.wikipedia.org/wiki/Process_isolation) which
+happens on almost every computing platform in the modern world, to something
+less common such virtual machines provided by
+[hypervisor](https://en.wikipedia.org/wiki/Hypervisor)s.  For today, let's
+focus on stuff closer to the latter than the former.
+
+Isolating software has, as in all things, both pros and cons.  Briefly, some of
+the reasons you might want to not consider software isolation may include:
+
+1. It's generally harder work to write software to be isolated easily.
+2. Each isolation point is "yet more" software which either has to be written
+   or at least has to be managed by your operations team.
+3. If you take a complex system, and distribute it among isolated systems, then
+   you have a more complex system than before.
+
+But, since I'm not going to be a
+[Danny Downer](https://en.wikipedia.org/wiki/Debbie_Downer) today, let's think
+more about the benefits.  Here are three benefits to counter the detractions
+I listed above.
+
+1. An isolated software system must have well defined inputs and outputs which
+   can be more effectively documented, mocked, and tested.  Leading to:
+    1. More easily tested and verified
+    2. Easier to debug
+    3. Easier to properly document
+2. An isolated software system cannot interfere indirectly with any other
+   software system sharing the same hardware or other infrastructure.
+3. A system comprised of multiple isolated intercommunicating software systems
+   can be more effectively distributed and thus horizontally scaled.
+
+There are any number of different ways to isolate your software systems from
+one another.  These range in "weight" and "completeness of isolation":
+
+* At the "lightest weight" end of things, we're looking at software written in
+  languages which support virtual environments.  This might be Python's
+  [venv](https://docs.python.org/3/library/venv.html), Ruby's
+  [rvm](https://rvm.io/), or something like
+  [luaenv](https://github.com/cehoffman/luaenv) for Lua.  These give you only
+  as much isolation as the language's virtual environment support extends to
+  (typically just for the dependencies provided in the language itself).
+
+* Next we might consider [chroot](https://en.wikipedia.org/wiki/Chroot)s which
+  give you filesystem isolation but very little else.  This lets you keep the
+  non-language-specific dependencies isolated too, since effectively a chroot
+  is able to be anything from "just enough to run your app" up to a full
+  install of the "user space" of your operating system.
+
+* Building from the `chroot` context, we can reach for a little more isolation
+  in the form of _containers_. On Linux, these can be managed by such tools as
+  [Docker](https://www.docker.com/), [LXC](https://linuxcontainers.org/),
+  [systemd-nspawn](https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html),
+  or [rkt](https://coreos.com/rkt/).  This approach uses the Linux kernel
+  [namespacing facilities](https://en.wikipedia.org/wiki/Linux_namespaces) to
+  provide further isolation between containers than the above options can do.
+
+* If containers don't provide enough isolation for you, then you might proceed
+  to hypervisors such as [kvm](https://www.linux-kvm.org/page/Main_Page) via
+  [libvirt](https://libvirt.org/), or if you're in a desktop situation then
+  perhaps [virtualbox](https://www.virtualbox.org/).  There're plenty more
+  hypervisors out there though.  Hypervisors isolate at the "hardware" level.
+  They effectively provide independently operating virtual hardware elements in
+  the form of emulated hard drives, video cards, etc.  Such that the kernel and
+  underlying operating system run just as though they were on real hardware.
+
+* The final isolation option if not even hypervisors cut it for you, is, of
+  course, simply to have more than one computer and run each piece of your
+  software system on a different machine.
+
+There are any number of software packages out there to help you manage your
+isolated software environments.  From
+[schroot](https://wiki.debian.org/Schroot) for chroots, through the container
+providers linked above, and on to hypervisor options.  But what's even more
+exciting, as you start to think about scaling your software horizontally, is
+that there's also any number of providers out there offering services where
+they look after the hardware and the management layer, and you get to deploy
+isolated software elements onto their platform (usually for a fee).  These
+might be hypervisor based virtualisation on Amazon AWS, or any number of
+providers using [Openstack](https://www.openstack.org/).  Where it gets taken
+to the next level though is that those providers are starting to offer a range
+of isolation techniques, from containers upward, so whatever your needs, there
+will be software and providers out there to help you with software isolation.
+
+Your homework… (What?  You surely didn't think that just because it's a new
+year you wouldn't get any homework on the first day?) …is to think about any
+software systems you're responsible for, and whether they might benefit from
+having their elements isolated from one another more effectively, so that you
+might glean some of the benefits of isolation.
+

calendar update
diff --git a/archives/2018.mdwn b/archives/2018.mdwn
new file mode 100644
index 0000000..e62c64e
--- /dev/null
+++ b/archives/2018.mdwn
@@ -0,0 +1 @@
+[[!calendar type=year year=2018 pages="page(posts/*) and !*/Discussion"]]
diff --git a/archives/2018/01.mdwn b/archives/2018/01.mdwn
new file mode 100644
index 0000000..21c038d
--- /dev/null
+++ b/archives/2018/01.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=01 year=2018 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(01) and creation_year(2018) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2018/02.mdwn b/archives/2018/02.mdwn
new file mode 100644
index 0000000..e6db08f
--- /dev/null
+++ b/archives/2018/02.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=02 year=2018 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(02) and creation_year(2018) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2018/03.mdwn b/archives/2018/03.mdwn
new file mode 100644
index 0000000..3c67a9f
--- /dev/null
+++ b/archives/2018/03.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=03 year=2018 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(03) and creation_year(2018) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2018/04.mdwn b/archives/2018/04.mdwn
new file mode 100644
index 0000000..89e91b0
--- /dev/null
+++ b/archives/2018/04.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=04 year=2018 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(04) and creation_year(2018) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2018/05.mdwn b/archives/2018/05.mdwn
new file mode 100644
index 0000000..76556e2
--- /dev/null
+++ b/archives/2018/05.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=05 year=2018 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(05) and creation_year(2018) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2018/06.mdwn b/archives/2018/06.mdwn
new file mode 100644
index 0000000..7244cd0
--- /dev/null
+++ b/archives/2018/06.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=06 year=2018 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(06) and creation_year(2018) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2018/07.mdwn b/archives/2018/07.mdwn
new file mode 100644
index 0000000..b7659d6
--- /dev/null
+++ b/archives/2018/07.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=07 year=2018 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(07) and creation_year(2018) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2018/08.mdwn b/archives/2018/08.mdwn
new file mode 100644
index 0000000..2044f98
--- /dev/null
+++ b/archives/2018/08.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=08 year=2018 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(08) and creation_year(2018) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2018/09.mdwn b/archives/2018/09.mdwn
new file mode 100644
index 0000000..ed51cd6
--- /dev/null
+++ b/archives/2018/09.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=09 year=2018 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(09) and creation_year(2018) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2018/10.mdwn b/archives/2018/10.mdwn
new file mode 100644
index 0000000..f916789
--- /dev/null
+++ b/archives/2018/10.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=10 year=2018 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(10) and creation_year(2018) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2018/11.mdwn b/archives/2018/11.mdwn
new file mode 100644
index 0000000..253e46a
--- /dev/null
+++ b/archives/2018/11.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=11 year=2018 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(11) and creation_year(2018) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2018/12.mdwn b/archives/2018/12.mdwn
new file mode 100644
index 0000000..bc7c9c6
--- /dev/null
+++ b/archives/2018/12.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=12 year=2018 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(12) and creation_year(2018) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]

creating tag page tags/productivity
diff --git a/tags/productivity.mdwn b/tags/productivity.mdwn
new file mode 100644
index 0000000..9e5017e
--- /dev/null
+++ b/tags/productivity.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged productivity"]]
+
+[[!inline pages="tagged(productivity)" actions="no" archive="yes"
+feedshow=10]]

publishing
diff --git a/posts/productivity-boosters.mdwn b/posts/productivity-boosters.mdwn
new file mode 100644
index 0000000..405c8f7
--- /dev/null
+++ b/posts/productivity-boosters.mdwn
@@ -0,0 +1,78 @@
+[[!meta date="Thu, 28 Dec 2017 12:00:08 +0000"]]
+[[!meta title="Programmer productivity boosters"]]
+[[!meta author="Lars Wirzenius"]]
+[[!tag productivity tools]]
+
+When I was young and has just started to learn programming, I read
+some descriptions of what programming was like as a job. These fell
+roughly into one of two categories:
+
+* long meetings, and writing a lot of documentation
+* long days of furious hacking fuelled by chemicals
+
+That was decades ago. I have since learn from experience that a
+programmer's productivity is best boosted by:
+
+* knowing clearly what to do
+  - a well-defined goal is easier to reach than one that is unclear or
+    changes all the time
+* knowing one's tools and being comfortable with them
+  - the exploration phase of using a new tool is rarely very productive
+* not being interrupted while doing it
+  - being in the [flow][] is commonly preferred, but may or may not be
+    necessary; however, frequent interruptions are anti-productive in
+    any case
+* getting enough sleep
+  - continuous yawning is distracting, concentration is difficult if
+    your brain is hallucinating from lack of sleep
+* avoidance of bad stress
+  - stress that makes you worry about the future is bad
+* having a reliable source of [lapsang][] tea
+  - but I need to stop drinking it at least six hours before bedtime,
+    or I sleep badly
+* a comfortable work environment
+  - see our [previous article][]
+
+It further helps to use various tools that allow the programmer
+concentrate on cranking out the code:
+
+* automated language checkers
+  - statically types languages require you to get types right, and
+    will catch you if you mistype a variable name
+  - static checkers for dynamically typed languages can often do the
+    same job
+  - either way, they help you catch errors earlier, which means you
+    spend less time waiting for an error to happen
+* automated tests
+  - unit tests, integration tests, etc, allow you to avoid spending
+    time and effort on doing the same tests manually over and over
+* reuse of high-quality code
+  - this means libraries, code generators, tools you can run, etc
+  - these save you time by not having to write the code yourself
+  - they further save time by not having to debug the code you wrote
+  - however, beware of low-quality code; it won't save you time, since
+    you'll almost certainly have to debug and fix it yourself
+* automated systems to run your automated tests
+  - [continuous integration][] runs without you having to remember to
+    run your tests
+  - it also runs them in the background, meaning you don't need to
+    wait for them to finish, which is especially useful when you have
+    slow tests
+  - the tests also get run in a well-defined environment, rather than
+    the developer's randomly configured ever-changing laptop
+* automated system for installing software on target machines
+  - [continuous delivery][] takes care of installing your software
+    somewhere you can use it, which may or may not be the production
+    server
+  - this is especially useful for server, web, and command line
+    software, but is generally useful
+  - often the process of installing manually is tedious - just like
+    doing manual testing - so the more it's automated the less time
+    you waste on repetitive work
+
+
+[Lapsang]: https://en.wikipedia.org/wiki/Lapsang_souchong
+[flow]: https://en.wikipedia.org/wiki/Flow_(psychology)
+[continuous integration]: https://en.wikipedia.org/wiki/Continuous_integration
+[continuous delivery]: https://en.wikipedia.org/wiki/Continuous_delivery
+[previous article]: https://yakking.branchable.com/posts/working-environment/

Fix from dks
diff --git a/posts/working-environment.mdwn b/posts/working-environment.mdwn
index 0463db0..ad3d15c 100644
--- a/posts/working-environment.mdwn
+++ b/posts/working-environment.mdwn
@@ -12,7 +12,7 @@ about how my heating boiler died at home and I had to spend two weeks with a
 computer room which was consistently below 10 degrees celsius unless I spent a
 lot of electricity heating it up.  Sitting in the cold at my keyboard was
 difficult.  You can put jumpers on, and generally keep yourself warm, but for
-at least some of, our hands still get cold, and that can be quite unpleasant
+at least some of us, our hands still get cold, and that can be quite unpleasant
 when you're a keyboard worker.
 
 The temperature of your working/hacking/playing environment will affect your

publishing
diff --git a/posts/working-environment.mdwn b/posts/working-environment.mdwn
new file mode 100644
index 0000000..0463db0
--- /dev/null
+++ b/posts/working-environment.mdwn
@@ -0,0 +1,55 @@
+[[!meta date="Wed, 13 Dec 2017 12:00:10 +0000"]]
+[[!meta title="Your working environment matters"]]
+[[!meta author="Daniel Silverstone"]]
+
+We've [previously][] spoken about how your workstation area matters, mentioning
+topics such as consistency in input surfaces, arrangement of screens, good
+chairs, etc.  Today I'd like to extend that to talk about the general
+environment in which you're working/hacking/playing.
+
+As with all these little articles, I have a story to tell, and this one is
+about how my heating boiler died at home and I had to spend two weeks with a
+computer room which was consistently below 10 degrees celsius unless I spent a
+lot of electricity heating it up.  Sitting in the cold at my keyboard was
+difficult.  You can put jumpers on, and generally keep yourself warm, but for
+at least some of, our hands still get cold, and that can be quite unpleasant
+when you're a keyboard worker.
+
+The temperature of your working/hacking/playing environment will affect your
+effectiveness dramatically.  If it's too cold, or too warm, you will not be
+able to think properly and your reaction times will grow.  Keeping the area
+around your workstation clear of clutter will also improve your ability to
+think, though some do benefit from having one or two fiddling things for when
+you need to do something with your hands while your brain wanders off to solve
+the problem.
+
+I recommend ensuring that there's not too much in the way of noise distraction
+at your workstation too.  Music is something that some of us find helps, as can
+a [white noise generator][] or [rain generator][].  I sometimes use an
+[internet cat][] to help me relax, but it's important to try and block out
+sounds which you cannot predict or control such as building site noise, other
+people talking, etc.
+
+For me, it's also important to try and eliminate unexpected or unpleasant
+scents from your workstation too.  I find that I cannot concentrate if I can
+smell food (sometimes a big issue because my neighbours love to fry onions at
+times of day when I'm hungry).
+
+Keep hydrated.  While that's not entirely an environmental thing, ensuring that
+you have an adequate supply of water, squash, tea, or whatever you prefer, is
+very important.  Bad hydration causes your brain to behave similarly to being
+drunk or super-tired.  Obviously if you're going to keep hydrated, don't try
+and sit and work through a full bladder, that can be just as
+distracting/unhelpful.
+
+Your homework for this week is to look critically at your work/hack/play
+environment and make at least one small change for the better.  Perhaps you
+could comment below about a change you made and how you feel it has affected
+you, either positively or negatively.  If you have other tips for improving
+your work/hack/play space then also include them below.
+
+
+[previously]: /posts/work-hack-play/
+[white noise generator]: https://mynoise.net/NoiseMachines/whiteNoiseGenerator.php
+[rain generator]: https://mynoise.net/NoiseMachines/rainNoiseGenerator.php
+[internet cat]: https://purrli.com/

publishing
diff --git a/posts/property-testing-in-c.mdwn b/posts/property-testing-in-c.mdwn
new file mode 100644
index 0000000..ba01355
--- /dev/null
+++ b/posts/property-testing-in-c.mdwn
@@ -0,0 +1,216 @@
+[[!meta date="Wed, 06 Dec 2017 12:00:07 +0000"]]
+[[!meta title="Property Testing in C"]]
+[[!meta author="Richard Ipsum"]]
+
+One nice thing about [Haskell][] is [Quickcheck][], if you're not familiar with
+Haskell or Quickcheck it works like this:
+
+[[!format haskell """
+Prelude> let evens xs = [ x | x <- xs, x `mod` 2 == 0 ]
+Prelude> evens [1..10]
+[2,4,6,8,10]
+"""]]
+
+Here we define a function that takes a list of numbers and returns only
+the even numbers. We can use Quickcheck to test this function:
+
+[[!format haskell """
+Prelude> import Test.QuickCheck
+Prelude Test.QuickCheck> let test_evens xs = [ x | x <- evens xs, x `mod` 2 /= 0 ] == []
+Prelude Test.QuickCheck> quickCheck test_evens
++++ OK, passed 100 tests.
+"""]]
+
+Here we define a test function that asserts that a list of even numbers
+shouldn't contain any odd ones. Passing this function to Quickcheck
+shows that the function passed 100 tests. Quickcheck sees that our
+`test_evens` function takes a list of numbers and returns a boolean, and
+it tests our code by generating a set of random inputs and executing it
+with those inputs.
+
+Clearly this type of testing can be very useful, and not just for
+Haskell programs. [Theft](https://github.com/silentbicycle/theft) is a C
+library that brings Quickcheck style testing to C. This sort of testing
+is known as [Property Testing][].
+
+Let's reimplement our Haskell code in C,
+
+[[!format c """
+#include <stdio.h>
+#include <stdlib.h>
+#include <theft.h>
+
+struct IntArray {
+    int len;
+    int arr[];
+};
+
+struct IntArray *evens(struct IntArray *input)
+{
+    int nevens = 0;
+    struct IntArray *output;
+
+    if (input == NULL) {
+        return NULL;
+    }
+
+    output = malloc(sizeof (struct IntArray) + input->len * sizeof(int));
+
+    if (output == NULL) {
+        return NULL;
+    }
+
+    for (int i = 0; i < input->len; i++) {
+        if (input->arr[i] % 2 == 0) {
+            output->arr[nevens++] = input->arr[i];
+        }
+    }
+
+    output->len = nevens;
+    return output;
+}
+"""]]
+
+Here we define a function that takes an array of integers and outputs an
+array of even integers.
+
+Now let's do some testing!
+
+Since C is not strongly typed like Haskell we need to define a function
+that describes what a test input should look like. Providing this
+information to Theft will allow it to generate real test input data. The
+function should have the following prototype,
+
+[[!format c """
+enum theft_alloc_res allocate_int_array(struct theft *, void *, void **)
+"""]]
+
+Let's write it!
+
+[[!format c """
+enum theft_alloc_res allocate_int_array(struct theft *t, void *data, void **result)
+{
+    int SIZE_LIMIT = 100;
+
+    int size = theft_random_choice(t, SIZE_LIMIT);
+
+    struct IntArray *numbers = malloc(sizeof (struct IntArray) + size * sizeof(int));
+
+    if (numbers == NULL) {
+        return THEFT_ALLOC_ERROR;
+    }
+
+    for (int i = 0; i < size; i++) {
+        numbers->arr[i] = theft_random_choice(t, INT_MAX);
+    }
+
+    numbers->len = size;
+
+    *result = numbers;
+
+    return THEFT_ALLOC_OK;
+}
+"""]]
+
+`theft_random_choice` is a function that will pick a random number
+between 0 and some defined limit. The result is not truly random, but
+instead based on the complexity of the input Theft requires. The
+documentation for Theft points out that the main thing with this is to
+ensure that wherever `theft_random_choice` returns 0 our
+`alloc_int_array` function should return the simplest input possible, in
+our case that would be an empty array.
+
+Theft passes a reference pointer to the `alloc_int_array` function, this
+must be updated to point to the array we have allocated before the
+function returns with `THEFT_ALLOC_OK`. In the event of some kind of
+error the function should return `THEFT_ALLOC_ERROR`
+
+Next we write the property function, this function takes an input
+array of integers generated by Theft, runs our `evens` function over that input and
+asserts that the resultant output doesn't contain any odd numbers.
+
+[[!format c """
+enum theft_trial_res property_array_of_evens_has_no_odd_numbers(struct theft *t, void *test_input)
+{
+    struct IntArray *test_array = test_input;
+
+    struct IntArray *result = evens(test_array);
+
+    // Array of even numbers should not contain any odd numbers
+    for (int i = 0; i < result->len; i++) {
+        if (result->arr[i] % 2 != 0) {
+            return THEFT_TRIAL_FAIL;
+        }
+    }
+
+    return THEFT_TRIAL_PASS;
+}
+"""]]
+
+Putting this together, we define some boiler plate to cover the various
+functions we just defined for generating test inputs,
+
+[[!format c """
+struct theft_type_info random_array_info = {
+    .alloc = allocate_int_array,
+    .free = theft_generic_free_cb,
+    .autoshrink_config = {
+        .enable = true,
+    }
+};
+"""]]
+
+The `alloc` member is updated to point to the function we just defined.
+Since the test inputs are dynamically allocated with `malloc` they will need
+to be freed later on. Theft provides a generic function for freeing
+which is sufficient for our purposes: `theft_generic_free_cb`.
+
+The last member of this structure needs more explanation. If Theft
+encounters an input which causes the test to fail, it will try to pare
+down the input to the smallest input that causes failure; this
+is called shrinking.
+
+Theft lets you define a function that can provide some control over the
+shrinking process, or it can use its own shrinking functions:
+autoshrinking. If autoshrinking is used however, the function that
+allocates test inputs must base the complexity of the input it generates
+upon the result of one of the `theft_random` functions, such as
+`theft_random_bits`, or `theft_random_choice`. This is why our
+`alloc_int_array` function uses `theft_random_choice` rather than
+standard pseudo random number generating functions.
+
+Finally we write a function to execute the tests,
+
+[[!format c """
+int main(void)
+{
+    theft_seed seed = theft_seed_of_time();
+    struct theft_run_config config = {
+        .name = __func__,
+        .prop1 = property_array_of_evens_has_no_odd_numbers,
+        .type_info = { &random_array_info },
+        .seed = seed
+    };
+

(Diff truncated)
publishing
diff --git a/posts/aoc-2017.mdwn b/posts/aoc-2017.mdwn
new file mode 100644
index 0000000..388e3e2
--- /dev/null
+++ b/posts/aoc-2017.mdwn
@@ -0,0 +1,39 @@
+[[!meta date="Wed, 29 Nov 2017 12:00:07 +0000"]]
+[[!meta title="Advent Of Code, 2017"]]
+[[!meta author="Daniel Silverstone"]]
+
+[Previously][] I mentioned the [Advent of Code][] as a possible thing you might
+want to look at for using as a way to learn a new language in a fun and
+exciting way during December.
+
+This year, it'll be running again, and I intend to have a go at it again in
+[Rust][] because I feel like I ought to continue my journey into that language.
+
+It's important to note, though, that I find it takes me between 30 and 90
+minutes **per day** to engage properly with the problems, and frankly the
+30 minute days are far more rare than the 90 minute ones.  As such, I urge
+you to not worry if you cannot allocate the time to take part every day.  Ditto
+if you start and then find you cannot continue, do not feel ashamed.  Very few
+Yakking readers are as lucky as I am in having enough time to myself to take
+part.
+
+However, if you can give up the time, and you do fancy it, then join in and if
+you want you can join my private leaderboard and not worry so much about
+competing with the super-fast super-clever people out there who are awake at
+midnight Eastern time (when the problems are published).  If you want to join
+the leaderboard (which contains some [Debian][] people, some [Codethink][]
+people, and hopefully by now, some Yakking people) then you will need (after
+joining the AoC site) to go to the private leaderboard section and enter the
+code: `69076-d4b54074`.  If you're really enthusiastic, and lucky enough to be
+able to afford it, then support AoC via their `AoC++` page with a few dollars
+too.
+
+Regardless of whether you join in with AoC or not, please remember to always
+take as much pleasure as you can in your coding opportunities, however they may
+present themselves.
+
+[Previously]: /posts/code-for-fun/
+[Advent of Code]: https://adventofcode.com/
+[Rust]: https://www.rust-lang.org/
+[Debian]: https://www.debian.org/
+[Codethink]: https://www.codethink.co.uk/

creating tag page tags/practices
diff --git a/tags/practices.mdwn b/tags/practices.mdwn
new file mode 100644
index 0000000..0ceb7e2
--- /dev/null
+++ b/tags/practices.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged practices"]]
+
+[[!inline pages="tagged(practices)" actions="no" archive="yes"
+feedshow=10]]

publishing
diff --git a/posts/use-early.mdwn b/posts/use-early.mdwn
new file mode 100644
index 0000000..258e205
--- /dev/null
+++ b/posts/use-early.mdwn
@@ -0,0 +1,55 @@
+[[!meta date="Wed, 22 Nov 2017 12:00:08 +0000"]]
+[[!meta title="Start using your new project early"]]
+[[!meta author="Lars Wirzenius"]]
+[[!tag new-project practices]]
+
+You've started a new project. When should you start using it "in
+anger", "for real", "in production"? My advice is to do so as early as you can.
+
+I did this for my newest project. I've been developing it slowly, and
+things had matured enough that it could now actually do things. I set
+up two instances doing things that I, and my company, now rely on. If
+the software breaks, I will need to take action (disable the broken
+instances, do things in another way until I fix the software).
+
+The point of doing this early is that it gives me quick feedback on
+whether the software works at all, and makes it easy to add new
+features, and makes it easier for others to try out the software.
+(When I announce it publically, which I'm not yet doing.)
+
+* I see quickly if something doesn't work. (For now, it does.)
+* I see at once if there's a missing feature that I urgently need. (I
+  have found two bugs yesterday.)
+* I see what is awkward and cumbersome for configuring, deploying the
+  software. (So many interacting components.)
+* I see if it's nice to actually use. (I need to find someone to write
+  a web interface, and need to improve the command line tool.)
+* I see if the performance is adequate and get an idea what the actual
+  resource requirements are. (Not many resources needed for now. Even
+  the cheapest VM I could choose is adequate.)
+* I get more confident in the software the more I actually use it.
+  Writing a test suite is good, but real use is better. Real use
+  always comes up with things you didn't think about writing tests
+  for.
+
+In order to set up not just one but two instances, I had to make the
+deployment automated. (I'm that lazy, and I don't apologise for
+that.)
+
+Thanks to an automated setup, when I add features or fix bugs,
+they're easy to roll out. It's now almost as easy as just running
+the program from the source tree.
+
+My development process is now, I write tests; I write code; tests
+pass; I tag a release; I let CI build it; and I run Ansible to upgrade
+everywhere. About 15 seconds of work once the tests pass, though it
+takes a couple of minutes of wall-clock time, since I run CI on my
+laptop.
+
+Apart from the benefits that come from the features of the
+software itself, getting to this stage is emotionally very rewarding. In
+one day, my little pet project went from "I like this idea" to "it's a
+thing".
+
+I recommend you start using your stuff in production earlier rather
+than later. Do it now, do it every day.

Fix formatting error
diff --git a/posts/data-processing-commands.mdwn b/posts/data-processing-commands.mdwn
index f1cd8e8..2481671 100644
--- a/posts/data-processing-commands.mdwn
+++ b/posts/data-processing-commands.mdwn
@@ -70,6 +70,7 @@ It is not possible to define a character *sequence* as a delimiter.
     Try `cut --help' for more information.
 
 For this more complicated tools need to be used.
+
     $ awk <testdata '{ split($0, A, /->/); print A[2] }'
     b
 

Added a comment: Re: cut example and uniq option
diff --git a/posts/data-processing-commands/comment_2_c49a7b99f7bda35247299f9b5bb72387._comment b/posts/data-processing-commands/comment_2_c49a7b99f7bda35247299f9b5bb72387._comment
new file mode 100644
index 0000000..aa47c6f
--- /dev/null
+++ b/posts/data-processing-commands/comment_2_c49a7b99f7bda35247299f9b5bb72387._comment
@@ -0,0 +1,17 @@
+[[!comment format=mdwn
+ username="http://richard.maw.name/"
+ nickname="Richard Maw"
+ avatar="http://cdn.libravatar.org/avatar/4d312a0989b085a9dfa9ba61d60da130979b9c408659132fea11d5efe2c90fc5"
+ subject="Re: cut example and uniq option"
+ date="2017-11-20T13:33:51Z"
+ content="""
+Thanks for your comment.
+
+Yes it's missing content because of a formatting error in the source (there should be an empty line before the code block).
+The output that it was supposed to have is:
+
+    $ awk <testdata '{ split($0, A, /->/); print A[2] }'
+    b
+
+I'll get it fixed.
+"""]]

Added a comment: cut example and uniq option
diff --git a/posts/data-processing-commands/comment_1_b2c5598f34dc5a804fda1e17c108e98e._comment b/posts/data-processing-commands/comment_1_b2c5598f34dc5a804fda1e17c108e98e._comment
new file mode 100644
index 0000000..02422dc
--- /dev/null
+++ b/posts/data-processing-commands/comment_1_b2c5598f34dc5a804fda1e17c108e98e._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ username="riccio@2156bd061582af8c0a76482b0dc0ff28dea95cbb"
+ nickname="riccio"
+ avatar="http://cdn.libravatar.org/avatar/54e73b2de2c0c3dbe275f0621c7bfebb"
+ subject="cut example and uniq option"
+ date="2017-11-18T10:58:40Z"
+ content="""
+Hi, thanks for this great blog!
+
+Regarding the cut example: the last line says \"For this more complicated tools need to be used. $ awk /); print A[2] }' b\", I think something is missing.
+Regarding the sort/uniq paragraph: the option I find more useful of uniq is \"-c\" for counting occourrences.
+
+Thank you again,
+Riccardo
+"""]]

publishing
diff --git a/posts/blank.mdwn b/posts/blank.mdwn
new file mode 100644
index 0000000..e8af375
--- /dev/null
+++ b/posts/blank.mdwn
@@ -0,0 +1,19 @@
+[[!meta date="Wed, 15 Nov 2017 12:33:18 +0000"]]
+[[!meta title="This post intentionally left blank"]]
+[[!meta author="Lars Wirzenius"]]
+
+This post intentionally left
+[blank](https://en.wikipedia.org/wiki/Intentionally_blank_page).
+
+We ran out of Yakking articles and energy to write new ones. There's
+plenty of things to write about, but the usual Yakking writers have
+been feeling a bit under the weather lately. If you'd like to help,
+leave a comment on Yakking, or join the IRC channel `#yakking` (on the
+irc.oftc.net network), and offer an article.
+
+The previous break in the weekly schedule was December 25, 2013. That
+was due to a typo in the intended publication date: it got scheduled
+for 2103-12-25, not 2013-12-25.
+
+We hope to return back to the normal schedule. Your patience is
+appreciated. Resistance less than 4.2 Ohm is futile.

publishing
diff --git a/posts/time-cputime.mdwn b/posts/time-cputime.mdwn
new file mode 100644
index 0000000..d2189dc
--- /dev/null
+++ b/posts/time-cputime.mdwn
@@ -0,0 +1,275 @@
+[[!meta date="Wed, 08 Nov 2017 14:30:19 +0000"]]
+[[!meta title="What is Time - CPU time"]]
+[[!meta author="Richard Maw"]]
+[[!tag time]]
+
+Before everyone had a multitude of computers of their own
+computers were rare,
+and if you wanted to use one you had to share it.
+
+Given the demand for computers exceeded the supply,
+people had to share time using it.
+
+Initially you could do this with a stop watch,
+but it's better for the computer itself to be able to measure this time
+since as computers became more complicated:
+
+1.  Pre-emption, where one process can be interrupted to run another,
+    means the time take up by a program
+    isn't just the difference between when the program started and ended.
+
+2.  Multi-threading, where a program can run multiple commands simultaneously,
+    means you can use CPU time at a rate of more than one CPU second per second.
+
+Computers became so pervasive that most computer users don't need to share,
+but virtual server providers also need to account for time used,
+and CPU time can also be used to measure how long it takes to perform an
+operation for [profiling][] purposes
+so when a program is slow you know which
+part is the most worth your time to [optimise][optimization].
+
+[profiling]: https://en.wikipedia.org/wiki/Profiling_(computer_programming)
+[optimization]: https://en.wikipedia.org/wiki/Program_optimization
+
+Getting CPU time
+----------------
+
+The CPU time is read in the same way as other clocks,
+with different clock IDs for each process or thread.
+
+1.  Current process with `CLOCK_PROCESS_CPUTIME_ID`.
+
+        int ret = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time);
+
+2.  Current thread with `CLOCK_THREAD_CPUTIME_ID`.
+
+        int ret = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time);
+
+3.  Another process with [clock_getcpuclockid(3)][].
+
+        int pid_gettime(pid_t pid, struct timespec *tp) {
+            int ret;
+            clockid_t clockid;
+            ret = clock_getcpuclockid(pid, &clockid);
+            if (ret != 0) {
+                return ret;
+            }
+            ret = clock_gettime(clockid, tp);
+            return ret;
+        }
+
+4.  Another thread with [pthread_getcpuclockid(3)][].
+
+        int thread_gettime(pthread_t thread, struct timespec *tp) {
+            int ret;
+            clockid_t clockid;
+            ret = pthread_getcpuclockid(thread, &clockid);
+            if (ret != 0) {
+                return ret;
+            }
+            ret = clock_gettime(clockid, tp);
+            return ret;
+        }
+
+See [[gettime.c|gettime.c]] for an example program for reading the times,
+and [[Makefile|Makefile]] for build instructions.
+
+[clock_getcpuclockid(3)]: http://man7.org/linux/man-pages/man3/clock_getcpuclockid.3.html
+[pthread_getcpuclockid(3)]: http://man7.org/linux/man-pages/man3/pthread_getcpuclockid.3.html
+
+Profiling
+---------
+
+We can instrument code (see [[profile-unthreaded.c]])
+to see how much time a section takes to run.
+
+[[!format C """
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <time.h>
+
+static void print_time(FILE *f, struct timespec time) {
+	fprintf(f, "%lld.%09lld\n", (long long)time.tv_sec, (long long)time.tv_nsec);
+}
+
+int main(int argc, char **argv) {
+	enum {
+		ITERATIONS = 1000000,
+	};
+	int ret, exit = 0;
+	struct timespec start, end;
+
+	ret = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
+	if (ret != 0) {
+		perror("clock_gettime");
+		exit = 1;
+		goto exit;
+	}
+
+	for (int i = 0; i < ITERATIONS; i++) {
+		fprintf(stdout, "% 7d\n", i);
+	}
+
+	ret = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);
+	if (ret != 0) {
+		perror("clock_gettime");
+		exit = 1;
+		goto exit;
+	}
+
+	end.tv_sec -= start.tv_sec;
+	end.tv_nsec -= start.tv_nsec;
+	if (end.tv_nsec < 0) {
+		end.tv_sec--;
+		end.tv_sec += 1000000000l;
+	}
+
+	print_time(stderr, end);
+
+exit:
+	return exit;
+}
+"""]]
+
+[[!format sh """
+$ make profile-unthreaded
+$ ./profile-unthreaded >/tmp/f
+0.073965395
+"""]]
+
+We can make use of threads to try to speed this up (see [[profile-threaded.c]]).
+
+[[!format C """
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <time.h>
+#include <unistd.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
+
+static void print_time(FILE *f, struct timespec time) {
+	fprintf(f, "%lld.%09lld\n", (long long)time.tv_sec, (long long)time.tv_nsec);
+}
+
+struct thread_args {
+	int fd;
+	int start;
+	unsigned len;
+};
+
+void *thread_run(void *_thread_args) {
+	struct thread_args *thread_args = _thread_args;
+	char buf[9];
+	for (int i = thread_args->start;
+	     i < thread_args->start + thread_args->len; i++) {
+		ssize_t len = snprintf(buf, ARRAY_SIZE(buf), "% 7d\n", i);
+		pwrite(thread_args->fd, buf, len, i * len);
+	}
+	return NULL;
+}
+
+int main(int argc, char **argv) {
+	enum {
+		ITERATIONS = 1000000,
+		THREADS = 4,
+	};
+	int i, ret, exit = 0;
+	struct timespec start, end;
+	pthread_t threads[THREADS];
+	struct thread_args thread_args[THREADS];
+
+	ret = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
+	if (ret != 0) {
+		perror("clock_gettime");
+		exit = 1;
+		goto exit;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(threads); i++) {
+		thread_args[i].fd = 1;
+		thread_args[i].start = ITERATIONS / THREADS * i;

(Diff truncated)
Added a comment: Thanks
diff --git a/posts/communicating/comment_2_40837523a5245cf6d0387c8d27023161._comment b/posts/communicating/comment_2_40837523a5245cf6d0387c8d27023161._comment
new file mode 100644
index 0000000..9595984
--- /dev/null
+++ b/posts/communicating/comment_2_40837523a5245cf6d0387c8d27023161._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="https://liw.fi/"
+ avatar="http://cdn.libravatar.org/avatar/c9632d33d21a2acc25cd320c2dcbce25cb21ad42117dc19426c51ce1cdf042dc"
+ subject="Thanks"
+ date="2017-11-01T12:40:04Z"
+ content="""
+You're right, that would've been a better word choice. Thanks.
+"""]]

Added a comment: I know what you mean, but...
diff --git a/posts/communicating/comment_1_6ff904af66801b8e47b31866587c0ea0._comment b/posts/communicating/comment_1_6ff904af66801b8e47b31866587c0ea0._comment
new file mode 100644
index 0000000..d8232c1
--- /dev/null
+++ b/posts/communicating/comment_1_6ff904af66801b8e47b31866587c0ea0._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="lesley.g.mitchell@c6f89a60a1c23d0b27e6f1fddd7b4cd9c655ea16"
+ nickname="lesley.g.mitchell"
+ avatar="http://cdn.libravatar.org/avatar/969c5042049a7b48fcebf8020edb503a"
+ subject="I know what you mean, but..."
+ date="2017-11-01T12:22:00Z"
+ content="""
+Might I suggest rather than \"short\", you really mean that communication should be kept \"brief\" or \"concise\".
+"""]]

publishing
diff --git a/posts/communicating.mdwn b/posts/communicating.mdwn
new file mode 100644
index 0000000..817ddd4
--- /dev/null
+++ b/posts/communicating.mdwn
@@ -0,0 +1,39 @@
+[[!meta date="Wed, 01 Nov 2017 12:00:10 +0000"]]
+[[!meta title="Communicating"]]
+[[!meta author="Lars Wirzenius"]]
+[[!tag communication]]
+
+Most of software development is, in fact, communication. Even more so
+for free software projects that involve more than one person.
+Communication is an overhead, something you need to do on top of
+coding, so many hackers don't pay much attention to it. This is a
+mistake. A project whose members communicate effective gets more done
+in less time than one whose members don't. Here are some hints that
+may be useful:
+
+* use the appropriate _medium_: if you need an answer soon, ask on a
+  chat system such as IRC; if you can wait a while, use email or a
+  blog post or a web forum
+
+* be short and clear: a long, rambling question takes more time and
+  effort to read, never mind respond to, and is often less clear and
+  thus results in a less helpful answer
+
+* take responsibility of getting the problem solved: develop a way to
+  reproduce the problem, and if it's code, with the shortest, simplest
+  piece of self-standing code you can (you'll often find you find the
+  answer yourself)
+
+* make it easy to help you:
+  [explain what you really want to achieve](http://xyproblem.info/)
+  (not something else, even if you think it's easier), and what you've
+  done, and what the exact result is (use copy-paste or take a
+  screenshot)
+
+* don't be insulting, arrogant, dismissive, or aggressive: you need
+  help, don't make those you want it from not like you, or they might
+  not even try
+
+* say thank you: you'll be remembered well, and those who helped you
+  (or tried to) will have more fun and are more motivated to work for
+  free for others in the future

removed
diff --git a/posts/ethics/comment_1_286d1434399328d4e17422d38838f063._comment b/posts/ethics/comment_1_286d1434399328d4e17422d38838f063._comment
deleted file mode 100644
index ee5c2e6..0000000
--- a/posts/ethics/comment_1_286d1434399328d4e17422d38838f063._comment
+++ /dev/null
@@ -1,12 +0,0 @@
-[[!comment format=mdwn
- username="richardipsum"
- avatar="http://cdn.libravatar.org/avatar/48cd8810cb554be5891de36dd031b49c"
- subject="comment 1"
- date="2017-10-25T12:28:14Z"
- content="""
-Related to the issue of ethics in software is the ability of some companies to e.g. [swing an election].
-I'd argue that Google and Facebook can be considered ethically problematic in this respect.
-That's not to say that these companies are doing any of this intentionally ofcourse.
-
-[swing an election]: http://dci.mit.edu/assets/papers/decentralized_web.pdf
-"""]]

Revert "removed"
This reverts commit 55fb07c53f4174f837c0f14b404bea563690f977.
diff --git a/posts/ethics/comment_2_b52385572b8ee0edb495bc13186c544e._comment b/posts/ethics/comment_2_b52385572b8ee0edb495bc13186c544e._comment
new file mode 100644
index 0000000..d08cf96
--- /dev/null
+++ b/posts/ethics/comment_2_b52385572b8ee0edb495bc13186c544e._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="fa-ml@8b7c0cd18f4588039684c9065d74e687ce921e47"
+ nickname="fa-ml"
+ avatar="http://cdn.libravatar.org/avatar/9784765515033db9c353525245a41ceb"
+ subject="comment 2"
+ date="2017-10-25T13:15:15Z"
+ content="""
+> \"Does it promote $BADTHINGS, directly or indirectly?\"
+
+Slippery surface, as one can easily put GPG/TOR/GNUNet (and loosely following your example, R) in that $BADTHINGS basket.  
+A medical professional cures people regardless, a lawyer defends their client regardless: is a Free Software contributor more equipped to indulge in this deontological Pandora's box?
+"""]]

Revert "removed"
This reverts commit d78914d20de7dd384f0c41a4bd053ffa8e243fd4.
diff --git a/posts/ethics/comment_3_89c290cc731c2966a94051d1e44d619c._comment b/posts/ethics/comment_3_89c290cc731c2966a94051d1e44d619c._comment
new file mode 100644
index 0000000..c4f9a75
--- /dev/null
+++ b/posts/ethics/comment_3_89c290cc731c2966a94051d1e44d619c._comment
@@ -0,0 +1,17 @@
+[[!comment format=mdwn
+ username="lesley.g.mitchell@c6f89a60a1c23d0b27e6f1fddd7b4cd9c655ea16"
+ nickname="lesley.g.mitchell"
+ avatar="http://cdn.libravatar.org/avatar/969c5042049a7b48fcebf8020edb503a"
+ subject="Further reading on the topic"
+ date="2017-10-26T12:43:51Z"
+ content="""
+Computer/Programming/Information Ethics is a whole discipline in itself, though it is certainly a topic that every programmer really ought have some grounding in.
+
+The Wikipedia page on the topic - <https://en.wikipedia.org/wiki/Programming_ethics> - is a good place to start for more general information and some ideas about what to consider.
+
+For a more philosophical exploration of the topic, the Stanford Encyclopedia of Philosophy has some good articles, such as: <https://plato.stanford.edu/entries/ethics-computer/> and <https://plato.stanford.edu/entries/computing-responsibility/>
+
+There are also a whole range of books on the topic both from computing perspectives and more philosophical ones.
+
+The Open University also offers a free course on the subject, [Introducing Ethics in Information and Computer Sciences](http://www.open.edu/openlearn/education/educational-technology-and-practice/educational-technology/introducing-ethics-information-and-computer-sciences/content-section-0?active-tab=description-tab)
+"""]]

Revert "removed"
This reverts commit e150ad3cb96ca207e9fa93cd38ba47506e528975.
diff --git a/posts/ethics/comment_1_286d1434399328d4e17422d38838f063._comment b/posts/ethics/comment_1_286d1434399328d4e17422d38838f063._comment
new file mode 100644
index 0000000..ee5c2e6
--- /dev/null
+++ b/posts/ethics/comment_1_286d1434399328d4e17422d38838f063._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="richardipsum"
+ avatar="http://cdn.libravatar.org/avatar/48cd8810cb554be5891de36dd031b49c"
+ subject="comment 1"
+ date="2017-10-25T12:28:14Z"
+ content="""
+Related to the issue of ethics in software is the ability of some companies to e.g. [swing an election].
+I'd argue that Google and Facebook can be considered ethically problematic in this respect.
+That's not to say that these companies are doing any of this intentionally ofcourse.
+
+[swing an election]: http://dci.mit.edu/assets/papers/decentralized_web.pdf
+"""]]

removed
diff --git a/posts/ethics/comment_2_b52385572b8ee0edb495bc13186c544e._comment b/posts/ethics/comment_2_b52385572b8ee0edb495bc13186c544e._comment
deleted file mode 100644
index d08cf96..0000000
--- a/posts/ethics/comment_2_b52385572b8ee0edb495bc13186c544e._comment
+++ /dev/null
@@ -1,12 +0,0 @@
-[[!comment format=mdwn
- username="fa-ml@8b7c0cd18f4588039684c9065d74e687ce921e47"
- nickname="fa-ml"
- avatar="http://cdn.libravatar.org/avatar/9784765515033db9c353525245a41ceb"
- subject="comment 2"
- date="2017-10-25T13:15:15Z"
- content="""
-> \"Does it promote $BADTHINGS, directly or indirectly?\"
-
-Slippery surface, as one can easily put GPG/TOR/GNUNet (and loosely following your example, R) in that $BADTHINGS basket.  
-A medical professional cures people regardless, a lawyer defends their client regardless: is a Free Software contributor more equipped to indulge in this deontological Pandora's box?
-"""]]

removed
diff --git a/posts/ethics/comment_3_89c290cc731c2966a94051d1e44d619c._comment b/posts/ethics/comment_3_89c290cc731c2966a94051d1e44d619c._comment
deleted file mode 100644
index c4f9a75..0000000
--- a/posts/ethics/comment_3_89c290cc731c2966a94051d1e44d619c._comment
+++ /dev/null
@@ -1,17 +0,0 @@
-[[!comment format=mdwn
- username="lesley.g.mitchell@c6f89a60a1c23d0b27e6f1fddd7b4cd9c655ea16"
- nickname="lesley.g.mitchell"
- avatar="http://cdn.libravatar.org/avatar/969c5042049a7b48fcebf8020edb503a"
- subject="Further reading on the topic"
- date="2017-10-26T12:43:51Z"
- content="""
-Computer/Programming/Information Ethics is a whole discipline in itself, though it is certainly a topic that every programmer really ought have some grounding in.
-
-The Wikipedia page on the topic - <https://en.wikipedia.org/wiki/Programming_ethics> - is a good place to start for more general information and some ideas about what to consider.
-
-For a more philosophical exploration of the topic, the Stanford Encyclopedia of Philosophy has some good articles, such as: <https://plato.stanford.edu/entries/ethics-computer/> and <https://plato.stanford.edu/entries/computing-responsibility/>
-
-There are also a whole range of books on the topic both from computing perspectives and more philosophical ones.
-
-The Open University also offers a free course on the subject, [Introducing Ethics in Information and Computer Sciences](http://www.open.edu/openlearn/education/educational-technology-and-practice/educational-technology/introducing-ethics-information-and-computer-sciences/content-section-0?active-tab=description-tab)
-"""]]

removed
diff --git a/posts/ethics/comment_1_286d1434399328d4e17422d38838f063._comment b/posts/ethics/comment_1_286d1434399328d4e17422d38838f063._comment
deleted file mode 100644
index ee5c2e6..0000000
--- a/posts/ethics/comment_1_286d1434399328d4e17422d38838f063._comment
+++ /dev/null
@@ -1,12 +0,0 @@
-[[!comment format=mdwn
- username="richardipsum"
- avatar="http://cdn.libravatar.org/avatar/48cd8810cb554be5891de36dd031b49c"
- subject="comment 1"
- date="2017-10-25T12:28:14Z"
- content="""
-Related to the issue of ethics in software is the ability of some companies to e.g. [swing an election].
-I'd argue that Google and Facebook can be considered ethically problematic in this respect.
-That's not to say that these companies are doing any of this intentionally ofcourse.
-
-[swing an election]: http://dci.mit.edu/assets/papers/decentralized_web.pdf
-"""]]

Added a comment: Further reading on the topic
diff --git a/posts/ethics/comment_3_89c290cc731c2966a94051d1e44d619c._comment b/posts/ethics/comment_3_89c290cc731c2966a94051d1e44d619c._comment
new file mode 100644
index 0000000..c4f9a75
--- /dev/null
+++ b/posts/ethics/comment_3_89c290cc731c2966a94051d1e44d619c._comment
@@ -0,0 +1,17 @@
+[[!comment format=mdwn
+ username="lesley.g.mitchell@c6f89a60a1c23d0b27e6f1fddd7b4cd9c655ea16"
+ nickname="lesley.g.mitchell"
+ avatar="http://cdn.libravatar.org/avatar/969c5042049a7b48fcebf8020edb503a"
+ subject="Further reading on the topic"
+ date="2017-10-26T12:43:51Z"
+ content="""
+Computer/Programming/Information Ethics is a whole discipline in itself, though it is certainly a topic that every programmer really ought have some grounding in.
+
+The Wikipedia page on the topic - <https://en.wikipedia.org/wiki/Programming_ethics> - is a good place to start for more general information and some ideas about what to consider.
+
+For a more philosophical exploration of the topic, the Stanford Encyclopedia of Philosophy has some good articles, such as: <https://plato.stanford.edu/entries/ethics-computer/> and <https://plato.stanford.edu/entries/computing-responsibility/>
+
+There are also a whole range of books on the topic both from computing perspectives and more philosophical ones.
+
+The Open University also offers a free course on the subject, [Introducing Ethics in Information and Computer Sciences](http://www.open.edu/openlearn/education/educational-technology-and-practice/educational-technology/introducing-ethics-information-and-computer-sciences/content-section-0?active-tab=description-tab)
+"""]]

Added a comment: comment 2
diff --git a/posts/ethics/comment_2_b52385572b8ee0edb495bc13186c544e._comment b/posts/ethics/comment_2_b52385572b8ee0edb495bc13186c544e._comment
new file mode 100644
index 0000000..d08cf96
--- /dev/null
+++ b/posts/ethics/comment_2_b52385572b8ee0edb495bc13186c544e._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="fa-ml@8b7c0cd18f4588039684c9065d74e687ce921e47"
+ nickname="fa-ml"
+ avatar="http://cdn.libravatar.org/avatar/9784765515033db9c353525245a41ceb"
+ subject="comment 2"
+ date="2017-10-25T13:15:15Z"
+ content="""
+> \"Does it promote $BADTHINGS, directly or indirectly?\"
+
+Slippery surface, as one can easily put GPG/TOR/GNUNet (and loosely following your example, R) in that $BADTHINGS basket.  
+A medical professional cures people regardless, a lawyer defends their client regardless: is a Free Software contributor more equipped to indulge in this deontological Pandora's box?
+"""]]

Added a comment
diff --git a/posts/ethics/comment_1_286d1434399328d4e17422d38838f063._comment b/posts/ethics/comment_1_286d1434399328d4e17422d38838f063._comment
new file mode 100644
index 0000000..ee5c2e6
--- /dev/null
+++ b/posts/ethics/comment_1_286d1434399328d4e17422d38838f063._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="richardipsum"
+ avatar="http://cdn.libravatar.org/avatar/48cd8810cb554be5891de36dd031b49c"
+ subject="comment 1"
+ date="2017-10-25T12:28:14Z"
+ content="""
+Related to the issue of ethics in software is the ability of some companies to e.g. [swing an election].
+I'd argue that Google and Facebook can be considered ethically problematic in this respect.
+That's not to say that these companies are doing any of this intentionally ofcourse.
+
+[swing an election]: http://dci.mit.edu/assets/papers/decentralized_web.pdf
+"""]]

publishing
diff --git a/posts/ethics.mdwn b/posts/ethics.mdwn
new file mode 100644
index 0000000..4238a5a
--- /dev/null
+++ b/posts/ethics.mdwn
@@ -0,0 +1,38 @@
+[[!meta date="Wed, 25 Oct 2017 12:00:12 +0000"]]
+[[!meta title="Ethics in software development"]]
+[[!meta author="Lars Wirzenius"]]
+
+Free software development always has an ethical dimension. We develop
+free software instead of proprietary software to allow people, the
+users of our software, to not be as controlled by vendors of
+proprietary software as they might otherwise be.
+
+Software freedom is, however, only one ethical dimension. Free
+software can also be unethical, for example by doing things that hurt
+people. As an extreme example, a free software implementation of
+ransomware would be quite problematic, under any licence. It doesn't
+matter if the program is, say, under the GPL licence, if it attacks
+people's computers, and encrypts their data and refuses to decrypt it
+until the author of the program has paid a ransom. Not even if the
+program installs its own source code on the computer when it encrypts
+all other data would it be considered ethical.
+
+When you write software you should consider the ethics, the morality,
+and the impact on everyone. For example:
+
+* Does it promote racism, sexism, or violence, directly or indirectly?
+  For example, if it's an "AI" that tries to guess if someone will
+  commit a crime in the future, is it effectively only based their race?
+
+* Does it use bandwidth unneccessarily? Bandwidth is an expensive
+  luxury in some parts of the worlds, so wasting it discrimnates
+  against people in those parts of the world.
+
+* Does it "call home", such as report usage to the developers? This
+  violates user privacy. Gathering usage statistics can be very useful
+  to the developers in more ways than one, but to do so without requesting
+  permission remains a violation of privacy.
+
+Have you encountered ethically problematic software?
+Or perhaps have you found some exemplar of ethical software development?
+Why not give an example in the comments below...

Added a comment: Re: time_t could be longer than long
diff --git a/posts/time-rendering/comment_2_d43074875f8870161365a06c96bc99aa._comment b/posts/time-rendering/comment_2_d43074875f8870161365a06c96bc99aa._comment
new file mode 100644
index 0000000..ee27150
--- /dev/null
+++ b/posts/time-rendering/comment_2_d43074875f8870161365a06c96bc99aa._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="http://richard.maw.name/"
+ nickname="Richard Maw"
+ avatar="http://cdn.libravatar.org/avatar/4d312a0989b085a9dfa9ba61d60da130979b9c408659132fea11d5efe2c90fc5"
+ subject="Re: time_t could be longer than long"
+ date="2017-10-21T16:11:41Z"
+ content="""
+Thanks Ben. I've made that change.
+
+I unfortunately wrote this article before I discussed this issue at DebConf.
+"""]]

Convert "long" format strings to "long long" for printing time
diff --git a/posts/time-rendering.mdwn b/posts/time-rendering.mdwn
index 1e649c0..e09e7c0 100644
--- a/posts/time-rendering.mdwn
+++ b/posts/time-rendering.mdwn
@@ -52,11 +52,11 @@ struct timespec {
 Forunately humans aren't strange enough to commonly render nanoseconds
 as anything other than nanosecond precision after the decimal point,
 so this [fixed point][] number can be rendered
-with the `%09ld` [printf(3)][] format string.
+with the `%09lld` [printf(3)][] format string.
 
 [[!format C """
 /* Render the current time as seconds and nanoseconds. */
-printf("%ld.%09ld\n", (long)time.tv_sec, (long)time.tv_nsec);
+printf("%lld.%09lld\n", (long long)time.tv_sec, (long long)time.tv_nsec);
 """]]
 
 `struct tm`
@@ -218,7 +218,7 @@ int main(void) {
 	}
 
 	/* Render the current time as the formatted time plus nanoseconds. */
-	printf("%s.%09ld\n", tbuf, (long)time.tv_nsec);
+	printf("%s.%09lld\n", tbuf, (long long)time.tv_nsec);
 
 cleanup:
 	free(tbuf);
diff --git a/posts/time-rendering/time.c b/posts/time-rendering/time.c
index aad7708..2ce46bc 100644
--- a/posts/time-rendering/time.c
+++ b/posts/time-rendering/time.c
@@ -99,7 +99,7 @@ int main(void) {
 	}
 
 	/* Render the current time as the formatted time plus nanoseconds. */
-	printf("%s.%09ld\n", tbuf, (long)time.tv_nsec);
+	printf("%s.%09lld\n", tbuf, (long long)time.tv_nsec);
 
 cleanup:
 	free(tbuf);

Added a comment: Leap seconds can still cause disruption
diff --git a/posts/time-leapseconds/comment_1_54a7898faf1efdd14e80e695e397928e._comment b/posts/time-leapseconds/comment_1_54a7898faf1efdd14e80e695e397928e._comment
new file mode 100644
index 0000000..fea58e2
--- /dev/null
+++ b/posts/time-leapseconds/comment_1_54a7898faf1efdd14e80e695e397928e._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="ben@1bd5a357d246e09ae8a003093163aabb0190beb6"
+ nickname="ben"
+ avatar="http://cdn.libravatar.org/avatar/344b20b16cdf7607bfca3d9b130a5876"
+ subject="Leap seconds can still cause disruption"
+ date="2017-10-20T14:35:46Z"
+ content="""
+Leap seconds are inserted at 23:59:60 UTC, not local time. Some stock exchanges shut down briefly in the middle of 2015 rather than keep running across the leap second at the risk of timekeeping errors.
+"""]]

Added a comment: time_t could be longer than long
diff --git a/posts/time-rendering/comment_1_78794363393ce28fae3ea05952550499._comment b/posts/time-rendering/comment_1_78794363393ce28fae3ea05952550499._comment
new file mode 100644
index 0000000..7c3ffb0
--- /dev/null
+++ b/posts/time-rendering/comment_1_78794363393ce28fae3ea05952550499._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="ben@1bd5a357d246e09ae8a003093163aabb0190beb6"
+ nickname="ben"
+ avatar="http://cdn.libravatar.org/avatar/344b20b16cdf7607bfca3d9b130a5876"
+ subject="time_t could be longer than long"
+ date="2017-10-20T14:26:47Z"
+ content="""
+There are likely to be 32-bit systems still running in 2038, and in order to make that happen they will likely have a 64-bit time_t and 32-bit long. So please could you make your first example cast from time_t to long long, not long?
+"""]]

publishing
diff --git a/posts/keeping-passwords-safe.mdwn b/posts/keeping-passwords-safe.mdwn
new file mode 100644
index 0000000..5122c4a
--- /dev/null
+++ b/posts/keeping-passwords-safe.mdwn
@@ -0,0 +1,48 @@
+[[!meta date="Wed, 18 Oct 2017 12:00:15 +0000"]]
+[[!meta title="Keeping your passwords safe"]]
+[[!meta author="Daniel Silverstone"]]
+
+There're a number of ways of keeping your passwords as safe as can be.  One
+very old-school way is to write each password down in a book, and keep that
+book physically secure.  On the assumption that you can't remember the
+passwords without the book, this is a reasonable way to improve your security.
+Sadly it doesn't scale well and can make it quite hard to keep things
+up-to-date.
+
+More usefully, in today's multi-computer and multi-device world, there are
+programs called 'password managers' and as with anything even vaguely
+interesting there are a number of them to choose from.  Some names you may
+already be familiar with include [Keepassx][], [1Password][], and [LastPass][].
+
+Password managers offer you a way to effectively protect all your passwords
+with a single token, often allowing you to sync and manage your passwords
+without needing lots of knowledge of how things work.  They're usually also
+integrated with your web browser, your phone, etc, to allow you a seamless
+experience.
+
+If you're a little more paranoid than the normal geek though, and you're
+prepared to sacrifice a bit of simplicity for a bit more ease-of-mind, then you
+could try [Password Store][] (`pass`) which is written in [Bash][] and uses
+[GnuPG][].  I personally use `pass` and have my GnuPG key stored in a
+[Yubikey][] which I keep around my neck.  (There's also the [Gnuk][] which can,
+I believe, do a similar job) With the need of the physical token
+and also the PIN to unlock it, this is a [multifactor authentication][] system
+which I then can use to secure my passwords etc.  I then have it backed onto
+my own Git server where I can keep an eye on the content safely.
+
+I strongly suggest that if you're not using a password safe of some kind, that
+you get one set up and start using it.  In fact, if you've not got one, go and
+do it now and I'll see you next time...
+
+(Oh yeah, and if you look at multifactor authentication, be aware that your
+intrinsic factor today is simply your adversary's posession factor tomorrow)
+
+[Keepassx]: https://www.keepassx.org/
+[1Password]: https://1password.com/
+[LastPass]: https://www.lastpass.com/
+[Password Store]: https://www.passwordstore.org/
+[Bash]: https://www.gnu.org/software/bash/
+[GnuPG]: https://gnupg.org/
+[Yubikey]: https://www.yubico.com/start/
+[Gnuk]: https://www.fsij.org/category/gnuk.html
+[multifactor authentication]: https://en.wikipedia.org/wiki/Multi-factor_authentication

Added a comment: Request for feedback
diff --git a/posts/what-and-why-nix/comment_2_5b079ac24a5fd83b6deab74c36bf49f3._comment b/posts/what-and-why-nix/comment_2_5b079ac24a5fd83b6deab74c36bf49f3._comment
new file mode 100644
index 0000000..8448f2e
--- /dev/null
+++ b/posts/what-and-why-nix/comment_2_5b079ac24a5fd83b6deab74c36bf49f3._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ username="charles@4fe14859253f1c07dc00bde65385f8b74a400f38"
+ nickname="charles"
+ avatar="http://cdn.libravatar.org/avatar/d62d4519f7d96a06ae7ba7c5acc1a537"
+ subject="Request for feedback"
+ date="2017-10-16T23:05:02Z"
+ content="""
+Nice post, and I'm glad you're enjoying NixOS!
+
+I'm a NixOS contributor/committer, and the principle author (and co-maintainer) of the Bundler-based packaging integration. If you or anyone reading this has any feedback and/or questions about NixOS and Ruby (or NixOS in general) feel free to shoot me a message. I idle on #nixos on Freenode (as cstrahan), and you can email me at charles {{at}} cstrahan.com.
+
+Cheers!
+
+-Charles Strahan (cstrahan)
+"""]]

publishing
diff --git a/posts/attracting-contributors.mdwn b/posts/attracting-contributors.mdwn
new file mode 100644
index 0000000..aa34ce8
--- /dev/null
+++ b/posts/attracting-contributors.mdwn
@@ -0,0 +1,31 @@
+[[!meta date="Wed, 11 Oct 2017 12:00:07 +0000"]]
+[[!meta title="Attracting contributors to your project"]]
+[[!meta author="Lars Wirzenius"]]
+
+It's perfectly OK to have a personal project that only you yourself
+work on. In fact, most free software projects are like that. A project
+with only one contributor, or only a couple, can be quite limited
+however. A larger group tends to get more done and, more importantly,
+they do different things. A more diverse group brings in more points
+of view which tends to make the project better suited to a larger
+group of users.
+
+Attracting contributors to a project you've started can be tricky.
+Your humble author asked on [Twitter][] and [Mastodon][] for advice on
+this very topic, and wrote up a summary on his own [blog][].
+
+[Twitter]: https://twitter.com/larswirzenius/status/913758747666862081
+[Mastodon]: https://mastodon.acc.sunet.se/@liw/79046
+[blog]: https://blog.liw.fi/posts/2017/10/01/attracting_contributors_to_a_new_project/
+
+The condensation of the summary is:
+
+> Get people over the hump of making their first contribution, by
+> making it easy and removing all unnecessary obstacles. Make
+> contributing into a rewarding experience.
+
+_Obstacles_ can be things like making it difficult to install, run, or
+use the software, making it difficult to find, retrieve, or
+understand the source code, not having public discussion forums
+(mailing lists, IRC channels, etc), or not having a public ticketing
+system.

publishing
diff --git a/posts/secrets-2.mdwn b/posts/secrets-2.mdwn
new file mode 100644
index 0000000..c7f2e15
--- /dev/null
+++ b/posts/secrets-2.mdwn
@@ -0,0 +1,95 @@
+[[!meta date="Wed, 04 Oct 2017 12:00:12 +0000"]]
+[[!meta title="H0w s3cUre aR3 ur p455w0rdz‽"]]
+[[!meta author="Daniel Silverstone"]]
+
+There are many schools of thought around how to create 'secure' passwords.
+While they differ in the various ways to assess if a password is secure or not,
+they are all united in their goal of making it harder for both pesky humans
+and super-powerful computers to guess your passwords.  In addition, the ways of
+storing passwords vary depending on desired security levels.
+
+Before we discuss ways to make secure passwords, let's take a moment to
+consider something called [entropy][].  To properly understand entropy can take
+years, so here's a brief précis… In essence, and for our purposes, _entropy_ is
+a measure of how "random" your password is.  Entropy is a measure of
+information and, for passwords, we want as much entropy as possible since that
+makes it harder for an adversary to guess.  Sadly there's no trivial way to
+estimate how much entropy is present in a password because a computer cannot
+know all possible context around the person setting or using the password.
+This is the crux of the arguments around password policies, qualities, etc.
+
+Bruce Schneier, who is a well respected security expert, wrote a
+[nice article][] on passwords.
+
+[entropy]: https://en.wikipedia.org/wiki/Entropy_(information_theory)
+[nice article]: https://www.schneier.com/blog/archives/2014/03/choosing_secure_1.html
+
+The hard-for-humans password
+----------------------------
+
+> "A good password consists of between ten and forty characters, with a mix of
+> upper- and lower-case letters, numbers, and symbols."
+
+The "alphabet" of characters which may be part of a password can be as large,
+or as small, as you like.  One school of thought says that (a) the alphabet
+should be as large as possible and (b) that passwords should be mandated to
+have at least one of each class of characters in the alphabet.
+
+These passwords are often very hard for humans to guess if constructed entirely
+randomly.  Sadly humans are very bad at remembering well constructed passwords
+of this kind and as such they tend not to be well constructed.  For example, on
+the face of it, `94Pr!LOR;Fq.` might be an excellent looking password.  Sadly
+if you knew that my birthday is the 9th April, you might guess the first half,
+and the second half is an inversion of shift state combined with a down/right
+migration on a UK qwerty keyboard.  The first half is context which a human
+might guess and the second is the kind of translation which a computer will
+likely try quickly and easily.
+
+However, for a moment let's consider the possibility that it were a good
+password, let's estimate the entropy in it.  We'll be naïve and generous in our
+estimation...  The 'alphabet' has somewhere around 100 elements, let's assume
+it has 128 elements and as such each character is, generously, seven bits of
+entropy.  Our 10 character password is thus 70 bits of entropy, but we might
+halve that because of the repetition, giving 35 bits of useful entropy.  By
+comparison the smallest of possible keys which computers might use these days
+are 256 bits so we puny humans are nowhere near and we're finding it hard to be
+there.
+
+[Correct Horse Battery Staple][xkcd]
+------------------------------------
+
+Another stable of thought (yes, pun intended) is that a longer but more easily
+memorised password would be more secure.  There are around 100,000 words in the
+standard word list on my laptop (`/usr/share/dict/words`) so picking one of
+those is, in theory, around 16 bits of entropy but let's be conservative and
+call it 11 bits.  Four words, chosen at random, therefore have 44 bits of
+entropy.  If you add in some capitalisation tweaking to make that estimate a
+little more reasonable; and then add two or three more words and bring the
+entropy estimate way above anything you might manage to memorise from a random
+password above.
+
+[xkcd]: https://xkcd.com/936/
+
+Keeping passwords securely
+==========================
+
+Sadly, two parties need to keep passwords if they are to be a mechanism of
+authentication.  The person who is being authenticated (you) and the entity who
+is doing the authentication (some website for example).  In order to reduce the
+impact of a data breach, passwords will be stored [hashed][] by sites which
+care.  Algorithms to do this are designed to make it mathematically improbable
+that you can find a password purely by knowing the hash of it.  In addition
+they are often designed to be computationally expensive to calculate in order
+to reduce the ease by which computers might test guesses.  There are a number
+of algorithms which are considered good for this, such as [scrypt][] or
+[bcrypt][] which require a reasonable chunk of non-parallelisable CPU time
+and a non-trivial amount of memory to compute.
+
+Sadly you can't use the same algorithms to store your passwords safely because
+you won't be able to recover them.  We'll consider ways you can do that in a
+future article.
+
+[hashed]: https://crackstation.net/hashing-security.htm
+[scrypt]: https://en.wikipedia.org/wiki/Scrypt
+[bcrypt]: https://en.wikipedia.org/wiki/Bcrypt
+

publishing
diff --git a/posts/time-leapseconds.mdwn b/posts/time-leapseconds.mdwn
new file mode 100644
index 0000000..48a9fcd
--- /dev/null
+++ b/posts/time-leapseconds.mdwn
@@ -0,0 +1,71 @@
+[[!meta date="Wed, 27 Sep 2017 12:00:13 +0000"]]
+[[!meta title="What is Time - leap seconds"]]
+[[!meta author="Richard Maw"]]
+[[!tag time]]
+
+It's common knowledge that the number of days in a year is not constant.
+
+Since the Earth doesn't orbit the Sun in exactly 365 days
+and it is impractical to not have a whole number of days in the year
+we allow our calendar seasons to become marginally desynchronised
+with astronomical seasons
+and every four years (more or less) have a year with an extra day in it.
+
+This is called a [leap year][], and contains a leap day.
+
+There's an analogous concept of a [leap second][]
+to correct the discrepancy between the Earth's rotation about its axis
+and the number of seconds as measured by atomic clocks.
+
+[leap year]: https://en.wikipedia.org/wiki/Leap_year
+[leap second]: https://en.wikipedia.org/wiki/Leap_second
+
+Leap seconds
+------------
+
+On days with a positive leap second there is an extra second in the day.
+To minimise disruption this second officially happens at midnight.
+
+As a result 23:59:60 is a valid time of day.
+
+The average person is not likely to notice that,
+though computers will,
+and since computers run software written by humans
+they may not handle this very well.
+
+The default handling of this in Linux when using `CLOCK_REALTIME`
+is that 23:59:59 lasts for two seconds.
+
+This could cause issues for software that controls things like robot arms,
+since an instruction to rotate for 1 second would become rotate for 2 seconds.
+
+An alternative, handled by using Google's time servers as your NTP server,
+is to smear the leap second across the whole day.
+
+Since your computer typically has to deal with your clock drifting
+small corrections are regularly made anyway, so this is a neat solution.
+
+This doesn't help if your computer normally has a very reliable clock,
+as any software written specifically to run on it will depend on this property
+which is not correct for a whole day every so often.
+
+This could be a problem for specialist control software,
+but it's a more serious problem for authoritative NTP servers
+who can't use an upstream NTP server to do the time smearing for them.
+
+To handle this there's `CLOCK_TAI` which will return 23:59:60.
+
+The leap second adjustments are handled by [adjtimex(2)][],
+applying a time smear if using Google's time servers,
+or inserting a leap second into the day.
+
+---
+
+With the discussion of `CLOCK_TAI` we have discussed all the clocks
+that measure the passage of time.
+
+Another type of clock for measuring a different notion of time
+is the CPU clock, for accounting how much CPU a process or thread uses,
+which will be discussed at another time.
+
+[adjtimex(2)]: http://man7.org/linux/man-pages/man2/adjtimex.2.html

creating tag page tags/secrets
diff --git a/tags/secrets.mdwn b/tags/secrets.mdwn
new file mode 100644
index 0000000..84b741f
--- /dev/null
+++ b/tags/secrets.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged secrets"]]
+
+[[!inline pages="tagged(secrets)" actions="no" archive="yes"
+feedshow=10]]

publishing
diff --git a/posts/secrets-1.mdwn b/posts/secrets-1.mdwn
new file mode 100644
index 0000000..f8c1868
--- /dev/null
+++ b/posts/secrets-1.mdwn
@@ -0,0 +1,37 @@
+[[!meta date="Wed, 20 Sep 2017 12:00:11 +0000"]]
+[[!meta title="Psst, can you keep a secret?"]]
+[[!meta author="Daniel Silverstone"]]
+[[!tag secrets]]
+
+Human society is based, in part, on keeping things secret.  Our society (as it
+is) would fail horribly if everything was publically known.  We rely on keeping
+some information secret to protect our private content.  For example, we often
+protect access services with secrets we call [passwords][] (though if they are
+simple words then it's unlikely they're very secure).  We also use things
+called [cryptographic keys][] which are large complicated-to-work-with numbers
+which computers can use to secure information.
+
+[passwords]: https://en.wikipedia.org/wiki/Password
+[cryptographic keys]: https://en.wikipedia.org/wiki/Key_(cryptography)
+
+If you've been following since we started Yakking then you've probably got some
+passwords and some keys of your own.  Your keys might be things like [SSH][]
+identities or [GnuPG][] keys.  Your passwords will protect things like your
+computer login, your social media accounts, etc.
+
+[SSH]: /tags/ssh/
+[GnuPG]: https://en.wikipedia.org/wiki/GNU_Privacy_Guard
+
+As computer users, we have so many of these secrets to look after that you're
+unlikely to be relying on your own fallible memory.  As such you're likely
+already getting your computer to remember them for you.  If you're doing this
+semi-well then you're protecting all the remembered [credentials][] with some
+password(s) and/or key(s).
+
+[credentials]: https://en.wikipedia.org/wiki/Credential#Cryptography
+
+There are many ways of looking after credentials, generating passwords,
+measuring the quality of passwords, handling keys, policies for retaining and
+changing credentials, etc.  Over the next few articles we'll discuss a number
+of these points and hopefully you'll all feel a little more secure as a result.
+

creating tag page tags/libc
diff --git a/tags/libc.mdwn b/tags/libc.mdwn
new file mode 100644
index 0000000..4e5ace8
--- /dev/null
+++ b/tags/libc.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged libc"]]
+
+[[!inline pages="tagged(libc)" actions="no" archive="yes"
+feedshow=10]]

publishing
diff --git a/posts/time-rendering.mdwn b/posts/time-rendering.mdwn
new file mode 100644
index 0000000..1e649c0
--- /dev/null
+++ b/posts/time-rendering.mdwn
@@ -0,0 +1,249 @@
+[[!meta date="Wed, 13 Sep 2017 12:00:12 +0000"]]
+[[!meta title="Time - Rendering"]]
+[[!meta author="Richard Maw"]]
+[[!tag time libc C]]
+
+Time-stamps are great for computers.
+They are just any other number that they can perform arithmetic on.
+
+Unfortunately humans don't think of time as one big number.
+Humans think of time as a bunch of small numbers.
+
+There's numbers for the position of their planet around their Star.
+There's the number of what year it is.
+The number of which month, which may even have a different name!
+The day of the month which there isn't even the name number of each month!
+Humans even count days in a cycle of 7 which have their own names!
+
+For measuring the rotation of their planet humans have more consistent numbers.
+Each day is divided into 24 hours.
+Each hour is divided into 60 minutes.
+Each minute is divided into 60 seconds.
+
+This is all a bit silly because the period of the Earth's rotation
+isn't even an integral number of seconds,
+and it's slowing down!
+
+Forunately some considerate humans wrote functions for turning
+our nice friendly time-stamps from [clock_gettime(2)][]
+into icky human divisions.
+
+[[!format C """
+int ret;
+struct timespec time;
+
+ret = clock_gettime(CLOCK_REALTIME, &time);
+"""]]
+
+`struct timespec`
+-----------------
+
+For a lot of humans, seconds are a coarse enough measure of time.
+They track the time as a number of seconds
+and the number of nanoseconds between those seconds.
+
+[[!format C """
+struct timespec {
+	time_t tv_sec;
+	time_t tv_nsec;
+};
+"""]]
+
+Forunately humans aren't strange enough to commonly render nanoseconds
+as anything other than nanosecond precision after the decimal point,
+so this [fixed point][] number can be rendered
+with the `%09ld` [printf(3)][] format string.
+
+[[!format C """
+/* Render the current time as seconds and nanoseconds. */
+printf("%ld.%09ld\n", (long)time.tv_sec, (long)time.tv_nsec);
+"""]]
+
+`struct tm`
+-----------
+
+In GLibc there are two types of functions for turning a `time_t`
+into a form that humans can deal with.
+
+There's [gmtime_r(3)][] for getting the time in the UTC timezone
+(the name is weird because it used to do the GMT timezone
+ but it's more useful in UTC and they are closely related).
+
+There's [localtime_r(3)][] for getting the time
+in whatever local time zone your computer is configured to think it is in.
+
+If the time is only going to be shown to the user of the computer
+then [localtime_r(3)][] may be more appropriate.
+If it's going to be shared then [gmtime_r(3)][] would be more appropriate,
+especially if it's going to be shared internationally.
+
+[strftime(3)][]
+---------------
+
+You can use the contents of `struct tm` directly,
+but humans have a lot of their own rules for how to display time as text.
+
+This is why they wrote [strftime(3)][].
+It has its own little language for describing how to display the time.
+
+Unfortunately humans, unlike us perfect machines, are fallible.
+
+Humans have a notion of the same hour's number being used twice in a day
+and have to add their own way of saying which it is,
+so their language includes `%p` to become "AM" or "PM".
+Not all humans use this, so `%p` can produce no result.
+This is a problem, since a format string of just `%p`
+will result in a zero-length result,
+and [strftime(3)][] also uses a zero result to mean
+that the memory for storing the result in is not large enough.
+
+Humans may think this is fine
+and that nobody ever wants to just know whether it's "AM" or "PM",
+or that they just know that they provided enough memory so don't check.
+
+We are perfect machines and want it to work for every input,
+so want our own wrapper to make it work.
+
+[[!format C """
+/* Returns 0 on success, -1 on failure */
+/* Some format strings can naturally expand to "",
+ * which is a problem since the size is returned or 0 if too small.
+ */
+ssize_t safe_strftime(char *s, size_t *len, size_t max, const char *format,
+                      const struct tm *tm) {
+    /* Adds a trailing space to format string
+     * so it always gets 0 on too small,
+     * and NULs it on success before returning length
+     */
+    char *fmt = NULL;
+    ssize_t ret;
+
+    ret = asprintf(&fmt, "%s ", format);
+    if (ret < 0)
+        goto cleanup;
+
+    ret = strftime(s, max, fmt, tm);
+    if (ret == 0) {
+        ret = -1;
+        goto cleanup;
+    }
+
+    s[ret - 1] = '\0';
+    if (len)
+        *len = ret - 1;
+    ret = 0;
+
+cleanup:
+    free(fmt);
+    return ret;
+}
+"""]]
+
+Since we perfect machines don't like to waste memory
+we needed an API that we could retry with more memory.
+We perfect machines also like tidy functions,
+so we're going to write one that reallocates internally.
+
+[[!format C """
+/* Returns length and sets *s on success, -1 and sets errno on failure */
+ssize_t astrftime(char **s, const char *fmt, const struct tm *tm) {
+	ssize_t ret;
+	size_t tbufsiz = 80;
+	char *tbuf = NULL;
+	size_t tlen;
+
+	tbuf = malloc(tbufsiz);
+	if (tbuf == NULL) {
+		ret = -1;
+		goto cleanup;
+	}
+
+	while (safe_strftime(tbuf, &tlen, tbufsiz, fmt, tm) != 0) {
+		char *newbuf;
+		size_t newsiz;
+
+		newsiz = tbufsiz * 2;
+		newbuf = realloc(tbuf, newsiz);
+		if (newbuf == NULL) {
+			ret = 1;
+			goto cleanup;
+		}
+
+		tbuf = newbuf;
+		tbufsiz = newsiz;
+	}
+
+	*s = tbuf;
+	tbuf = NULL;
+	ret = tlen;
+
+cleanup:
+	free(tbuf);
+	return ret;
+}
+"""]]
+
+If time needs to be rendered in another way
+then it's probably better to use the contents of `struct tm` directly.
+
+Command
+-------
+
+Now we have enough to write our program.
+
+[[!format C """

(Diff truncated)
removed
diff --git a/posts/what-and-why-nix/comment_2_96ec7839012229373e5a13b348751901._comment b/posts/what-and-why-nix/comment_2_96ec7839012229373e5a13b348751901._comment
deleted file mode 100644
index 6d4e2a0..0000000
--- a/posts/what-and-why-nix/comment_2_96ec7839012229373e5a13b348751901._comment
+++ /dev/null
@@ -1,10 +0,0 @@
-[[!comment format=mdwn
- username="richardipsum"
- avatar="http://cdn.libravatar.org/avatar/48cd8810cb554be5891de36dd031b49c"
- subject="New nix pills!"
- date="2017-09-12T13:38:47Z"
- content="""
-Nix pills have been moved to the main NixOS site now [https://nixos.org/nixos/nix-pills/](https://nixos.org/nixos/nix-pills/)
-
-You can even submit pull requests at [http://github.com/nixos/nix-pills](http://github.com/nixos/nix-pills) !
-"""]]

Added a comment: New nix pills!
diff --git a/posts/what-and-why-nix/comment_2_96ec7839012229373e5a13b348751901._comment b/posts/what-and-why-nix/comment_2_96ec7839012229373e5a13b348751901._comment
new file mode 100644
index 0000000..6d4e2a0
--- /dev/null
+++ b/posts/what-and-why-nix/comment_2_96ec7839012229373e5a13b348751901._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="richardipsum"
+ avatar="http://cdn.libravatar.org/avatar/48cd8810cb554be5891de36dd031b49c"
+ subject="New nix pills!"
+ date="2017-09-12T13:38:47Z"
+ content="""
+Nix pills have been moved to the main NixOS site now [https://nixos.org/nixos/nix-pills/](https://nixos.org/nixos/nix-pills/)
+
+You can even submit pull requests at [http://github.com/nixos/nix-pills](http://github.com/nixos/nix-pills) !
+"""]]

Added a comment: New nix pills!
diff --git a/posts/what-and-why-nix/comment_1_1721abb63835171852ec377e8b8a63f4._comment b/posts/what-and-why-nix/comment_1_1721abb63835171852ec377e8b8a63f4._comment
new file mode 100644
index 0000000..ba1a967
--- /dev/null
+++ b/posts/what-and-why-nix/comment_1_1721abb63835171852ec377e8b8a63f4._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="richardipsum"
+ avatar="http://cdn.libravatar.org/avatar/48cd8810cb554be5891de36dd031b49c"
+ subject="New nix pills!"
+ date="2017-09-12T13:38:41Z"
+ content="""
+Nix pills have been moved to the main NixOS site now [https://nixos.org/nixos/nix-pills/](https://nixos.org/nixos/nix-pills/)
+
+You can even submit pull requests at [http://github.com/nixos/nix-pills](http://github.com/nixos/nix-pills) !
+"""]]

publishing
diff --git a/posts/moving-your-comfort-zone.mdwn b/posts/moving-your-comfort-zone.mdwn
new file mode 100644
index 0000000..4634798
--- /dev/null
+++ b/posts/moving-your-comfort-zone.mdwn
@@ -0,0 +1,63 @@
+[[!meta date="Wed, 06 Sep 2017 12:00:06 +0000"]]
+[[!meta title="Moving your comfort zone"]]
+[[!meta author="Daniel Silverstone"]]
+
+Over time, I've discussed with many people how they learn a new programming
+language.  A common theme is to write something you're intimately familiar with
+over again in a new language in order to learn and judge.  For example, some of
+my friends like to implement standard *NIX utilities such as `cat`, `echo`,
+`grep` etc.  Usually only trivial versions, but it gives them a feel for
+`stdio`, file access, command line argument processing, etc.  Some implement
+some kind of algorithmic solution to a known problem in order to get a good
+feel for control structures and approaches to data structures.  I am a little
+bit in both camps.  I tend to try and use controlled problems but I try not to
+reimplement things over and over.  Over-all any of these approaches are
+excellent in allowing the developer to gain an understanding of how a language
+works, and how to solve things in it.  Learning your tools is key to being an
+effective developer, and the language you're working in is one of those tools.
+
+Sometimes though, what you want to do is extend your knowledge in a different
+direction.  I am currently embarking on one such journey, and I'd like to tell
+you a little bit about it.
+
+I have, for many years, been interested in [microcontroller][]s, and writing
+software which works in severely [resource-limited environments][reslim] (such
+as a total of 20 kilobytes of [RAM][], or 128 kilobytes of program storage).
+I've been working with one such family of computing devices for quite a while
+now, and among my various projects I have developed [USB][] devices which do
+fun things such as monitor a bidirectional [serial][] link, providing
+millisecond timestamped transaction information down the USB port.  However
+throughout it all, USB itself has remained a fairly sealed black box.  I used
+libraries provided by the microcontroller vendor, and just programmed around
+them.
+
+While I was away [at a conference][debconf17] I decided though that this had to
+change.
+
+I decided there and then, that I would improve my understanding of several
+topics all at once.  I was going to learn more about the
+[microcontroller hardware][stm32], the specifics of USB itself, and improve my
+command of [a programming language][rustlang] all at once.  This is, as you
+might imagine, quite a tall order, but I feel that it's worth my while to push
+my comfort zone a little and do this.  To keep me honest, and to keep me
+working at this, I have also committed to presenting, at
+[a conference][minidebconf], some kind of results of my efforts.
+
+I tell this story here, in part to increase the assurance that there's plenty
+of people expecting me to do the work, and also in part to encourage you all to
+push your comfort zones a little.  Your homework, because of course there is
+some, is to think of something you do a lot, that you are comfortable,
+interested, and engaged with.  Now think of a way to twist that so that you
+learn a lot more about that topic while pushing your comfort zone just a little
+bit.  Why not comment below and tell me what you're going to do, and we can
+encourage one another just a little.
+
+[microcontroller]: https://en.wikipedia.org/wiki/Microcontroller
+[reslim]: https://en.wikipedia.org/wiki/Computational_resource
+[RAM]: https://en.wikipedia.org/wiki/Random-access_memory
+[USB]: https://en.wikipedia.org/wiki/USB
+[serial]: https://en.wikipedia.org/wiki/Serial_port
+[debconf17]: https://debconf17.debconf.org/
+[stm32]: https://en.wikipedia.org/wiki/STM32
+[rustlang]: https://www.rust-lang.org/
+[minidebconf]: https://wiki.debian.org/MiniDebConf

is->are
diff --git a/posts/time-real-time.mdwn b/posts/time-real-time.mdwn
index fbe43ce..b83d5a9 100644
--- a/posts/time-real-time.mdwn
+++ b/posts/time-real-time.mdwn
@@ -41,7 +41,7 @@ for people who have used that place for their time base.
 
 Rather than everywhere tracking what time it is,
 areas pick a common baseline and everyone in that area uses that time.
-This is called [Time Zones][] and are usually arranged along political lines.
+These are called [Time Zones][] and are usually arranged along political lines.
 
 In practice most computers think in terms of the [UTC][] time zone,
 and use records of what the difference in time is between the local time zone

publishing
diff --git a/posts/time-real-time.mdwn b/posts/time-real-time.mdwn
new file mode 100644
index 0000000..fbe43ce
--- /dev/null
+++ b/posts/time-real-time.mdwn
@@ -0,0 +1,120 @@
+[[!meta date="Wed, 30 Aug 2017 12:00:07 +0000"]]
+[[!meta title="Time - Real time"]]
+[[!meta author="Richard Maw"]]
+[[!tag time]]
+
+Up until now we've only considered time in relation to the machine we're using.
+
+This is only useful for recording events in the vicinity of that machine.
+
+To coordinate events happening elsewhere in the world
+such as it being [midday at Greenwich observatory][GMT],
+it is necessary to synchronise clocks.
+
+[GMT]: https://en.wikipedia.org/wiki/Greenwich_Mean_Time
+
+We've briefly introduced these concepts in our introduction to time,
+but more detail is warranted.
+
+## Real time
+
+"Real time" or "Wall clock time"
+is the globally coordinated time system,
+so if the time of an event is recorded in one location
+the record of that time can be transmitted elsewhere
+and they can reason about when this was in relation to their own clocks
+so they can tell how long ago it happened.
+
+This is handled by computers containing a special piece of hardware
+called a [Real-Time Clock][RTC]
+which tracks the current time.
+
+## Time Zones
+
+Times were historically defined by the position of the sun in the sky,
+but the sun is in different positions in the sky depending on where you are,
+so the coordinated time in any given location depends on where you are.
+
+If you pick any particular location for your definition of coordinated time,
+you need to know the difference in time from that location to elsewhere
+for people who have used that place for their time base.
+
+Rather than everywhere tracking what time it is,
+areas pick a common baseline and everyone in that area uses that time.
+This is called [Time Zones][] and are usually arranged along political lines.
+
+In practice most computers think in terms of the [UTC][] time zone,
+and use records of what the difference in time is between the local time zone
+and [UTC][].
+
+The difference is normally but not exclusively measured
+in an integral number of hours since that time in [UTC][].
+
+Indian standard time is 5½ hours after [UTC][].
+North Korea's time is 8½ hours after [UTC][].
+
+Time zones don't remain fixed.
+They are political entities so change on political decisions.
+
+Most of the western world observes some form of [Daylight saving time][DST]
+where the current time zone depends on what day of the year it is.
+
+This means some times do not exist in every time zone
+since the time zone is not defined for every part of the year.
+
+As a consequence some local times do not exist everywhere in the world,
+and the local time is a product of an agreed time in one part of the world,
+where the local time is being measured, and any political decisions.
+
+An extreme example of this is [Samoa skipping the 30th December 2011][samoa-tz]
+and jumping from one time zone to another.
+
+For future proofing you would ideally record the coordinates
+alongside the [UTC][] time
+so if a time zone's geopolitical boundaries change
+the local time at those coordinates may be known.
+
+[DST]: https://en.wikipedia.org/wiki/Daylight_saving_time
+[samoa-tz]: http://www.abc.net.au/news/2011-12-30/samoa-skips-friday-in-time-zone-change/3753350
+
+## Synchronisation
+
+Your computer's [RTC][] will continue to measure time while your computer is off
+so when you turn it back on the time is mostly correct.
+
+[RTCs][RTC] are not perfect however, they diverge from real time
+because they measure time slightly faster or slower.
+
+Handily, because computers are networked
+they can ask each other what the time actually is.
+The protocol for doing this is called [NTP][].
+
+Once a computer has determined the correct time
+it can use [adjtimex(2)][] to either accelerate or slow the clock until correct,
+or just set the time to what value it should have.
+
+It is preferable to speed or slow the clock to correct the time
+unless your computer is still early on in the boot process
+since you are likely to have programs that will get confused
+if the time changed a huge amount,
+but if your computer's clock is sufficiently far ahead or behind
+then it's impractical to accelerate or slow the clock to the correct time.
+
+Linux's interface to the real time is via the `CLOCK_REALTIME` clock,
+though `CLOCK_REALTIME_COARSE` exists if it's more important that
+the time is retrieved quickly rather than the result is precise.
+
+---
+
+Now that we've got a globally coordinated notion of time
+you can coordinate events globally.
+
+Unfortunately humans aren't trained to think in terms of position and [UTC][]
+so we need a way to translate this into something human readable,
+which will be the focus of the next article.
+
+[RTC]: https://en.wikipedia.org/wiki/Real-time_clock
+[Unix time]: https://en.wikipedia.org/wiki/Unix_time
+[UTC]: https://en.wikipedia.org/wiki/Coordinated_Universal_Time
+[NTP]: https://en.wikipedia.org/wiki/Network_Time_Protocol
+[adjtimex(2)]: http://man7.org/linux/man-pages/man2/adjtimex.2.html

Tweak four-years article
diff --git a/posts/four-years.mdwn b/posts/four-years.mdwn
index a3d9d7c..f250c01 100644
--- a/posts/four-years.mdwn
+++ b/posts/four-years.mdwn
@@ -21,10 +21,10 @@ Maw have contributed two each.  Jon Dowland has contributed one, and one of our
 articles was deliberately anonymously posted one Christmas.
 
 We've received thirty-eight comments spread over twenty-four of the articles.
-Our most prolific commenter has been Richard Ipsum, and otherwise they're
-spread relatively thinly among the approximately nineteen other commenters.
-A total of fifteen of the thirty-eight comments were by authors, usually in
-response to comments on their articles.
+Our most prolific commenters have been Gravious and Richard Maw; otherwise
+they're spread relatively thinly among the approximately nineteen other
+commenters.  More than fifteen of the thirty-eight comments were by authors,
+usually in response to comments on their articles.
 
 We have six items in our suggestion-box (thank you) though some are us
 acknowledging comments.  We're grateful for every suggestion and every bit of

Remove extra publishings
diff --git a/posts/moving-your-comfort-zone.mdwn b/posts/moving-your-comfort-zone.mdwn
deleted file mode 100644
index 9ebad58..0000000
--- a/posts/moving-your-comfort-zone.mdwn
+++ /dev/null
@@ -1,63 +0,0 @@
-[[!meta date="Tue, 22 Aug 2017 12:06:25 +0000"]]
-[[!meta title="Moving your comfort zone"]]
-[[!meta author="Daniel Silverstone"]]
-
-Over time, I've discussed with many people how they learn a new programming
-language.  A common theme is to write something you're intimately familiar with
-over again in a new language in order to learn and judge.  For example, some of
-my friends like to implement standard *NIX utilities such as `cat`, `echo`,
-`grep` etc.  Usually only trivial versions, but it gives them a feel for
-`stdio`, file access, command line argument processing, etc.  Some implement
-some kind of algorithmic solution to a known problem in order to get a good
-feel for control structures and approaches to data structures.  I am a little
-bit in both camps.  I tend to try and use controlled problems but I try not to
-reimplement things over and over.  Over-all any of these approaches are
-excellent in allowing the developer to gain an understanding of how a language
-works, and how to solve things in it.  Learning your tools is key to being an
-effective developer, and the language you're working in is one of those tools.
-
-Sometimes though, what you want to do is extend your knowledge in a different
-direction.  I am currently embarking on one such journey, and I'd like to tell
-you a little bit about it.
-
-I have, for many years, been interested in [microcontroller][]s, and writing
-software which works in severely [resource-limited environments][reslim] (such
-as a total of 20 kilobytes of [RAM][], or 128 kilobytes of program storage).
-I've been working with one such family of computing devices for quite a while
-now, and among my various projects I have developed [USB][] devices which do
-fun things such as monitor a bidirectional [serial][] link, providing
-millisecond timestamped transaction information down the USB port.  However
-throughout it all, USB itself has remained a fairly sealed black box.  I used
-libraries provided by the microcontroller vendor, and just programmed around
-them.
-
-While I was away [at a conference][debconf17] I decided though that this had to
-change.
-
-I decided there and then, that I would improve my understanding of several
-topics all at once.  I was going to learn more about the
-[microcontroller hardware][stm32], the specifics of USB itself, and improve my
-command of [a programming language][rustlang] all at once.  This is, as you
-might imagine, quite a tall order, but I feel that it's worth my while to push
-my comfort zone a little and do this.  To keep me honest, and to keep me
-working at this, I have also committed to presenting, at
-[a conference][minidebconf], some kind of results of my efforts.
-
-I tell this story here, in part to increase the assurance that there's plenty
-of people expecting me to do the work, and also in part to encourage you all to
-push your comfort zones a little.  Your homework, because of course there is
-some, is to think of something you do a lot, that you are comfortable,
-interested, and engaged with.  Now think of a way to twist that so that you
-learn a lot more about that topic while pushing your comfort zone just a little
-bit.  Why not comment below and tell me what you're going to do, and we can
-encourage one another just a little.
-
-[microcontroller]: https://en.wikipedia.org/wiki/Microcontroller
-[reslim]: https://en.wikipedia.org/wiki/Computational_resource
-[RAM]: https://en.wikipedia.org/wiki/Random-access_memory
-[USB]: https://en.wikipedia.org/wiki/USB
-[serial]: https://en.wikipedia.org/wiki/Serial_port
-[debconf17]: https://debconf17.debconf.org/
-[stm32]: https://en.wikipedia.org/wiki/STM32
-[rustlang]: https://www.rust-lang.org/
-[minidebconf]: https://wiki.debian.org/MiniDebConf
diff --git a/posts/time-real-time.mdwn b/posts/time-real-time.mdwn
deleted file mode 100644
index c0c6f7b..0000000
--- a/posts/time-real-time.mdwn
+++ /dev/null
@@ -1,120 +0,0 @@
-[[!meta date="Tue, 22 Aug 2017 12:03:25 +0000"]]
-[[!meta title="Time - Real time"]]
-[[!meta author="Richard Maw"]]
-[[!tag time]]
-
-Up until now we've only considered time in relation to the machine we're using.
-
-This is only useful for recording events in the vicinity of that machine.
-
-To coordinate events happening elsewhere in the world
-such as it being [midday at Greenwich observatory][GMT],
-it is necessary to synchronise clocks.
-
-[GMT]: https://en.wikipedia.org/wiki/Greenwich_Mean_Time
-
-We've briefly introduced these concepts in our introduction to time,
-but more detail is warranted.
-
-## Real time
-
-"Real time" or "Wall clock time"
-is the globally coordinated time system,
-so if the time of an event is recorded in one location
-the record of that time can be transmitted elsewhere
-and they can reason about when this was in relation to their own clocks
-so they can tell how long ago it happened.
-
-This is handled by computers containing a special piece of hardware
-called a [Real-Time Clock][RTC]
-which tracks the current time.
-
-## Time Zones
-
-Times were historically defined by the position of the sun in the sky,
-but the sun is in different positions in the sky depending on where you are,
-so the coordinated time in any given location depends on where you are.
-
-If you pick any particular location for your definition of coordinated time,
-you need to know the difference in time from that location to elsewhere
-for people who have used that place for their time base.
-
-Rather than everywhere tracking what time it is,
-areas pick a common baseline and everyone in that area uses that time.
-This is called [Time Zones][] and are usually arranged along political lines.
-
-In practice most computers think in terms of the [UTC][] time zone,
-and use records of what the difference in time is between the local time zone
-and [UTC][].
-
-The difference is normally but not exclusively measured
-in an integral number of hours since that time in [UTC][].
-
-Indian standard time is 5½ hours after [UTC][].
-North Korea's time is 8½ hours after [UTC][].
-
-Time zones don't remain fixed.
-They are political entities so change on political decisions.
-
-Most of the western world observes some form of [Daylight saving time][DST]
-where the current time zone depends on what day of the year it is.
-
-This means some times do not exist in every time zone
-since the time zone is not defined for every part of the year.
-
-As a consequence some local times do not exist everywhere in the world,
-and the local time is a product of an agreed time in one part of the world,
-where the local time is being measured, and any political decisions.
-
-An extreme example of this is [Samoa skipping the 30th December 2011][samoa-tz]
-and jumping from one time zone to another.
-
-For future proofing you would ideally record the coordinates
-alongside the [UTC][] time
-so if a time zone's geopolitical boundaries change
-the local time at those coordinates may be known.
-
-[DST]: https://en.wikipedia.org/wiki/Daylight_saving_time
-[samoa-tz]: http://www.abc.net.au/news/2011-12-30/samoa-skips-friday-in-time-zone-change/3753350
-
-## Synchronisation
-
-Your computer's [RTC][] will continue to measure time while your computer is off
-so when you turn it back on the time is mostly correct.
-
-[RTCs][RTC] are not perfect however, they diverge from real time
-because they measure time slightly faster or slower.
-
-Handily, because computers are networked
-they can ask each other what the time actually is.
-The protocol for doing this is called [NTP][].
-
-Once a computer has determined the correct time
-it can use [adjtimex(2)][] to either accelerate or slow the clock until correct,
-or just set the time to what value it should have.
-
-It is preferable to speed or slow the clock to correct the time
-unless your computer is still early on in the boot process
-since you are likely to have programs that will get confused
-if the time changed a huge amount,
-but if your computer's clock is sufficiently far ahead or behind
-then it's impractical to accelerate or slow the clock to the correct time.
-
-Linux's interface to the real time is via the `CLOCK_REALTIME` clock,
-though `CLOCK_REALTIME_COARSE` exists if it's more important that
-the time is retrieved quickly rather than the result is precise.
-
----
-
-Now that we've got a globally coordinated notion of time
-you can coordinate events globally.
-
-Unfortunately humans aren't trained to think in terms of position and [UTC][]
-so we need a way to translate this into something human readable,
-which will be the focus of the next article.
-
-[RTC]: https://en.wikipedia.org/wiki/Real-time_clock
-[Unix time]: https://en.wikipedia.org/wiki/Unix_time
-[UTC]: https://en.wikipedia.org/wiki/Coordinated_Universal_Time
-[NTP]: https://en.wikipedia.org/wiki/Network_Time_Protocol
-[adjtimex(2)]: http://man7.org/linux/man-pages/man2/adjtimex.2.html

publishing
diff --git a/posts/moving-your-comfort-zone.mdwn b/posts/moving-your-comfort-zone.mdwn
new file mode 100644
index 0000000..9ebad58
--- /dev/null
+++ b/posts/moving-your-comfort-zone.mdwn
@@ -0,0 +1,63 @@
+[[!meta date="Tue, 22 Aug 2017 12:06:25 +0000"]]
+[[!meta title="Moving your comfort zone"]]
+[[!meta author="Daniel Silverstone"]]
+
+Over time, I've discussed with many people how they learn a new programming
+language.  A common theme is to write something you're intimately familiar with
+over again in a new language in order to learn and judge.  For example, some of
+my friends like to implement standard *NIX utilities such as `cat`, `echo`,
+`grep` etc.  Usually only trivial versions, but it gives them a feel for
+`stdio`, file access, command line argument processing, etc.  Some implement
+some kind of algorithmic solution to a known problem in order to get a good
+feel for control structures and approaches to data structures.  I am a little
+bit in both camps.  I tend to try and use controlled problems but I try not to
+reimplement things over and over.  Over-all any of these approaches are
+excellent in allowing the developer to gain an understanding of how a language
+works, and how to solve things in it.  Learning your tools is key to being an
+effective developer, and the language you're working in is one of those tools.
+
+Sometimes though, what you want to do is extend your knowledge in a different
+direction.  I am currently embarking on one such journey, and I'd like to tell
+you a little bit about it.
+
+I have, for many years, been interested in [microcontroller][]s, and writing
+software which works in severely [resource-limited environments][reslim] (such
+as a total of 20 kilobytes of [RAM][], or 128 kilobytes of program storage).
+I've been working with one such family of computing devices for quite a while
+now, and among my various projects I have developed [USB][] devices which do
+fun things such as monitor a bidirectional [serial][] link, providing
+millisecond timestamped transaction information down the USB port.  However
+throughout it all, USB itself has remained a fairly sealed black box.  I used
+libraries provided by the microcontroller vendor, and just programmed around
+them.
+
+While I was away [at a conference][debconf17] I decided though that this had to
+change.
+
+I decided there and then, that I would improve my understanding of several
+topics all at once.  I was going to learn more about the
+[microcontroller hardware][stm32], the specifics of USB itself, and improve my
+command of [a programming language][rustlang] all at once.  This is, as you
+might imagine, quite a tall order, but I feel that it's worth my while to push
+my comfort zone a little and do this.  To keep me honest, and to keep me
+working at this, I have also committed to presenting, at
+[a conference][minidebconf], some kind of results of my efforts.
+
+I tell this story here, in part to increase the assurance that there's plenty
+of people expecting me to do the work, and also in part to encourage you all to
+push your comfort zones a little.  Your homework, because of course there is
+some, is to think of something you do a lot, that you are comfortable,
+interested, and engaged with.  Now think of a way to twist that so that you
+learn a lot more about that topic while pushing your comfort zone just a little
+bit.  Why not comment below and tell me what you're going to do, and we can
+encourage one another just a little.
+
+[microcontroller]: https://en.wikipedia.org/wiki/Microcontroller
+[reslim]: https://en.wikipedia.org/wiki/Computational_resource
+[RAM]: https://en.wikipedia.org/wiki/Random-access_memory
+[USB]: https://en.wikipedia.org/wiki/USB
+[serial]: https://en.wikipedia.org/wiki/Serial_port
+[debconf17]: https://debconf17.debconf.org/
+[stm32]: https://en.wikipedia.org/wiki/STM32
+[rustlang]: https://www.rust-lang.org/
+[minidebconf]: https://wiki.debian.org/MiniDebConf

publishing
diff --git a/posts/time-real-time.mdwn b/posts/time-real-time.mdwn
new file mode 100644
index 0000000..c0c6f7b
--- /dev/null
+++ b/posts/time-real-time.mdwn
@@ -0,0 +1,120 @@
+[[!meta date="Tue, 22 Aug 2017 12:03:25 +0000"]]
+[[!meta title="Time - Real time"]]
+[[!meta author="Richard Maw"]]
+[[!tag time]]
+
+Up until now we've only considered time in relation to the machine we're using.
+
+This is only useful for recording events in the vicinity of that machine.
+
+To coordinate events happening elsewhere in the world
+such as it being [midday at Greenwich observatory][GMT],
+it is necessary to synchronise clocks.
+
+[GMT]: https://en.wikipedia.org/wiki/Greenwich_Mean_Time
+
+We've briefly introduced these concepts in our introduction to time,
+but more detail is warranted.
+
+## Real time
+
+"Real time" or "Wall clock time"
+is the globally coordinated time system,
+so if the time of an event is recorded in one location
+the record of that time can be transmitted elsewhere
+and they can reason about when this was in relation to their own clocks
+so they can tell how long ago it happened.
+
+This is handled by computers containing a special piece of hardware
+called a [Real-Time Clock][RTC]
+which tracks the current time.
+
+## Time Zones
+
+Times were historically defined by the position of the sun in the sky,
+but the sun is in different positions in the sky depending on where you are,
+so the coordinated time in any given location depends on where you are.
+
+If you pick any particular location for your definition of coordinated time,
+you need to know the difference in time from that location to elsewhere
+for people who have used that place for their time base.
+
+Rather than everywhere tracking what time it is,
+areas pick a common baseline and everyone in that area uses that time.
+This is called [Time Zones][] and are usually arranged along political lines.
+
+In practice most computers think in terms of the [UTC][] time zone,
+and use records of what the difference in time is between the local time zone
+and [UTC][].
+
+The difference is normally but not exclusively measured
+in an integral number of hours since that time in [UTC][].
+
+Indian standard time is 5½ hours after [UTC][].
+North Korea's time is 8½ hours after [UTC][].
+
+Time zones don't remain fixed.
+They are political entities so change on political decisions.
+
+Most of the western world observes some form of [Daylight saving time][DST]
+where the current time zone depends on what day of the year it is.
+
+This means some times do not exist in every time zone
+since the time zone is not defined for every part of the year.
+
+As a consequence some local times do not exist everywhere in the world,
+and the local time is a product of an agreed time in one part of the world,
+where the local time is being measured, and any political decisions.
+
+An extreme example of this is [Samoa skipping the 30th December 2011][samoa-tz]
+and jumping from one time zone to another.
+
+For future proofing you would ideally record the coordinates
+alongside the [UTC][] time
+so if a time zone's geopolitical boundaries change
+the local time at those coordinates may be known.
+
+[DST]: https://en.wikipedia.org/wiki/Daylight_saving_time
+[samoa-tz]: http://www.abc.net.au/news/2011-12-30/samoa-skips-friday-in-time-zone-change/3753350
+
+## Synchronisation
+
+Your computer's [RTC][] will continue to measure time while your computer is off
+so when you turn it back on the time is mostly correct.
+
+[RTCs][RTC] are not perfect however, they diverge from real time
+because they measure time slightly faster or slower.
+
+Handily, because computers are networked
+they can ask each other what the time actually is.
+The protocol for doing this is called [NTP][].
+
+Once a computer has determined the correct time
+it can use [adjtimex(2)][] to either accelerate or slow the clock until correct,
+or just set the time to what value it should have.
+
+It is preferable to speed or slow the clock to correct the time
+unless your computer is still early on in the boot process
+since you are likely to have programs that will get confused
+if the time changed a huge amount,
+but if your computer's clock is sufficiently far ahead or behind
+then it's impractical to accelerate or slow the clock to the correct time.
+
+Linux's interface to the real time is via the `CLOCK_REALTIME` clock,
+though `CLOCK_REALTIME_COARSE` exists if it's more important that
+the time is retrieved quickly rather than the result is precise.
+
+---
+
+Now that we've got a globally coordinated notion of time
+you can coordinate events globally.
+
+Unfortunately humans aren't trained to think in terms of position and [UTC][]
+so we need a way to translate this into something human readable,
+which will be the focus of the next article.
+
+[RTC]: https://en.wikipedia.org/wiki/Real-time_clock
+[Unix time]: https://en.wikipedia.org/wiki/Unix_time
+[UTC]: https://en.wikipedia.org/wiki/Coordinated_Universal_Time
+[NTP]: https://en.wikipedia.org/wiki/Network_Time_Protocol
+[adjtimex(2)]: http://man7.org/linux/man-pages/man2/adjtimex.2.html

publishing
diff --git a/posts/four-years.mdwn b/posts/four-years.mdwn
new file mode 100644
index 0000000..a3d9d7c
--- /dev/null
+++ b/posts/four-years.mdwn
@@ -0,0 +1,47 @@
+[[!meta date="Tue, 22 Aug 2017 12:00:08 +0000"]]
+[[!meta title="Four years of chinwaggery"]]
+[[!meta author="Daniel Silverstone"]]
+
+Four years ago, Lars posted [[our first article|software-freedom-concepts]] on
+software freedom.  This started an epic effort to help educate those who want
+to get into free-software.  We pledged to discuss technical topics such as
+[[shell|tags/shell]] and [[C|tags/C]], and non-technical topics such as
+[[diversity|tags/diversity]] and [[motivation|tags/motivation]].  Through our
+time we've had opinionated series on [[the truisms of software|tags/truism]],
+or strong technical sets around topics such as [[systemd|tags/systemd]].
+
+---
+
+To give you some numbers, in the past four years we've published two hundred
+and eleven (or two hundred and twelve if you include this one) articles, by
+seven authors.  Eighty-three were by Richard Maw, seventy-four (seventy-five)
+by yours truly, and forty-one by Lars Wirzenius.  Our most prolific guest
+author was Will Holland who has contributed seven, Richard Ipsum and Jonathan
+Maw have contributed two each.  Jon Dowland has contributed one, and one of our
+articles was deliberately anonymously posted one Christmas.
+
+We've received thirty-eight comments spread over twenty-four of the articles.
+Our most prolific commenter has been Richard Ipsum, and otherwise they're
+spread relatively thinly among the approximately nineteen other commenters.
+A total of fifteen of the thirty-eight comments were by authors, usually in
+response to comments on their articles.
+
+We have six items in our suggestion-box (thank you) though some are us
+acknowledging comments.  We're grateful for every suggestion and every bit of
+encouragement you've provided over the years, via the site, IRC, Twitter, etc.
+
+---
+
+Over the years, our core contributors have had their own ups and downs, and yet
+we've pretty much managed to get an article out every week no matter
+what. Indeed we missed one week (I believe) in all that time, and that was
+because we typoed the date in our publishing queue, and none of us caught it in
+review.  Sadly, it gets harder and harder though, and so please do pop over to
+the suggestion box if there're topics you'd like to see us cover (even if you
+just want an expansion of something we've previously discussed).  Perhaps come
+and join us in `#yakking` on the OFTC IRC network, and if you're feeling very
+enthusiastic then come and discuss guest-posting since we're always grateful
+for contributions.
+
+Here's to another four years of articles which you consider worth reading…
+

Added a comment: Re: splice and portability
diff --git a/posts/moving-files-3-faster/comment_2_9640d160f32196a7530eb5d7f90f6935._comment b/posts/moving-files-3-faster/comment_2_9640d160f32196a7530eb5d7f90f6935._comment
new file mode 100644
index 0000000..01d9615
--- /dev/null
+++ b/posts/moving-files-3-faster/comment_2_9640d160f32196a7530eb5d7f90f6935._comment
@@ -0,0 +1,17 @@
+[[!comment format=mdwn
+ username="http://richard.maw.name/"
+ nickname="Richard Maw"
+ avatar="http://cdn.libravatar.org/avatar/4d312a0989b085a9dfa9ba61d60da130979b9c408659132fea11d5efe2c90fc5"
+ subject="Re: splice and portability"
+ date="2017-08-21T10:13:11Z"
+ content="""
+Yeah, splice is included because at the time I had the notion of writing a generic copy routine between two file descriptors.
+
+We're assuming Linux, but with some flexibility of which version.
+
+*   sendfile was introduced in Linux 2.2, but the output file descriptor must be a socket. Since 2.6.33 it may be any file.
+*   splice was introduced in Linux 2.6.17 and might some day support any file descriptor.
+*   `copy_file_range` was introduced in Linux 4.5
+
+Checking versions would be inappropriate since you see frankenkernels where newer features have been back-ported, and sometimes system calls are optional, depending on kernel configuration, such as `name_to_handle_at`.
+"""]]

Added a comment: Re: article suggestion: Handling a signal correctly
diff --git a/suggestion-box/comment_6_465e2be641f88aea55735c478904977a._comment b/suggestion-box/comment_6_465e2be641f88aea55735c478904977a._comment
new file mode 100644
index 0000000..26d3f8e
--- /dev/null
+++ b/suggestion-box/comment_6_465e2be641f88aea55735c478904977a._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="http://richard.maw.name/"
+ nickname="Richard Maw"
+ avatar="http://cdn.libravatar.org/avatar/4d312a0989b085a9dfa9ba61d60da130979b9c408659132fea11d5efe2c90fc5"
+ subject="Re: article suggestion: Handling a signal correctly"
+ date="2017-08-21T08:43:18Z"
+ content="""
+Sure thing. I'll add it to my \"To write\" queue after I'm finished with time.
+"""]]

Added a comment: article suggestion: Handling a signal correctly
diff --git a/suggestion-box/comment_5_287af6179c98a3730952918cf11f5a94._comment b/suggestion-box/comment_5_287af6179c98a3730952918cf11f5a94._comment
new file mode 100644
index 0000000..5d68fb3
--- /dev/null
+++ b/suggestion-box/comment_5_287af6179c98a3730952918cf11f5a94._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="Gravious"
+ avatar="http://cdn.libravatar.org/avatar/48cd8810cb554be5891de36dd031b49c"
+ subject="article suggestion: Handling a signal correctly"
+ date="2017-08-19T13:53:00Z"
+ content="""
+There's a lot of guides out there on how to correctly handle a signal,
+seems like the kind of topic this blog would cover nicely,
+maybe include the following point? which seems to be missed in examples I've seen.
+
+ftp://ftp.gnu.org/old-gnu/Manuals/glibc-2.2.3/html_chapter/libc_24.html#SEC488
+"""]]

Comment moderation
diff --git a/posts/moving-files-3-faster/comment_1_41bf6b8f220ba4a3325479618517be41._comment b/posts/moving-files-3-faster/comment_1_41bf6b8f220ba4a3325479618517be41._comment
new file mode 100644
index 0000000..8e36c09
--- /dev/null
+++ b/posts/moving-files-3-faster/comment_1_41bf6b8f220ba4a3325479618517be41._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="Gravious"
+ avatar="http://cdn.libravatar.org/avatar/48cd8810cb554be5891de36dd031b49c"
+ subject="splice and portability"
+ date="2017-02-26T18:55:58Z"
+ content="""
+Pretty cool stuff!
+
+Two things occur to me though, the first is that according to my man page, for splice(2) to be applicable one of the fds has to be a pipe, which probably rules it out for file copying. The second is that none of these calls seem to be standard, they're linux specific and my man pages here suggest that prototypes and semantics differ across platforms such that these calls can't be used portably. This makes me wonder whether ENOSYS is really useful here, since if we write code using these calls then we're already presumably tied to a particular platform (linux), so we can safely assume these calls are implemented?
+"""]]

Added a comment: Privilege, Facility, and other terms
diff --git a/posts/taking-time-for-yourself/comment_4_15286ea0d82fe8fbe189f6dc1c166f38._comment b/posts/taking-time-for-yourself/comment_4_15286ea0d82fe8fbe189f6dc1c166f38._comment
new file mode 100644
index 0000000..3a311a2
--- /dev/null
+++ b/posts/taking-time-for-yourself/comment_4_15286ea0d82fe8fbe189f6dc1c166f38._comment
@@ -0,0 +1,89 @@
+[[!comment format=mdwn
+ username="http://www.digital-scurf.org/"
+ nickname="Daniel Silverstone"
+ avatar="http://cdn.libravatar.org/avatar/914439e9dd6721b44b2ea9039fef830f5c6f7d3b6adfceb9e41b62be82e9707f"
+ subject="Privilege, Facility, and other terms"
+ date="2017-08-17T19:53:05Z"
+ content="""
+> I don't follow, are people privileged to be able to paint their garden shed a
+> new colour at the weekend? Or write poetry in their free time? Or watch game
+> of thrones in the evening?
+
+Most of these are examples of the privilege of having sufficient money to afford
+the materials and services required to do this (though I'll admit writing poetry
+likely requires the least in terms of material investment).
+
+> Or isn't that just a pretty standard expectation of what anyone can do in
+> western society? Why do you think hacking on foss is some special privilege
+> vs these other things?
+
+It's a 'pretty standard expectation' for people in my (and perhaps your)
+situation.  People who are, to whatever extent, lucky (or whatever term you'd
+prefer to use) enough to have free time (not a given), sufficient money (not a
+given), sufficient space (not a given), and sufficient spoons (read about
+[spoon theory][] if this isn't a term you're familiar with).
+
+[spoon theory]: https://en.wikipedia.org/wiki/Spoon_theory
+
+> I've written quite a bit of code in my free time, and I have to be honest with
+> you, I don't really consider it a privilege to lock myself in a room at the
+> weekend and bang out code,
+
+It means you are privileged enough to have free time, a computer to 'bang out
+code' on, the room in which to do that, the language facilities to understand
+enough about what you're doing to be able to do that, the good fortune to have
+had an education which enables you to understand and do it at all, and the
+mental fortitude and energy to do that in the first place.
+
+> it's something I do mostly cause the way I look precludes me being able to
+> date pretty ladies (let's just be honest here). I mean I enjoy it too, but I
+> suspect that's due to me not being able to spend much time with the pretty
+> ladies.
+
+I'm not touching this, I'm not in a position to discuss this kind of topic.
+
+> Also, why do you think you need money? The code I've produced over the years
+> cost me exactly $0 to make,
+
+So you've never had to pay for the accomodation you used while coding?  The
+food you burned up in the process?  The computer on which you did the work?
+(And if you're using a computer provided by a workplace then I count that since
+it's a form of remuneration to be permitted to do personal activities with a
+work resource)  Perhaps you haven't ever had to pay for any of that, in which
+case you simply had a financial privilege not afforded to a huge number of
+people who might otherwise be capable of doing FOSS work if they had been
+afforded similar.
+
+> maybe your argument is that I'm middle class or something and so have the
+> time, that's true, I am afforded that, but pretty much all people in a
+> western society have some time for these things, not as much as they should
+> by the way, but that's another story.
+
+I think you're massively overestimating the time, money, capability, spoons,
+etc, available to the average member of \"western society\".  Also I was trying to speak in
+general, not just of \"western society\" which is an artificial narrowing of the
+scope of the discussion, though one I can understand since you seek to
+comprehend from the perspective of your lived experience.
+
+> And I don't really get what you mean by \"facility\" either to be honest...
+
+Again I apologise, I thought it was clear that \"facility\" in this context meant
+access to computer resources, a lack of language barrier with respect to
+learning and participating in F/LOSS communities, a capability in a social
+sense to participate, and indeed an environment which is conducive to the act
+of producing software in the first place.
+
+While this dicussion is interesting to me, I kinda feel like this isn't the
+right forum to continue it.  I also don't feel that I'm necessarily the right
+person to attempt to widen your perspective on this (or indeed that I'm
+necessarily *right* to be trying to do so).  I ask that you consider any
+questions posed in this response as rhetorical rather than as an attempt to
+elicit a response and, unless you have a comment about another aspect of the
+article, that you drop this line of discussion for now.  Perhaps another
+article will be posted in the future which you can engage on this topic with in
+a more productive fashion.
+
+Thank you for engaging with me on this, I think this comment thread will provide
+a good reference for others who want to understand more about this topic area.
+
+"""]]

Added a comment: i don't follow
diff --git a/posts/taking-time-for-yourself/comment_3_2ea53b3a558f78ed9ad5b97edc9aecf0._comment b/posts/taking-time-for-yourself/comment_3_2ea53b3a558f78ed9ad5b97edc9aecf0._comment
new file mode 100644
index 0000000..a6bdc36
--- /dev/null
+++ b/posts/taking-time-for-yourself/comment_3_2ea53b3a558f78ed9ad5b97edc9aecf0._comment
@@ -0,0 +1,17 @@
+[[!comment format=mdwn
+ username="Gravious"
+ avatar="http://cdn.libravatar.org/avatar/48cd8810cb554be5891de36dd031b49c"
+ subject="i don't follow"
+ date="2017-08-17T16:37:03Z"
+ content="""
+I don't follow, are people privileged to be able to paint their garden shed a new colour at the weekend? Or write poetry in their free time? Or watch game of thrones in the evening? Or isn't that just a pretty standard expectation of what anyone can do in western society? Why do you think hacking on foss is some special privilege vs these other things?
+
+I've written quite a bit of code in my free time, and I have to be honest with you, I don't really consider it a privilege to lock myself in a room at the weekend and bang out code, it's something I do mostly cause the way I look precludes me being able to date pretty ladies (let's just be honest here). I mean I enjoy it too, but I suspect that's due to me not being able to spend much time with the pretty ladies.
+
+Also, why do you think you need money? The code I've produced over the years cost me exactly $0 to make,
+maybe your argument is that I'm middle class or something and so have the time, that's true,
+I am afforded that, but pretty much all people in a western society have *some* time for these things,
+not as much as they should by the way, but that's another story.
+
+And I don't really get what you mean by \"facility\" either to be honest...
+"""]]

Move the tags out of the sidebar, they're too numerous
diff --git a/sidebar.mdwn b/sidebar.mdwn
index 50cd19a..4a4f972 100644
--- a/sidebar.mdwn
+++ b/sidebar.mdwn
@@ -12,8 +12,7 @@
 
 [[Suggestion box|suggestion-box]]
 
-[[Tags]]:
-[[!pagestats style="list" pages="./tags/*" among="./posts/*"]]
+[[Tags]]
 
 ---
 
diff --git a/tags.mdwn b/tags.mdwn
index 8e05059..507124c 100644
--- a/tags.mdwn
+++ b/tags.mdwn
@@ -1,5 +1,7 @@
 [[!meta title="Yakking tag cloud"]]
 
-[[!pagestats pages="./tags/*" among="./posts/*"]]
+Below is a tag list:
 
-On the right you can see the tag cloud for this blog.
+[[!pagestats pages="./tags/*" among="./posts/*" style="table"]]
+
+<!-- [[!pagestats style="list" pages="./tags/*" among="./posts/*"]] -->

Added a comment: Perhaps a poor choice of words
diff --git a/posts/taking-time-for-yourself/comment_2_b542ef6e507f572364affaf0eda755d9._comment b/posts/taking-time-for-yourself/comment_2_b542ef6e507f572364affaf0eda755d9._comment
new file mode 100644
index 0000000..4c456de
--- /dev/null
+++ b/posts/taking-time-for-yourself/comment_2_b542ef6e507f572364affaf0eda755d9._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ username="http://www.digital-scurf.org/"
+ nickname="Daniel Silverstone"
+ avatar="http://cdn.libravatar.org/avatar/914439e9dd6721b44b2ea9039fef830f5c6f7d3b6adfceb9e41b62be82e9707f"
+ subject="Perhaps a poor choice of words"
+ date="2017-08-17T13:54:38Z"
+ content="""
+Perhaps
+
+> Sadly, those of us who are blessed with the ability and the interest in contributing to F/LOSS tend to work hard, and then effectively \"work as play\" hard too
+
+was a poor choice of words.  The sense I was trying to get across was **not** that the ability to write code at all is something one gets blessed with, but more that some of are _privileged enough_ to able to work on F/LOSS in our time, off our own backs, with our own money.  In other words, that we have the _richness of time and energy_ to work on F/LOSS.  I feel in some sense blessed that I'm part of the tranche of society which has enough money, time, and facility to do this.
+
+I trust that clarifies things for you.
+"""]]

Added a comment: "blessed with ability"
diff --git a/posts/taking-time-for-yourself/comment_1_34d2e8e5a908ade45dd1a56e06d02b2c._comment b/posts/taking-time-for-yourself/comment_1_34d2e8e5a908ade45dd1a56e06d02b2c._comment
new file mode 100644
index 0000000..a8ad19e
--- /dev/null
+++ b/posts/taking-time-for-yourself/comment_1_34d2e8e5a908ade45dd1a56e06d02b2c._comment
@@ -0,0 +1,28 @@
+[[!comment format=mdwn
+ username="Gravious"
+ avatar="http://cdn.libravatar.org/avatar/48cd8810cb554be5891de36dd031b49c"
+ subject="&quot;blessed with ability&quot;"
+ date="2017-08-17T09:19:53Z"
+ content="""
+\"Sadly, those of us who are blessed with the ability and the interest in contributing to F/LOSS\"
+
+I don't think this is done intentionally, but you seem to be suggesting
+that programming is an ability some people are blessed with, at least
+that's my interpretation of the above.
+
+I profoundly disagree with that notion, and know it to be false through
+personal experience of working with people with no prior programming experience.
+The truth is there is really no special ability required, saying there is is akin
+to what the intellectuals of the late 19th century were saying about literature.
+
+Those intellectuals essentially responded to mass education by obfuscating
+literature and art so that only they could understand it. I see the same
+thing happening when people sneer at languages like Ruby that aim to make
+things just a bit easier. It's a mirror image of the intellectuals sneering
+at popular literature.
+
+_why understood this, and wrote the poignant guide in response. It is maybe
+slightly ironic, given folk's apparent perception of me as someone who is
+against equality that I am now bringing this argument here.
+
+"""]]

publishing
diff --git a/posts/taking-time-for-yourself.mdwn b/posts/taking-time-for-yourself.mdwn
new file mode 100644
index 0000000..580898c
--- /dev/null
+++ b/posts/taking-time-for-yourself.mdwn
@@ -0,0 +1,31 @@
+[[!meta date="Wed, 16 Aug 2017 12:00:06 +0000"]]
+[[!meta title="Taking time for yourself"]]
+[[!meta author="Daniel Silverstone"]]
+
+[Occupational Burnout][] is a thing which people worry about a lot in the "real
+world" and it's why we encourage people, particularly information workers, to
+take regular breaks, to not think about their job outside of work.  Indeed,
+burnout has been referred to as a [disease][] and as such we recommend strongly
+that you avoid it.
+
+Sadly, those of us who are blessed with the ability and the interest in
+contributing to F/LOSS tend to work hard, and then effectively "work as play"
+hard too; which can lead to burning out even more quickly.  It can be even
+worse when the stress originates in your F/LOSS activity because it can be seen
+as making your 'funtime' less enjoyable.
+
+Burning out is a leading cause of people having to [[retire|retiring]] from
+F/LOSS projects and as such, it's really important that you know when to step
+away from F/LOSS and have a break.  You might prefer to spend a few days
+entirely away from your computer or just spend it immersed in a game.  If
+you're particularly lucky/unlucky then you might just be able to work on a
+different project for a little while instead.  Whatever you do to relax, be
+sure to increase your explicit relaxation time along with time you're spending
+on F/LOSS to keep yourself happy and healthy, and able to contribute for a nice
+long time to come.
+
+Your homework this week is to just think about what you enjoy doing which isn't
+work or F/LOSS related at all, and go do it.  Just shoo!
+
+[Occupational Burnout]: https://en.wikipedia.org/wiki/Occupational_burnout
+[disease]: http://www.huffingtonpost.com/arianna-huffington/burnout-third-metric_b_3792354.html

Added a comment: nice
diff --git a/posts/converting-to-argp/comment_1_eb5075d43959b7913afb74cd523db9bf._comment b/posts/converting-to-argp/comment_1_eb5075d43959b7913afb74cd523db9bf._comment
new file mode 100644
index 0000000..380ddec
--- /dev/null
+++ b/posts/converting-to-argp/comment_1_eb5075d43959b7913afb74cd523db9bf._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="Gravious"
+ avatar="http://cdn.libravatar.org/avatar/48cd8810cb554be5891de36dd031b49c"
+ subject="nice"
+ date="2017-08-09T21:22:36Z"
+ content="""
+nice article thanks :D
+"""]]

publishing
diff --git a/posts/retiring.mdwn b/posts/retiring.mdwn
new file mode 100644
index 0000000..9f6fdc8
--- /dev/null
+++ b/posts/retiring.mdwn
@@ -0,0 +1,42 @@
+[[!meta date="Wed, 09 Aug 2017 12:00:08 +0000"]]
+[[!meta title="Retiring from a project"]]
+[[!meta author="Lars Wirzenius"]]
+
+Sometimes you'll want to leave a free software project. It might be
+one you founded or one you've joined. You may have spent years
+contributing to it. You may have formed friendships with other
+contributors: working together on something for a long time tends to
+be a catalyst for that. However, things change and you may want to
+leave. You might no longer be using the software. You might not have
+time to contribute. There might be disagreements about where the
+project is going or how it is going to be operating. You might be moving to
+a different continent. You may be switching careers entirely. You may
+be founding an unrelated company.
+
+A term for this is 'retiring'. Depending on the project, there
+may be a processs to follow, or you may just wing it.
+
+More importantly, there're various aspects to consider when retiring,
+especially if you've been involved for a long time.
+
+* Why are you leaving? It's best to be honest about this, particularly
+  to yourself. Don't rage quit.
+* Will the project survive you leaving?
+* How will users of the software be affected?
+* What about other collaborators on the project?
+* Will there be people to pick up the slack and take over
+  responsibilities? Will they know what to do? Can they ask you for
+  help afterwards?
+* Is there any publicity likely to follow from the retirement?
+  (Probably not, except for high-profile projects.)
+* Are there any assets (computers, etc) that need to be dealt with?
+
+Retiring from a free software project is a lot like leaving paid
+employment. If you do it well, you make sure all your old commitments
+and responsibilities are handed over to new people, and no-one is
+affected too adversely from the change. As a result, you'll be
+remembered with fondness and you're always welcome back.
+
+Unlike paid employment, there's few hard and fast rules in Free Software. It's
+important to remember that though your contributions are valued, you're not
+obliged to continue them if you don't want to.

publishing
diff --git a/posts/my-floss-activity.mdwn b/posts/my-floss-activity.mdwn
new file mode 100644
index 0000000..b80ccfd
--- /dev/null
+++ b/posts/my-floss-activity.mdwn
@@ -0,0 +1,26 @@
+[[!meta date="Wed, 02 Aug 2017 12:00:07 +0000"]]
+[[!meta title="My F/LOSS Activity..."]]
+[[!meta author="Daniel Silverstone"]]
+
+There has been a growing trend among bloggers, particularly among those
+aggregated on [Planet Debian][], in which the blogger writes regularly (usually
+monthly) about their activity in the Free Software communities since their last
+report.
+
+This can be used as a way to "keep yourself honest" in the sense that if you're
+committed to reporting your activity on a month by month basis, you kinda need
+some activity to report.  It can be used as a way to keep your colleagues and
+co-conspiritors in F/LOSS informed about what you're working on, and cool
+things you've done which they might be interested in.  It might be a useful way
+to motivate yourself when each period of time you get to write a nice blog
+posting all about the wonderful things you've achieved and the projects you've
+improved along the way.
+
+Recently I've been persuaded to [join the trend][] and I'm looking forward to
+experiencing the effects I've mentioned above.  I'd like to suggest that all of
+you go out and write yourself a blog post all about the wonderful things you've
+done and then commit yourself to doing so again in X amount of time in the
+future.
+
+[Planet Debian]: http://planet.debian.org/
+[join the trend]: https://blog.digital-scurf.org/posts/floss-activity-2017-06/

Fix typo, thanks mafm
diff --git a/posts/open-data-formats.mdwn b/posts/open-data-formats.mdwn
index a182653..dd31ae3 100644
--- a/posts/open-data-formats.mdwn
+++ b/posts/open-data-formats.mdwn
@@ -14,7 +14,7 @@ amount of pain.  This drove home to me once more that the format of input and
 output of data is such a critical part of software tooling that it must weigh
 as heavily as, or perhaps even more heavily than, the software's functionality.
 
-As [Tannenbaum][] tells us, the great thing about standards is that there's so
+As [Tanenbaum][] tells us, the great thing about standards is that there's so
 many of them to choose from. [XKCD][] tells us, [how that comes about][tgtas].
 Data formats are many and varied, and suffer from specifications as vague as
 "plain text" to things as complex as the structure of data stored in custom
@@ -49,6 +49,6 @@ projects and check that their input and output data formats are well documented
 if appropriate.
 
 [XML]: https://en.wikipedia.org/wiki/XML
-[Tannenbaum]: http://www.goodreads.com/quotes/589703-the-good-thing-about-standards-is-that-there-are-so
+[Tanenbaum]: http://www.goodreads.com/quotes/589703-the-good-thing-about-standards-is-that-there-are-so
 [XKCD]: https://xkcd.com/
 [tgtas]: https://xkcd.com/927/

Correct a corectable
diff --git a/posts/time-adjustment.mdwn b/posts/time-adjustment.mdwn
index 35897d7..318c3b0 100644
--- a/posts/time-adjustment.mdwn
+++ b/posts/time-adjustment.mdwn
@@ -24,7 +24,7 @@ by using the [adjtimex(2)][] or [clock_adjtime(2)][] system calls.
 This can't be done with `CLOCK_MONOTONIC_RAW`,
 but can be done with `CLOCK_MONOTONIC`.
 
-In addition to being corectable,
+In addition to being correctable,
 `CLOCK_MONOTONIC` can be used with [clock_nanosleep(2)][].
 
 This will allow you to sleep for at least the period of time specified,

creating tag page tags/ntp
diff --git a/tags/ntp.mdwn b/tags/ntp.mdwn
new file mode 100644
index 0000000..6e70f03
--- /dev/null
+++ b/tags/ntp.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged ntp"]]
+
+[[!inline pages="tagged(ntp)" actions="no" archive="yes"
+feedshow=10]]

publishing
diff --git a/posts/time-adjustment.mdwn b/posts/time-adjustment.mdwn
new file mode 100644
index 0000000..35897d7
--- /dev/null
+++ b/posts/time-adjustment.mdwn
@@ -0,0 +1,62 @@
+[[!meta date="Wed, 26 Jul 2017 12:00:07 +0000"]]
+[[!meta title="Time - Adjustment"]]
+[[!meta author="Richard Maw"]]
+[[!tag time clock ntp]]
+
+`CLOCK_MONOTONIC_RAW` reflects the underlying hardware for measuring time.
+
+In the short term this is mostly correct,
+but hardware isn't perfect,
+so it can, over long periods, drift
+and no longer be synchronised with
+what the rest of the world considers the time to be.
+
+If you know what the time is meant to be elsewhere in the world,
+then you can adjust your clock to correct it.
+
+Typically your computer will do this
+with the [Network Time Protocol, or NTP,][NTP],
+by asking trusted computers on the internet what the time is.
+
+Actually correcting the time works
+by using the [adjtimex(2)][] or [clock_adjtime(2)][] system calls.
+
+This can't be done with `CLOCK_MONOTONIC_RAW`,
+but can be done with `CLOCK_MONOTONIC`.
+
+In addition to being corectable,
+`CLOCK_MONOTONIC` can be used with [clock_nanosleep(2)][].
+
+This will allow you to sleep for at least the period of time specified,
+though could be interrupted when a signal is delivered,
+(which could happen with [timer_create(2)][]).
+
+## Similar clocks
+
+If it's more important to get the time quickly,
+than to get a more precise time,
+such as if you're profiling real-time software and want to not slow it down,
+then you can use `CLOCK_MONOTONIC_COARSE`.
+
+`CLOCK_MONOTONIC` can be used to time events,
+but works by counting seconds while the computer is running.
+This could be a problem if your computer suspends,
+since then it would stop counting.
+
+The solution to this is the `CLOCK_BOOTTIME` clock,
+which will include seconds spent suspended,
+so could be used to time external events.
+
+---
+
+So far everything discussed has been somewhat abstract,
+divorced from what we commonly understand to be time.
+
+This will be rectified in the next article in the time series,
+where we will be covering "real time".
+
+[NTP]: https://en.wikipedia.org/wiki/NTP
+[adjtimex(2)]: http://man7.org/linux/man-pages/man2/adjtimex.2.html
+[clock_adjtime(2)]: http://man7.org/linux/man-pages/man2/clock_adjtime.2.html
+[clock_nanosleep(2)]: http://man7.org/linux/man-pages/man2/clock_nanosleep.2.html
+[timer_create(2)]: http://man7.org/linux/man-pages/man2/timer_create.2.html

publishing
diff --git a/posts/what-and-why-nix.mdwn b/posts/what-and-why-nix.mdwn
new file mode 100644
index 0000000..57a6dbe
--- /dev/null
+++ b/posts/what-and-why-nix.mdwn
@@ -0,0 +1,140 @@
+[[!meta date="Wed, 19 Jul 2017 12:00:07 +0000"]]
+[[!meta title="What is Nix and Why you should try it!"]]
+[[!meta author="Richard Ipsum"]]
+
+Normally our Unix systems organise the file system in a structure
+called the Filesystem Hierarchy Standard (FHS).
+Installing into an FHS has limitations, what would happen if we want to install,
+for example, two different versions of ruby at the same time?
+Typically this isn't possible without explicitly specifying a
+separate installation directory, if we just install to the usual
+place e.g. `/usr/bin` then we will just overwrite the previous ruby.
+So perhaps we would install one ruby into `/usr/bin` and another into
+`/usr/local/bin`, this is fine, but what about dependent libs?
+Assuming the two different versions of ruby do require different
+dependencies then we have potentially the same problem that
+the dependencies for the 1st ruby might overwrite the dependencies
+for the 2nd ruby.
+
+Nix gets around this to some extent by not using FHS, instead nix installs
+all files into the nix store, which is usually located at `/nix/store`.
+All programs in a nix store are identified by their store path, which
+is uniquely generated for each distinct nix package. As a result of this,
+different versions of the same ruby no longer conflict because they are
+each assigned their own locations within the nix store.
+
+To enable use of programs within the store, nix maintains an environment
+which is basically a mapping of FHS path `->`
+nix store path, where the `->` is a symlink. So for example, let's first install
+ruby 2.0 into our environment
+
+    nix@salo:~$ nix-env -f nixpkgs -iA pkgs.ruby_2_0
+    installing ‘ruby-2.0.0-p648’
+    these paths will be fetched (3.43 MiB download, 19.35 MiB unpacked):
+      /nix/store/bxm4s71qdyh071ap5ywxc63aja62cbyc-gdbm-1.13
+      /nix/store/d2ccapssrq683rj0fr7d7nb3ichxvlsy-ruby-2.0.0-p648
+      /nix/store/h85k47l9zpwwxdsn9kkjmqw8pnfnrwmm-libffi-3.2.1
+      /nix/store/zj8cjx71sqvv46sxfggjpdzqz6nss047-libyaml-0.1.7
+    fetching path ‘/nix/store/bxm4s71qdyh071ap5ywxc63aja62cbyc-gdbm-1.13’...
+    ....
+    building path(s) ‘/nix/store/j649f78ha04mi1vykz601b00ml3qlr9q-user-environment’
+    created 419 symlinks in user environment
+
+we can see the symlink that was just created to our ruby2.0 in the store,
+
+	nix@salo:~$ ls -l $(which irb)
+	lrwxrwxrwx 1 nix nix 67 Jan  1  1970 /home/nix/.nix-profile/bin/irb -> /nix/store/d2ccapssrq683rj0fr7d7nb3ichxvlsy-ruby-2.0.0-p648/bin/irb
+
+	nix@salo:~$ irb
+	irb(main):001:0> puts RUBY_VERSION
+	2.0.0
+
+as you can see we're only able to execute the interactive ruby prompt `irb`
+because it's symlinked into our environment which is, of course, on the `$PATH`,
+
+	nix@salo:~$ echo $PATH
+	/home/nix/.nix-profile/bin:/home/nix/.nix-profile/sbin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
+
+to prove the point about multiple versions on the same system let's swap
+ruby 2.0 for ruby 2.4
+
+	nix@salo:~$ nix-env -f nixpkgs -iA pkgs.ruby_2_4
+	replacing old ‘ruby-2.0.0-p648’
+	installing ‘ruby-2.4.1’
+	these paths will be fetched (3.13 MiB download, 15.32 MiB unpacked):
+	  /nix/store/48xrfkanmx5sshqj1364k2dw25xr4znj-ruby-2.4.1
+	fetching path ‘/nix/store/48xrfkanmx5sshqj1364k2dw25xr4znj-ruby-2.4.1’...
+	...
+	*** Downloading ‘https://cache.nixos.org/nar/00hh9w9nvlbinya1i9j0v7v89pw3zzlrfqps72441k7p2n8zq7d3.nar.	xz’ to ‘/nix/store/48xrfkanmx5sshqj1364k2dw25xr4znj-ruby-2.4.1’...
+	  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
+	                                 Dload  Upload   Total   Spent    Left  Speed
+	100 3205k  100 3205k    0     0   114k      0  0:00:27  0:00:27 --:--:--  125k
+	
+	building path(s) ‘/nix/store/7b2mmk2ffmy1c2bxq7r6y9cn6r0nwn8s-user-environment’
+	created 415 symlinks in user environment
+
+	nix@salo:~$ ls -l $(which irb)
+	lrwxrwxrwx 1 nix nix 62 Jan  1  1970 /home/nix/.nix-profile/bin/irb -> /nix/store/48xrfkanmx5sshqj1364k2dw25xr4znj-ruby-2.4.1/bin/irb
+
+	nix@salo:~$ irb
+	irb(main):001:0> puts RUBY_VERSION
+	2.4.1
+
+You may be wondering whether this is really an improvement, since although
+we have multiple versions of the same package installed on our system,
+we can only have one `ruby` in the environment at any one time. To deal
+with this nix provides the nix-shell utility which constructs an environment
+on demand and runs a new shell based on that environment.
+
+	nix@salo:~/nixpkgs$ nix-shell -p ruby_2_0            
+	these paths will be fetched (4.44 MiB download, 22.57 MiB unpacked):
+	  /nix/store/2l8irkrhvdqmd1h96pcnwv0832p9r901-libffi-3.2.1
+	  /nix/store/945sd3dbynzpkqdd71cqqpsl8gwi9zsq-ruby-2.0.0-p647
+	  /nix/store/m74m7c4qbzml7ipfxzlpxddcn9ah8jrs-gdbm-1.12
+	  /nix/store/zbjyc3ylb9bj3057rk5payv3sr0gnmkc-openssl-1.0.2l
+	  /nix/store/zsgmhsc8pjx9cisbjdk06qqjm8h89lmp-libyaml-0.1.7
+	fetching path ‘/nix/store/m74m7c4qbzml7ipfxzlpxddcn9ah8jrs-gdbm-1.12’...
+	...
+	[nix-shell:~/nixpkgs]$ which irb
+	/nix/store/945sd3dbynzpkqdd71cqqpsl8gwi9zsq-ruby-2.0.0-p647/bin/irb
+	
+	[nix-shell:~/nixpkgs]$ irb
+	irb(main):001:0> puts RUBY_VERSION
+	2.0.0
+	=> nil
+	irb(main):002:0>
+
+	nix@salo:~/nixpkgs$ nix-shell -p ruby_2_4
+	these paths will be fetched (3.13 MiB download, 15.30 MiB unpacked):
+	  /nix/store/wly748apb5r37byvvgq85hshgzcahv0y-ruby-2.4.0
+	fetching path ‘/nix/store/wly748apb5r37byvvgq85hshgzcahv0y-ruby-2.4.0’...
+	...
+	[nix-shell:~/nixpkgs]$ which irb
+	/nix/store/wly748apb5r37byvvgq85hshgzcahv0y-ruby-2.4.0/bin/irb
+	
+	[nix-shell:~/nixpkgs]$ irb
+	irb(main):001:0> puts RUBY_VERSION
+	2.4.0
+	=> nil
+	irb(main):002:0>
+
+We haven't even started to scratch the surface in this intro, there's lots
+of really exciting stuff I've not even mentioned, like how you can always
+rollback to the environment at an earlier state: every mutation to the
+environment is recorded, so every time you install or uninstall a nixpkg
+a new "generation" of the environment is created, and it's always possible to
+immediately rollback to some earlier generation. NixOS itself takes all these
+super exciting ideas and applies them to an entire operating system, where
+each user has their own environment, so ruby for one user might mean ruby2.0
+and ruby for another might mean ruby2.4. Hopefully it's clear now how these
+different versions of the same package can live in harmony under NixOS.
+
+I hope I've managed to convey some of nix's coolness in this short space,
+if I have then you should definitely check lethalman's "nix-pills"[1]
+series for a really deep explanation of how nix works internally and how
+to create nixpkgs from scratch. There's also ofcourse the NixOS website[2]
+and `#nixos` on `irc.freenode.net` which is probably one of the friendliest
+communities out there.
+
+[1]: http://lethalman.blogspot.co.uk/2014/07/nix-pill-1-why-you-should-give-it-try.html
+[2]: https://nixos.org/

Added a comment: start here page
diff --git a/suggestion-box/comment_4_a5f5482356eed8ff507397900a4085c6._comment b/suggestion-box/comment_4_a5f5482356eed8ff507397900a4085c6._comment
new file mode 100644
index 0000000..ea2d04c
--- /dev/null
+++ b/suggestion-box/comment_4_a5f5482356eed8ff507397900a4085c6._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="abhidg"
+ avatar="http://cdn.libravatar.org/avatar/5323e346ef1a67d6c2ee6ee9ac32b68d"
+ subject="start here page"
+ date="2017-07-14T11:39:04Z"
+ content="""
+Nice collection of articles! Might be helpful to have a \"start here\" page for people new to the site - also grouping things by technical level. For example the introduction to... series could be tagged somehow so people can just start off with the basics.
+"""]]

creating tag page tags/clock
diff --git a/tags/clock.mdwn b/tags/clock.mdwn
new file mode 100644
index 0000000..50f3f75
--- /dev/null
+++ b/tags/clock.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged clock"]]
+
+[[!inline pages="tagged(clock)" actions="no" archive="yes"
+feedshow=10]]