[Valid Atom 1.0] This is a valid Atom 1.0 feed.


This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.


  1. <?xml version="1.0" encoding="utf-8"?>
  2. <feed xmlns=""><title></title><link href="" rel="alternate"></link><link href="" rel="self"></link><id></id><updated>2019-04-14T22:05:00-04:00</updated><entry><title>Including Remotes in Team Offsites</title><link href="" rel="alternate"></link><published>2019-04-14T22:05:00-04:00</published><updated>2019-04-14T22:05:00-04:00</updated><author><name>Annika Backstrom</name></author><id>,2019-04-14:/including-remotes-in-team-offsites</id><summary type="html">&lt;p&gt;&lt;em&gt;This post was originally written in October 2014, but I lost it in my drafts.
  3. It's still relevant in 2019, so I'm sending it out into the world.&lt;/em&gt;&lt;/p&gt;
  4. &lt;p&gt;I'm starting this blog post on a northbound train, fresh off my team's annual
  5. planning offsite. As a remote worker I'm fortunate …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;This post was originally written in October 2014, but I lost it in my drafts.
  6. It's still relevant in 2019, so I'm sending it out into the world.&lt;/em&gt;&lt;/p&gt;
  7. &lt;p&gt;I'm starting this blog post on a northbound train, fresh off my team's annual
  8. planning offsite. As a remote worker I'm fortunate to have a manager who would
  9. bring me to Brooklyn for a single day, especially so soon after a week-long
  10. office visit.&lt;/p&gt;
  11. &lt;p&gt;A trip like this is recognition that we're not great at including remote folks
  12. in this type of brainstorming session. (I know this first-hand from last year's
  13. planning.) So, yay for me, I was there in person this year. Unfortunately, our
  14. other remote team member was &lt;em&gt;not&lt;/em&gt; able to make the trip. His experience was far
  15. inferior to the experience of people in the room. After 7 hours he probably had
  16. trouble staying awake, much less engaged.&lt;/p&gt;
  17. &lt;p&gt;Our only piece of equipment during this session was a MacBook with a fisheye
  18. lens. I noticed a few problems, much which I'm extrapolating from my own
  19. experiences as a remote participant:&lt;/p&gt;
  20. &lt;ul&gt;
  21. &lt;li&gt;
  22. &lt;p&gt;Video quality. The wider angle of a fisheye gets more people into the frame,
  23.  but makes it harder to read what those people may be writing. Even without
  24.  fisheye, large enough writing would limit the amount of stuff you can fit on
  25.  the board.&lt;/p&gt;
  26. &lt;/li&gt;
  27. &lt;li&gt;
  28. &lt;p&gt;Static view. You're totally dependent on people moving the camera when the
  29.  speaker changes or people move around.&lt;/p&gt;
  30. &lt;/li&gt;
  31. &lt;li&gt;
  32. &lt;p&gt;Can't easily reference artifacts from earlier exercises. We had lots of notes
  33.  taped to walls or written on whiteboards. A remote person can't wander around
  34.  and reflect on these things.&lt;/p&gt;
  35. &lt;/li&gt;
  36. &lt;li&gt;
  37. &lt;p&gt;More generally: dropped audio is going to happen, and it's jarring. Also, some
  38.  people just talk quietly, or get drowned out by background chatter if several
  39.  groups are working nearby.&lt;/p&gt;
  40. &lt;/li&gt;
  41. &lt;/ul&gt;
  42. &lt;p&gt;Right away, there's a short list of things I'd like to try.&lt;/p&gt;
  43. &lt;p&gt;First, use real microphones. Should the person leading a session have a lapel
  44. mic and repeat questions and comments? Can you easily feed two or more mics into
  45. one laptop? Are there omnidirectional mics that don't suck? Sometimes you want
  46. audio from all over the room, sometimes you want a smaller range. Do you need a
  47. bunch of microphones for different situations?&lt;/p&gt;
  48. &lt;p&gt;Remote people need reassurance that they can speak up when they can't see/hear.
  49. I am sometimes reluctant to speak up as the only remote person, but the people
  50. on the other end probably have no idea you can't hear them. It's a difficult but
  51. necessary disruption. What can we do to make this easier on everyone involved?&lt;/p&gt;
  52. &lt;p&gt;Make a conscious effort to engage remote participants. Have they said anything
  53. lately? Do they have that glassy look in their eyes? If you're writing things
  54. down, take a moment to read them out loud, &lt;em&gt;especially&lt;/em&gt; if people in the room
  55. can see those notes. &lt;em&gt;Ask&lt;/em&gt; if they can see/hear.&lt;/p&gt;
  56. &lt;p&gt;Other solutions are not as easy to come by. How do you share whiteboards in
  57. real-time? How do you make artifacts available remotely? What's the digital
  58. analog of moving sticky notes around a wall? I would love to know, or help
  59. create these tools.&lt;/p&gt;
  60. &lt;p&gt;I love to work with my team face-to-face, but for those times when I can't, I
  61. hope we can find and create the tools and processes that foster collaboration
  62. and engagement.&lt;/p&gt;</content><category term="remotes"></category></entry><entry><title>Tracking Cash with You Need a Budget</title><link href="" rel="alternate"></link><published>2019-01-30T00:00:00-05:00</published><updated>2019-01-30T00:00:00-05:00</updated><author><name>Annika Backstrom</name></author><id>,2019-01-30:/tracking-cash-with-you-need-a-budget</id><summary type="html">&lt;p&gt;You Need a Budget (YNAB) is a budgeting app for web and mobile that helps
  63. you track your spending and save for the future. Here's how I track cash spending
  64. in YNAB without wanting to tear my hair out.&lt;/p&gt;</summary><content type="html">&lt;p&gt;You Need a Budget (aka YNAB, &lt;a href=""&gt;referral
  65. link&lt;/a&gt;) is a budgeting app for
  66. web and mobile that helps you track your spending and save for the future.
  67. Here's how I track cash spending in YNAB without wanting to tear my hair out.&lt;/p&gt;
  68. &lt;h2&gt;What's YNAB?&lt;/h2&gt;
  69. &lt;p&gt;If you're already familiar with YNAB, feel free to skip ahead! For those of you
  70. who haven't used it, YNAB is a bit like a spreadsheet in Turbo Mode. As a
  71. budgeting app, it lets you allocate your money into categories of your choosing,
  72. like a virtual "envelope" system. You can track spending by category or category
  73. groups, differentiate between budgeted funds and other assets like 401(k) or
  74. investments, and link accounts to a bank to import transactions.&lt;/p&gt;
  75. &lt;p&gt;If you're using a debit card, YNAB is pretty straightforward. You plug your
  76. paychecks in as income, that money goes into a "To be Budgeted" pool, you
  77. allocate those funds into your categories. Enter in your transactions, assign
  78. each to a category, and the "balance" in your categories goes down. If you
  79. overallocate or overspend a category, YNAB will warn you that you've spent more
  80. money that you have. Credit cards are a just a shade more complicated, but the
  81. basics are the same.&lt;/p&gt;
  82. &lt;p&gt;For more background on the basics, check the quick intro video &lt;a href=""&gt;on their help
  83. site&lt;/a&gt;.&lt;/p&gt;
  84. &lt;h2&gt;Why cash?&lt;/h2&gt;
  85. &lt;p&gt;Credit and debit transactions are convenient, because both can be auto-imported
  86. right from your bank or card company. Missing a transaction isn't the end of the
  87. world: you can just get it off the credit card statement later. Your wallet has
  88. no such safety net.&lt;/p&gt;
  89. &lt;p&gt;But, cash is a necessity in lots of situations. I tried to fudge it for a while,
  90. using throwaway categories like "Stuff I Forgot to Budget For" (yes, this is a
  91. default category). That wasn't helping me understand where my money was going,
  92. which was one of my early goals. Rather than continuing to ignore the problem, I
  93. tweaked my setup. Now I can use as much cash as I need without breaking my
  94. budget.&lt;/p&gt;
  95. &lt;h2&gt;Taming your Wallet&lt;/h2&gt;
  96. &lt;p&gt;The first thing I did was &lt;strong&gt;create a new Cash account&lt;/strong&gt;. The initial balance of
  97. the account should be whatever's in your wallet. YNAB will add that money into
  98. &lt;em&gt;To be Budgeted&lt;/em&gt;, and you'll have to allocate it to categories.&lt;/p&gt;
  99. &lt;p&gt;At this point, &lt;strong&gt;your wallet is now part of the budget.&lt;/strong&gt; Just as importantly,
  100. your budget is just &lt;strong&gt;a pool of money&lt;/strong&gt; spread across several debit and credit
  101. accounts. What's in your wallet isn't specifically allocated towards coffee or
  102. gas or groceries or anything else. If you stop at a food truck to spend the last
  103. $10 in your Dining Out category, YNAB doesn't force you to spend from a certain
  104. source. Cash only merchant? No problem, it's in the budget.&lt;/p&gt;
  105. &lt;p&gt;This sounds straightforward but it takes discipline. You can't be lazy with
  106. entering your expenses: the auto-import safety net is gone. There are a few
  107. things that make cash tracking easier for me:&lt;/p&gt;
  108. &lt;ul&gt;
  109. &lt;li&gt;&lt;strong&gt;Enter ATM withdrawals as a transfer.&lt;/strong&gt; You don't have to recategorize
  110.  transfers. Move money around between your checking and cash as it suits you,
  111.  without affecting the budget.&lt;/li&gt;
  112. &lt;li&gt;&lt;strong&gt;Try and enter expenses as you make them.&lt;/strong&gt; It's tempting to batch receipts,
  113.  but you can lose track of them, or the stack can become overwhelming. If
  114.  that's a risk for you, try to enter your transactions as soon as you make a
  115.  purchase. Added bonus: the app will associate the Payee with a location and
  116.  suggest it next time.&lt;/li&gt;
  117. &lt;li&gt;&lt;strong&gt;Round up to the nearest dollar.&lt;/strong&gt; This one is critical for me. It's too
  118.  overwhelming if I have to track pennies and reconcile them away when my wallet
  119.  doesn't match YNAB. I usually make a handful of cash transactions every week,
  120.  and the change gets donated or saved for parking meters.&lt;/li&gt;
  121. &lt;/ul&gt;
  122. &lt;p&gt;Tracking cash helps me every week. I can carry cash without guilt that it's
  123. unaccounted for. I can stop at cash-only shops. I have a few dollars on hand for
  124. a tip. My spending habits are more accurately reflected in YNAB, not quite down
  125. to the penny but certainly better than before. My budget is about as accurate as
  126. it can be. 💸&lt;/p&gt;</content><category term="money"></category><category term="budgeting"></category><category term="ynab"></category></entry><entry><title>2018 Retrospective</title><link href="" rel="alternate"></link><published>2018-12-30T16:58:00-05:00</published><updated>2018-12-30T16:58:00-05:00</updated><author><name>Annika Backstrom</name></author><id>,2018-12-30:/2018-retrospective</id><summary type="html">&lt;p&gt;As is tradition, I find myself thinking about the past
  127. year and future plans over the December holiday.&lt;/p&gt;</summary><content type="html">&lt;p&gt;As is &lt;a href="/tag/irresolutions"&gt;tradition&lt;/a&gt;, I find myself thinking about the past
  128. year and future plans over the December holiday.&lt;/p&gt;
  129. &lt;h2&gt;Social Networking&lt;/h2&gt;
  130. &lt;p&gt;In 2018, I ditched all my big social accounts. I deleted my Twitter accounts in
  131. August, Facebook in November, and this month deleted my little-used Instagram
  132. account. (Instagram was acquired by Facebook in 2012.) I've been all-in on
  133. &lt;a href=""&gt;Mastodon&lt;/a&gt; since 2017, creating my first account in
  134. March before settling at &lt;a href=""&gt;@[email protected]&lt;/a&gt; the
  135. following month.&lt;/p&gt;
  136. &lt;p&gt;The incentives for the big social networks are all wrong, and I think that sort
  137. of centralization weakens the web. I would rather see a diverse ecosystem of
  138. smaller interconnected servers, than these large ad-supported databases that
  139. prey on our worst impulses. I plan to keep advocating for the Fediverse and
  140. similar alternatives in 2019.&lt;/p&gt;
  141. &lt;h2&gt;Shopping&lt;/h2&gt;
  142. &lt;p&gt;I severely cut back on my Amazon purchasing this year. I live in a rural area and
  143. delivery services overall hold great appeal, but it's harder and harder to
  144. justify supporting Amazon. It's easy to mindlessly purchase everything from
  145. Amazon, but with a little effort, I'm finding that other shops are very
  146. competitive on price and delivery time.&lt;/p&gt;
  147. &lt;p&gt;Ideally, I could completely eliminate Amazon delivery in 2019. We still have
  148. Kindle readers, but I'm looking into &lt;a href=""&gt;Kobo&lt;/a&gt; as an
  149. alternative. I'm a heavy &lt;a href=""&gt;Twitch&lt;/a&gt; user (Amazon bought Twitch
  150. in 2014) and I'm stuck with that for a long while, but I hope to stop Amazon
  151. Prime next year and I don't &lt;em&gt;think&lt;/em&gt; I would miss any Twitch benefits.&lt;/p&gt;
  152. &lt;h2&gt;Mobile Phone&lt;/h2&gt;
  153. &lt;p&gt;This actually happened near the end of 2017, but I made the jump from Apple iOS
  154. (iPhone) to Google Android (Samsung Galaxy Note8). Google's not &lt;em&gt;much&lt;/em&gt; better
  155. than Apple in a lot of regards, but at least the app store ecosystem is more
  156. diverse.&lt;/p&gt;
  157. &lt;p&gt;There are some open-source mobiles on the market, and I'll continue to watch
  158. them in 2019.&lt;/p&gt;
  159. &lt;h2&gt;Email&lt;/h2&gt;
  160. &lt;p&gt;Gmail (via Google Apps) hosted my email for years. Their dashboard says I created my account
  161. in 2008, but I don't have records of when Gmail became my primary. I think I was
  162. still using self-hosted qmail before that move.&lt;/p&gt;
  163. &lt;p&gt;In 2018, I migrated from Gmail to
  164. &lt;a href=""&gt;Fastmail&lt;/a&gt; (referral link). It's been a
  165. very positive experience, I really have nothing but good things to say about
  166. Fastmail.&lt;/p&gt;
  167. &lt;h2&gt;Web Search&lt;/h2&gt;
  168. &lt;p&gt;Around the same time as the Fastmail move, I changed my primary search engine
  169. back to &lt;a href=""&gt;DuckDuckGo&lt;/a&gt;. I tried to do this years ago, but
  170. DDG just wasn't there yet. This time around, the change has stuck. I miss Google
  171. about 5% of the time, and when I need it, Google (and hundreds of other
  172. searches) are just a &lt;a href=""&gt;bang&lt;/a&gt; away.&lt;/p&gt;
  173. &lt;h2&gt;Final Thoughts&lt;/h2&gt;
  174. &lt;p&gt;In 2019, I'll continue to look for alternatives that are better aligned with my
  175. own values. For gaming, I'm hoping we'll see more DRM-free titles on Steam
  176. alternatives like &lt;a href=""&gt;Kartridge&lt;/a&gt; and
  177. &lt;a href=""&gt;;/a&gt;. Music consumption also leaves room for improvement.
  178. Maybe this means more consistent scrobbling from Spotify to
  179. &lt;a href=""&gt;;/a&gt; and making sure I buy DRM-free or physical
  180. copies of songs in heavy rotation.&lt;/p&gt;</content><category term="new year"></category><category term="irresolutions"></category></entry><entry><title>Ansible Playbook: Reboot Servers</title><link href="" rel="alternate"></link><published>2018-10-08T10:50:00-04:00</published><updated>2018-10-08T10:50:00-04:00</updated><author><name>Annika Backstrom</name></author><id>,2018-10-08:/ansible-playbook-reboot-servers</id><summary type="html">&lt;p&gt;An Ansible playbook to reboot servers that require a reboot.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Here's a short Ansible playbook that reboots servers that need a reboot.&lt;/p&gt;
  181. &lt;pre&gt;&lt;code&gt;---
  182. - hosts: all
  183.  tasks:
  184.    - name: check if reboot is required
  185.      stat: path=/var/run/reboot-required
  186.      register: reboot_file
  187.    - name: reboot host
  188.      reboot:
  189.      when: reboot_file.stat.exists == true
  190. &lt;/code&gt;&lt;/pre&gt;
  192. &lt;p&gt;My hosts are configured to auto-install security updates. Many times, when I log
  193. into a host, I'll get a notification that a reboot is required. This playbook
  194. lets me broadcast a conditional reboot to all my hosts.&lt;/p&gt;
  195. &lt;p&gt;The above playbook requires the &lt;a href=""&gt;reboot module&lt;/a&gt; added in Ansible 2.7. The
  196. &lt;code&gt;command&lt;/code&gt; module could be used on earlier hosts to manually issue a &lt;code&gt;reboot&lt;/code&gt;.&lt;/p&gt;</content><category term="ansible"></category><category term="Linux"></category></entry><entry><title>Easier Drone Deploys with Docker Images</title><link href="" rel="alternate"></link><published>2018-06-16T12:23:00-04:00</published><updated>2018-06-16T12:23:00-04:00</updated><author><name>Annika Backstrom</name></author><id>,2018-06-16:/easier-drone-deploys-docker-images</id><summary type="html">&lt;p&gt;Custom Docker images for Drone CI can reduce redundancy and simplify setup of
  197. new deployments. We'll look at incrementally building custom images for deploys,
  198. and how that affects our Drone configuration.&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;Custom Docker images for Drone CI can reduce redundancy and simplify setup of
  199. new deployments. We'll look at incrementally building custom images for deploys,
  200. and how that affects our Drone configuration.&lt;/em&gt;&lt;/p&gt;
  201. &lt;p&gt;&lt;a href=""&gt;Drone CI&lt;/a&gt; configuration files have a straightforward syntax similar to that
  202. of a &lt;code&gt;docker-compose.yml&lt;/code&gt; file: you specify different build stages, pick an image
  203. for each stage, and run commands against that image:&lt;/p&gt;
  204. &lt;pre&gt;&lt;code&gt;---
  205. pipeline:
  206.  build:
  207.    image: golang
  208.    commands:
  209.      - go get
  210.      - go build
  211.      - go test
  212. &lt;/code&gt;&lt;/pre&gt;
  214. &lt;p&gt;Drone also supports &lt;a href=""&gt;secrets&lt;/a&gt;, which keep sensitive information like SSH keys
  215. and API tokens out of your (possibly public) configuration file. A deploy step
  216. might look for credentials in environment variables, store those credentials in
  217. a file, and perform commands using those credentials:&lt;/p&gt;
  218. &lt;pre&gt;&lt;code&gt;---
  219. pipeline:
  220.  deploy:
  221.    image: alpine
  222.    secrets: [ ssh_private_key, ssh_host_key ]
  223.    commands:
  224.      - mkdir &amp;quot;$${HOME}/.ssh&amp;quot;
  225.      - echo -n &amp;quot;$${SSH_PRIVATE_KEY}&amp;quot; &amp;gt; &amp;quot;$${HOME}/.ssh/id_rsa&amp;quot;
  226.      - chmod 700 &amp;quot;$${HOME}/.ssh/id_rsa&amp;quot;
  227.      - echo &amp;quot;$${SSH_HOST_KEY}&amp;quot; &amp;gt;&amp;gt; &amp;quot;$${HOME}/.ssh/known_hosts&amp;quot;
  228.      - scp -r ./output [email protected]:/var/www/html
  229. &lt;/code&gt;&lt;/pre&gt;
  231. &lt;p&gt;This is prone to repetition. For each new repository (at least without the paid
  232. &lt;a href=""&gt;global secrets&lt;/a&gt; feature), you have to inject secrets which may be the same
  233. as other projects in your CI environment, and perform the same setup steps to
  234. expose those secrets to shell commands.&lt;/p&gt;
  235. &lt;h2&gt;Building custom deploy images&lt;/h2&gt;
  236. &lt;p&gt;Instead of all this repetition, let's build our secrets into images we control.
  237. We'll start with a generic image, &lt;a href=""&gt;drone-rsync-ssh&lt;/a&gt;:&lt;/p&gt;
  238. &lt;pre&gt;&lt;code&gt;FROM alpine:3.7
  239. COPY /usr/bin/drone-ssh-keys
  240. RUN apk add --no-cache openssh-client rsync
  241. &lt;/code&gt;&lt;/pre&gt;
  243. &lt;p&gt;The mkdir/echo/chmod behavior from our verbose Drone config file is wrapped up
  244. in &lt;a href=""&gt;;/a&gt;, for easy calling in the future. Here's how
  245. &lt;code&gt;.drone.yml&lt;/code&gt; changes under this image:&lt;/p&gt;
  246. &lt;pre&gt;&lt;code&gt;---
  247. pipeline:
  248.  deploy:
  249.    image: drone-rsync-ssh
  250.    secrets: [ ssh_private_key, ssh_host_key ]
  251.    commands:
  252.      - drone-ssh-keys
  253.      - scp -r ./output [email protected]:/var/www/html
  254. &lt;/code&gt;&lt;/pre&gt;
  256. &lt;p&gt;We've already cleaned up this build stage, but there's still redundancy in
  257. adding secrets to the Drone repository. Plus, if our secrets change, we have to
  258. update every repository that uses them.&lt;/p&gt;
  259. &lt;p&gt;We can go a step further if we have access to private Docker images. Let's
  260. bundle those credentials right in our image with a new Dockerfile:&lt;/p&gt;
  261. &lt;pre&gt;&lt;code&gt;FROM alpine:3.7
  262. COPY deploy-assets /deploy-assets
  263. RUN apk add --no-cache openssh-client rsync &amp;amp;&amp;amp; \
  264.  mkdir /root/.ssh &amp;amp;&amp;amp; \
  265.  cd /deploy-assets &amp;amp;&amp;amp; \
  266.  cp deploy.key known_hosts config /root/.ssh &amp;amp;&amp;amp; \
  267.  chmod 0600 /root/.ssh/deploy.key
  268. &lt;/code&gt;&lt;/pre&gt;
  270. &lt;p&gt;The &lt;code&gt;deploy-assets&lt;/code&gt; directory contains everything we need to run deploys,
  271. including secrets and file copying utilities. Our new &lt;code&gt;.drone.yml&lt;/code&gt; is very
  272. compact, and we can call our commands without any additional setup:&lt;/p&gt;
  273. &lt;pre&gt;&lt;code&gt;---
  274. pipeline:
  275.  deploy:
  276.    image: drone-rsync-ssh-secrets
  277.    commands:
  278.      - scp -r ./output deploy:/var/www/html
  279. &lt;/code&gt;&lt;/pre&gt;
  281. &lt;p&gt;This image is available in your environment for any pipeline that has similar
  282. deploy steps. No more setting up secrets every time you add a repository to
  283. Drone.&lt;/p&gt;
  284. &lt;h2&gt;Security considerations&lt;/h2&gt;
  285. &lt;p&gt;As always, treat your secrets with care. At minimum, keep these things in mind:&lt;/p&gt;
  286. &lt;ul&gt;
  287. &lt;li&gt;Don't push your secrets to publicly available image repositories (e.g. public
  288.  Docker Hub) or Git repositories&lt;/li&gt;
  289. &lt;li&gt;When using Drone secrets, use &lt;a href=""&gt;skip branches&lt;/a&gt; to avoid exposing your
  290.  secrets to untrusted code&lt;/li&gt;
  291. &lt;/ul&gt;
  292. &lt;!-- links --&gt;</content><category term="docker"></category><category term="drone ci"></category></entry><entry><title>TaskPaper 3: Child-aware Archiving</title><link href="" rel="alternate"></link><published>2018-06-10T12:11:00-04:00</published><updated>2018-06-10T12:11:00-04:00</updated><author><name>Annika Backstrom</name></author><id>,2018-06-10:/taskpaper-3-child-aware-archiving</id><summary type="html">&lt;p&gt;Here's a TaskPaper "Archive" replacement that keeps child tasks with
  293. their parents.&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href=""&gt;TaskPaper 3&lt;/a&gt; is a critical part of my workflow.&lt;sup id=sf-taskpaper-3-child-aware-archiving-1-back&gt;&lt;a href=#sf-taskpaper-3-child-aware-archiving-1 class=simple-footnote title="For more about why I like and how I use TaskPaper, see IFTTT and Taskpaper TODO Workflow."&gt;1&lt;/a&gt;&lt;/sup&gt; I use it to track what's happening now and what's up
  294. next, but also what I've done recently. Having a log of recent tasks is
  295. great during times of retrospection or when timeline questions come up.&lt;/p&gt;
  296. &lt;p&gt;In TaskPaper, completed tasks are tagged with &lt;code&gt;@done&lt;/code&gt;. The built-in
  297. "Archive" action moves all done tasks to an "Archive" Project.
  298. Unfortunately, Archive will also disconnect child tasks from their
  299. parents:&lt;/p&gt;
  300. &lt;video autoplay loop="" controls preload=none poster="/media/2018/06/taskpaper.jpg" src="/media/2018/06/taskpaper.mp4"&gt;&lt;/video&gt;
  302. &lt;p&gt;&lt;em&gt;Fotunately&lt;/em&gt;, TaskPaper is scriptable with JavaScript, and we can create
  303. a custom action with archiving more to my liking.&lt;/p&gt;
  304. &lt;h2&gt;Goals&lt;/h2&gt;
  305. &lt;p&gt;Ideally, we would not move child tasks to the Archive until their parent
  306. task is complete. The default behavior disconnects children from their
  307. parents, losing important context. Here's a document before archiving:&lt;/p&gt;
  308. &lt;pre&gt;&lt;code&gt;Inbox:
  309.  - Build login page @due(2018-12-01)
  310.    - Add auth0 library to composer.json @done(2018-08-17)
  311.    - Write tests
  312.    - Create tables
  313.  - Book hotel for trip to sf @done(2018-06-10)
  314.    - Get corporate card @done(2018-06-10)
  315. &lt;/code&gt;&lt;/pre&gt;
  317. &lt;p&gt;And, after archiving:&lt;/p&gt;
  318. &lt;pre&gt;&lt;code&gt;Inbox:
  319.  - Build login page @due(2018-12-01)
  320.    - Write tests
  321.    - Create tables
  322. Archive:
  323.  - Add auth0 library to composer.json @done(2018-08-17)
  324.  - Book hotel for trip to sf @done(2018-06-10)
  325.    - Get corporate card @done(2018-06-10)
  326. &lt;/code&gt;&lt;/pre&gt;
  328. &lt;p&gt;Our history no longer tells us why the auth0 task existed. My preference
  329. is to keep the hierarchy, and only archive the "top-level" tasks that
  330. sit directly below a Project.&lt;/p&gt;
  331. &lt;h2&gt;The "Smart Archive" extension&lt;/h2&gt;
  332. &lt;p&gt;Here's an extension that accomplishes our goal.&lt;/p&gt;
  333. &lt;ol&gt;
  334. &lt;li&gt;Ensure an "Archive" project exists&lt;/li&gt;
  335. &lt;li&gt;Find all &lt;code&gt;@done&lt;/code&gt; tasks that are immediate children of projects (excluding the
  336. Archive project)&lt;/li&gt;
  337. &lt;li&gt;Move these items to the Archive, preserving their current order in the
  338. document&lt;/li&gt;
  339. &lt;/ol&gt;
  340. &lt;pre&gt;&lt;code class=js&gt;var TaskPaper = Application('TaskPaper');
  342. function fn(editor, options) {
  343.    var outline = editor.outline;
  344.    var archive = outline.evaluateItemPath("//Archive:")[0];
  346.    var insertBeforeElement = archive ? archive.firstChild : undefined;
  348.    outline.groupUndoAndChanges(() =&amp;gt; {
  349.        if (typeof archive === "undefined") {
  350.            archive = outline.createItem("Archive:");
  351.            outline.root.appendChildren(archive);
  352.        }
  354.        var topLevelItems = outline.evaluateItemPath("project/@done except Archive//*")
  356. =&amp;gt; {
  357.            item.removeFromParent();
  358.            if (insertBeforeElement) {
  359.                archive.insertChildrenBefore(item, insertBeforeElement)
  360.            } else {
  361.                archive.appendChildren(item);
  362.            }
  363.        });
  364.    });
  365. }
  367. TaskPaper.documents[0].evaluate({
  368.    script: fn.toString()
  369. })
  370. &lt;/code&gt;&lt;/pre&gt;
  372. &lt;p&gt;If we install this script to the TaskPaper Scripts folder, we can quickly run it
  373. using &lt;code&gt;cmd-shift-P&lt;/code&gt; and typing the script name. I find it's also helpful to
  374. &lt;a href=""&gt;rebind the default Archive shortcut&lt;/a&gt; in the Keyboard preference pane to
  375. combat muscle memory.&lt;/p&gt;
  376. &lt;p&gt;For more information about TaskPaper scripting, see the &lt;a href=""&gt;Scripting API&lt;/a&gt;, &lt;a href=""&gt;Extensions Wiki&lt;/a&gt;, and &lt;a href=""&gt;script installation guide&lt;/a&gt;.&lt;/p&gt;&lt;ol class=simple-footnotes&gt;&lt;li id=sf-taskpaper-3-child-aware-archiving-1&gt;For more about
  377. why I like and how I use TaskPaper, see &lt;a href=ifttt-and-taskpaper-todo-workflow&gt;IFTTT and Taskpaper TODO
  378. Workflow&lt;/a&gt;. &lt;a href=#sf-taskpaper-3-child-aware-archiving-1-back class=simple-footnote-back&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;</content><category term="taskpaper"></category><category term="javascript"></category></entry><entry><title>Ansible, Postfix, and Mailgun</title><link href="" rel="alternate"></link><published>2018-03-20T14:27:00-04:00</published><updated>2018-03-20T14:27:00-04:00</updated><author><name>Annika Backstrom</name></author><id>,2018-03-20:/ansible-postfix-and-mailgun</id><summary type="html">&lt;p&gt;Here's a simple set of Ansible files that allow your hosts to send mail through
  379. Mailgun via Postfix.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Here's a simple set of Ansible files that allow your hosts to send mail through
  380. &lt;a href=""&gt;Mailgun&lt;/a&gt; via Postfix. I've incorporated this setup into my &lt;code&gt;common.yml&lt;/code&gt; playbook that
  381. acts as a baseline configuration for all my hosts (handling timezone setup,
  382. hostname normalization, etc.). With this addition, all my hosts can send email,
  383. regardless of their network.&lt;/p&gt;
  384. &lt;p&gt;This setup aggressively rewrites any outgoing From: addresses. This may not suit
  385. your needs, but it's fine for me.&lt;/p&gt;
  386. &lt;p&gt;&lt;code&gt;roles/common/tasks/smtp.yml&lt;/code&gt;, included from &lt;code&gt;roles/common/tasks/main.yml&lt;/code&gt;:&lt;/p&gt;
  387. &lt;pre&gt;&lt;code&gt;- name: install mail packages
  388.  apt: pkg={{item}}
  389.  with_items:
  390.    - postfix
  391.    - mailutils
  392.    - libsasl2-modules
  394. - name: install postfix config files
  395.  template: src={{item.src}} dest={{item.dest}} owner=root group=root mode=0644
  396.  notify: restart postfix
  397.  with_items:
  398.    - { src: header_check, dest: /etc/postfix/header_check }
  399.    - { src:, dest: /etc/postfix/ }
  400.    - { src: mailname, dest: /etc/mailname }
  401.    - { src: sender_canonical_maps, dest: /etc/postfix/sender_canonical_maps }
  403. - name: enable postfix service
  404.  service: name=postfix enabled=yes
  405. &lt;/code&gt;&lt;/pre&gt;
  407. &lt;p&gt;&lt;code&gt;roles/common/defaults/;/code&gt;, default variables:&lt;/p&gt;
  408. &lt;pre&gt;&lt;code&gt;---
  409. # 2525 is allowed on Google Cloud Platform
  410. mailgun_port: 2525
  411. mailgun_domain:
  412. mailgun_from_address: [email protected]{{mailgun_domain}}
  413. mailgun_from: &amp;quot;\&amp;quot;{{inventory_hostname_short}}\&amp;quot; &amp;lt;{{mailgun_from_address}}&amp;gt;&amp;quot;
  415. # smtp login credentials
  416. mailgun_login: [email protected]{{mailgun_domain}}
  417. mailgun_api_key: put-your-api-key-here
  418. &lt;/code&gt;&lt;/pre&gt;
  420. &lt;p&gt;&lt;code&gt;roles/common/templates/;/code&gt;:&lt;/p&gt;
  421. &lt;pre&gt;&lt;code&gt;# {{ansible_managed}}
  423. # See /usr/share/postfix/ for a commented, more complete version
  425. # Debian specific:  Specifying a file name will cause the first
  426. # line of that file to be used as the name.  The Debian default
  427. # is /etc/mailname.
  428. myorigin = /etc/mailname
  430. smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)
  431. biff = no
  433. # appending .domain is the MUA's job.
  434. append_dot_mydomain = no
  436. # Uncomment the next line to generate &amp;quot;delayed mail&amp;quot; warnings
  437. #delay_warning_time = 4h
  439. readme_directory = no
  441. #
  442. # mailgun configuration
  443. #
  444. #
  446. mydestination = localhost.localdomain, localhost
  447. relayhost = []:{{mailgun_port}}
  448. smtp_sasl_auth_enable = yes
  449. smtp_sasl_password_maps = static:{{mailgun_login}}:{{mailgun_api_key}}
  450. smtp_sasl_security_options = noanonymous
  452. # TLS support
  453. smtp_tls_security_level = may
  454. smtpd_tls_security_level = may
  455. smtp_tls_note_starttls_offer = yes
  457. smtpd_tls_key_file = /etc/ssl/private/smtpd.key
  458. smtpd_tls_cert_file = /etc/ssl/certs/smtpd.crt
  459. smtpd_tls_CApath = /etc/ssl/certs
  461. sender_canonical_classes = envelope_sender, header_sender
  462. sender_canonical_maps =  regexp:/etc/postfix/sender_canonical_maps
  463. smtp_header_checks = regexp:/etc/postfix/header_check
  464. &lt;/code&gt;&lt;/pre&gt;
  466. &lt;p&gt;&lt;code&gt;roles/common/templates/mailname&lt;/code&gt;, appended to bare addresses. Probably not needed due to other
  467. rewrites, but I include it for completeness. See also: &lt;a href=""&gt;EtcMailName&lt;/a&gt;.&lt;/p&gt;
  468. &lt;pre&gt;&lt;code&gt;{{mailgun_domain}}
  469. &lt;/code&gt;&lt;/pre&gt;
  471. &lt;p&gt;&lt;code&gt;roles/common/templates/header_check&lt;/code&gt;:&lt;/p&gt;
  472. &lt;pre&gt;&lt;code&gt;# {{ansible_managed}}
  473. /From:.*/ REPLACE From: {{mailgun_from}}
  474. &lt;/code&gt;&lt;/pre&gt;
  476. &lt;p&gt;&lt;code&gt;roles/common/templates/sender_canonical_maps&lt;/code&gt;:&lt;/p&gt;
  477. &lt;pre&gt;&lt;code&gt;# {{ansible_managed}}
  478. /.+/    {{mailgun_from_address}}
  479. &lt;/code&gt;&lt;/pre&gt;</content><category term="ansible"></category><category term="smtp"></category><category term="mailgun"></category><category term="postfix"></category><category term="Linux"></category></entry><entry><title>Termux for Android</title><link href="" rel="alternate"></link><published>2018-03-17T16:56:00-04:00</published><updated>2018-03-17T16:56:00-04:00</updated><author><name>Annika Backstrom</name></author><id>,2018-03-17:/termux-for-android</id><summary type="html">&lt;p&gt;Wherein Termux for Android lets me build my website on my phone.&lt;/p&gt;</summary><content type="html">&lt;p&gt;I'm a newbie to the Android scene, having made the switch from an iPhone 6 to
  480. my Samsung Galaxy Note8 only last November. I've embraced the ecosystem rather
  481. than trying to exactly replicate my old iOS setup, but struggled to find a Git
  482. client I liked.&lt;/p&gt;
  483. &lt;p&gt;&lt;a href=""&gt;Pedro Veloso&lt;/a&gt;'s article on &lt;a href=""&gt;How to have a proper Git client on Android&lt;/a&gt;
  484. got me looking at &lt;a href=""&gt;Termux&lt;/a&gt;, which isn't a Git client but a terminal emulator
  485. and Linux environment that works with your average Android device (rooting not
  486. required).&lt;/p&gt;
  487. &lt;p&gt;I've only scratched the surface of Termux (&lt;a href=""&gt;lots of functionality&lt;/a&gt; is initially
  488. hidden behind keyboard shortcuts and a menu gesture) but I've already got my
  489. target workflow going: I'm writing this post directly inside my site's Git
  490. repository in Vim, and my site will auto-deploy when I push this post in a new
  491. commit.&lt;/p&gt;
  492. &lt;p&gt;I've installed a few other things like tmux and golang, and I'm looking forward
  493. to playing around in this environment, especially with an external keyboard and
  494. &lt;a href="/media/2018/03/dex.jpg"&gt;DeX&lt;/a&gt;.&lt;/p&gt;
  495. &lt;p&gt;&lt;img alt="Termux on the Galaxy Note8" src="/media/2018/03/note8.1024x768.jpg"&gt;&lt;/p&gt;</content><category term="Linux"></category><category term="Android"></category><category term="Terminal"></category></entry><entry><title>Miracle Cube Timer</title><link href="" rel="alternate"></link><published>2017-09-13T18:36:00-04:00</published><updated>2017-09-13T18:36:00-04:00</updated><author><name>Annika Backstrom</name></author><id>,2017-09-13:/miracle-cube-timer</id><summary type="html">&lt;p&gt;Here are a few ways I've used the dead-simple Miracle Cube Timer from Datexx.&lt;/p&gt;</summary><content type="html">&lt;p&gt;I'm really liking the "Miracle Cube Timer" by Datexx. It's dead-simple to use
  496. (no buttons to press), and while it does have a countdown timer, it's very
  497. small and easy to hide and won't distract.&lt;/p&gt;
  498. &lt;p&gt;Here are a few ways I've used this timer:&lt;/p&gt;
  499. &lt;ul&gt;
  500. &lt;li&gt;&lt;strong&gt;Quickly timebox an activity.&lt;/strong&gt; Sitting down to work on a meaty problem? Set
  501.  a 30 minute timer. If the timer goes off and you're not making progress,
  502.  figure out what needs to change.&lt;/li&gt;
  503. &lt;li&gt;&lt;strong&gt;Follow up on notifications.&lt;/strong&gt; When you get a 10 minute meeting reminder,
  504.  set a 5 minute timer on the cube. Find a stopping point on your current task
  505.  before those 5 minutes are up: when the timer goes off, that's your last
  506.  chance to stretch your legs, use the bathroom, or refill your drink.&lt;/li&gt;
  507. &lt;li&gt;&lt;strong&gt;Use the &lt;a href=""&gt;Pomodoro technique&lt;/a&gt;.&lt;/strong&gt; Quickly create timers for 30 minute work
  508.  periods and 5 minute breaks without fiddling with apps on your phone or
  509.  computer. (Traditional Pomodoro uses 25 minute work/5 minute rest, but I
  510.  found the 5/15/30/60 timer less expensive and more aesthetically pleasing
  511.  than the red 5/10/20/25 timer.)&lt;/li&gt;
  512. &lt;/ul&gt;
  513. &lt;p&gt;All these things are possible with any timer, but the simple, unobtrusive
  514. interface of the cube doesn't break my concentration. I find myself reaching
  515. for it often, if only just as a reminder that time is passing (say, reminding
  516. myself that a meeting is half over).&lt;/p&gt;
  517. &lt;p&gt;&lt;img alt="Photo of Miracle Cube Timer" src="/media/2017/09/miracle-cube-timer/cube.600x600.jpg"&gt;&lt;/p&gt;</content><category term="productivity"></category></entry><entry><title>Goodbye, Burrito</title><link href="" rel="alternate"></link><published>2017-09-07T22:38:00-04:00</published><updated>2017-09-07T22:38:00-04:00</updated><author><name>Annika Backstrom</name></author><id>,2017-09-07:/goodbye-burrito</id><summary type="html">&lt;p&gt;We say goodbye to a little friend.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Burrito, my little black and white hooded rat, died around 17:15 this
  518. evening. He passed away in Charlotte's arms, in his favorite blanket
  519. next to his friend Abraham, with our hands to keep him warm in his last
  520. moments.&lt;/p&gt;
  521. &lt;p&gt;A rescue rat, he had chronic breathing trouble from the day we met,
  522. likely the result of scarring from exposure to ammonia fumes during his
  523. life before being rescued. He and Abe never really got along with our
  524. other rats; they preferred their own quiet, away from the youngsters.&lt;/p&gt;
  525. &lt;p&gt;He was always the weakest of the bunch. Even Abe with his recurring
  526. tumor was more energetic. We noticed Burrito losing weight over the last
  527. few weeks, and he was cold to the touch the past day or two. I knew in
  528. my heart that today was probably his last day with us.&lt;/p&gt;
  529. &lt;p&gt;Burrito, I'll miss your little wheeze as you rested on my lap: I always
  530. felt this weird kinship with you, me with my asthma, you with your own
  531. breathing troubles. I'll miss the way you closed your eyes tight and
  532. leaned into my scritches. I'll miss the way you would peek out and come
  533. to the door whenever you heard my voice. I'll miss the way, only in the
  534. last days before you passed on, you finally, determinedly, climbed out
  535. of your habitat onto my shoulders and hands.&lt;/p&gt;
  536. &lt;p&gt;&lt;img alt="Breath" src="/media/2017/09/burrito/breath.gif" title="Burrito taking some breaths, laying on a blanket"&gt;&lt;/p&gt;
  537. &lt;p&gt;Many thanks to &lt;a href=""&gt;Mainley Rat Rescue&lt;/a&gt; and the &lt;a href=""&gt;Flower City Critters Small
  538. Animal Rescue&lt;/a&gt; for facilitating the Holbook rescue, finding Burrito
  539. his foster home, and finally his forever home with us.&lt;/p&gt;
  540. &lt;p&gt;&lt;a href="/media/2017/09/burrito/look.jpg"&gt;&lt;img alt="Look" src="/media/2017/09/burrito/" title="Burrito looking down from his cage"&gt;&lt;/a&gt; &lt;a href="/media/2017/09/burrito/sniff.jpg"&gt;&lt;img alt="Sniff" src="/media/2017/09/burrito/" title="Burrito having a sniff"&gt;&lt;/a&gt; &lt;a href="/media/2017/09/burrito/snuggle.jpg"&gt;&lt;img alt="Snuggle" src="/media/2017/09/burrito/" title="Burrito enjoying a snuggle under a scarf"&gt;&lt;/a&gt; &lt;a href="/media/2017/09/burrito/tired.jpg"&gt;&lt;img alt="Tired" src="/media/2017/09/burrito/" title="Burrito, very tired, resting on a blanket"&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="rats"></category><category term="pets"></category><category term="loss"></category></entry></feed>

If you would like to create a banner that links to this page (i.e. this validation result), do the following:

  1. Download the "valid Atom 1.0" banner.

  2. Upload the image to your own server. (This step is important. Please do not link directly to the image on this server.)

  3. Add this HTML to your page (change the image src attribute if necessary):

If you would like to create a text link instead, here is the URL you can use:

Copyright © 2002-9 Sam Ruby, Mark Pilgrim, Joseph Walton, and Phil Ringnalda