Zip R2 Objects in Memory with Cloudflare Workers

It seems like a super basic thing you’d want to do with Cloudflare Workers when you store files in R2, but I’ve googled far and wide and couldn’t find an example of this anywhere.

So here’s my take on this, after much trial and error:

import { z } from 'zod';
import { ZipWriter, BlobReader, configure } from '@zip.js/zip.js';
// Without this, we get uncaught error due to Workers runtime bug
// See:
useCompressionStream: false,
// Payload schema that lists the files to be bundled, their filenames and the archive filename
const schema = z.object({
archive_filename: z.string().endsWith('.zip'),
files: z.array(
r2key: z.string(),
filename: z.string(),
export default {
async fetch(request, env, ctx): Promise<Response> {
const body = await request.json();
const payload = schema.safeParse(body);
if (!payload.success) {
return new Response(JSON.stringify({ status: 'failed', error: payload.error }), {
status: 409,
headers: { 'Content-Type': 'application/json' },
let { readable, writable } = new IdentityTransformStream();
// Create a ZIP archive stream
const archive = new ZipWriter(writable);
// Store all the promises to catch any errors all together later
let promises: Promise<any>[] = [];
for (const receipt of {
const { r2key, filename } = receipt;
// Fetch the receipt from the R2 bucket
const fileContent = await env.STORAGE_BUCKET.get(r2key);
if (!fileContent) {
return new Response(`Object not found: ${r2key}`, { status: 404 });
// Add the receipt to the ZIP archive
promises.push(archive.add(filename, new BlobReader(await fileContent.blob())));
Promise.all(promises).catch((err) => {
return new Response(readable, {
headers: {
'Content-Type': 'application/zip',
'Content-Disposition': `attachment; filename="${}"`,
} satisfies ExportedHandler<Env>;
view raw index.ts hosted with ❤ by GitHub

You can just POST to it a JSON object with the output filename for the zip file and a list of R2 object keys with a filename to set for each.

Don’t forget to set your bindings in the wrangler.toml.

Simple Japanese Inheritance Calculator

I made a thing again!

If you’re like me, a foreigner in your 40s living in Japan, the thought of dealing with inheritance taxes for your parents back home can be daunting. Understanding how these taxes might be applied is essential.

To help with this, I created an app: Japanese Inheritance Tax Calculator.

Here are a few insights:

  • It’s not as straightforward as you might think.
  • The calculation method actually offers some advantages for foreigners.
  • The base deduction is quite high, meaning many people won’t owe taxes on even fairly sizable inheritances.

Bear in mind that this is a super simplified calculator: it only works in the case of a standard family unit where a parent of the user passes away and the user is the only heir living in Japan. But that’s fits me and a lot of my friends in Japan, so #scratchyourownitch it is.

It’s a tiny single page app built on Vite + React with shadcn/ui components. You can check out the code in the GitHub repo.

Cost Breakdown of a Birth in Tokyo

In June 2021, my wife and I were extremely happy to welcome our first son into the world. I know many people express curiousness towards the costs involved with delivery in Japan as the system of it not being covered by health insurance but also somewhat covered by various subventions from the national and local governments makes it all pretty confusing. So here’s a full breakdown of what it ended up costing us.

tldr; I thought I would be claiming a massive 医療費控除 but after tabulating all of my receipts, it seems I severely underestimated how much cash-back I got from various institutions. I ended up in the black even though my wife gave birth with an epidural at the top hospital in Tokyo.

Delivery costs

Wife spent a month in Minato-ku’s Aiiku hospital (she was at risk of going in early labor) from week 31 to 34. She was in a private room per doctor’s order so no extra charge there. Then she gave birth with an epidural and stayed for the standard 5 days after delivery in a private room at 30,000¥ per night.

Total cost: 1,317,059¥

Then we got all of the following cash back:

  • Basic childbirth coverage: 420,000¥
  • Minato-ku resident childbirth coverage: 280,000¥
  • Company’s insurance childbirth coverage: 30,000¥
  • Company’s congratulations bonus: 10,000¥ (fucking stingy bastards)
  • Company’s insurance extra payments (not sure where this is coming from, I did claim 高額療養費制度): 282,100¥
  • Wife’s life insurance coverage (Nissay): 240,019¥
  • Wife’s women’s health insurance coverage (Coop Kyosai – subscribed after we confirmed pregnancy just in case): 228,000¥

Total cash-back: 1,490,119¥

Baby stayed in NICU for 2 weeks but that was almost free with extra coverage from Minato-ku. I think we only ended up paying the cost of diapers.

Grand total: 173,060¥ profit

But this profit was offset by other costs accrued during pregnancy as detailed in the next paragraph.

Other costs

The many many pregnancy checkups at Aiiku Clinic, even with the city office coupons, ended up costing quite a bit: I counted 65,000¥ total and it’s missing 2 or 3 visits that happened in 2020 calendar year for which I don’t have the numbers unless I fish out last year’s tax return forms.

We also forked out for the NIPT test which is not covered by insurance nor by 医療費控除 and cost about 180,000¥.

Starting a business – part 1

Since the start of the year Fumina and I have been playing with the idea of starting an outbound online shop—selling Japanese products to customers abroad. Last month we finally launched Chuchulu and it’s proven to be quite an interesting adventure.

Fashion accessories made in Japan

Finding a product

The first step was to find a product category:

  • It had to be something we like. We’re not natural born salesmen so there’s no way we could push something we don’t genuinely like ourselves.
  • It had be interesting to people abroad (obviously) and not largely available outside of Japan. With shipping costs and potential tariffs, it would be difficult for us to beat local shops competition.
  • It had to be easily shippable. Heavy bulky items would push up shipping cost and make us less attractive to foreign customers.
  • Unit costs had to be on the lower side, at least at the beginning. We did not want to invest massively into a starting stock in case things just don’t work out.

Ultimately, we came to choose fashion accessories (earrings, bracelets and the like) with typical Japanese designs as our first products.

Fumina knew a good craftsman’s workshop specialized in traditional prayer beads who could work with her on order made bracelets based on her designs. That was our first product.

Agates in various colors and Black Onyx, made in Kyoto.

We also started visiting industry events to meet with suppliers. That’s how we found our second product line: Minoyaki tile earrings from Tajimi in Gifu prefecture. We also went to visit the workshop where these are made.

Various colors of tile earrings made in Tajimi, Gifu prefecture.

The next steps of this adventure will be coming in future posts…


5 years ago today I am in the Yamanote line going to an interview for what is to become my current job. The shaking hits as we are pulling in to the platform at Shinjuku station. I brace myself as the car sways to what feels like 30º angles and people standing don’t know if they should jump out or jump in.

As things calm down, the station master announces on the PA system that all trains are stopped probably for the rest of the day and asks everyone to exit the station calmly. I glance up at towers in Shinjuku west side, they are still swaying back and forth 10 minutes after the quake.

I leave the station but all I can think about is how to make it to my interview. I try calling the HR guy and my agent maybe 20 times but nothing is going through. I send a couple emails to my family and friends abroad saying I’m fine so that they don’t worry as they wake up and see a big earthquake on the news.

I try desperately to catch a taxi that could bring me closer to Tamachi where my interview is. Finally I manage to get through to the HR guy who tells me everyone left the building and the interview is cancelled.

That’s the moment when I look up at the giant video screen on the front of the Flags building at Shinjuku south entrance. It shows the live view from a helicopter as the wave unfurls over Tohoku. I realize how bad the situation is and how silly I was focusing on my stupid job interview…

I stay maybe 30 minutes in shock, watching the live images of the disaster with hundreds of business people and shoppers. Then I realize there is nothing to do but walk home. I curse myself for buying new business shoes for this interview as I walk 5km back to Ikebukuro.

Server move

This blog, as well as all my other personal websites, used to be hosted on servers provided by MacBidouille (which I’m co-founder of). However, sharing a server with other people and services was starting to feel a bit restrictive (I couldn’t easily move from Apache to nginx or support IPv6) so about a year ago I decided to move to my own little VPS box.

I started out with Gandi Cloud VPS and it was fine, but last week I decided to trash that and shop for something better. I chose Linode and I must say I’m very happy.

  • Gandi VPS – 1 share – 16€/month (1556¥) for 256MB of memory. Server was hosted in France which was very slow for me.
  • Linode 512 – $19.95/month (1568¥) for 512MB of memory. Server can be hosted in Tokyo datacenter which is super fast and responsive for me.

So for the same price in Yen (thank you super low exchange rates), I get a server with twice the RAM and hosted right next door. Also Linode has a much nicer admin interface (Gandi is pretty messy and slow):

linode dashboard

Added advantage of hosting my own server: since I changed job last year, I don’t get to play with linux at work anymore (or any technical stuff actually), so this way I compensate a little bit for my technology withdrawal….

Update: oops! I just realized that Gandi has recently increased the minimum specs of their hosting service, so I could have had a 512MB memory box for the same price since June, making it pretty much equivalent to Linode… Well anyways, the added performance of having the server in a datacenter in Tokyo is invaluable.

New Feature: Deep Linking

Yesterday I did a bit of coding and added a new feature to Sunsetter: deep linking. When you make a query to find a sunrise or sunset forecast, the address bar will update with a link you can copy and share or send to friends to show the same page you were on.

For example, here’s a link to the alignment between the Tokyo Skytree and Fujisan:,139.810744&poi=35.363976,138.732217


Early November or February should make for some nice pictures!

Feel free to share some nice alignments with your friends.