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.

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.

Foreman and Procfile tips&tricks

Lately I’ve been playing around with the Heroku stack and I’d like to share little tricks that might be common knowledge but which I’ve not seen mentioned on the Heroku standard documentation.

So the doc tells you you must set your Procfile as such (for a Python app):

web: gunicorn app:app -b$PORT -w 3

You can then run the webserver on your machine for development with the command:

foreman start

But if your app also uses memcached, redis, postgres or others, then you must open as many additional tabs in your terminal to run each service (I don’t want to have the daemons running all the time on my all-purpose macbook air).

What you can do to make your life easier is to create a new file which you should add to .gitignore (so that it is not uploaded to Heroku and does not affect your production environment) and add all those services in there:

web: python
memcached: memcached -v
coffee: coffee --watch --output static/js/ --compile lib/

And run it with:

foreman start -f

This will launch my app with standard Python (easier for quick debugging than gunicorn), my memcached instance and even compile my CoffeeScript on the fly so I can edit freely while testing my changes.

Looking in your terminal you’ll even see each service logs all pretty and color coded.

Is Softbank’s new iPad 2 campaign worth it?

Updated 2011/11/09 — see bottom of the post

For the iPhone 4S launch, Masayoshi Son, Softbank’s CEO, decided to go all out to keep his customers from going to aU by KDDI.

Fun Fact: during his keynote on October 7th, one day after Steve Jobs passing away, Son-san declared tearfully that these campaigns were his gifts to spread the Steve’s “art work” (he used the word 作品 as opposed to 製品) to as many people as possible.

One of these campaigns is for the iPad and it’s description is very confusing. See the figure below:

Many people take this to mean that you can get a 16GB iPad for free, ¥0 per month, if you are already a Softbank iPhone subscriber. That’s not true because ¥1,860 monthly discount on the second to last line only applies to the communications charge and not to the iPad hardware monthly cost. Going to the cost simulation page shows this well: your minimum monthly bill is still ¥1,860 (the cost of the 16GB iPad 2) and the data plan is free up to 100MB.

So in the end we get a free data plan if we don’t use 3G internet (almost don’t use it, 100MB a month wouldn’t get you much farther than regular email checking). That sounds like a classic mobile operator swindle: giving you something for almost free and then hammering you with extra high fees whenever you go over the pathetically low usage limits. Let’s look at the data plan details:

So yes, free for 100MB, capped at ¥4,980 over 111.5MB or you could choose to just pay a flat plan ¥4,410 every month whatever your usage. You end up paying a ¥470 premium for the flexibility of paying nothing the months when you almost don’t use 3G… Is that actually a good deal? I put the numbers into Numbers to see:

  • iPad 2 16GB + ZERO data plan under 100MB usage per month: ¥22,320 per year
  • iPad 2 16GB + ZERO data plan over 111.5MB user every month: ¥63,540 per year
  • iPad 2 16GB + FLAT data plan: ¥56,700 per year

So over the course of a year you would be saving money only if you stay under the 100MB cap for more than 3 months.

Is that a good deal? If you only use the 3G for emailing in the train and turn off the modem when not in use (letting it check your mail every 15min in the background would significantly bump your usage) then it could be. But having already an iPhone, do you really need to check your email on your iPad?

Myself, I will probably pass on that campaign. What would really be interesting would be a tethering option for my iPhone at a reasonable price… Well reasonable would really be ¥0 as I consider I’m already paying for the bandwidth and how I use it is none of Softbank’s business. An “acceptable” price would be maybe ¥1,000.

Please tell me in the comments what you think about this campaign.

Update: Very good point added in the comments by Maddy. If you get an iPad from Softbank for that campaign and remove the SIM card, never to use it again, you still get to download the Softbank WiFi roaming profile that lets you connect to all Softbank / FON / YahooBB / Tokyo Metro access points for free (that’s a pretty extensive network in Tokyo) and a GPS to boot. That’s a clear advantage over buying a WiFi only version from Apple for the exact same price.

iPad 3G and Pocket WiFi alternatives in Japan

So the pricing for the iPad in Japan are out. It seems even devices sold at the Apple Stores will be SIM-locked to Softbank, breaking many hopes of seeing DoCoMo come into the picture and shaking things up a little.

This has definitely not changed my thoughts on the device, it would very much piss me off to pay twice for 2 iPhone and iPad data plans.

This morning, my buddy @kuriburi who is more enthusiastic than I am, called me from the line at the Softbank shop to discuss the situation. Interestingly enough, people were fleeing in droves after receiving the pamphlet describing the full pricing… We entertained the thought that it might be more interesting to buy a WiFi version and get a Pocket WiFi device from data plan specialist eMobile.

@kuriburi left the Softbank line and we started gathering data from the web:

And after crunching numbers here are the results, first for the iPad 64GB version:

And for the iPad 32GB version:

It still seems the Softbank’s default option with integrated 3G is very aggressively priced compared to the competition. But if you can limit yourself to less than 300MB of 3G data per month, you can save up to ¥9.000 on the 32GB iPad over the 2 years contract period. Not much for sure, but some people might be interested…

Note: we have not taken into account the eMobile Super Light data plan because a 3MB/month data plan will not let you do anything but access email (and then without pictures) which does not count as a full usage of the device.

Note 2: here’s a link to the spreadsheet, if you see anything wrong with the data please notify me in the comments.

Activate tethering on Softbank iPhone

A friend of mine, @kuriburi, just sent me this to publish for him, so here you go:

So it was the news of the day: Someone managed to activate tethering on an AT&T iPhone 3G by means of a “carrier support file”, a.k.a. “ipcc”. Here in Japan, the story was a bit more tricky : Softbank declared that they would not support tethering on their network and had no plans to do so in the future either.


Nice! So, with those brand new Macbook Pro without any pc express card slot, how are we supposed to use Softbank’s data cards? This bothered me to no end thus I went on a search for a Softbank carrier support file that I could modify somehow. I stumbled onto this very interesting post on a Japanese blog that talked about the same thing, but with a beta version of the iPhone OS 3.0.

OK, well, doesn’t hurt to try with the official release, right?

Now I need to get my hands on that damn file. Turns out, it was right on my hard drive at work since August 2008. So I went to work and modified the file (which incidentally is just a bundle in a zip archive with a special extension) and tried to update my iPhone with it. The steps are simple :

  1. in the Terminal, execute defaults write carrier-testing -bool TRUE
    on windows, execute C:\Program Files\iTunes\iTunes.exe /setPrefInt carrier-testing 1 in a DOS window
  2. plug your iPhone to your computer via the USB cable
  3. in iTunes, display the summary page of your iPhone
  4. while pressing the “alt” (option) key, click on the “check for update” button
  5. you will be presented with a finder window. Locate the Softbank_jp.ipcc file, select it
  6. iTunes will now update your iPhone with the modified carrier settings
  7. Once finished, unplug the iPhone, go see into Settings>General>Network, and voilà! screenshot

Oh, before I forget, if Apple or Softbank issues an update through iTunes, wait a bit before updating… you never know…

Update: I updated the link with the latest file from @kuriburi that activates both tethering and MMS. よろしく!

Update: This IPCC file does not work with 3.1 update. If you value your tethering, do not upgrade yet.

WWDC Keynote on with App Engine

No bandwidth, no servers, no infrastructure, no money required. Just a bit of python and a tad of javascript and you can live stream an event to 10.000 people concurrently (theoretic figure, Analytics said the live-blog site had 30.000 visits in all) within Google App Engine‘s free quotas.

keynote requests per second

This is the graph taken from my App Engine dashboard the morning after the WWDC’09 Keynote after live-blogged the event in French through my application. We always had scaling problems while Google’s infrastructure was in beta and we were bound by smallish quotas, but since they opened fully the service a couple months ago, the sky is the limit.

Blacklisting words in Twitter Tools

There’s a new game trending on Twitter these days, Spymaster, and it likes to write out stuff to your twitter feed. There’s a good controversy running on the web whether these tweets are spam or not. I’m playing and I’ve set it up to tweet out only level ups which is pretty minimalist.

However, I am also running the Twitter Tools plugin to copy my tweets back from twitter to my blog. But if I’m fine with exposing my twitter followers with #spymaster notifications, I’d rather not show them to my blog readers.

There is no way currently in the plugin to exclude tweets based on words, so I made a patch for it:

blacklist in the twitter tools options menu

You can download the patch for the current 1.6 version and apply it with the following command:

patch twitter-tools.php < twittertools-blacklist.patch

I hope this feature will make it in the next version of the plugin.

Web Identity and business cards

With the recent talk of business cards, I decided to make me some personal 名刺 for use in non-corporate context. I used to have some – home-made by my graphic designer brother – but even those were linked to one of my activities, co-founder at, and not 100% personal.

This all got me thinking of my web identity – the face I show to the web, which in this day and age is the only world that matters. I am lucky enough to have a rare name, rare enough that I am pretty sure me and my cose-related family (8 people) are the only bearer of this family name. So when you search my name on Google, all the results are actually related to me, myself and I.

However, when looking at these results, the top ones are my profiles at various websites such as LinkedIn or Flickr. Although these are mine and I define what is written there, I do not have 100% dictator-like control over them. This bothers me a little…


So I started working on my webpresence portal, a website that defines me and should eventually become the nº1 search result for my name. It will be the website I write on my business card, and although it currently only shows links to stuff I do, I have plans to expand it with new functionalities: a portfolio and make it an OpenID provider for starters.

Update: Google now offers 10.000 sets of business cards to their Google Profile users.


They are kinda lame and cool at the same time. Anyways, they do no ship to Japan so I won’t get one. My design is better.

Meta-tags proposal for the new DiggBar

Many think the DiggBar is evil. I don’t. I find it ingenious, especially the pre-pending which will automatically generate a shortened URL for you as well as a “Submit to Digg” button if the page URL has not been submitted yet.

prepending for the digg bar

unsubmitted diggbar

However, when you submit a link to Digg by this way, the title and description of the item are empty by default, placing the burden to fill up these fields on the submitter. He needs to go back to the page, copy the title, copy some text of the article or make up a better description, which is all a pain and poses a big hurdle…

Digg submission - all empty

Digg offers a way for webmasters to create a link that will pre-fill these fields with the data you want your readers to use. This is done by simply setting some parameter in a URL to put as target of the link:

But this process is not compatible with the DiggBar and its URL pre-pending feature. What we, webmasters, need is a way to define these values that will work everytime.

Why not Meta tags? Step 1 of the step 2 in the screenshot above is Digg downloading the page to check it really exists and provide potential thumbnails for the submission. At this stage they could read a couple of meta tags in the <head> of the page and use that to pre-fill these fields.

<meta name="digg-title"  content="My title here" />
<meta name="digg-description" content="My 350 characters excerpt." />

It would then be trivial to write a WordPress plugin that generates these meta from your post title and excerpt (or similar concepts in other CMS platforms).

If you think this would be a feature you would like to see, I invite you to digg this blog post:

Twitter integration

As you might have noticed, in the past weeks I have more tightly integrated my twitter messages into the blog. When they used to just show up in the sidebar, they are now posted simultaneously here as full blog posts, albeit with a special minimalistic styling.


You can clickity-click on the cute blue birdie to go to the post page and comment, as with every other post, on the inane stuff I post there. This wonder of technology is brought to you by the twitter tools wordpress plugin and my awesome coding skills.

Alas, I know some of you are already following my tweets on your twitter account and might find the double punch effect of reading these messages twice, in your twitter timeline and in your RSS feedreader, a bit overwhelming.


Which is why I created an extra RSS feed to which you can subscribe and get only the fat fleshy blog posts, free of the 140 characters tweets. You can switch to that, I won’t begrudge you…

United States of France

Speaking of politics, this article from TIME Magazine is pretty funny, if not pathetic:

You just know the Frogs have only increased their disdain for us, if that is indeed possible. And why shouldn’t they? The average American is working two and a half jobs, gets two weeks off and has all the employment security of a one-armed trapeze artist. The Bush Administration has preached the “ownership society” to America: own your house, own your retirement account; you don’t need the government in your way. So Americans mortgaged themselves to the hilt to buy overpriced houses they can no longer afford and signed up for 401(k) programs that put money — where, exactly? In the stock market! Where rich Republicans fleeced them.


We’ve always dismissed the French as exquisitely fed wards of their welfare state. They work, what, 27 hours in a good week, have 19 holidays a month, go on strike for two days and enjoy a glass of wine every day with lunch — except for the 25% of the population working for the government, who have an even sweeter deal. They retire before their kids finish high school, and they don’t have to save for $45,000-a-year college tuition, because college is free. For this, they pay a tax rate of about 103%, and their labor laws are so restrictive that they haven’t had a net gain in jobs since Napoleon. There is no way the French government can pay for this lifestyle forever, except that it somehow does.

Anyways, with all the financial institutions going bankrupt everywhere, I’m just glad Nomura bought back Lehman Brothers’ Asian operation and saved the jobs of 2 of my friends… at least for now.

Joss Whedon’s Dr. Horrible: don’t miss it

Joss Whedon’s pet project, in answer to the writer’s guild nonsense, came out yesterday: Dr. Horrible’s sing-along blog. A mini-series exclusively for internet and for free, done on the cheap with his friends.

It’s a mix between Pinky and the Brain, Mistery Men and a video blog, executed musical-style. It’s brilliant, especially since I was a big Doogie Howser fan as a kid (Neil Patrick Harris) and burned through Whedon’s Firefly series in a straight 2-days stand.

Act 2 will come out July 17th and Act 3 on July 19th. Catch it while it lasts!