<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://blog.iphoting.com/atom.xml" rel="self" type="application/atom+xml" /><link href="https://blog.iphoting.com/" rel="alternate" type="text/html" /><updated>2026-04-09T23:37:44+00:00</updated><id>https://blog.iphoting.com/atom.xml</id><title type="html">Ronald’s Scribblings</title><subtitle>Observations and ramblings where I write about Technology and Current Affairs.</subtitle><entry><title type="html">IPv6, Docker(-compose), and Shorewall6/ip6tables</title><link href="https://blog.iphoting.com/blog/2021/02/10/ipv6-docker-docker-compose-and-shorewall6-ip6tables/" rel="alternate" type="text/html" title="IPv6, Docker(-compose), and Shorewall6/ip6tables" /><published>2021-02-10T19:24:15+00:00</published><updated>2021-02-10T19:24:15+00:00</updated><id>https://blog.iphoting.com/blog/2021/02/10/ipv6-docker-docker-compose-and-shorewall6-ip6tables</id><content type="html" xml:base="https://blog.iphoting.com/blog/2021/02/10/ipv6-docker-docker-compose-and-shorewall6-ip6tables/"><![CDATA[<p>The default docker configuration is limited to IPv4 networking and the docker daemon transparently takes care of IPv4-based SNAT. However, when IPv6 is enabled, this automagic is neither included nor has been built into docker. This write-up will walkthough the 3 approaches to address this shortcoming.</p>

<!-- more -->

<p>IETF strongly discourages IPv6-SNAT because SNAT was conceptually just a workaround for the lack of IPv4 addresses in the addressing space.</p>

<p>I fully disagree!</p>

<p>There are benefits to running networks isolated from globally addressable IPv6-space, and using SNAT to bridge the private subnet with the public globally addressable IPv6-space. One of the biggest benefits is that your private subnet is not globally addressable, i.e., a natural “firewall” is thus created, similar to what you get in IPv4 world with RFC1918 private-space addresses (familiar to most people in the form of 192.168.0.x). Imagine if all your containers were fully reachable from the global internet just by getting hold of their IPv6 addresses, including their “unexposed” container ports!</p>

<h2 id="primer-on-ipv6-addressing-types">Primer on IPv6 addressing types</h2>

<p>So in IPv6, broadly speaking for the ease of this write-up, there are 3 major groups of addresses.</p>

<ol>
  <li>“Global”;</li>
  <li>“Link-local” (now deprecated in <a href="https://tools.ietf.org/html/rfc3879">RFC3879</a>); and</li>
  <li>“Unique Local Address” (ULA).</li>
</ol>

<p>“Link-local” addresses are automatically generated by the OS and are only valid within a link-segment, and cannot be used across routers. This is similar to the IPv4 stateless address auto-configuration (169.254.0.0/16), and cannot be used across routers. Practically useless.</p>

<p>ULAs can be seen as an RFC1918 IPv4 private address space  equivalent (i.e., 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8), which are separate from the “Link-local” addresses. ULAs can be used across routers, depending on the routing table, but they are still not globally routable.</p>

<p>To enable IPv6 on docker and also within the docker-compose default network, ULA IPv6 prefixes will be used. The ULA prefix is defined as <code class="language-plaintext highlighter-rouge">fd00::/8</code>. Docker requires a minimum addressable space of <code class="language-plaintext highlighter-rouge">/80</code>. Generally, I will assign <code class="language-plaintext highlighter-rouge">fd00:ffff::/80</code> to the default <code class="language-plaintext highlighter-rouge">docker0</code> bridge, in violation of <a href="https://tools.ietf.org/html/rfc4193#section-3.2.1">RFC4193</a>, but I don’t see any negative impact to doing so for this <code class="language-plaintext highlighter-rouge">docker0</code> bridge configuration.</p>

<h2 id="enabling-ipv6-in-docker-and-the-docker0-bridge">Enabling IPv6 in Docker and the docker0 bridge</h2>

<p>I’ve adapted these instructions from the <a href="https://docs.docker.com/config/daemon/ipv6/">docker docs</a>.</p>

<p>To enable IPv6 in Docker and assign a ULA prefix to the <code class="language-plaintext highlighter-rouge">docker0</code> bridge, put the following in <code class="language-plaintext highlighter-rouge">/etc/docker/daemon.json</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
	"ipv6": true,
	"fixed-cidr-v6": "fd00:ffff::/80"
}
</code></pre></div></div>

<p>Then, restart the docker daemon:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo service docker restart.
</code></pre></div></div>

<p>You should see that the default <code class="language-plaintext highlighter-rouge">docker0</code> bridge has IPv6 enabled:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker network inspect bridge | grep -i "ipv6\|subnet\|gateway"
		"EnableIPv6": true,
				"Subnet": "172.17.0.0/16",
				"Gateway": "172.17.0.1"
				"Subnet": "fd00:ffff::/80",
				"Gateway": "fd00:ffff::1"
</code></pre></div></div>

<p>Test it with this command:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker run --rm -it debian ping6 google.com -c3
</code></pre></div></div>

<p>Success?</p>

<p>Probably not. The container might still not be able to obtain connectivity because there is no packet forwarding between the global addressable network and the ULA subnet. This is the automagic from docker IPv4 support that is missing in docker’s current IPv6 implementation.</p>

<h2 id="3-approaches">3 approaches</h2>

<p>I will discuss 3 approaches to address these shortcomings: Docker’s experimental <code class="language-plaintext highlighter-rouge">ip6tables</code> support, <code class="language-plaintext highlighter-rouge">docker-ipv6nat</code>, and Shorewall[6] (if you are already using <a href="https://shorewall.org">Shorewall</a> to define your firewall).</p>

<h3 id="ip6tables-approach">ip6tables approach</h3>

<p>Before you proceed, you will need a very recent docker installation, version <code class="language-plaintext highlighter-rouge">20.10.2</code> or later, recently merged with this <a href="https://github.com/moby/moby/pull/41622">pull-request</a>. Otherwise, manually set-up SNAT using ip6tables, or try the other 2 approaches.</p>

<p>To enable <code class="language-plaintext highlighter-rouge">ip6tables</code> handling, the <code class="language-plaintext highlighter-rouge">experimental</code> flag must be enabled. Put the following in <code class="language-plaintext highlighter-rouge">/etc/docker/daemon.json</code> or adjust to fit:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
	"ipv6": true,
	"fixed-cidr-v6": "fd00:ffff::/80",
	"ip6tables": true,
	"experimental": true
}
</code></pre></div></div>

<p>Then, restart the docker daemon:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo service docker restart.
</code></pre></div></div>

<p>Review the <code class="language-plaintext highlighter-rouge">ip6tables</code> <code class="language-plaintext highlighter-rouge">FORWARD</code> chain for new rules:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo ip6tables -L
</code></pre></div></div>

<p>Test it with this command:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker run --rm -it debian ping6 google.com -c3
</code></pre></div></div>

<p>If it’s successful, you should see the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PING google.com(sa-in-x66.1e100.net (2404:6800:4003:c00::66)) 56 data bytes
64 bytes from sa-in-x66.1e100.net (2404:6800:4003:c00::66): icmp_seq=1 ttl=110 time=3.10 ms
64 bytes from sa-in-x66.1e100.net (2404:6800:4003:c00::66): icmp_seq=2 ttl=110 time=3.62 ms
64 bytes from sa-in-x66.1e100.net (2404:6800:4003:c00::66): icmp_seq=3 ttl=110 time=3.14 ms

--- google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 5ms
rtt min/avg/max/mdev = 3.099/3.284/3.619/0.241 ms
</code></pre></div></div>

<p>Done!</p>

<h3 id="docker-ipv6nat-approach">docker-ipv6nat approach</h3>

<p>I don’t recommend this approach as it does not work with IPv6 networking in <code class="language-plaintext highlighter-rouge">docker-compose</code>. Eventually, once the <code class="language-plaintext highlighter-rouge">ip6tables</code> support in docker is mature, <a href="https://github.com/robbertkl/docker-ipv6nat"><code class="language-plaintext highlighter-rouge">docker-ipv6nat</code></a> will be <a href="https://github.com/robbertkl/docker-ipv6nat/issues/65">retired</a>. Otherwise, this is an easy approach for pure docker containers.</p>

<p>Run <code class="language-plaintext highlighter-rouge">docker-ipv6nat</code> as a docker container:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker run -d --rm --name ipv6nat --cap-add NET_ADMIN --cap-add NET_RAW --cap-add SYS_MODULE --cap-drop ALL --network host --restart unless-stopped -v /var/run/docker.sock:/var/run/docker.sock:ro -v /lib/modules:/lib/modules:ro robbertkl/ipv6nat
</code></pre></div></div>

<p>Or with <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> and <code class="language-plaintext highlighter-rouge">docker-compose up -d</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>version: '2.3'
services:
  ipv6nat:
    image: "robbertkl/ipv6nat"
    container_name: ipv6nat
    restart: unless-stopped
    network_mode: host
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /lib/modules:/lib/modules:ro
    cap_add:
      - NET_ADMIN
      - NET_RAW
      - SYS_MODULE
    cap_drop:
      - ALL
</code></pre></div></div>

<p>Review the <code class="language-plaintext highlighter-rouge">ip6tables</code> <code class="language-plaintext highlighter-rouge">FORWARD</code> chain for new rules:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo ip6tables -L
</code></pre></div></div>

<p>Test it with this command:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker run --rm -it debian ping6 google.com -c3
</code></pre></div></div>

<p>If it’s successful, you should see the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PING google.com(sa-in-x66.1e100.net (2404:6800:4003:c00::66)) 56 data bytes
64 bytes from sa-in-x66.1e100.net (2404:6800:4003:c00::66): icmp_seq=1 ttl=110 time=3.10 ms
64 bytes from sa-in-x66.1e100.net (2404:6800:4003:c00::66): icmp_seq=2 ttl=110 time=3.62 ms
64 bytes from sa-in-x66.1e100.net (2404:6800:4003:c00::66): icmp_seq=3 ttl=110 time=3.14 ms

--- google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 5ms
rtt min/avg/max/mdev = 3.099/3.284/3.619/0.241 ms
</code></pre></div></div>

<p>Done!</p>

<h3 id="shorewall6-approach">Shorewall6 approach</h3>

<p>I’ve adapted these instructions from <a href="https://gist.github.com/lukasnellen/20761a20286f32efc396e207d986295d">this gist</a>.</p>

<p>Enable docker support in <code class="language-plaintext highlighter-rouge">/etc/shorewall/shorewall.conf</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DOCKER=Yes
</code></pre></div></div>

<p>Take note that this directive is not available in <code class="language-plaintext highlighter-rouge">shorewall6.conf</code>, which is fine.</p>

<p>Define your <code class="language-plaintext highlighter-rouge">/etc/shorewall[6]/interfaces</code> files to capture all the docker bridges, i.e., <code class="language-plaintext highlighter-rouge">docker0</code> and <code class="language-plaintext highlighter-rouge">br-xxx</code>, where <code class="language-plaintext highlighter-rouge">docker</code> is the zone defined for docker traffic, using the <code class="language-plaintext highlighter-rouge">physical</code> option:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>?FORMAT 2
#ZONE	INTERFACE	OPTIONS
-	lo		ignore
net	eth0		dhcp,routeback,accept_ra=2
docker	docker0		routeback=1,physical=docker+
docker	br		routeback=1,physical=br-+
</code></pre></div></div>

<p>Be sure to set <code class="language-plaintext highlighter-rouge">accept_ra=2</code> in your public-facing interface, <code class="language-plaintext highlighter-rouge">eth0</code> in my case.</p>

<p>Enable SNAT by adding the following line to <code class="language-plaintext highlighter-rouge">/etc/shorewall6/snat</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#ACTION			SOURCE			DEST		PROTO	PORT	IPSEC	MARK	USER	SWITCH	ORIGDEST	PROBABILITY
MASQUERADE		-			eth0
</code></pre></div></div>

<p>Older versions of Shorewall use the <code class="language-plaintext highlighter-rouge">masq</code> file instead. Adapt the above accordingly.</p>

<p>You don’t need to enable SNAT for IPv4 as docker does it for you automagically.</p>

<p>Check your shorewall[6] configurations:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo shorewall check
$ sudo shorewall6 check
</code></pre></div></div>

<p>Once error-free, activate the new rule sets:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo shorewall safe-restart
$ sudo shorewall6 safe-restart
</code></pre></div></div>

<p>Review the <code class="language-plaintext highlighter-rouge">ip6tables</code> <code class="language-plaintext highlighter-rouge">FORWARD</code> chain for new rules:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo ip6tables -L
</code></pre></div></div>

<p>Test it with this command:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker run --rm -it debian ping6 google.com -c3
</code></pre></div></div>

<p>If it’s successful, you should see the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PING google.com(sa-in-x66.1e100.net (2404:6800:4003:c00::66)) 56 data bytes
64 bytes from sa-in-x66.1e100.net (2404:6800:4003:c00::66): icmp_seq=1 ttl=110 time=3.10 ms
64 bytes from sa-in-x66.1e100.net (2404:6800:4003:c00::66): icmp_seq=2 ttl=110 time=3.62 ms
64 bytes from sa-in-x66.1e100.net (2404:6800:4003:c00::66): icmp_seq=3 ttl=110 time=3.14 ms

--- google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 5ms
rtt min/avg/max/mdev = 3.099/3.284/3.619/0.241 ms
</code></pre></div></div>

<p>Done!</p>

<h2 id="docker-compose">docker-compose</h2>

<p>Now that plain docker containers have IPv6 connectivity, it’s time to tackle docker-compose IPv6 bridge networking.</p>

<p>In a typical <code class="language-plaintext highlighter-rouge">docker-compose.yml</code>, whenever there is more than one service defined, docker-compose will automatically create a default bridge network, named after the project. This project-specific default bridge network connects the containers together and configures their  hostnames according to their service names. This bridge is IPv4-only and additional directives are required in the compose file to override this.</p>

<p>As of now, docker swarm does not support IPv6 and hence, the <code class="language-plaintext highlighter-rouge">enable_ipv6</code> directive is only available for <a href="https://docs.docker.com/compose/compose-file/compose-file-v2/#enable_ipv6">file format</a> versions <em>below</em> 3, e.g., 2.4.</p>

<p>Similar to the <code class="language-plaintext highlighter-rouge">fixed-cidr-v6</code> configuration in docker <code class="language-plaintext highlighter-rouge">daemon.json</code> above, a separate ULA needs to be manually assigned to each <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> project, annoyingly.</p>

<p>To prevent conflicts (as expounded by RFC4193) with other projects and to ease tracking, I recommend generating this prefix in a simple way using the project name like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ echo "projectname" | shasum | cut -c1-8
109b2e51
</code></pre></div></div>

<p>Using the above 8 digit hash to fill up the following 2 groups of 16-bits of the ULA prefix (fd00::/8): <code class="language-plaintext highlighter-rouge">fd00:109b:2e51::/80</code>.</p>

<p>Append or update the following <code class="language-plaintext highlighter-rouge">networks</code> key to the project <code class="language-plaintext highlighter-rouge">docker-compose.yml</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>networks:
  default:
    enable_ipv6: true
    ipam:
      driver: default
      config: 
        - subnet: fd00:109b:2e51::/80
          gateway: fd00:109b:2e51::1
</code></pre></div></div>

<p>You <em>do not</em> need to manually assign IPv4 or IPv6 addresses to each of your containers using the <code class="language-plaintext highlighter-rouge">networks</code> key under each service entry.</p>

<p>Test your config and bring up the services and the IPv6 enabled bridge:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker-compose config
$ docker-compose down ; docker-compose up -d
</code></pre></div></div>

<p>Inspect the network bridge:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker network inspect projectname_default  | grep -i ipv6
 "EnableIPv6": true,
		"IPv6Address": "fd00:109b:2e51::2/80"
</code></pre></div></div>

<p>Test it with one of the following commands:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker-compose run --rm projectname ping6 google.com -c3
$ docker-compose exec projectname ping6 google.com -c3
</code></pre></div></div>

<p>If it’s successful, you should see the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PING google.com(sa-in-x66.1e100.net (2404:6800:4003:c00::66)) 56 data bytes
64 bytes from sa-in-x66.1e100.net (2404:6800:4003:c00::66): icmp_seq=1 ttl=110 time=3.10 ms
64 bytes from sa-in-x66.1e100.net (2404:6800:4003:c00::66): icmp_seq=2 ttl=110 time=3.62 ms
64 bytes from sa-in-x66.1e100.net (2404:6800:4003:c00::66): icmp_seq=3 ttl=110 time=3.14 ms

--- google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 5ms
rtt min/avg/max/mdev = 3.099/3.284/3.619/0.241 ms
</code></pre></div></div>

<p>Done!</p>

<h2 id="conclusion">Conclusion</h2>
<p>IPv6 is now available in Docker and docker-compose, as long as you know how to set it up and populate your <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> file. Hopefully, the docker automagic will get implemented in IPv6 configurations by default, and the workarounds discussed in this write-up will no longer be necessary.</p>

<p>Special thanks to Felix and James for helping me proofread this write-up.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://medium.com/@skleeschulte/how-to-enable-ipv6-for-docker-containers-on-ubuntu-18-04-c68394a219a2">How to enable IPv6 for Docker containers on Ubuntu 18.04</a></li>
  <li><a href="https://collabnix.com/enabling-ipv6-functionality-for-docker-and-docker-compose/">Walkthrough: Enabling IPv6 Functionality for Docker &amp; Docker Compose</a></li>
</ul>]]></content><author><name></name></author><summary type="html"><![CDATA[The default docker configuration is limited to IPv4 networking and the docker daemon transparently takes care of IPv4-based SNAT. However, when IPv6 is enabled, this automagic is neither included nor has been built into docker. This write-up will walkthough the 3 approaches to address this shortcoming.]]></summary></entry><entry><title type="html">Jekyll 3 Post Excerpts</title><link href="https://blog.iphoting.com/blog/2015/12/27/jekyll-3-post-excerpts/" rel="alternate" type="text/html" title="Jekyll 3 Post Excerpts" /><published>2015-12-27T08:20:28+00:00</published><updated>2015-12-27T08:20:28+00:00</updated><id>https://blog.iphoting.com/blog/2015/12/27/jekyll-3-post-excerpts</id><content type="html" xml:base="https://blog.iphoting.com/blog/2015/12/27/jekyll-3-post-excerpts/"><![CDATA[<p>When I upgraded from Jekyll 2.x to 3.x, there were some minor incompatibilities, the most significant is that my code for adding “Read More” links to excepted posts on the index page broke.</p>

<!-- more -->

<p>I can’t remember why my pervious code used the <code class="language-plaintext highlighter-rouge">post.excepted</code> liquid variable as it no longer exists in Jekyll 3.x. Fortunately, Sean has a <a href="http://www.seanbuscay.com/blog/jekyll-teaser-pager-and-read-more/">solution</a>.</p>

<p>The <a href="http://www.seanbuscay.com/blog/jekyll-teaser-pager-and-read-more/">solution</a> compares the word count between the post content and the post except variables, and if they are not equal, the “Read More” link is added.</p>

<p>It’s not significantly slower but at least it works.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[When I upgraded from Jekyll 2.x to 3.x, there were some minor incompatibilities, the most significant is that my code for adding “Read More” links to excepted posts on the index page broke.]]></summary></entry><entry><title type="html">Why I Gym Religiously</title><link href="https://blog.iphoting.com/blog/2015/12/24/why-i-gym-religiously/" rel="alternate" type="text/html" title="Why I Gym Religiously" /><published>2015-12-24T14:20:28+00:00</published><updated>2015-12-24T14:20:28+00:00</updated><id>https://blog.iphoting.com/blog/2015/12/24/why-i-gym-religiously</id><content type="html" xml:base="https://blog.iphoting.com/blog/2015/12/24/why-i-gym-religiously/"><![CDATA[<p>Since my graduation from university, I have picked up a routine to visit the gym on a regular basis, starting from 2 times a week, to the current 5 times a week. The gradual progression took place over a span of 1.5 years as I learnt more about my body.</p>

<!-- more -->

<p>I started off in my school gym during my undergrad days, doing regular cardio (running), just to keep my mind sharp and efficient during intensive school semesters. I tried my best to maintain a twice weekly routine, even during the intensive “hell weeks”, where we had presentations and report submissions for most of our 4-5 modules for that semester.</p>

<p>By maintaining some form of exercise, I have experienced first hand, the reduction in the number of sleeping hours required before I feel rested for the next day. Admittedly during school semesters, sleep was never sufficient, but exercising reduces my mind’s demand for sleep. Without exercise, I will definitely need 7-8 hours of rest before I felt awake. However, with regular exercise, I was able to get by with just 6-7 hours of rest. Secondly, or as a result of this, I needed less caffeine to get through the day.</p>

<p><strong>Benefit 1: Rest efficiency improves.</strong></p>

<p>As I neared graduation, I learnt about the benefits of weight lifting. Lifting weights triggers bone density growth and muscle growth. This has two major benefits: 1) Stronger bones reduces the probability of breaking upon impact. As we grow older, bone loses calcium and density, making them more brittle. Any impact from any falls will increase the chances of a fracture, which is best avoided in old age. 2) More muscles gives me more strength and raises my <a href="https://en.wikipedia.org/wiki/Basal_metabolic_rate">basal metabolic rate (BMR)</a>. Strength, if built appropriately, can improve my posture and stability. BMR will help burn calories when idle, and such increases can help offset a general reduction in BMR brought about by ageing.</p>

<p><strong>Benefit 2: Stronger bones, preparation for old age.</strong></p>

<p><strong>Benefit 3: Higher BMR, burning more calories at rest</strong>.</p>

<p>With this context in mind, I searched the web for an efficient and effective set of exercises and started myself on an adapted (simplified due to limited access to the required equipment) version of the <a href="http://stronglifts.com/5x5/">StrongLifts 5x5</a> workout.</p>

<p>It was tough in the beginning as I was extremely new to free-weight workouts, as opposed to the guided movements provided by the resistance machines. Free movement has an added benefit of training auxiliary muscles that work to provide stability. For the greatest benefit, I’ll always opt for free-weight workouts.</p>

<p>Around the same time, I began to have a greater appreciation over my diet and the macro nutrients that I consume on a daily basis. I picked up the basics of a balanced diet and the required nutrients to support strength training.</p>

<p>With ever slight adjustments and improvements to my diet, coupled with exercise, I managed to slowly increase my weight, without increasing my waist size.</p>

<p>While I’ve seen the hype over group fitness (GX) classes held in the gyms, I had always been skeptical over its effectiveness. Fast-forward to the weeks leading to my graduation, I was introduced to a GX programme, <a href="http://www.lesmills.com/workouts/fitness-classes/bodypump/">BodyPump</a>, by a friend.</p>

<p>I was hooked.</p>

<p>Strength training, checked. Light-cardio, checked. Good music, checked. Self-paced, checked. The group nature of the exercise class makes it easier and more motivating to attend and complete the full 60 minutes of it.</p>

<p>The hardest element of keeping fit is the intrinsic motivation. When it gets boring, tough, and painful, most people would not want to repeat the experience again; but with all things fitness, no pain, no gain.</p>

<p>I think the most sustainable way of keeping fit and being ready for old age is to find 1-2 friends to keep doing an activity or a few activities together regularly, like a routine, that meets all the fitness criteria and goals.</p>

<p>In a subsequent post, I shall write about my experience with <a href="https://en.wikipedia.org/wiki/Yoga">Yoga</a>.</p>

<p>In the meantime, stay fit, stay healthy!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Since my graduation from university, I have picked up a routine to visit the gym on a regular basis, starting from 2 times a week, to the current 5 times a week. The gradual progression took place over a span of 1.5 years as I learnt more about my body.]]></summary></entry><entry><title type="html">S3 Custom Redirection Rules</title><link href="https://blog.iphoting.com/blog/2015/12/23/s3-custom-redirection-rules/" rel="alternate" type="text/html" title="S3 Custom Redirection Rules" /><published>2015-12-23T13:27:06+00:00</published><updated>2015-12-23T13:27:06+00:00</updated><id>https://blog.iphoting.com/blog/2015/12/23/s3--custom-redirection-rules</id><content type="html" xml:base="https://blog.iphoting.com/blog/2015/12/23/s3-custom-redirection-rules/"><![CDATA[<p>Amazon Web Services (AWS) <a href="http://aws.amazon.com/s3/">Simple Storage Service (S3)</a> is a really economical way of hosting static websites. With <a href="http://docs.aws.amazon.com/AmazonS3/latest/dev/HowDoIWebsiteConfiguration.html#configure-bucket-as-website-routing-rule-syntax">advanced “redirection rules” support</a>, a static blog, even with legacy permalinks, can be hosted completely on AWS S3. When configured with <a href="https://aws.amazon.com/cloudfront/">CloudFront</a> and <a href="https://www.cloudflare.com/">CloudFlare</a>, full SSL support for your own <a href="https://www.startssl.com/">custom</a> <a href="https://letsencrypt.org/">certificates</a> can be used.</p>

<p>Prior to S3 supporting advanced redirection rules, there was only a binary option to redirect <em>all</em> requests to a specified hostname. While this feature is useful for handling blanket redirections, but useless if you need selective redirection like what a <code class="language-plaintext highlighter-rouge">.htaccess</code> file can do, it is insufficient for site that have legacy permalinks to maintain.</p>

<p>Fortunately, S3’s advanced redirect rules allows you to somewhat mimic a subset of what an Apache <code class="language-plaintext highlighter-rouge">.htaccess</code> file can do: 1) substitute the hostname; and/or 2) substitute the path, based on 2 possible conditions: a) HTTP error codes from S3, and/or b) request path prefix, albeit with a clumsy XML syntax. With a good text-editor with XML intelligence, it’s not too challenging to hand-craft.</p>

<p>Thanks to this feature, I’ve managed to set up redirect rules to map permalinks from my previous blog engine to the current permalinks layout.</p>

<!-- more -->

<p>To begin, assuming that you’ve already got a S3 site up, check out the awesome <a href="http://docs.aws.amazon.com/AmazonS3/latest/dev/HowDoIWebsiteConfiguration.html#configure-bucket-as-website-routing-rule-syntax">documentation</a> for this feature.</p>

<p>One gotcha that took me a while is that object-keys within S3 buckets do not begin with a <code class="language-plaintext highlighter-rouge">/</code>, even though the request path traditionally begins with a <code class="language-plaintext highlighter-rouge">/</code>. As such, do not include the preceding <code class="language-plaintext highlighter-rouge">/</code> within the <code class="language-plaintext highlighter-rouge">KeyPrefixEquals</code> or <code class="language-plaintext highlighter-rouge">ReplaceKeyPrefixWith</code> keys. Refer to <code class="language-plaintext highlighter-rouge">Example 1</code> within the docs for an illustration.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Amazon Web Services (AWS) Simple Storage Service (S3) is a really economical way of hosting static websites. With advanced “redirection rules” support, a static blog, even with legacy permalinks, can be hosted completely on AWS S3. When configured with CloudFront and CloudFlare, full SSL support for your own custom certificates can be used. Prior to S3 supporting advanced redirection rules, there was only a binary option to redirect all requests to a specified hostname. While this feature is useful for handling blanket redirections, but useless if you need selective redirection like what a .htaccess file can do, it is insufficient for site that have legacy permalinks to maintain. Fortunately, S3’s advanced redirect rules allows you to somewhat mimic a subset of what an Apache .htaccess file can do: 1) substitute the hostname; and/or 2) substitute the path, based on 2 possible conditions: a) HTTP error codes from S3, and/or b) request path prefix, albeit with a clumsy XML syntax. With a good text-editor with XML intelligence, it’s not too challenging to hand-craft. Thanks to this feature, I’ve managed to set up redirect rules to map permalinks from my previous blog engine to the current permalinks layout.]]></summary></entry><entry><title type="html">Getting f.lux onto your iOS Device with Xcode</title><link href="https://blog.iphoting.com/blog/2015/12/09/getting-f-lux-onto-your-ios-device-with-xcode/" rel="alternate" type="text/html" title="Getting f.lux onto your iOS Device with Xcode" /><published>2015-12-09T15:32:43+00:00</published><updated>2015-12-09T15:32:43+00:00</updated><id>https://blog.iphoting.com/blog/2015/12/09/getting-f-lux-onto-your-ios-device-with-xcode</id><content type="html" xml:base="https://blog.iphoting.com/blog/2015/12/09/getting-f-lux-onto-your-ios-device-with-xcode/"><![CDATA[<p>On 11 November 2015, all hell broke lose when <a href="https://justgetflux.com/">f.lux</a> announced that they had come up with a <a href="https://justgetflux.com/sideload/">solution</a> to load their iOS version of f.lux onto iOS devices without <a href="https://en.wikipedia.org/wiki/IOS_jailbreaking">Jailbreaking</a>. (For the record, I’m a strong believer of <em>not</em> Jailbreaking iOS devices as the security mechanisms are in-place to protect your important private data.)</p>

<p>This is made possible due to a <a href="http://9to5mac.com/2015/06/10/xcode-7-allows-anyone-to-download-build-and-sideload-ios-apps-for-free/">policy change by Apple</a>, announced in WWDC 2015, to allow personal apps developed and compiled on Xcode to be loaded on and run on personal iOS devices, without needing an iOS Developer Program membership, bypassing the App Store. This thus becomes a technique for which unapproved apps can be distributed (in source form) and built by adventurous users and loaded onto their own iOS devices. Apps “sideloaded” with this method have a lifespan of 90 days, after which, they require to be resigned and reloaded onto the iOS devices before they can continue functioning.</p>

<p>In my opinion, this is a great way for OpenSource applications to enter a user’s device, without having to go through the hassle of the AppStore process, where there may be conflicts with the source code license.</p>

<p>Unfortunately, as f.lux distributed a skeletal Xcode project with the bulk of the application logic hidden within a compiled binary, Apple contacted the f.lux developers to remove the download as it violated the Developer Program Agreement.</p>

<p>My suspicions was that they were distributing a binary, rather than unobfuscated source code, and that is potentially risky for users. Who knows, the binary blob could be secretly uploading personal data and nobody would know better. I believe that it is for this reason that prompted Apple to respond so quickly. After all, <a href="http://www.gba4iosapp.com">GBA4IOS</a> is still available and in <a href="https://bitbucket.org/rileytestut/gba4ios/">source form</a>.</p>

<p>Thankfully, there’s another unaffiliated alternative, <a href="https://github.com/anthonya1999/GoodNight">GoodNight</a>. It nicely mimics most of f.lux’s features, without the annoying bugs stuck in the last posted version of f.lux before it was taken down.
<!-- more --></p>

<p>To install GoodNight on your iOS device, you’ll need the following:</p>

<ul>
  <li>Apple ID</li>
  <li>iOS device</li>
  <li>Xcode (7.1 is fine)</li>
  <li>USB connection to your iOS device</li>
</ul>

<p>If you have them all, follow these <a href="http://bouk.co/blog/sideload-iphone/">fine instructions</a>, but using the latest released version of Xcode instead of the beta. The latest version of Xcode was of this writing is 7.2.</p>

<p>Good luck and have a better sleep! Good night!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[On 11 November 2015, all hell broke lose when f.lux announced that they had come up with a solution to load their iOS version of f.lux onto iOS devices without Jailbreaking. (For the record, I’m a strong believer of not Jailbreaking iOS devices as the security mechanisms are in-place to protect your important private data.) This is made possible due to a policy change by Apple, announced in WWDC 2015, to allow personal apps developed and compiled on Xcode to be loaded on and run on personal iOS devices, without needing an iOS Developer Program membership, bypassing the App Store. This thus becomes a technique for which unapproved apps can be distributed (in source form) and built by adventurous users and loaded onto their own iOS devices. Apps “sideloaded” with this method have a lifespan of 90 days, after which, they require to be resigned and reloaded onto the iOS devices before they can continue functioning. In my opinion, this is a great way for OpenSource applications to enter a user’s device, without having to go through the hassle of the AppStore process, where there may be conflicts with the source code license. Unfortunately, as f.lux distributed a skeletal Xcode project with the bulk of the application logic hidden within a compiled binary, Apple contacted the f.lux developers to remove the download as it violated the Developer Program Agreement. My suspicions was that they were distributing a binary, rather than unobfuscated source code, and that is potentially risky for users. Who knows, the binary blob could be secretly uploading personal data and nobody would know better. I believe that it is for this reason that prompted Apple to respond so quickly. After all, GBA4IOS is still available and in source form. Thankfully, there’s another unaffiliated alternative, GoodNight. It nicely mimics most of f.lux’s features, without the annoying bugs stuck in the last posted version of f.lux before it was taken down.]]></summary></entry><entry><title type="html">Hey Siri, Give Us a Hint</title><link href="https://blog.iphoting.com/blog/2015/09/10/hey-siri-give-us-a-hint/" rel="alternate" type="text/html" title="Hey Siri, Give Us a Hint" /><published>2015-09-10T15:59:59+00:00</published><updated>2015-09-10T15:59:59+00:00</updated><id>https://blog.iphoting.com/blog/2015/09/10/hey-siri-give-us-a-hint</id><content type="html" xml:base="https://blog.iphoting.com/blog/2015/09/10/hey-siri-give-us-a-hint/"><![CDATA[<p>On 9 September 2015, Apple made 3 big product announcements (<a href="http://technicallywell.com/apples-september-2015-event-quick-summary">summary here</a>): the much rumoured iPad Pro, the new Siri-powered Apple TV, and the expected iPhone 6s/+.</p>

<!-- more -->

<h3 id="apple-tv">Apple TV</h3>

<p>What jumped out at me is the newly announced <a href="http://www.theverge.com/2015/9/9/9297131/new-apple-tv-update-remote-2015-hands-on-video-photos">Apple TV</a>. Back in my 2012 post on <a href="/blog/2012/10/16/revolutionary-user-interfaces/">Revolutionary User Interfaces</a>, I wrote that Apple TV will gain Siri support as its main user interface:</p>

<blockquote>
  <p>I believe that the “Apple TV” in its eventual successful form will have Siri power a large part of its UI. To control our living rooms, the Mouse, the Click Wheel (remote controls), and Multi-touch are largely limiting and inadequate. Google TV has shown us how unpopular these input-methods are. Siri, with our voices, will become the controlling interface for these technologies that provide entertainment and services, throughout the household.</p>
</blockquote>

<p>With yesterday’s announcement, it became clearer that we are moving closer to the day where our voices can take a greater role in controlling computer interfaces.</p>

<p>Another exciting prospect with the new Apple TV is the introduction of an App Store for the tvOS. The television is a huge canvas for collaborative and individual couch gaming, as seen from the very good sales of Xbox, PS, and Wii, of a few years ago.</p>

<p>Unfortunately, these mainstream consoles have dropped the ball when it comes to content distribution. Seriously, who still heads out to a store to buy DVDs? The makers of Xbox and PS have only recently understood this by having respective online stores for purchasing and downloading games, but the consoles out there are not originally designed for primarily downloading new content, due to their limited internal storage capacities. I know add-on external storage options exist, but they are cumbersome.</p>

<p>With 32 GB of storage as basic on the new Apple TV, I believe there will be a huge potential for it to be the next console platform. Code once, and have your game logic run on 3 different platforms: iOS (iPhones, iPads), Mac OS X, tvOS (Apple TV), is a rather attractive proposition for developers. The Windows gaming market-share may not be the biggest after all.</p>

<h3 id="3d-touch">3D Touch</h3>

<p>3D Touch is the obvious (but difficult to implement) evolution of MultiTouch, and no, you don’t have to run out to get it yet; it’s not revolutionary. Having read <a href="http://www.bloomberg.com/features/2015-how-apple-built-3d-touch-iphone-6s/">“How Apple Built 3D Touch”</a> by Bloomberg, I’m convinced that this technology will not be easily copied by competitors in the near future, leaving Apple to define and set new expectations of touch screen interaction paradigms.</p>

<h3 id="summary">Summary</h3>

<p>Overall, I would say that this is an iOS roadmap event, for customers and developers alike. The future of Apple and these technologies laid out are a vast canvas, awaiting developers to exercise their imagination and coding prowess. The holy grail of a healthy platform would be user-generated content, allowing a (relatively-)free market of demand and supply to fuel and sustain the platform and its growth.</p>

<p>Apple as a company has a very sustainable near-to-mid-term future. I am quite pleased.</p>

<p><em>Disclosure: I own AAPL shares.</em></p>]]></content><author><name></name></author><summary type="html"><![CDATA[On 9 September 2015, Apple made 3 big product announcements (summary here): the much rumoured iPad Pro, the new Siri-powered Apple TV, and the expected iPhone 6s/+.]]></summary></entry><entry><title type="html">Investment Portfolio Tracking</title><link href="https://blog.iphoting.com/blog/2015/06/28/investment-portfolio-tracking/" rel="alternate" type="text/html" title="Investment Portfolio Tracking" /><published>2015-06-28T16:59:27+00:00</published><updated>2015-06-28T16:59:27+00:00</updated><id>https://blog.iphoting.com/blog/2015/06/28/investment-portfolio-tracking</id><content type="html" xml:base="https://blog.iphoting.com/blog/2015/06/28/investment-portfolio-tracking/"><![CDATA[<p>As my holdings of equity increases, I realised that I needed to track these (un)realised gains/losses as the strike prices for each transaction varies. Calculating the percentage gain/loss is not just simply taking the change over the purchase price! To appropriately calculate returns, the <a href="https://en.wikipedia.org/wiki/Internal_rate_of_return">IRR function</a> (or its derivatives) is required due to compounding effects and different base prices.</p>

<p>Fortunately, the great guys over at <a href="http://www.investmentmoats.com/">Investment Moats</a> have already created a <em>Stock Portfolio Tracker</em> google spreadsheet that is easy to use and is a good place to start.
<!-- more --></p>

<p>To begin, follow their <a href="http://www.investmentmoats.com/stock-market-commentary/portfolio-management/introducing-our-free-stock-portfolio-tracker-spreadsheet/">introduction / instructions</a>.</p>

<p>By tracking your portfolio over time, you’ll be able to see poorly performing positions and do something about them before it’s too late due to opportunity costs.</p>

<p>Unfortunately, in the most recent version of the tracker, the authors removed the <code class="language-plaintext highlighter-rouge">XIRR</code> function as it is too computationally intensive for the browser, if you have many different stock counters.</p>

<p>To enable for selected counters, follow the instructions in the version 1.8 row of the <code class="language-plaintext highlighter-rouge">Version History</code> table within the <code class="language-plaintext highlighter-rouge">Read This First</code> tab, but use the following formula instead:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=IFERROR(XIRR(
	SPLIT(JOIN("~", QUERY(Transactions!A$2:R, "SELECT R WHERE (B = 'Buy' OR B = 'Sell' OR B = 'Div') AND C = '" &amp; B2 &amp; "'"), U2, "~" ), "~"),
	SPLIT(JOIN(",", QUERY(Transactions!A$2:R, "SELECT A WHERE( B = 'Buy' OR B = 'Sell' OR B = 'Div') AND C = '" &amp; B2 &amp; "'"), TODAY(), ","), ",")
), 0)
</code></pre></div></div>

<p>This updated formula takes into account the structural changes of the <code class="language-plaintext highlighter-rouge">Transactions</code> sheet up until version 1.13 (dated: 2015/05/24). For use with the <code class="language-plaintext highlighter-rouge">Transactions USD</code> sheet and others, update the formula above, replacing <code class="language-plaintext highlighter-rouge">Transactions!A$2:R</code> throughout with <code class="language-plaintext highlighter-rouge">'Transactions USD'!A$2:R</code>.</p>

<p>With that, happy investing!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[As my holdings of equity increases, I realised that I needed to track these (un)realised gains/losses as the strike prices for each transaction varies. Calculating the percentage gain/loss is not just simply taking the change over the purchase price! To appropriately calculate returns, the IRR function (or its derivatives) is required due to compounding effects and different base prices. Fortunately, the great guys over at Investment Moats have already created a Stock Portfolio Tracker google spreadsheet that is easy to use and is a good place to start.]]></summary></entry><entry><title type="html">Love Wins</title><link href="https://blog.iphoting.com/blog/2015/06/28/love-wins/" rel="alternate" type="text/html" title="Love Wins" /><published>2015-06-28T06:21:02+00:00</published><updated>2015-06-28T06:21:02+00:00</updated><id>https://blog.iphoting.com/blog/2015/06/28/love-wins</id><content type="html" xml:base="https://blog.iphoting.com/blog/2015/06/28/love-wins/"><![CDATA[<p><a href="http://www.nytimes.com/2015/06/27/us/supreme-court-same-sex-marriage.html">NYTimes</a> reports:</p>

<blockquote>
  <p>“No longer may this liberty be denied,” Justice Anthony M. Kennedy wrote for the majority in the historic decision. “No union is more profound than marriage, for it embodies the highest ideals of love, fidelity, devotion, sacrifice and family. In forming a marital union, two people become something greater than once they were.”</p>
</blockquote>

<p>In <a href="https://twitter.com/joshzepps/status/614444765342347265">Josh Zepps’s</a> words:</p>

<blockquote class="twitter-tweet" lang="en"><p lang="en" dir="ltr">It&#39;s almost poetic, for judges…&#10;&#10;<a href="https://twitter.com/hashtag/SCOTUS?src=hash">#SCOTUS</a> <a href="http://t.co/c2ylIN2MtP">pic.twitter.com/c2ylIN2MtP</a></p>&mdash; Josh Zepps (@joshzepps) <a href="https://twitter.com/joshzepps/status/614444765342347265">June 26, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>Truly a long-awaited day for equality. A good time to celebrate, but remember that the fight for equality continues.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[NYTimes reports: “No longer may this liberty be denied,” Justice Anthony M. Kennedy wrote for the majority in the historic decision. “No union is more profound than marriage, for it embodies the highest ideals of love, fidelity, devotion, sacrifice and family. In forming a marital union, two people become something greater than once they were.” In Josh Zepps’s words: It&#39;s almost poetic, for judges…&#10;&#10;#SCOTUS pic.twitter.com/c2ylIN2MtP&mdash; Josh Zepps (@joshzepps) June 26, 2015 Truly a long-awaited day for equality. A good time to celebrate, but remember that the fight for equality continues.]]></summary></entry><entry><title type="html">The Jekyll Reboot</title><link href="https://blog.iphoting.com/blog/2015/06/11/the-jekyll-reboot/" rel="alternate" type="text/html" title="The Jekyll Reboot" /><published>2015-06-11T15:14:55+00:00</published><updated>2015-06-11T15:14:55+00:00</updated><id>https://blog.iphoting.com/blog/2015/06/11/the-jekyll-reboot</id><content type="html" xml:base="https://blog.iphoting.com/blog/2015/06/11/the-jekyll-reboot/"><![CDATA[<p>It took me almost a year, but with the release of <a href="http://octopress.org/2015/01/15/octopress-3.0-is-coming/">Octopress 3</a>, I’ve finally had an option to get this blog back into a functional state. With the scheduled discontinuation of <a href="http://www.webink.com/">WebINK</a> on 30 June 2015, my deadline had been set.</p>

<p>The version 2 release of <a href="http://jekyllrb.com/">Jekyll</a> has finally modernised the static page generator that this blog currently runs on, and it now runs on the latest version of Ruby (currently version 2.2.2).</p>

<p>There were a few challenges for a straight-out migration. Some older plugins and liquid tags are no longer supported, but some much sought after features are now built-in.
<!-- more --></p>

<h1 id="rack-app">Rack App</h1>

<p>With the new version of Jekyll, the project folder is not pre-configured to run as a Rack app. The <code class="language-plaintext highlighter-rouge">config.ru</code> supplied by the previous Octopress installation is too full of cruft and now would be a perfect time to improve it.</p>

<p>To serve static pages from a folder, Rack has <a href="http://www.rubydoc.info/github/rack/rack/Rack/Static"><code class="language-plaintext highlighter-rouge">Rack::Static</code></a> that can be used directly. Unfortunately, it does not have automagic support for returning a custom error 404 page for file-not-found errors.</p>

<p>Fortunately, <a href="https://github.com/kmikael/vienna">Vienna</a> can be used (almost) out-of-the-box for this. The default is to serve files from a <code class="language-plaintext highlighter-rouge">public</code> directory, but for Jekyll, the generated site is found under <code class="language-plaintext highlighter-rouge">_site</code>. For the file-not-found custom error page to work, a <code class="language-plaintext highlighter-rouge">404.html</code> needs to be generated within the <code class="language-plaintext highlighter-rouge">_site</code> directory.</p>

<p>My <code class="language-plaintext highlighter-rouge">config.ru</code> now contains only 2 lines of Ruby code:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env rackup</span>
<span class="c1">#\ -E deployment</span>

<span class="nb">require</span> <span class="s1">'vienna'</span>
<span class="n">run</span> <span class="no">Vienna</span><span class="o">::</span><span class="no">Application</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s1">'_site'</span><span class="p">)</span>
</code></pre></div></div>

<p>Include the following in your <code class="language-plaintext highlighter-rouge">Gemfile</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gem</span> <span class="s1">'rack'</span>
<span class="n">gem</span> <span class="s1">'vienna'</span>
</code></pre></div></div>

<p>Done! To test this our, run <code class="language-plaintext highlighter-rouge">rackup</code> in the project directory, and Rack will spawn a webserver to serve contents from the <code class="language-plaintext highlighter-rouge">_site</code> folder.</p>

<p>Push to Heroku and it will work. Include a <code class="language-plaintext highlighter-rouge">Procfile</code> if you want a custom webserver, such as <a href="http://rainbows.bogomips.org/"><code class="language-plaintext highlighter-rouge">Rainbows!</code></a>.</p>

<h1 id="incompatible-change">Incompatible Change</h1>

<p>The biggest incompatible change that I could not workaround and had to edit every of my affected posts was the removal of <code class="language-plaintext highlighter-rouge">include_code</code> liquid tag. Fortunately, <a href="https://github.com/octopress/render-code"><code class="language-plaintext highlighter-rouge">render-code</code></a> solves the same problem with a minor change in function name. A quick find-and-replace or <code class="language-plaintext highlighter-rouge">sed</code> solved it.</p>

<h1 id="local-search">Local Search</h1>

<p>With the new Jekyll theme, there is no search engine configured for the blog. I used Google’s <a href="https://cse.google.com.sg/cse/">Custom Search Engine</a> with a <em>2-page layout</em> for this and installation was a breeze.</p>

<p>Basically, I embedded a custom searchbox on the top right menu, and added a <code class="language-plaintext highlighter-rouge">search.html</code> that the searchbox will <code class="language-plaintext highlighter-rouge">POST</code> to.</p>

<p>Create a <code class="language-plaintext highlighter-rouge">search.html</code> in the project directory with the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>---
layout: page
title: Search
permalink: /search/
---
&lt;gcse:search&gt;
</code></pre></div></div>

<p>Then within the header, implement a <code class="language-plaintext highlighter-rouge">form</code> with a <code class="language-plaintext highlighter-rouge">textbox</code> and <code class="language-plaintext highlighter-rouge">POST</code> to <code class="language-plaintext highlighter-rouge">/search/</code> with <code class="language-plaintext highlighter-rouge">q</code> as the query parameter.</p>

<p>Finally, include the Google-supplied JavaScript code into the header or footer of the site.</p>

<p>Done!</p>

<h1 id="jekyll-plugins">Jekyll Plugins</h1>

<p>Here are the list of Jekyll plugins that I currently use within my <code class="language-plaintext highlighter-rouge">Gemfile</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">group</span> <span class="ss">:jekyll_plugins</span> <span class="k">do</span>
	<span class="n">gem</span> <span class="s1">'octopress-filters'</span>
	<span class="n">gem</span> <span class="s1">'octopress-include-tag'</span>
	<span class="n">gem</span> <span class="s1">'octopress-minify-html'</span>
	<span class="n">gem</span> <span class="s1">'octopress-gist'</span>
	<span class="n">gem</span> <span class="s1">'octopress-solarized'</span>
	<span class="n">gem</span> <span class="s1">'octopress-codefence'</span>
	<span class="n">gem</span> <span class="s1">'octopress-render-code'</span>
	<span class="n">gem</span> <span class="s1">'octopress-linkblog'</span>
	<span class="n">gem</span> <span class="s1">'octopress-feeds'</span>
	<span class="n">gem</span> <span class="s1">'octopress-littlefoot'</span>
	<span class="n">gem</span> <span class="s1">'octopress-image-tag'</span>
	<span class="n">gem</span> <span class="s1">'octopress-return-tag'</span>
	<span class="n">gem</span> <span class="s1">'jekyll-assets'</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Your mileage may vary, but this should be a good starting point.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[It took me almost a year, but with the release of Octopress 3, I’ve finally had an option to get this blog back into a functional state. With the scheduled discontinuation of WebINK on 30 June 2015, my deadline had been set. The version 2 release of Jekyll has finally modernised the static page generator that this blog currently runs on, and it now runs on the latest version of Ruby (currently version 2.2.2). There were a few challenges for a straight-out migration. Some older plugins and liquid tags are no longer supported, but some much sought after features are now built-in.]]></summary></entry><entry><title type="html">Forward-Thinking</title><link href="https://blog.iphoting.com/blog/2013/09/17/forward-thinking/" rel="alternate" type="text/html" title="Forward-Thinking" /><published>2013-09-17T17:38:00+00:00</published><updated>2013-09-17T17:38:00+00:00</updated><id>https://blog.iphoting.com/blog/2013/09/17/forward-thinking</id><content type="html" xml:base="https://blog.iphoting.com/blog/2013/09/17/forward-thinking/"><![CDATA[<p>On September 10, 2013, Apple unveiled the <a href="http://www.theverge.com/products/iphone-5c/7327">iPhone 5c</a> and <a href="http://www.theverge.com/products/iphone-5s/7328">iPhone 5s</a> to the public. The 5c is essentially a previous-generation iPhone 5 with a new plastic enclosure, bigger battery, and an HD front-facing FaceTime camera. Nothing too fascinating on the technological side of things, but from the economics and business perspective, Ben Thompson has written a <a href="http://stratechery.com/2013/thinking-about-iphone-pricing/">great piece</a>.</p>

<p>The iPhone 5s deserves further scrutiny. There were only 3 major features announced: First, a 64-bit capable <a href="http://en.wikipedia.org/wiki/Apple_A7">Apple-designed A7</a> chip, that employs the ARMv8 architecture. As a sub-point, the <a href="http://en.wikipedia.org/wiki/Apple_M7">Apple M7</a> motion co-processor was introduced as a low-power chip, designed to “collect sensor data from integrated accelerometers, gyroscopes and compasses and off load the collecting and processing of sensor data” from A7 chip in the iPhone 5s. Second, an amazingly powerful camera system, with hardware and software innovations. Third, <a href="http://en.wikipedia.org/wiki/Touch_ID">Touch ID</a>, a fingerprint sensor that authenticates the user, just by touching the home-button.</p>

<p>All three features revealed have <em>huge</em> implications. However, I have time only to write on one idea, and briefly touch on the rest.</p>

<!-- more -->

<h2 id="camera">Camera</h2>
<p>The best camera is the one you have with you. Instagram has consistently showed us that iPhones take the best photos, and with these hardware and software improvements, it is unlikely that other devices can take its place.</p>

<h2 id="touch-id">Touch ID</h2>
<p>Finally bringing their acquired AuthenTec technology into their products, we get an excellent fingerprint reader integrated into the home-button. One that does not require sliding, works in any direction, recognises only live fingers, and does it fast. I wouldn’t be surprised that the efficiency and speed of A7 plays a huge part to make this happen, seamlessly.</p>

<p>That said, the software implementation is rather impressive. Touch ID will refuse to unlock restarted phones, or phones that have not been unlocked within the last 48 hours—a manual passcode input is required for these cases. Clearly, somebody has thought about the weakness of the technology before bringing it to the mass-market.</p>

<p>A secret enclave within the new A7 chip was highlighted as a storage area for fingerprint data. Brian Roemmele wrote a <a href="http://qr.ae/NK5Lp">great piece</a> on the significance of this secret enclave. Essentially, Brian suggests that 7 years of secret innovation ended up in that secret enclave, opening up the world to new applications and possibilities, including but not limited to mobile transactions. As A7 chip and its successors become more mainstream, exciting possibilities await us.</p>

<h2 id="m7">M7</h2>

<p>While a new 64-bit processor with twice the transistors is nothing to belittle, it is however, not the most significant. Tucked away with little fanfare is the M7 co-processor. Marketing says that it continuously tracks data from sensors without having to wake the CPU up, saving battery.</p>

<p>No doubt, the sharp ones will notice that this engineering effort for a new chip cannot be just for saving battery on the iPhone 5s; there has got to be a more significant and wide-spread use, which the iPhone 5s is merely a test-bed for.</p>

<p>Imagine for a moment. Marketing says that the phone will be able to tell if one is walking, driving, or sitting. What if the M7 is on a different device, one that is always with you, one that has even more sensors? Would this be the foundation of Apple’s wearable-computing solution?</p>

<h2 id="ruis">RUIs</h2>

<p>Last October, I wrote about <a href="/blog/2012/10/16/revolutionary-user-interfaces/">Revolutionary User Interfaces</a>  and suggested that Siri is the forth RUI. Guess what, in iOS 7, Siri has left its beta status and it seems Apple is confident in Siri’s comprehension abilities, even as it continues to self-improve. Siri eyes-free has also now been rebranded as “<a href="http://en.wikipedia.org/wiki/IOS_in_the_Car">iOS in the Car</a>”.</p>

<p>At the end of the post, I suggested that Passbook—where location-based events meet contact-less commerce, might be the fifth RUI. I think I need to update that statement given new revelations.</p>

<p>While Touch ID wouldn’t qualify as a user interface alone as it is an authentication enabler, but together with the M7 chip and its sensors, the combination might just fit the bill as the fifth RUI. Passbook is just the graphical interface and I believe it will evolve into something even more powerful and encompassing.</p>

<p>Location-based events can be triggered by motion sensors, including low-power bluetooth iBeacons, tracked by the M7, and authenticated with Touch ID, unlocking secret identity credentials stored with the CPU’s secure enclave, enabling contact-less commerce.</p>

<p>While most of the hardware pieces are only just coming together,  when it is full envisioned, we can expect major disruption to current transaction networks; we can formally bid farewell to NFC.</p>

<p>It sounds super far-fetched and illegal but Apple could just theoretically become the biggest Bitcoin exchange overnight.</p>

<p>Just think about that for a moment.</p>]]></content><author><name></name></author><category term="apple" /><category term="computers" /><category term="technology" /><summary type="html"><![CDATA[On September 10, 2013, Apple unveiled the iPhone 5c and iPhone 5s to the public. The 5c is essentially a previous-generation iPhone 5 with a new plastic enclosure, bigger battery, and an HD front-facing FaceTime camera. Nothing too fascinating on the technological side of things, but from the economics and business perspective, Ben Thompson has written a great piece. The iPhone 5s deserves further scrutiny. There were only 3 major features announced: First, a 64-bit capable Apple-designed A7 chip, that employs the ARMv8 architecture. As a sub-point, the Apple M7 motion co-processor was introduced as a low-power chip, designed to “collect sensor data from integrated accelerometers, gyroscopes and compasses and off load the collecting and processing of sensor data” from A7 chip in the iPhone 5s. Second, an amazingly powerful camera system, with hardware and software innovations. Third, Touch ID, a fingerprint sensor that authenticates the user, just by touching the home-button. All three features revealed have huge implications. However, I have time only to write on one idea, and briefly touch on the rest.]]></summary></entry></feed>