<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Doa</title><description>A casual notebook</description><link>https://doany.io/</link><language>en</language><item><title>Consolidating My Small-to-Medium Web Apps on SvelteKit + Bun + SQLite</title><link>https://doany.io/en/posts/svelte-bun-sqlite/</link><guid isPermaLink="true">https://doany.io/en/posts/svelte-bun-sqlite/</guid><description>Ever since I rewrote an app built with Django + Nuxt as a single SvelteKit app, everything I build on my own uses the same stack. Drawing on the actual repositories, I go over why I stopped splitting out the backend, why I picked Svelte over React, why I decided SQLite was enough, and the pitfalls I hit with Bun.</description><pubDate>Fri, 04 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;It’s been a while since I wrote about something web-related.&lt;br&gt;
For about the past year, every web app I’ve built on my own has used SvelteKit + Bun + SQLite, so I figured I’d write down how I ended up there.&lt;/p&gt;
&lt;section&gt;&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;a class=&quot;anchor&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Here’s what I currently have running.&lt;/p&gt;


































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;What it does&lt;/th&gt;&lt;th&gt;Public&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;https://w.doany.io/&quot;&gt;worklog&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Builds timesheets from Slack and GitHub logs&lt;/td&gt;&lt;td&gt;Service only&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;https://x.doany.io/&quot;&gt;𝕏ool&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Automatically posts a “report card” of your day on 𝕏&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://github.com/DAnything/xool&quot;&gt;Source&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;mogiri&lt;/td&gt;&lt;td&gt;QR-based admission, pre-ordering and self-checkout&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://github.com/5ym/mogiri&quot;&gt;Source&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;denpa&lt;/td&gt;&lt;td&gt;TV program guide, scheduling, recording, and streaming&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://github.com/DAnything/denpa&quot;&gt;Source&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;https://ts.doany.io/&quot;&gt;tamasagashi&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Browser for a vehicle master database (model code → common name and specs) built from public data&lt;/td&gt;&lt;td&gt;Service only&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;All five have nearly identical package.json files: SvelteKit 2 + Svelte 5, adapter-node, Blades (the successor to Pico CSS), bun:sqlite, TypeScript 7, and they all start with &lt;code&gt;bun build/index.js&lt;/code&gt;.&lt;br&gt;
I didn’t set out to standardize them. Honestly, I rewrote one, it was pleasant, and everything after that just followed suit.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;how-the-rewrite-came-about&quot;&gt;How the rewrite came about&lt;a class=&quot;anchor&quot; href=&quot;#how-the-rewrite-came-about&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;mogiri (formerly Smart QR Payment) was originally built around 2024 as two separate pieces: a Django REST Framework backend and a Nuxt (Vuetify) frontend.&lt;br&gt;
I didn’t touch it for a while after that, but Renovate PRs keep coming whether you touch a project or not. Looking at the history for July 2026, it went something like this, with commits in between that did nothing but bump dependencies and then carry them through to a working state.&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;text&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:63ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;2026-07-28  Update dependency Django to v3.2.25&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;2026-07-28  Update dependency @nuxt/eslint-config to ^0.7.0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;2026-07-29  Carry the dependency bumps through to a working app&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;Two languages, two runtimes, two containers. Keeping up with updates for each of them didn’t feel worth it for an app this size.&lt;br&gt;
Splitting out the backend is overkill for a small application, and a single container seemed like the better call, so in August 2026 I rewrote the whole thing. The API and the UI are merged into one SvelteKit app: one container, and on k3s a single Pod with one PVC.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;why-svelte&quot;&gt;Why Svelte&lt;a class=&quot;anchor&quot; href=&quot;#why-svelte&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Since the original was Vue, the obvious choices would have been React or just staying on Nuxt, but I went with Svelte.&lt;/p&gt;&lt;p&gt;The reason is simple: you write less code, and the generated code is easier to read. State handling with Svelte 5 runes is straightforward, and, as I’ll get to later, since I have AI write a lot of the implementation, whether I can read and fix the code it produces matters quite a bit.&lt;br&gt;
With React, the React Compiler means you no longer have to think about managing &lt;code&gt;useMemo&lt;/code&gt; in tsx, but I don’t think it has become as easy to write as Svelte. On top of that, I personally have no desire to keep up with the speed and weight of the React ecosystem. Next.js has a lot of Vercel-specific features, and those get in the way when you deploy to your own k3s.&lt;/p&gt;&lt;p&gt;I dropped Nuxt because it has a lot of breaking changes, and it didn’t offer much of the benefits I listed above.&lt;/p&gt;&lt;p&gt;With SvelteKit, the API and the UI become one. You can do that with Next too, but SvelteKit is more straightforward to put together, and since adapter-node outputs a plain Node server, it runs anywhere.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;on-sqlite&quot;&gt;On SQLite&lt;a class=&quot;anchor&quot; href=&quot;#on-sqlite&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;bun:sqlite ships with Bun and is fast; there’s no driver or separate process to run, and the database is a single file. On k3s it’s self-contained with one PVC, and a backup is just a file copy.&lt;/p&gt;&lt;p&gt;I use it knowing its limits. Writes go through a single writer, and you can’t scale horizontally. In denpa, both the scheduler and the program guide fetcher write to one SQLite database, so it runs a single replica with &lt;code&gt;strategy: Recreate&lt;/code&gt;.&lt;br&gt;
I still chose SQLite because, for a small app, the extra overhead of running PostgreSQL outweighs the benefits. SQLite is increasingly being adopted even for medium-sized apps, and services built for production use, like Cloudflare’s D1, have appeared, so I judged that it holds up fine in production. It’s a choice I made having already decided to move to PostgreSQL if things get bigger.&lt;/p&gt;&lt;p&gt;This is all I set at startup.&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;ts&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:54ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;db&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;Database&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;(url, { create: &lt;/span&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; });&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;db.&lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;exec&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;&apos;PRAGMA journal_mode = WAL;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;db.&lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;exec&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;&apos;PRAGMA foreign_keys = ON;&apos;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;ensureSchema&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;(db);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;section&gt;&lt;h3 id=&quot;on-indexes&quot;&gt;On indexes&lt;a class=&quot;anchor&quot; href=&quot;#on-indexes&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;tamasagashi is an example of how search is plenty fast with SQLite as long as you index properly. Public data such as the MLIT (Ministry of Land, Infrastructure, Transport and Tourism, 国交省) fuel economy listings, type designations published in the Official Gazette (kanpō), and recall notifications are converted to JSONL, imported into SQLite at build time, and baked into the image; at runtime the database is opened read-only. Since there are no writes, the single-writer limitation never comes into play in the first place.&lt;br&gt;
For the model codes and common names used as search keys, I want to ignore differences between hiragana and katakana, full-width and half-width characters, spaces, and hyphens. Doing that on the query side every time would defeat the indexes, so at import time I store separate normalized columns, &lt;code&gt;code_norm&lt;/code&gt; and &lt;code&gt;name_norm&lt;/code&gt;, index those, and look them up with &lt;code&gt;LIKE&lt;/code&gt;. The key is running the same normalization function at import time and at search time, so all the handling of spelling variations is finished at the import stage.&lt;br&gt;
Beyond that, things like making tables that are only looked up by a composite primary key &lt;code&gt;WITHOUT ROWID&lt;/code&gt;, and loading data with &lt;code&gt;PRAGMA synchronous = OFF&lt;/code&gt; during import, then compacting it with &lt;code&gt;wal_checkpoint(TRUNCATE)&lt;/code&gt; and &lt;code&gt;VACUUM&lt;/code&gt; before shipping, are all handled with standard SQLite features. At this scale, I have yet to feel that something is “slow because it’s SQLite.”&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;on-orms&quot;&gt;On ORMs&lt;a class=&quot;anchor&quot; href=&quot;#on-orms&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The first version of the rewrite used Drizzle ORM, but at this scale there weren’t really any differences for the ORM to smooth over, and writing SQL directly is easier to read and fix, so I removed it the same day. Now there’s just a thin layer: the schema in &lt;code&gt;ddl.ts&lt;/code&gt; and raw SQL in &lt;code&gt;repo.ts&lt;/code&gt;.&lt;/p&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;As a very SQLite-style trick, denpa makes a recording’s &lt;code&gt;state&lt;/code&gt; a generated column. SQLite rejects any attempt to write to it, so the facts and the state can’t drift apart. Back when I stored it separately as a string, that drift was a breeding ground for bugs.&lt;/p&gt;
&lt;/blockquote&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;on-bun&quot;&gt;On Bun&lt;a class=&quot;anchor&quot; href=&quot;#on-bun&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Two things: bun:sqlite and Bun.password (argon2id) are bundled, so the database and password hashing are covered without any native dependencies; and it runs TypeScript as-is and starts up fast.&lt;/p&gt;&lt;p&gt;As a side effect, node_modules disappeared from production. adapter-node bundles devDependencies into the server build and leaves only dependencies external, so if you move everything actually needed at runtime into devDependencies, dependencies ends up empty and the app runs with just Bun and the build output. The install step vanished from the final stage of the Dockerfile.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;where-bun-tripped-me-up&quot;&gt;Where Bun tripped me up&lt;a class=&quot;anchor&quot; href=&quot;#where-bun-tripped-me-up&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;There’s no point in only writing about the good parts, so here are the places I actually got stuck.&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;It can’t start on Node&lt;br&gt;
The moment you use bun:sqlite, the app only runs on Bun. You end up writing “Always start this with Bun” in the README. I’ve closed off my own escape route for switching away.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Things break when the local Bun and the container’s Bun are different versions&lt;br&gt;
After getting bitten by this once, I moved all development into the container. If you run &lt;code&gt;bun install&lt;/code&gt; at the repository root, the preinstall below stops it (the message reads “Don’t install locally”).&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;json&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:90ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;&quot;preinstall&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;&quot;test &lt;/span&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;\&quot;&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;$PWD&lt;/span&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;\&quot;&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt; = /usr/src/app || { echo &apos;ローカルで install しないでください&apos;; exit 1; }&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To run Vite on Bun you have to write &lt;code&gt;bunx --bun vite&lt;/code&gt;&lt;br&gt;
Plain &lt;code&gt;vite&lt;/code&gt; starts up on Node.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Getting TypeScript 7 and svelte-check to coexist&lt;br&gt;
To run svelte-check with the native TypeScript 7 (tsgo), you need both &lt;code&gt;typescript@~6&lt;/code&gt; and an alias for &lt;code&gt;@typescript/native&lt;/code&gt; installed. It does get faster, but the dependency list looks a bit off.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Biome only looks at the &lt;code&gt;&amp;#x3C;script&gt;&lt;/code&gt; in &lt;code&gt;.svelte&lt;/code&gt; files&lt;br&gt;
It flags variables used in the template as unused, so I’ve turned off the unused-variable checks for &lt;code&gt;.svelte&lt;/code&gt; files.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;adapter-node closes its listener the moment it receives SIGTERM&lt;br&gt;
This is less about Bun and more about running SvelteKit on k3s as a long-running process. denpa is designed so that if a deploy comes in mid-recording, it sticks around until the recording finishes, but adapter-node’s default cleanup ran first, leaving the process alive with only its port closed. I had to catch the stop signal myself and remove that cleanup, and because of the timing of when it’s registered, I had to do it twice. The details are in denpa’s &lt;a href=&quot;https://github.com/DAnything/denpa/blob/main/docs/architecture.md&quot;&gt;architecture.md&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;on-biome-and-css&quot;&gt;On Biome and CSS&lt;a class=&quot;anchor&quot; href=&quot;#on-biome-and-css&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I chose Biome largely because I’m a neat freak and dislike having libraries scattered around. The fewer libraries the better. Right after the mogiri rewrite I was using ESLint + Prettier, but I didn’t like keeping separate libraries and config files for linting and formatting, and on top of that having to check how well they play together, so I consolidated on a single &lt;code&gt;biome.json&lt;/code&gt; and a single command, &lt;code&gt;biome check --write&lt;/code&gt;. It’s written in Rust and fast, so putting it alongside type checking in the &lt;code&gt;check&lt;/code&gt; script doesn’t bother me.&lt;br&gt;
The downside, as I mentioned earlier, is that it only looks at the &lt;code&gt;&amp;#x3C;script&gt;&lt;/code&gt; in &lt;code&gt;.svelte&lt;/code&gt; files and flags variables used in the template as unused, so I use it with just that rule turned off.&lt;/p&gt;&lt;p&gt;For a long time my CSS was Tailwind + daisyUI. At the start of a project it’s easy to build a UI with fixed choices, and when you want to customize something, Tailwind makes it easy to tweak directly. I’d start with &lt;code&gt;class=&quot;btn btn-primary&quot;&lt;/code&gt; and only add utilities where I didn’t like something, as in &lt;code&gt;class=&quot;btn btn-primary rounded-full px-8&quot;&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;But when I looked back after finishing a pass over all the apps, the components that actually showed up were basically just tables, forms, notices, and tags. For that little, I was loading 3,000 lines of component CSS, and the markup had become strings of ten classes like &lt;code&gt;class=&quot;card bg-base-100 border-base-300 flex flex-col gap-3 p-4 shadow-sm&quot;&lt;/code&gt; that I couldn’t make sense of six months later. Utility classes are fast to write, but when you &lt;strong&gt;read&lt;/strong&gt; them, you have to mentally convert every class name back into CSS.&lt;/p&gt;&lt;p&gt;So at one point I moved everything over to &lt;a href=&quot;https://picocss.com/&quot;&gt;Pico CSS&lt;/a&gt;. It’s a classless CSS framework that styles plain &lt;code&gt;&amp;#x3C;button&gt;&lt;/code&gt; and &lt;code&gt;&amp;#x3C;table&gt;&lt;/code&gt; elements as-is.&lt;/p&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;Update (October 2026): I’ve since dropped Pico CSS itself and switched all the apps to &lt;a href=&quot;https://blades.ninja/&quot;&gt;Blades&lt;/a&gt; (&lt;code&gt;@anyblades/blades&lt;/code&gt;), which carries on Pico’s maintenance. It’s distributed as plain CSS, so I no longer need Sass and could remove &lt;code&gt;sass-embedded&lt;/code&gt; and the related Vite config. What follows describes the setup after moving to Blades.&lt;/p&gt;
&lt;/blockquote&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;css&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:45ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;@import&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;&quot;@anyblades/blades/pico&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;layer&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FFAB70;--1:#FFAB70&quot;&gt;pico&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;Blades is loaded into a layer (&lt;code&gt;@layer&lt;/code&gt;) named &lt;code&gt;pico&lt;/code&gt;. My own rules, which aren’t in a layer, always win over rules inside the layer regardless of specificity, so there’s no need to get into a tug-of-war of rewriting selectors to match Pico’s strong ones.&lt;/p&gt;&lt;p&gt;On top of that, I put only the things shared across apps into &lt;code&gt;app.css&lt;/code&gt;, about ten names like &lt;code&gt;.panel&lt;/code&gt; (a white box), &lt;code&gt;.note&lt;/code&gt; (a notice), &lt;code&gt;.tag&lt;/code&gt; (a tag), and &lt;code&gt;.field&lt;/code&gt; (label + input), and confined screen-specific tweaks to Svelte’s &lt;code&gt;&amp;#x3C;style&gt;&lt;/code&gt; (scoped). As a result, the markup from earlier became just two words, &lt;code&gt;class=&quot;panel body&quot;&lt;/code&gt;, and each screen’s particulars live only inside that screen’s file.&lt;/p&gt;&lt;p&gt;For colors, I mostly stick with Blades’ defaults. If my own tokens just pull from Pico’s &lt;code&gt;--pico-*&lt;/code&gt; variables, light/dark switching follows Blades automatically, so the apps no longer have to take care of a dark theme themselves.&lt;/p&gt;&lt;p&gt;I’ve only added &lt;a href=&quot;https://bits-ui.com/&quot;&gt;Bits UI&lt;/a&gt; for components like dropdowns and dialogs, where keyboard interaction and focus need to be taken care of. It’s headless with no styling of its own, so it doesn’t clash with my approach of writing the CSS myself.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;what-i-dont-do-in-sveltekit&quot;&gt;What I don’t do in SvelteKit&lt;a class=&quot;anchor&quot; href=&quot;#what-i-dont-do-in-sveltekit&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I don’t do everything with this stack. When I need heavy computation, or processing that holds a queue and keeps running for a long time, I use .NET.&lt;br&gt;
denpa’s tuner agent is one of those. It’s a component that just grabs the signal and streams the raw TS, and I originally wrote it in bun, but after measuring it on real hardware I rewrote it in .NET (it’s Native AOT, so it’s a single executable with no dependencies).&lt;br&gt;
As for what went wrong with bun: first, when something fails, say the tuner gets taken away, you have to tear down the whole connection so the receiving side doesn’t see it as a normal end, but the bun version couldn’t do that, so truncated recordings counted as “recorded.” Second, the kernel’s dvr ring buffer is 1.8 MB by default, which at terrestrial broadcasting’s 18 Mbit/s is only 0.9 seconds’ worth, so it overflows if the reader stalls even briefly, say for GC. I tuned this on real hardware while counting the overflows, but in the end only the .NET version could call ioctl directly, so that’s where I landed. The numbers I measured are written up as-is in &lt;a href=&quot;https://github.com/DAnything/denpa/blob/main/docs/agent.md&quot;&gt;agent.md&lt;/a&gt; if you’re interested.&lt;br&gt;
UI and API in SvelteKit; things close to the hardware, things that need to be real-time, and heavy computation in .NET. That rough split hasn’t caused me any trouble so far.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;on-ai&quot;&gt;On AI&lt;a class=&quot;anchor&quot; href=&quot;#on-ai&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To be honest, Claude wrote most of the mogiri rewrite. The task was to read the Django + Nuxt code and rebuild the same screen flows in SvelteKit. What the human did was make the decisions described above, and poke at and fix what came out.&lt;/p&gt;&lt;p&gt;The choice of one language, one process, and readable generated code pays off precisely because of that premise. Having tons of code I can’t read generated for me wouldn’t help, so choosing Svelte, with less code to write and a straightforward structure, was also a decision to widen the range of what I can hand off to AI.&lt;br&gt;
I wrote about how this feels in practice in the second half of &lt;a href=&quot;https://doany.io/en/posts/slack-search-read/&quot;&gt;my Slack post&lt;/a&gt;.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;a class=&quot;anchor&quot; href=&quot;#summary&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;For small-to-medium apps, don’t split out the backend. One SvelteKit app, one container&lt;/li&gt;
&lt;li&gt;I picked Svelte for how little you write and how readable it is. I have no intention of keeping up with the React ecosystem&lt;/li&gt;
&lt;li&gt;Use SQLite knowing its limits. It’s self-contained with one PVC, and if it gets bigger, PostgreSQL&lt;/li&gt;
&lt;li&gt;I picked Bun for how much it bundles. In exchange, the escape route to Node is closed&lt;/li&gt;
&lt;li&gt;Carve out heavy computation and anything that needs a queue into .NET&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;I built five and they all settled into the same shape, so the next time I build something I’ll probably start with this setup too. If you’re struggling with this stack, or think something here should be done differently, I’d appreciate a comment.&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>You Can&apos;t Distribute a Slack App That Fetches All of Your Own Messages</title><link>https://doany.io/en/posts/slack-search-read/</link><guid isPermaLink="true">https://doany.io/en/posts/slack-search-read/</guid><description>I tried to ship an app that uses search.messages to other people&apos;s workspaces, only to find search:read named outright as a rejected scope in the Slack Marketplace guidelines. These are my notes on what I found, alternative routes included, and the shape I ended up going with.</description><pubDate>Fri, 31 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I set out to build a Slack app that pulls every message I’ve posted across a workspace, and found out it can’t be listed on the Slack Marketplace. The scope it needs, &lt;code&gt;search:read&lt;/code&gt;, is called out by name in the guidelines as a rejected scope.&lt;br&gt;
Every workaround turned out to be closed off too, and in the end the only option left was to have users create an internal app themselves. I’m writing this down with quotes and sources so that the next person looking into the same thing doesn’t burn a whole day on it.&lt;/p&gt;
&lt;section&gt;&lt;h2 id=&quot;what-i-wanted-to-do&quot;&gt;What I wanted to do&lt;a class=&quot;anchor&quot; href=&quot;#what-i-wanted-to-do&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The idea was to auto-generate a timesheet (a list of working hours) from the timestamps of my Slack messages.&lt;br&gt;
All I need are the timestamps, not the message bodies. If I know who posted when, I can infer the start, end, and breaks of a workday from clusters of activity.&lt;/p&gt;&lt;p&gt;The API for this is &lt;code&gt;search.messages&lt;/code&gt;. With a query like &lt;code&gt;from:&amp;#x3C;@USER_ID&gt; after:2026-06-01 before:2026-07-01&lt;/code&gt; you can pull just your own messages for a given period. The only scope it needs is &lt;code&gt;search:read&lt;/code&gt;.&lt;br&gt;
If anything, I’d say that’s on the small side as permissions go. It doesn’t read whole channel histories; it only returns your own messages.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;how-searchread-is-treated&quot;&gt;How search:read is treated&lt;a class=&quot;anchor&quot; href=&quot;#how-searchread-is-treated&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The guidelines spell it out directly.&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;legacy/restricted scopes or methods, scopes that provide extensive access to workspace data without a clear use case that requires them, or coded workflow scopes (e.g. &lt;code&gt;admin.*&lt;/code&gt;, &lt;code&gt;identity.*&lt;/code&gt;, &lt;strong&gt;&lt;code&gt;search:read&lt;/code&gt;&lt;/strong&gt;, &lt;code&gt;workflow.steps:execute&lt;/code&gt;, &lt;code&gt;triggers:*&lt;/code&gt;)
Source: &lt;a href=&quot;https://docs.slack.dev/slack-marketplace/slack-marketplace-app-guidelines-and-requirements/&quot;&gt;Slack Marketplace app guidelines and requirements&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;The scope reference also marks it as legacy.&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;This is a legacy scope
Source: &lt;a href=&quot;https://docs.slack.dev/reference/scopes/search.read/&quot;&gt;search:read scope&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;blockquote class=&quot;admonition bdm-important&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;IMPORTANT&lt;/span&gt;
&lt;p&gt;It hasn’t been deprecated. With a user token, &lt;code&gt;search.messages&lt;/code&gt; still works just fine. You just can’t list it on the Marketplace. Mix these two up and you’ll wrongly conclude “it doesn’t work anymore,” so be careful.&lt;/p&gt;
&lt;/blockquote&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;working-around-it-with-history&quot;&gt;Working around it with *:history&lt;a class=&quot;anchor&quot; href=&quot;#working-around-it-with-history&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If search is off the table, the obvious next thought is to just read channel history instead. But the same page lists that as something you must not do.&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;Request user token &lt;code&gt;*:history&lt;/code&gt; and &lt;code&gt;files:read&lt;/code&gt; scopes &lt;strong&gt;for the collection of message and file data&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;On top of that, the rate limits changed in May 2025, and this route is effectively dead.&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;conversations.history&lt;/code&gt; API method rate limit for commercially distributed apps created after May 29, 2025 … will be limited to 1 request per minute … These methods will have a new rate limit of 15 messages per request.
Source: &lt;a href=&quot;https://docs.slack.dev/changelog/2025/05/29/rate-limit-changes-for-non-marketplace-apps/&quot;&gt;Rate limit changes for non-Marketplace apps&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;One request per minute, 15 messages per request. For going back a month and aggregating, that’s a non-starter.&lt;br&gt;
And fetching history means reading every message from everyone in the channel in the first place. I only want my own messages, yet I’d be asking for permissions far broader than search. You’d be trading friction for something more invasive, which is a bad deal.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;the-real-time-search-api&quot;&gt;The Real-time Search API&lt;a class=&quot;anchor&quot; href=&quot;#the-real-time-search-api&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In February 2026 Slack released the Real-time Search API as a replacement for &lt;code&gt;search:read&lt;/code&gt;. It consists of &lt;code&gt;assistant.search.context&lt;/code&gt; and finer-grained scopes like &lt;code&gt;search:read.public&lt;/code&gt; / &lt;code&gt;.private&lt;/code&gt; / &lt;code&gt;.im&lt;/code&gt;.&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;Source: &lt;a href=&quot;https://docs.slack.dev/changelog/2026/02/17/slack-mcp/&quot;&gt;Announcing the Slack MCP server and Real-time Search API&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;This one can be listed. But it comes with four conditions of use, and depending on your use case, every one of them can bite.&lt;/p&gt;
























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Condition&lt;/th&gt;&lt;th&gt;Details&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;No data storage&lt;/td&gt;&lt;td&gt;”You must not store or copy any of the data retrieved from this API.”&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;No guest access&lt;/td&gt;&lt;td&gt;”Workspace guests are not permitted to access apps using platform AI features”&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Plan restrictions&lt;/td&gt;&lt;td&gt;Semantic search requires Business+ / Enterprise+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AI features only&lt;/td&gt;&lt;td&gt;”exclusively in your app using AI features”&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;blockquote&gt;
&lt;p&gt;Source: &lt;a href=&quot;https://docs.slack.dev/apis/web-api/real-time-search-api/&quot;&gt;Using the Real-time Search API&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;For my use case the second one was fatal. People working on-site at a client or as contractors often join the client’s workspace as guests, so this condition excludes exactly the users I was targeting.&lt;br&gt;
There are also caps of up to 20 results per request and 10 requests per minute per user.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;workspace-app-approval-settings&quot;&gt;Workspace app approval settings&lt;a class=&quot;anchor&quot; href=&quot;#workspace-app-approval-settings&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This was the biggest takeaway. I’d assumed an internal app would just get blocked by an admin anyway, but when I looked into it, it was the other way around.&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;By default, members can install apps without approval from a Workspace Owner.
Source: &lt;a href=&quot;https://slack.com/help/articles/222386767-Manage-app-approval-for-your-workspace&quot;&gt;Manage app approval for your workspace&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;By default, no approval is needed. Here’s how things behave when the settings are tightened.&lt;/p&gt;
























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Workspace setting&lt;/th&gt;&lt;th&gt;Marketplace-listed app&lt;/th&gt;&lt;th&gt;Self-built internal app&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;No restrictions (default)&lt;/td&gt;&lt;td&gt;Allowed&lt;/td&gt;&lt;td&gt;Allowed&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Only allow apps from the Marketplace&lt;/td&gt;&lt;td&gt;Allowed&lt;/td&gt;&lt;td&gt;Allowed&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Approval required for all apps&lt;/td&gt;&lt;td&gt;Pending approval&lt;/td&gt;&lt;td&gt;Pending approval&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;Slack states the second row explicitly.&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;Workspace Owners can set a permission to “Only allow apps from the Slack Marketplace” … &lt;strong&gt;This will not prevent members from creating and installing internal apps.&lt;/strong&gt;
Source: &lt;a href=&quot;https://slack.com/help/articles/202035138-Add-apps-to-your-Slack-workspace&quot;&gt;Add apps to your Slack workspace&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;In other words, the “Marketplace only” setting doesn’t stop internal apps. That’s the opposite of what the name suggests.&lt;br&gt;
And with the third row, “approval required for all apps,” Marketplace-listed apps end up pending approval too, so there’s no setting that puts internal apps at a disadvantage.&lt;/p&gt;&lt;p&gt;On top of that, the rate limit change I mentioned earlier says this:&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Internal customer-built apps will not notice any changes.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;Internal apps are exempt from the tightened limits.&lt;br&gt;
So the internal-app approach isn’t a second-best option you settle for because you can’t get listed; for this use case it was the only option that actually works properly.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;how-i-actually-distribute-it&quot;&gt;How I actually distribute it&lt;a class=&quot;anchor&quot; href=&quot;#how-i-actually-distribute-it&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I went with having users create the app themselves. To cut down the effort, I hand them an App Manifest embedded in a URL.&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:73ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;https://api.slack.com/apps?new_app=1&amp;#x26;manifest_yaml=&amp;#x3C;URL エンコードした manifest&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;(The placeholder reads “URL-encoded manifest”.)&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;Source: &lt;a href=&quot;https://docs.slack.dev/app-manifests/configuring-apps-with-app-manifests/&quot;&gt;Configuring apps with app manifests&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;This way they start with the app name, description, and scopes already filled in. The only scope requested is &lt;code&gt;search:read&lt;/code&gt;, so the consent screen is light too.&lt;br&gt;
Reading the docs, it looks like it’s done in “click the link → Create → Install → copy the token,” but when you actually do it there are two traps.&lt;/p&gt;&lt;section&gt;&lt;h3 id=&quot;the-red-error-on-creation&quot;&gt;The red error on creation&lt;a class=&quot;anchor&quot; href=&quot;#the-red-error-on-creation&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;In Step 2, when you press Create and Install, a white popup flashes open and closes, and then you see this:&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:70ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;Installation was not completed. Click Create and Install to try again.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;The app itself has been created. Reload the list and it shows up.&lt;br&gt;
The cause is probably that this app has no bot scopes at all. It’s a flow that creates and installs in one go, but there’s no bot to install, so it misfires. Adding a bot scope would probably make it go away, but increasing the requested permissions just for that would defeat the purpose, so I’ve left it as is.&lt;br&gt;
A red error on the very first step of onboarding is a guaranteed drop-off without an explanation, so all I could do was write “this is normal” in the instructions.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;token-rotation&quot;&gt;Token rotation&lt;a class=&quot;anchor&quot; href=&quot;#token-rotation&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;At the top of the OAuth &amp;#x26; Permissions page there’s an option labeled like this:&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Recommended&lt;/strong&gt; for developers building on or for security-minded organizations&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;When something says Recommended you want to turn it on, but if you do, tokens start expiring after a short time, and any app without a refresh mechanism can no longer connect.&lt;br&gt;
The generated manifest sets &lt;code&gt;token_rotation_enabled: false&lt;/code&gt;, but it can still be turned on later from the UI, so the instructions also say plainly “please don’t enable this.”&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;about-ai&quot;&gt;About AI&lt;a class=&quot;anchor&quot; href=&quot;#about-ai&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To be upfront: I had AI (Claude Code) write almost all of the code. What I did as the human was decide what to build and what not to build.&lt;br&gt;
That was not easy work. AI will build something that works if you tell it to, but if your instructions are wrong, you get the wrong thing, built to a high standard.&lt;/p&gt;&lt;p&gt;For a concrete example, I initially designed the free plan’s cutoff as “Slack integration is free, other services are paid.” Since Slack is the main data source, it seemed like a plausible line. The implementation, the pricing page, and the decision records were all written on that premise, and the tests passed.&lt;br&gt;
I only noticed the mistake when I used it myself. Someone who has only connected GitHub couldn’t generate anything on the free plan; they’d sign up, connect, look at an empty timesheet, and leave. The whole point of the free plan was to show the full value for free, and I’d drawn a line that broke that very goal.&lt;br&gt;
I changed the line to “one integration, regardless of which service.” Around the same time, real data showed that building a timesheet from commit times alone came out to only 9 hours of actual work for a month, which led to tuning the inference model.&lt;/p&gt;&lt;p&gt;My current feeling is that you can hand the building to AI, but you can’t hand off making sure you’re building the right thing. Tests tell you whether it works, but not whether it’s right.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;a class=&quot;anchor&quot; href=&quot;#summary&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;For anyone wanting to distribute an app that uses &lt;code&gt;search.messages&lt;/code&gt;, here’s the rundown.&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;It can’t be listed on the Marketplace. &lt;code&gt;search:read&lt;/code&gt; is named outright as a rejected scope&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;*:history&lt;/code&gt; workaround is closed off too. It’s prohibited by the guidelines, and the rate limit is 1 req/min with 15 messages&lt;/li&gt;
&lt;li&gt;The Real-time Search API can be listed, but it comes with no data storage, no guest access, and plan restrictions&lt;/li&gt;
&lt;li&gt;The internal-app approach is alive and well. Even the workspace’s “Marketplace only” setting doesn’t stop it, and it’s exempt from the tightened rate limits&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Here’s what I built. It infers each day’s start, end, breaks, and actual working time from your Slack and GitHub activity logs and produces a draft timesheet. It works even in environments where you can’t install a monitoring agent at the client site, and it can generate timesheets retroactively for past months.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://w.doany.io&quot;&gt;https://w.doany.io&lt;/a&gt;&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>Rebuilding This Site with Astro + k3s</title><link>https://doany.io/en/posts/renewal/</link><guid isPermaLink="true">https://doany.io/en/posts/renewal/</guid><description>I fully migrated this blog from Next.js to Fuwari, an Astro-based theme. Here&apos;s a full rundown of the current setup, from build and delivery to comments and subscriptions. For comments, I went through giscus, remark42, and Artalk, and in the end wrote my own.</description><pubDate>Wed, 29 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This site was originally built with HUGO and later ran on Next.js (tailwind-nextjs-starter-blog), but I’ve now fully migrated it to &lt;a href=&quot;https://github.com/saicaca/fuwari&quot;&gt;Fuwari&lt;/a&gt;, an Astro-based theme.&lt;br&gt;
While I’m at it, I’ll write up the current setup from start to finish.&lt;/p&gt;
&lt;a class=&quot;card-github no-styling&quot; href=&quot;https://github.com/saicaca/fuwari&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;div class=&quot;gc-titlebar&quot;&gt;&lt;div class=&quot;gc-titlebar-left&quot;&gt;&lt;div class=&quot;gc-owner&quot;&gt;&lt;div class=&quot;gc-avatar&quot; style=&quot;background-image: url(https://avatars.githubusercontent.com/u/25200299?v=4); background-color: transparent&quot;&gt;&lt;/div&gt;&lt;div class=&quot;gc-user&quot;&gt;saicaca&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;gc-divider&quot;&gt;/&lt;/div&gt;&lt;div class=&quot;gc-repo&quot;&gt;fuwari&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;github-logo&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;gc-description&quot;&gt;✨A static blog template built with Astro. &lt;/div&gt;&lt;div class=&quot;gc-infobar&quot;&gt;&lt;div class=&quot;gc-stars&quot;&gt;5.1K&lt;/div&gt;&lt;div class=&quot;gc-forks&quot;&gt;1.3K&lt;/div&gt;&lt;div class=&quot;gc-license&quot;&gt;MIT&lt;/div&gt;&lt;span class=&quot;gc-language&quot;&gt;Astro&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;
&lt;section&gt;&lt;h2 id=&quot;the-big-picture&quot;&gt;The big picture&lt;a class=&quot;anchor&quot; href=&quot;#the-big-picture&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:70ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;Posts (Markdown)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:2ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;↓ pnpm build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;Astro → dist/ + Pagefind search index&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:2ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;↓ docker build (multi-stage)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;Build on node:24-slim → put only dist/ on caddy:2-alpine&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:2ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;↓ GitHub Actions&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;Push to ghcr.io → automatically rewrite the k3s manifest and commit it&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:2ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;↓ ArgoCD watches Git and syncs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;k3s + Traefik (Let&apos;s Encrypt / DNS-01) → Cloudflare → readers&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;No server-side application runs at all; everything served is purely static files.&lt;/p&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;Update (October 2026): I’ve since switched the package manager from pnpm to Bun, so the build is now &lt;code&gt;bun run build&lt;/code&gt; and the build image is &lt;code&gt;oven/bun&lt;/code&gt; instead of &lt;code&gt;node:24-slim&lt;/code&gt;. I also replaced Swup with Astro’s built-in ClientRouter for page transitions. The rest of the flow is unchanged. See the &lt;a href=&quot;https://github.com/DAnything/blog&quot;&gt;repository&lt;/a&gt; for the latest setup.&lt;/p&gt;
&lt;/blockquote&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;build&quot;&gt;Build&lt;a class=&quot;anchor&quot; href=&quot;#build&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Since it’s Astro, the output is static HTML. Page transitions are handled by Swup, so pages switch without a full reload.&lt;/p&gt;&lt;p&gt;For full-text search I use Pagefind. It scans &lt;code&gt;dist/&lt;/code&gt; at build time to create the index, so no search server is needed. Japanese text gets matched properly too.&lt;/p&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;Pagefind doesn’t support stemming for Japanese, so it won’t match across inflected forms, but I think it’s perfectly practical for searching a personal blog.&lt;/p&gt;
&lt;/blockquote&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;serving-with-caddy&quot;&gt;Serving with Caddy&lt;a class=&quot;anchor&quot; href=&quot;#serving-with-caddy&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The image has a two-stage setup: &lt;code&gt;node:24-slim&lt;/code&gt; for the build and &lt;code&gt;caddy:2-alpine&lt;/code&gt; for serving. The final image contains only &lt;code&gt;dist/&lt;/code&gt; and the &lt;code&gt;Caddyfile&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;There’s a reason I chose Caddy over nginx.&lt;br&gt;
Astro uses &lt;code&gt;trailingSlash: &quot;always&quot;&lt;/code&gt;, so every request to &lt;code&gt;/posts/foo&lt;/code&gt; gets redirected to &lt;code&gt;/posts/foo/&lt;/code&gt;. nginx builds that redirect URL from &lt;code&gt;$scheme&lt;/code&gt;, so if TLS is terminated at a proxy in front, it 301s to &lt;code&gt;http://&lt;/code&gt;, which is a classic trap. You can avoid it by writing &lt;code&gt;absolute_redirect off;&lt;/code&gt;, but if you don’t know about it, you’re guaranteed to fall in.&lt;br&gt;
Caddy returns relative redirects, so this problem simply doesn’t exist. Here’s what it actually looks like.&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;console&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:58ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;$ curl -sI https://doany.io/posts/truck &lt;/span&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;grep&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;-i&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;location&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;location: https://doany.io/posts/truck/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;On top of that, a single line, &lt;code&gt;encode zstd gzip&lt;/code&gt;, gets you zstd compression. Using zstd with nginx requires building a module, so this convenience is a big deal. The top page goes from 97,465 bytes down to 16,264 bytes.&lt;/p&gt;&lt;p&gt;I also consolidated redirects from old URLs into Caddy.&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;text&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:49ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;@blogPost path_regexp blogPost ^/blog/(.+?)/?$&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;redir @blogPost /posts/{re.blogPost.1}/ permanent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;/blog/*&lt;/code&gt;, &lt;code&gt;/tags/*&lt;/code&gt;, &lt;code&gt;/projects&lt;/code&gt;, &lt;code&gt;/feed.xml&lt;/code&gt; and the like are URLs from the old setup, so they all get 301’d to their new locations.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;deploying-with-gitops&quot;&gt;Deploying with GitOps&lt;a class=&quot;anchor&quot; href=&quot;#deploying-with-gitops&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This is my personal favorite part: I never run &lt;code&gt;kubectl apply&lt;/code&gt; from my machine.&lt;/p&gt;&lt;p&gt;When I push to &lt;code&gt;main&lt;/code&gt;, GitHub Actions first builds the image and pushes it to &lt;code&gt;ghcr.io&lt;/code&gt;. Then the same job rewrites the image tag in &lt;code&gt;deploy/deployment.yaml&lt;/code&gt; to the commit SHA and commits it back to the repository.&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;yaml&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:121ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;Update deployment image tag&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:2ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;run&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:4ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;sed -i &quot;s#image: ${REGISTRY}/${IMAGE_NAME}:.*#image: ${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA}#&quot; deploy/deployment.yaml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;On the k3s side, ArgoCD runs permanently and watches this repository. When it detects a change to the manifest, it syncs automatically and the Pods are swapped out for ones running the new image.&lt;br&gt;
In other words, Git is the state of the cluster, and you can tell what’s running just by looking at the repository. If I want to roll back, I just &lt;code&gt;git revert&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;ArgoCD itself is also declared as a manifest using k3s’s &lt;code&gt;HelmChart&lt;/code&gt; CRD, so even if I rebuild the cluster, I can bring it back with the same steps. Login goes through Entra ID via OIDC, and the local admin account is disabled.&lt;br&gt;
Traefik obtains certificates from Let’s Encrypt using the DNS-01 challenge. Cloudflare sits in front.&lt;/p&gt;&lt;section&gt;&lt;h3 id=&quot;handling-secrets&quot;&gt;Handling secrets&lt;a class=&quot;anchor&quot; href=&quot;#handling-secrets&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Secrets like the comment system’s signing key and the OIDC client secret aren’t kept in Git. They live in a self-hosted Infisical instance, and its official operator on the k3s side reads them and turns them into Secrets. The manifest only says which Infisical folder to look at.&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;yaml&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:41ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;secretsScope&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:2ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;projectSlug&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;doa&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:2ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;envSlug&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;prod&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:2ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;secretsPath&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;/yosegaki/yosegaki-secrets&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;I used to commit secrets encrypted with Sealed Secrets, but re-sealing them every time I changed a value was a pain, so I moved to the current setup, where editing a value in Infisical’s UI swaps out the Pods within seconds. It breaks the “everything is in Git” property, but the references are still in Git, so I can track what lives where.&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;i-wrote-my-own-comment-system&quot;&gt;I wrote my own comment system&lt;a class=&quot;anchor&quot; href=&quot;#i-wrote-my-own-comment-system&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This went back and forth a few times, so I’ll write up the whole story. In the end I’m running something I wrote myself.&lt;/p&gt;&lt;section&gt;&lt;h3 id=&quot;on-giscus&quot;&gt;On giscus&lt;a class=&quot;anchor&quot; href=&quot;#on-giscus&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Right after the migration I had giscus (the one that uses GitHub Discussions), but requiring a GitHub account to comment just kept bothering me.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;on-remark42&quot;&gt;On remark42&lt;a class=&quot;anchor&quot; href=&quot;#on-remark42&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;So I switched to remark42. It’s a single Go binary and its storage is a BoltDB file, so no separate database is needed. Setting &lt;code&gt;AUTH_ANON=true&lt;/code&gt; lets people post without an account.&lt;/p&gt;&lt;p&gt;Readers can stay anonymous, but someone still needs to be able to delete things when trolls show up. So I set it up so that only the admin logs in, via Entra ID. It reuses the same app registration as ArgoCD and my other internal services.&lt;br&gt;
I got stuck here for a bit. If the app registration is single-tenant, you can’t use the default &lt;code&gt;/common&lt;/code&gt; endpoint.&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;text&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:73ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;AADSTS50194: Application is not configured as a multi-tenant application.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;Usage of the /common endpoint is not supported for such applications.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;You need an option to specify the tenant explicitly, and that was only available in remark42 v1.16 and later. Making the app registration multi-tenant would have gotten it working, but that registration is shared with other services, so I wanted to avoid widening its authentication scope. In the end, I solved it by upgrading remark42.&lt;/p&gt;&lt;p&gt;After using it for a while, what bothered me was the look. The site is built with Tailwind, yet the comment section alone clearly looked like something else.&lt;br&gt;
When I tried to fix it, I found out that remark42 is embedded in an iframe. The host page’s CSS can’t reach inside, so there’s no room to tweak it from outside, and the only exposed knob is &lt;code&gt;theme: light | dark&lt;/code&gt;. In other words, if you don’t like the look, your only option is to switch.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;comparing-the-candidates&quot;&gt;Comparing the candidates&lt;a class=&quot;anchor&quot; href=&quot;#comparing-the-candidates&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I lined up the options that support anonymous comments and whose look I could actually control.&lt;/p&gt;














































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Implementation / DB&lt;/th&gt;&lt;th&gt;Rendering&lt;/th&gt;&lt;th&gt;Styling&lt;/th&gt;&lt;th&gt;Admin auth&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Artalk&lt;/td&gt;&lt;td&gt;Single Go binary / SQLite&lt;/td&gt;&lt;td&gt;Host DOM&lt;/td&gt;&lt;td&gt;CSS variables, dark mode&lt;/td&gt;&lt;td&gt;Password&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Isso&lt;/td&gt;&lt;td&gt;Python / SQLite&lt;/td&gt;&lt;td&gt;Host DOM&lt;/td&gt;&lt;td&gt;Completely free&lt;/td&gt;&lt;td&gt;Password&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Comentario&lt;/td&gt;&lt;td&gt;Go / SQLite&lt;/td&gt;&lt;td&gt;Web Component&lt;/td&gt;&lt;td&gt;Can be fully disabled and replaced&lt;/td&gt;&lt;td&gt;OIDC supported&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Waline&lt;/td&gt;&lt;td&gt;Node.js / various&lt;/td&gt;&lt;td&gt;Host DOM&lt;/td&gt;&lt;td&gt;CSS variables&lt;/td&gt;&lt;td&gt;Password&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;remark42&lt;/td&gt;&lt;td&gt;Single Go binary / BoltDB&lt;/td&gt;&lt;td&gt;iframe&lt;/td&gt;&lt;td&gt;light/dark only&lt;/td&gt;&lt;td&gt;OIDC supported&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;I ruled out Cusdis because it’s archived, Commento because it’s been abandoned since 2022 (Comentario is its successor), and utterances and giscus because they require a GitHub account.&lt;/p&gt;&lt;p&gt;I had written Isso off as old, but that was a mistake. With &lt;code&gt;data-isso-css=&quot;false&quot;&lt;/code&gt; you can drop its default stylesheet entirely; it’s designed with the assumption that you’ll write your own CSS. Its default look is just plain, and you can make it match your site as closely as you like.&lt;/p&gt;&lt;p&gt;I looked at the numbers too. Isso has the most stars at 5,293, but that’s accumulated over 14 years. In Docker pulls, which are closer to real-world usage, Artalk had 639K, while Isso was spread across unofficial images for a total of around 360K. Comentario was in the three digits; good features, but almost no track record.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;on-artalk&quot;&gt;On Artalk&lt;a class=&quot;anchor&quot; href=&quot;#on-artalk&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The deciding factor was that Artalk runs the same way remark42 does. It’s a single Go binary with SQLite, so I could reuse the one-PVC, single-Pod setup as-is. The manifest only needed the hostname and image swapped.&lt;br&gt;
Since it renders into the host page’s DOM rather than an iframe, there’s still a way to apply CSS, and by wiring its CSS variables to the site’s color scheme I got it to blend in reasonably well.&lt;/p&gt;&lt;p&gt;It came at a cost. Artalk doesn’t have OIDC, so I lost the Entra ID admin login I’d set up. The admin panel uses a single shared password. Since I’m the only admin and all I do is delete spam, I accepted that.&lt;/p&gt;&lt;p&gt;As I used it more, other things started to bother me. Some parts of the Japanese translation were questionable, and the notification center gets separate CSS inside an iframe, so font settings meant for Chinese show up as-is. I sent PRs upstream, but the fact remained that the fonts and wording were decided somewhere out of my reach.&lt;br&gt;
As for styling, I matched what I could with variables, but small overrides kept piling up, like re-inserting stylesheets that disappeared on page transitions and closing gaps in the sort menu. I wanted the site to decide how the comment section looks, but the comment system had its own CSS and the two kept fighting.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;on-yosegaki&quot;&gt;On yosegaki&lt;a class=&quot;anchor&quot; href=&quot;#on-yosegaki&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;At that point I figured it would be faster to write my own, and that’s how &lt;a href=&quot;https://github.com/DAnything/yosegaki&quot;&gt;yosegaki&lt;/a&gt; came about. It’s SvelteKit + Bun + SQLite, my usual stack as described in &lt;a href=&quot;https://doany.io/en/posts/svelte-bun-sqlite/&quot;&gt;another post&lt;/a&gt;.&lt;/p&gt;&lt;a class=&quot;card-github no-styling&quot; href=&quot;https://github.com/DAnything/yosegaki&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;div class=&quot;gc-titlebar&quot;&gt;&lt;div class=&quot;gc-titlebar-left&quot;&gt;&lt;div class=&quot;gc-owner&quot;&gt;&lt;div class=&quot;gc-avatar&quot; style=&quot;background-image: url(https://avatars.githubusercontent.com/u/143234231?v=4); background-color: transparent&quot;&gt;&lt;/div&gt;&lt;div class=&quot;gc-user&quot;&gt;DAnything&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;gc-divider&quot;&gt;/&lt;/div&gt;&lt;div class=&quot;gc-repo&quot;&gt;yosegaki&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;github-logo&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;gc-description&quot;&gt;寄せ書き。SvelteKit + Bun + SQLite のコメントサーバ。管理画面なし、どのコメント欄からでもサイト全体の新着と自分宛ての返信が見える&lt;/div&gt;&lt;div class=&quot;gc-infobar&quot;&gt;&lt;div class=&quot;gc-stars&quot;&gt;0&lt;/div&gt;&lt;div class=&quot;gc-forks&quot;&gt;0&lt;/div&gt;&lt;div class=&quot;gc-license&quot;&gt;AGPL-3.0&lt;/div&gt;&lt;span class=&quot;gc-language&quot;&gt;TypeScript&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;p&gt;Here are the principles behind it.&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;No styling of its own. The bundled CSS doesn’t pick colors; it just fades the host page’s text color for borders and backgrounds, so it follows light and dark automatically. You can also throw it out entirely with &lt;code&gt;data-css=&quot;false&quot;&lt;/code&gt; and write your own&lt;/li&gt;
&lt;li&gt;No admin panel. Administration happens inside the comment section itself. The notification panel shows new comments and ones awaiting approval across the whole site, and you can approve, delete, and search across everything right there. Readers never see this entry point; the login button only appears when you open a post’s URL with &lt;code&gt;#yosegaki-admin&lt;/code&gt; appended&lt;/li&gt;
&lt;li&gt;Admin authentication is OIDC only. Maintaining password authentication yourself is a pain to keep up with, so I never built it. It reuses the same Entra ID app registration as ArgoCD and only lets in people in the admins group. What I set up with remark42 and lost with Artalk came back here&lt;/li&gt;
&lt;li&gt;Comments are published immediately without approval. Bots are stopped with Cloudflare Turnstile and a honeypot. Turnstile only appears when needed, so normally you don’t see anything&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;The API returns only JSON, and the embed script is just one of its consumers. Even if I want to rebuild the UI, the API stays usable as-is.&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;html&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:52ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;&quot;yosegaki&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;&quot;https://yk.doany.io/embed.js&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;It’s deployed as a Helm chart: CI pushes it to &lt;code&gt;ghcr.io&lt;/code&gt; as an OCI artifact, and this blog’s &lt;code&gt;deploy/&lt;/code&gt; references it as a HelmChart. It just keeps one SQLite file on a PVC, so operationally nothing has changed since Artalk.&lt;br&gt;
For the record, when I decided to switch, there were zero comments. Honestly, part of why I could take the plunge so many times is that migration cost was effectively zero.&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;subscriptions-and-support&quot;&gt;Subscriptions and support&lt;a class=&quot;anchor&quot; href=&quot;#subscriptions-and-support&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;These are the things in the sidebar.&lt;/p&gt;&lt;p&gt;The official Ko-fi widget loads an external script, so I implemented it as a plain link. It doesn’t affect page speed and follows the theme’s light/dark mode as-is.&lt;/p&gt;&lt;p&gt;For the newsletter I use Buttondown. I originally planned to use its feature that watches an RSS feed and sends emails automatically, but that turned out to be a $9/month add-on. A bit much for a blog where I write only a few posts a year.&lt;br&gt;
On the other hand, the API is available even on the free plan. All I want is to create an email when a new post is added, so I can write that myself. So I added a workflow that runs when I push a post.&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;yaml&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:73ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;Collect newly added posts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:2ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;run&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:4ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;# Only added (A) files. Edits to existing posts don&apos;t trigger a send.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:4ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;FILES=$(git diff --name-only --diff-filter=A &quot;$BEFORE&quot; &quot;$SHA&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:6ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;-- &apos;src/content/posts/**.md&apos; | tr &apos;\n&apos; &apos; &apos;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;The key is narrowing it down to newly added files with &lt;code&gt;--diff-filter=A&lt;/code&gt;. Without it, subscribers would get an email every time I fix a typo.&lt;br&gt;
It only goes as far as creating a draft; I review the content in Buttondown and hit send manually. I plan to switch to automatic sending once I’m used to it, but emails can’t be unsent, so I’ll leave it like this for now.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;keeping-up-with-upstream&quot;&gt;Keeping up with upstream&lt;a class=&quot;anchor&quot; href=&quot;#keeping-up-with-upstream&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I’m using Fuwari as a theme, so I want to pull in updates from upstream. That said, checking manually every time isn’t sustainable.&lt;br&gt;
So I added a workflow that automatically checks upstream once a month and opens a PR. If the merge goes through without conflicts it opens a PR; if there’s a conflict, it doesn’t create a PR and instead notifies me with an Issue.&lt;/p&gt;&lt;p&gt;To make this work, I also massaged the Git history. This repository and Fuwari originally had no common ancestor at all, so as-is, &lt;code&gt;git merge upstream/main&lt;/code&gt; would be rejected with “refusing to merge unrelated histories.” So during the migration, I rebuilt the history so that my changes sit as a single commit on top of all of Fuwari’s commits (the top commit below reads “feat: migrate the Next.js blog’s content onto Fuwari”).&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;console&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:60ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;$ git log --oneline -3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;xxxxxxx feat: Next.js 版ブログの内容を Fuwari の上に移行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;6d39b0d chore(deps): bump the patch-updates group ... (#681)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;415fb97 chore(deps): bump the patch-updates group ... (#648)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;It doesn’t show up as a fork on GitHub, but this achieves what I wanted a fork for (tracking upstream).&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;a class=&quot;anchor&quot; href=&quot;#summary&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Since it’s a static site, there’s almost nothing that can go down, and posts go live just by writing them and pushing. Comments are the one thing I now have to look after myself, but in exchange readers can post without an account.&lt;br&gt;
All the source code is public on &lt;a href=&quot;https://github.com/DAnything/blog&quot;&gt;GitHub&lt;/a&gt;. If you have any questions about the setup, I’d appreciate a comment.&lt;/p&gt;&lt;a class=&quot;card-github no-styling&quot; href=&quot;https://github.com/DAnything/blog&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;div class=&quot;gc-titlebar&quot;&gt;&lt;div class=&quot;gc-titlebar-left&quot;&gt;&lt;div class=&quot;gc-owner&quot;&gt;&lt;div class=&quot;gc-avatar&quot; style=&quot;background-image: url(https://avatars.githubusercontent.com/u/143234231?v=4); background-color: transparent&quot;&gt;&lt;/div&gt;&lt;div class=&quot;gc-user&quot;&gt;DAnything&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;gc-divider&quot;&gt;/&lt;/div&gt;&lt;div class=&quot;gc-repo&quot;&gt;blog&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;github-logo&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;gc-description&quot;&gt;doany.io のソース — Fuwari ベースの Astro 製静的ブログ&lt;/div&gt;&lt;div class=&quot;gc-infobar&quot;&gt;&lt;div class=&quot;gc-stars&quot;&gt;0&lt;/div&gt;&lt;div class=&quot;gc-forks&quot;&gt;0&lt;/div&gt;&lt;div class=&quot;gc-license&quot;&gt;MIT&lt;/div&gt;&lt;span class=&quot;gc-language&quot;&gt;Astro&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/section&gt;</content:encoded></item><item><title>I Registered My Station Wagon as a Cargo Vehicle to Cut My Taxes</title><link>https://doany.io/en/posts/truck/</link><guid isPermaLink="true">https://doany.io/en/posts/truck/</guid><description>The vehicle inspection certificate (shakensho) has a field called &quot;use&quot;. By changing it from passenger to cargo, you change which tax bracket applies and can pay less tax. Here&apos;s how to do it and what the requirements are.</description><pubDate>Wed, 06 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;It’s been a while since my last post. Lately, instead of web stuff, I’ve been completely absorbed in cars.&lt;br&gt;
On that note, I recently had my Subaru Legacy (a “3-number” passenger car) registered as a cargo vehicle (a “1-number” vehicle), so I’m going to write up how I did it.&lt;/p&gt;
&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;Update (September 2026): I got tired of retracing the steps in this article myself every time, so I built a ledger that turns everything from buying the car to selling it into a series of stages. It’s called &lt;a href=&quot;https://tk.doany.io/?from=blog-truck&quot;&gt;Todoroku&lt;/a&gt;. I also put the weight distribution Excel sheet I used to work out the maximum payload on the web, so you can use it via the &lt;a href=&quot;https://tk.doany.io/tools/weight?from=blog-truck&quot;&gt;weight distribution calculator&lt;/a&gt; mentioned later.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;section&gt;&lt;h2 id=&quot;what-is-cargo-registration&quot;&gt;What is cargo registration?&lt;a class=&quot;anchor&quot; href=&quot;#what-is-cargo-registration&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;First, what is cargo registration? The vehicle inspection certificate (shakensho) has a field called “use”. The idea is to change it from passenger to cargo, which changes the tax bracket that applies to the car.&lt;br&gt;
I’ll skip the details of the tax system here, but for the vehicle I registered as cargo this time, the taxes changed as follows. (As of October 3, 2026. For the old weight tax and compulsory liability insurance, I’ve divided the 24-month amounts by 2. The insurance figures are the base rates for policies starting on or after November 1, 2026.)&lt;/p&gt;


































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Before&lt;/th&gt;&lt;th&gt;After&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Automobile tax&lt;/td&gt;&lt;td&gt;51,750&lt;/td&gt;&lt;td&gt;8,800&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Weight tax&lt;/td&gt;&lt;td&gt;22,800&lt;/td&gt;&lt;td&gt;8,200&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Compulsory liability insurance (jibaiseki)&lt;/td&gt;&lt;td&gt;9,280&lt;/td&gt;&lt;td&gt;17,930&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Total&lt;/td&gt;&lt;td&gt;83,830&lt;/td&gt;&lt;td&gt;34,930&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Difference&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;-48,900&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;As you can see, it gets quite a bit cheaper, but there are a few downsides.&lt;br&gt;
The big one is that your ETC (electronic toll collection) toll class goes from standard to mid-size, so tolls go up. Let’s work out how much driving it takes before the savings are wiped out, using the Shin-Tomei Expressway (standard section). NEXCO tolls are (terminal charge of 150 yen + 24.6 yen × distance) × vehicle class ratio × 1.1 (consumption tax), where mid-size is 1.2 times standard, and the portion beyond 100 km gets cheaper through the long-distance discount. Dividing the 48,900 yen difference above by the difference per km gives the yearly expressway mileage at which the savings are wiped out.&lt;/p&gt;












































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Distance per trip&lt;/th&gt;&lt;th&gt;Standard&lt;/th&gt;&lt;th&gt;Mid-size&lt;/th&gt;&lt;th&gt;Difference&lt;/th&gt;&lt;th&gt;Difference per km&lt;/th&gt;&lt;th&gt;Yearly distance where savings are wiped out&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;50 km&lt;/td&gt;&lt;td&gt;1,518 yen&lt;/td&gt;&lt;td&gt;1,822 yen&lt;/td&gt;&lt;td&gt;304 yen&lt;/td&gt;&lt;td&gt;about 6.1 yen&lt;/td&gt;&lt;td&gt;about 8,100 km&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;100 km&lt;/td&gt;&lt;td&gt;2,871 yen&lt;/td&gt;&lt;td&gt;3,445 yen&lt;/td&gt;&lt;td&gt;574 yen&lt;/td&gt;&lt;td&gt;about 5.7 yen&lt;/td&gt;&lt;td&gt;about 8,500 km&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;200 km&lt;/td&gt;&lt;td&gt;4,900 yen&lt;/td&gt;&lt;td&gt;5,881 yen&lt;/td&gt;&lt;td&gt;980 yen&lt;/td&gt;&lt;td&gt;about 4.9 yen&lt;/td&gt;&lt;td&gt;about 10,000 km&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;300 km&lt;/td&gt;&lt;td&gt;6,795 yen&lt;/td&gt;&lt;td&gt;8,154 yen&lt;/td&gt;&lt;td&gt;1,359 yen&lt;/td&gt;&lt;td&gt;about 4.5 yen&lt;/td&gt;&lt;td&gt;about 10,800 km&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;The longer each trip, the more the discount helps and the smaller the gap gets. Unless you drive 8,000 km or more a year on expressways, it’s not a problem.&lt;br&gt;
That said, the holiday discount (30% off) applies only to standard cars and kei cars, not mid-size, so if you mostly drive on weekends and holidays, the gap widens. (The late-night discount does apply to mid-size.)&lt;/p&gt;












































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Distance per trip&lt;/th&gt;&lt;th&gt;Standard (holiday discount)&lt;/th&gt;&lt;th&gt;Mid-size&lt;/th&gt;&lt;th&gt;Difference&lt;/th&gt;&lt;th&gt;Difference per km&lt;/th&gt;&lt;th&gt;Yearly distance where savings are wiped out&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;50 km&lt;/td&gt;&lt;td&gt;1,063 yen&lt;/td&gt;&lt;td&gt;1,822 yen&lt;/td&gt;&lt;td&gt;759 yen&lt;/td&gt;&lt;td&gt;about 15.2 yen&lt;/td&gt;&lt;td&gt;about 3,200 km&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;100 km&lt;/td&gt;&lt;td&gt;2,010 yen&lt;/td&gt;&lt;td&gt;3,445 yen&lt;/td&gt;&lt;td&gt;1,436 yen&lt;/td&gt;&lt;td&gt;about 14.4 yen&lt;/td&gt;&lt;td&gt;about 3,400 km&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;200 km&lt;/td&gt;&lt;td&gt;3,430 yen&lt;/td&gt;&lt;td&gt;5,881 yen&lt;/td&gt;&lt;td&gt;2,450 yen&lt;/td&gt;&lt;td&gt;about 12.3 yen&lt;/td&gt;&lt;td&gt;about 4,000 km&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;300 km&lt;/td&gt;&lt;td&gt;4,756 yen&lt;/td&gt;&lt;td&gt;8,154 yen&lt;/td&gt;&lt;td&gt;3,397 yen&lt;/td&gt;&lt;td&gt;about 11.3 yen&lt;/td&gt;&lt;td&gt;about 4,300 km&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;The other one is that the vehicle inspection (shaken) becomes yearly. That adds an inspection fee once a year, but it’s only around 2,600 yen (the fee for bringing a standard-size car in for inspection yourself, after the April 2026 revision). Some of you probably leave your shaken to a dealer or a shop, but if it’s a Japanese car, it’ll pass the inspection with hardly any maintenance. If you’re reading this article, you presumably want to save on running costs, so if you want to keep them down, buy your own tools and at least google the bare-minimum maintenance needed to keep a car going. If you can’t do that, just do what the dealer says and pay up. That’s what the service fee is for.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;the-cargo-registration-process&quot;&gt;The cargo registration process&lt;a class=&quot;anchor&quot; href=&quot;#the-cargo-registration-process&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3 id=&quot;getting-a-parking-space-certificate&quot;&gt;Getting a parking space certificate&lt;a class=&quot;anchor&quot; href=&quot;#getting-a-parking-space-certificate&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This isn’t actually related to cargo registration, but you need it to transfer the vehicle into your name.&lt;br&gt;
In my case, I submitted the documents below to the police station with jurisdiction over the parking lot I rent. Each prefectural police force publishes which station covers which area on its website. If yours doesn’t, just call and ask.&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Application for a parking space certificate (2 copies (1 copy if there’s no revenue stamp, e.g. when paying cashless))&lt;/li&gt;
&lt;li&gt;Location map and layout diagram of the parking space&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;I filled in both of the above by editing the XLSX files on the page below on my computer. They don’t need to be handwritten.&lt;br&gt;
&lt;a href=&quot;https://www.keishicho.metro.tokyo.lg.jp/tetsuzuki/kotsu/hokan/syako_tetsuzuki/jidousha_syomei.html&quot;&gt;Parking space certificate application procedures&lt;/a&gt; (Japanese)&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Certificate of consent to use the parking space&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;For this one, if it meets the requirements (lessor, lessee, contract date, contract end date, etc.), you may be able to use something like your parking lot lease instead.&lt;br&gt;
I always apply with a copy of my parking lot lease. It’s a good idea to ask the police station whether your own lease will be accepted.&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;Reference: &lt;a href=&quot;https://www.keishicho.metro.tokyo.lg.jp/tetsuzuki/kotsu/hokan/syako_syousai/youken.html&quot;&gt;Parking space requirements and documents proving the right to use it&lt;/a&gt; (Japanese)&lt;/p&gt;
&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;getting-a-temporary-license-plate&quot;&gt;Getting a temporary license plate&lt;a class=&quot;anchor&quot; href=&quot;#getting-a-temporary-license-plate&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The car I put through this time had already been temporarily deregistered (ichiji massho) when I bought it, so I went to borrow a temporary license plate (kari number). Without one, you can’t drive the car to the inspection or to get it serviced for inspection.&lt;br&gt;
To borrow a temporary plate you need compulsory liability insurance, but the car I’d bought had already let its policy lapse, so I took out a 5-day policy under a “commercial vehicle” contract.&lt;br&gt;
It used to be possible to get a one-month compulsory liability policy for a deregistered vehicle, but apparently the rules on compulsory liability insurance were tightened, and now, when the purpose is borrowing a temporary plate, you can only get a 5-day contract as a commercial vehicle.&lt;br&gt;
Also, some compulsory liability insurance agents don’t handle commercial vehicle contracts. You can reliably get one at the administrative scrivener (gyoseishoshi) offices on or near the grounds of the regional transport bureau (rikuunkyoku) office, or at an insurance company’s branch office. The car accessory stores I tried didn’t offer it.&lt;br&gt;
Once you have the insurance, go to your nearest city hall or a branch office that lends temporary plates and apply to borrow one. The maximum loan period is 5 days, so I recommend going to borrow it only after the parking space certificate above has been issued and the pre-screening described later is done.
I got the order wrong myself once and nearly ran out of my 5 days, so the stages in &lt;a href=&quot;https://tk.doany.io/?from=blog-truck&quot;&gt;Todoroku&lt;/a&gt; include this order, what to bring to each counter, and where you need cash.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;modifications-for-cargo-registration&quot;&gt;Modifications for cargo registration&lt;a class=&quot;anchor&quot; href=&quot;#modifications-for-cargo-registration&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4 id=&quot;criteria-for-being-classified-as-cargo&quot;&gt;Criteria for being classified as cargo&lt;a class=&quot;anchor&quot; href=&quot;#criteria-for-being-classified-as-cargo&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;First, how are the inspection criteria for each type of use set for cars? They’re determined by government ordinances from the Ministry of Land, Infrastructure, Transport and Tourism (MLIT).&lt;br&gt;
Some of you might be thinking, “Isn’t that set out in the Road Transport Vehicle Act?”, but the Act only covers things in broad strokes, and how to interpret it is laid down separately in ordinances.&lt;br&gt;
The official line is that automotive technology changes so fast that it has to be done this way, but I think it’s really a system for keeping everyone involved happy.&lt;br&gt;
The criteria for passing inspection as a cargo vehicle are roughly as follows. I’ve pulled these from the ordinance and rewritten them in plain language.&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;The floor area of the cargo space must be at least 1 m2&lt;/li&gt;
&lt;li&gt;The floor area of the cargo space must be larger than the floor area of the passenger seating&lt;/li&gt;
&lt;li&gt;The load weight must exceed the occupant weight of the passenger seating&lt;/li&gt;
&lt;li&gt;The opening for loading and unloading goods must be at least 800mm*800mm (height and width), with a projected area of at least 0.64 m2&lt;/li&gt;
&lt;li&gt;There must be a protective partition between the occupants and the cargo space&lt;/li&gt;
&lt;/ul&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;“Passenger seating” means the second row and beyond. It doesn’t include the first row, where the driver’s seat is.&lt;/p&gt;
&lt;/blockquote&gt;&lt;blockquote&gt;
&lt;p&gt;Reference: &lt;a href=&quot;https://www.mlit.go.jp/jidosha/kensatoroku/kensa/kns07_1.htm&quot;&gt;On the Classification of Vehicle Uses, etc. (Directive)&lt;/a&gt; (Japanese)&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;On top of what’s written in the ordinance, NALTEC (the National Agency for Automobile and Land Transport Technology, which carries out vehicle inspections) has its own Examination Procedures Regulations (shinsa jimu kitei), which spell out how to handle points the ordinance doesn’t cover.&lt;br&gt;
You might wonder whether a body that isn’t even the national government gets to interpret laws and ordinances however it likes, but swallow that for the sake of saving money.&lt;br&gt;
Below are the provisions that add extra conditions for cargo registration. I’ve cut the irrelevant parts.&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;Chapter 4: How examinations related to vehicle inspections, etc. are conducted&lt;br&gt;
4-17 Examination of cargo vehicles&lt;br&gt;
4-17-1 Determination of use&lt;br&gt;
(2) For designated vehicles, etc. with four or more wheels certified as passenger vehicles (limited to those whose body type is box, hood or station wagon), and for parallel-imported vehicles classified as “related to designated vehicles, etc.” with respect to such vehicles, the space for carrying the occupants’ belongings shall not be deemed a goods loading facility under the use classification directive.&lt;br&gt;
However, limited to vehicles whose body type is station wagon (including vehicles other than station wagons that can be classified as station wagons when 6.2.7. of Attachment 3, “Guidelines for Examination of Parallel-Imported Vehicles”, is applied mutatis mutandis, and hood-type vehicles whose hood behind the seats extends to near the rear end of the vehicle), where the rear seats, etc. are removed (including vehicles certified with multiple seating capacity settings that were certified in a state equivalent to having the rear seats, etc. removed) or stowed and fixed to the floor, the resulting floor surface and the space for carrying the occupants’ belongings that is continuous with that floor surface shall be deemed a goods loading facility.
Source: &lt;a href=&quot;https://www.naltec.go.jp/publication/regulation/hbh5ss0000002mk7-att/gtg5d20000000fn8.pdf&quot;&gt;Chapter 4: How examinations related to vehicle inspections, etc. are conducted&lt;/a&gt; (Japanese)&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;In short, it comes down to this:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;The luggage space that box-type, hood-type and station wagon bodies come with from the factory is not counted as a goods loading facility under the use classification directive.&lt;/li&gt;
&lt;li&gt;For station wagons, or vehicles that can be classified as station wagons by applying 6.2.7. of the Guidelines for Examination of Parallel-Imported Vehicles, the space created by removing or stowing seats plus the original luggage space together count as the goods loading facility.&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4 id=&quot;what-i-actually-modified&quot;&gt;What I actually modified&lt;a class=&quot;anchor&quot; href=&quot;#what-i-actually-modified&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Here are the modifications I made to meet the requirements above.&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;The opening for loading and unloading goods must be at least 800mm*800mm (height and width), with a projected area of at least 0.64 m2&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;For this one, measure with a tape measure; if it’s 800mm or more, you’re basically fine. Note that it’s the height of the opening that matters, not the height of the cargo area.&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;The floor area of the cargo space must be at least 1 m2&lt;/li&gt;
&lt;li&gt;The floor area of the cargo space must be larger than the floor area of the passenger seating&lt;/li&gt;
&lt;li&gt;The load weight must exceed the occupant weight of the passenger seating&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;This time I decided to fold down the right side of the second-row seat and fix it in that position (the NALTEC procedures say “stowed and fixed”, so it has to be fixed) to increase the cargo area.&lt;br&gt;
As for how to fix it, I put a bolt through the headrest hole, attached a chain to it, and connected the chain to the striker under the seat that holds the seat cushion in place. It’s easy to remove, but I think most people would still call that fixed.&lt;br&gt;
Also, some inspectors will tell you the floor has to be flat, but under the law and the ordinances it doesn’t have to be.&lt;br&gt;
Note 2(2) of the &lt;code&gt;自動車の用途等の区分について（依命通達）&lt;/code&gt; (“On the Classification of Vehicle Uses, etc. (Directive)”) I listed in the references earlier says: &lt;code&gt;タイヤえぐり、蓄電池箱等の占める面積は、物品の積載に支障がない限り物品積載設備の床面積に含めるものとする。&lt;/code&gt; (“The area occupied by wheel wells, battery boxes, etc. shall be included in the floor area of the goods loading facility as long as it does not interfere with loading goods.”)&lt;br&gt;
Reading this, it says that even areas with curved parts such as wheel wells count as loading area as long as they don’t get in the way of loading goods. “Goods” isn’t defined here and could include things like gravel, so I think it’s reasonable to read it as not requiring a flat floor.&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;There must be a protective partition between the occupants and the cargo space&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;I bought one of those wire mesh panels used for shelving at a 100-yen shop, bent it 90 degrees and slid it under the folded seat.&lt;br&gt;
During the inspection, the inspector pointed out that it wasn’t fixed with bolts. I replied, “Even if it were bolted in, you could take it out by turning the bolts, so there’s no meaningful structural difference, right?” The inspector wandered off somewhere muttering to himself, and the inspection passed without a hitch.&lt;br&gt;
Besides, nothing specifies what kind of fixing a protective partition actually needs, so something like the tension rods you see in ordinary vans should be fine too.&lt;br&gt;
As you can see, the safety standards compliance inspection that NALTEC carries out involves a lot of arbitrary judgment by inspectors, so on inspection day it’s best to have a solid understanding of the actual ordinances and the reasoning behind them, enough to win the argument with the inspector.&lt;br&gt;
In the first place, as the Road Transport Vehicle Act states, NALTEC staff are required to report the results of their standards compliance examination in writing to MLIT. In other words, if they write something in the examination results that contradicts the Road Transport Vehicle Act or the procedures drawn up in line with MLIT ordinances, it could constitute the crime of making a false official document bearing a seal. In fact, there has been a case in the past where someone was charged, though prosecution was ultimately suspended.&lt;br&gt;
If even that doesn’t get you anywhere, an inspection decision is, I believe, a form of administrative action, so filing a request for administrative review (shinsa seikyu) is another option.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4 id=&quot;about-the-maximum-payload&quot;&gt;About the maximum payload&lt;a class=&quot;anchor&quot; href=&quot;#about-the-maximum-payload&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;For cargo registration there’s a provision like the one below. Earlier, you’ll also remember the requirement &lt;code&gt;the load weight must exceed the occupant weight of the passenger seating&lt;/code&gt;.&lt;br&gt;
On top of that, the ordinance defines passenger seating as everything except the front seats, and NALTEC’s rules say the payload is calculated in 50 kg increments.&lt;br&gt;
Putting it all together, the payload has to be at least 100 kg and must not push the car past the maximum gross vehicle weight within the same classification.&lt;br&gt;
In my case, I only stowed and fixed the seat, so the car could end up exceeding the maximum gross vehicle weight within the same classification (you can find this out by asking the manufacturer or NALTEC).&lt;br&gt;
That means you’ll need to remove things like the spare tire. If that’s still not enough, try removing the onboard tools, the luggage board, the car navigation system and other interior parts.&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;(e) For designated vehicles, etc. whose permissible limits for gross vehicle weight and axle load are not clear, it shall be specified within a range not exceeding the maximum gross vehicle weight within the classification of the same model.
(f) For vehicles other than those specified in (a) through (e), it shall be specified within a range not exceeding the weight obtained by multiplying the number of occupants for the removed passenger seating by 55 kg.
Source: &lt;a href=&quot;https://www.naltec.go.jp/publication/regulation/hbh5ss0000002mk7-att/gtg5d20000002lfr.pdf&quot;&gt;7-124, 8-124 Maximum payload&lt;/a&gt; (Japanese)&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;Now take a look at the provision below. Based on the wording &lt;code&gt;産出される物品の積載量のうち最大のものとする&lt;/code&gt; (“shall be the largest of the calculated payloads”), NALTEC will tell you to take the largest payload possible within the gross weight.&lt;br&gt;
The problem here is that taking the maximum payload can push the gross vehicle weight over 2 tonnes. Over 2 tonnes, the weight tax goes up, so I think it’s better to keep it as light as you can.&lt;br&gt;
To make it easier to follow, take a car with a spec maximum gross vehicle weight of 2,040 kg, and compare a measured vehicle weight of 1,895 kg + 100 kg = 1,995 kg with 1,890 kg + 150 kg = 2,040 kg.&lt;br&gt;
In that case, making it too light actually increases your weight tax, so it takes some fine-tuning. Usually you can adjust it by leaving in or taking out the luggage board and interior parts.&lt;br&gt;
For these calculations, I’ve put the Excel sheet I made for myself on the web, so use the &lt;a href=&quot;https://tk.doany.io/tools/weight?from=blog-truck&quot;&gt;weight distribution calculator&lt;/a&gt;. Enter the front and rear axle weights, the wheelbase, the position of the seats you’re keeping, the payload and the tires’ load index, and it gives you the gross vehicle weight and the maximum payload you can get from the classification (in 50 kg increments). It also checks the front axle load ratio when loaded (it won’t pass below 20%) and the tire load ratio. You can see the verdict without logging in.&lt;br&gt;
I haven’t verified whether it actually works yet, but for cars like one with a maximum gross vehicle weight of 2,090 kg, which ends up over 2 tonnes no matter what you do, I think you could register it once at over 2 tonnes and then get the gross weight to 2 tonnes or less by fully removing the seats and taking out interior parts to lighten it.&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;(1) The maximum payload of a vehicle shall be the largest of the payloads of goods calculated based on the standards in (2) through (11), as the amount that can be loaded within a range that complies with the provisions of this chapter, ensures safe operation, and prevents pollution.&lt;/p&gt;
&lt;/blockquote&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;pre-screening&quot;&gt;Pre-screening&lt;a class=&quot;anchor&quot; href=&quot;#pre-screening&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;As of April 2025, the rules were revised to make it clear that pre-screening is not required for some structural modifications.&lt;br&gt;
Even under the previous rules, depending on how you read them, pre-screening didn’t seem to be required for things like station wagon -&gt; van, but interpretations probably differed between regional NALTEC offices, and I suspect the revision was meant to keep that from becoming a problem.&lt;br&gt;
So this section is no longer needed, but there’s always a chance that document screening will be required again in the future, so I’m leaving it here.&lt;br&gt;
&lt;a href=&quot;https://www.naltec.go.jp/news/hbh5ss0000001vvf-att/hbh5ss0000001vvv.pdf&quot;&gt;Partial revision of the Examination Procedures Regulations (63rd revision)&lt;/a&gt; (Japanese)&lt;/p&gt;&lt;p&gt;Since October 28, 2024, you can submit the documents online. However, some documents have to be prepared on your end and then uploaded, so you’ll apply once you have the full set ready.&lt;br&gt;
It takes up to 15 days from submission to get a response. So if you’ve already had your parking space certificate issued, submit early. (A parking space certificate is valid for about a month.) In my case, when I mentioned upfront that my parking space certificate was close to expiring, they processed it quickly.&lt;/p&gt;&lt;section&gt;&lt;h4 id=&quot;how-to-submit&quot;&gt;How to submit&lt;a class=&quot;anchor&quot; href=&quot;#how-to-submit&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;Create an account on &lt;a href=&quot;https://naltecsss.service-now.com/naltec&quot;&gt;this site&lt;/a&gt;. You can do it via Log in -&gt; Register account&lt;/li&gt;
&lt;li&gt;Once you’ve created an account and logged in, click New Inspection Notification&lt;/li&gt;
&lt;li&gt;Click Create Notification&lt;/li&gt;
&lt;li&gt;Select and fill in the following, then click Next
&lt;ol&gt;
&lt;li&gt;Notification method: &lt;code&gt;個別届出&lt;/code&gt; (“Individual notification”)&lt;/li&gt;
&lt;li&gt;Supplementary provision: &lt;code&gt;技術基準等の審査を要する自動車、自動車予備検査証の交付を受けた自動車又は使用の過程にある自動車若しくは特定の大型特殊自動車&lt;/code&gt; (“Vehicles requiring examination against technical standards, etc., vehicles issued a preliminary vehicle inspection certificate, vehicles in use, or certain large special vehicles”)&lt;/li&gt;
&lt;li&gt;Category: &lt;code&gt;3.2.（1）技術基準等の審査を要する自動車&lt;/code&gt; (“3.2.(1) Vehicles requiring examination against technical standards, etc.”)&lt;/li&gt;
&lt;li&gt;Submit to: select the inspection office where you’ll take the inspection&lt;/li&gt;
&lt;li&gt;Make, model and chassis number: fill in exactly as on the vehicle inspection certificate&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Fill in the New Inspection Notification Form (Form No. 1) (Part 1) as follows
&lt;ol&gt;
&lt;li&gt;Classification number or vehicle specification code: select &lt;code&gt;類別区分番号&lt;/code&gt; (“Classification number”) and enter the classification number shown on the vehicle inspection certificate in the input field&lt;/li&gt;
&lt;li&gt;Structures and equipment changed relative to the designated vehicle of that model and classification number
&lt;ol&gt;
&lt;li&gt;Structure/equipment changed: select &lt;code&gt;有&lt;/code&gt; (“Yes”)&lt;/li&gt;
&lt;li&gt;Option: select &lt;code&gt;（記入欄のとおり）&lt;/code&gt; (“(As described in the entry field)”)&lt;/li&gt;
&lt;li&gt;Entry field: enter the following (adjust to match your car’s current specs)&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:23ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;乗車定員の変更（5人→3人）2列目右側座席固定&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;用途変更（乗用→貨物）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;車体形状の変更（ステーションワゴン→バン）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
(“Change of seating capacity (5 → 3), right-hand second-row seat fixed / Change of use (passenger → cargo) / Change of body type (station wagon → van)”)
&lt;ol&gt;
&lt;li&gt;Changes to structures/equipment related to the noise prevention device: &lt;code&gt;無&lt;/code&gt; (“No”)&lt;/li&gt;
&lt;li&gt;Over-revolution prevention device related to the noise prevention device: &lt;code&gt;無&lt;/code&gt; (“No”)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;The documents to upload for the New Inspection Notification Form (Form No. 1) (Part 2) can be created with a Windows application that NALTEC distributes. Download the zip file from the link below and run InitPreEntry.exe in the bin folder to launch it.
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.naltec.go.jp/hbh5ss0000000tvb-att/a1743469302713.zip&quot;&gt;New Inspection Notification Form No. 1, Part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;You can fill in the values by actually measuring the car or by asking the manufacturer for its spec sheet.&lt;/li&gt;
&lt;li&gt;For the parts that need calculations, some NALTEC offices may give you something called a weight distribution calculation sheet, but this seems to vary from office to office. Just copy over the numbers from the &lt;a href=&quot;https://tk.doany.io/tools/weight?from=blog-truck&quot;&gt;weight distribution calculator&lt;/a&gt; mentioned earlier.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;For the document identifying the vehicle, upload a scan of the Registration Identification Information Notice (toroku shikibetsu joho-to tsuchisho)&lt;/li&gt;
&lt;li&gt;For the spec sheet or vehicle specification table, upload the specs you got from the manufacturer and the four-view exterior drawing&lt;/li&gt;
&lt;li&gt;For the document certifying compliance with technical standards, upload the “Document on Compliance with the Technical Requirements of Each Provision of the Safety Standards”. Fill it in with calculations that fit your own car.
&lt;ul&gt;
&lt;li&gt;※ Update (October 2026): I’ve stopped publishing the Word file with sample inputs that I used to distribute here. Now, on the structural modification screen in &lt;a href=&quot;https://tk.doany.io/?from=blog-truck&quot;&gt;Todoroku&lt;/a&gt;, you can generate this document and the four-view exterior drawing from the vehicle values you’ve entered in the ledger.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Under other documents, upload the following
&lt;ul&gt;
&lt;li&gt;A side-view photo annotated with the overall length&lt;/li&gt;
&lt;li&gt;A front-view photo annotated with the overall width&lt;/li&gt;
&lt;li&gt;A rear-view photo annotated with the overall height, the cargo area height and the opening height&lt;/li&gt;
&lt;li&gt;Photos showing the fixed seat cushion and the partition between the seats and the cargo area&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;registering-for-cashless-payment&quot;&gt;Registering for cashless payment&lt;a class=&quot;anchor&quot; href=&quot;#registering-for-cashless-payment&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Since January 2023, you can pay the fees for inspection and registration cashless.&lt;br&gt;
You can register your payment details below.&lt;br&gt;
&lt;a href=&quot;https://www.car-cashless.mlit.go.jp/cashless-web/&quot;&gt;Vehicle ownership procedures: payment information registration service&lt;/a&gt; (Japanese)&lt;/p&gt;&lt;p&gt;When registering your payment details, set the procedure type to used-vehicle new registration (chuko shinki). Otherwise, you can’t change the procedure type later, and you’ll end up paying in cash when the vehicle inspection certificate is issued.&lt;br&gt;
If the car still has inspection time left and you’re not getting a tax refund via temporary deregistration or the like, choose structural modification.&lt;br&gt;
Compulsory liability insurance is handled separately, so you’ll need cash just for that. Car accessory stores let you buy compulsory liability insurance by card, so getting it there is another option.&lt;br&gt;
Also, the license plate fee at the plate center and the hole-punching fee for keeping your old plates as a souvenir are cash only.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;inspection-day&quot;&gt;Inspection day&lt;a class=&quot;anchor&quot; href=&quot;#inspection-day&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Before inspection day, you need to book through the site below. When booking, the inspection type is &lt;code&gt;構造等変更検査&lt;/code&gt; (“structural modification inspection”) even if the car has already been temporarily deregistered.&lt;br&gt;
That said, at some locations it can be very hard to get a booking.&lt;br&gt;
That’s because some businesses grab booking slots for multiple cars and cancel only the ones for cars that don’t show up on the day. There’s no particular penalty for doing this, so slots can fill up.&lt;br&gt;
On the other hand, businesses that operate like this often cancel bookings on the day, so slots sometimes open up the same day. It’s worth checking early in the morning or during the first round of inspections.&lt;br&gt;
&lt;a href=&quot;https://www.reserve.naltec.go.jp/&quot;&gt;Vehicle inspection online booking system&lt;/a&gt; (Japanese)&lt;/p&gt;&lt;p&gt;Here’s roughly how the day goes. The finer details probably differ between transport bureaus, so I’ll leave them out.&lt;br&gt;
Also, if you didn’t register for cashless payment, you’ll have two or three extra forms to fill in on the day, so I recommend doing the procedures cashless if you can. The steps below leave out the cash-payment procedures.&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;There’s a PC for printing inspection reception documents near the inspection reception counter at the registration office; scan the QR code on the vehicle inspection certificate and print the reception documents&lt;/li&gt;
&lt;li&gt;Go through the inspection in the regular periodic inspection lane&lt;/li&gt;
&lt;li&gt;Pick up the screening documents at the inspection booth where you submitted them ※not needed if you didn’t do pre-screening&lt;/li&gt;
&lt;li&gt;Have the vehicle weight and so on measured in the new-inspection lane&lt;/li&gt;
&lt;li&gt;Take out compulsory liability insurance as a standard cargo vehicle&lt;/li&gt;
&lt;li&gt;Whether it’s a used-vehicle new registration or a structural modification, fill in Form No. 1 and submit it at the registration counter&lt;/li&gt;
&lt;li&gt;Fill in the tax declaration form, go to the tax declaration counter and submit it&lt;/li&gt;
&lt;li&gt;Get your license plates at the plate counter (if the car still has its old plates, remove them first)&lt;/li&gt;
&lt;li&gt;Put the plates on and have them sealed&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;wrap-up&quot;&gt;Wrap-up&lt;a class=&quot;anchor&quot; href=&quot;#wrap-up&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I’m not a pro at administrative procedures, politics or law, so there may be mistakes in this post or in my thinking. If you spot any, I’d really appreciate your comments.
As I mentioned at the top, I’ve turned the flow in this article, plus buying the car beforehand and selling it afterwards, into an 8-stage ledger in &lt;a href=&quot;https://tk.doany.io/?from=blog-truck&quot;&gt;Todoroku&lt;/a&gt;. If you’re the next person to go through this, starting from the ledger keeps what to bring, where you need cash and what you spent all in one place, which should make things easier.
I did cargo registration this time to save on taxes, but I’m left with some questions: isn’t the current tax system barely based on the beneficiary-pays principle in the first place? And aren’t these regulations driven mostly by the agendas of NALTEC and the police, rather than actually contributing to safer roads? What do you all think?&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>Notes on Unlocking the ATA Lock on a Mercedes NTG</title><link>https://doany.io/en/posts/ntg-ssd/</link><guid isPermaLink="true">https://doany.io/en/posts/ntg-ssd/</guid><description>Personal notes on swapping an SSD into a Mercedes NTG head unit</description><pubDate>Sat, 25 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There’s a well-known method using xboxhdm, described in the thread below, but xboxhdm v1.9 doesn’t support SATA connections, so it can’t be used on NTG 4.5 and later, which use SATA.&lt;br&gt;
&lt;a href=&quot;https://mhhauto.com/Thread-NTG4-5-4-7-HDD-imaging-project&quot;&gt;NTG4.5 / 4.7 HDD imaging project&lt;/a&gt;&lt;br&gt;
That thread also covers approaches like using fujtool, but I wanted to know whether a SMART editing tool for Windows could unlock the drive in the first place.&lt;br&gt;
smartctl is a SMART tool that runs on Windows and supports USB, SATA and so on, and the latest xboxhdm, v2.3, actually uses smartctl for its unlockhd command.&lt;/p&gt;
&lt;p&gt;So my guess is that, combined with the password generation steps described in the forum thread above, commands like the following should be able to unlock the drive.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;shell&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:81ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;./smartctl.exe&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;-s&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;security-unlock,&quot;longUserPasswordFromMelcoCalculator&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;/dev/sdb&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;./smartctl.exe&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;-s&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;security-disable,&quot;longUserPasswordFromMelcoCalculator&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;/dev/sdb&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;</content:encoded></item><item><title>Registering a Vehicle Ownership Transfer Online with OSS</title><link>https://doany.io/en/posts/oss-transfer/</link><guid isPermaLink="true">https://doany.io/en/posts/oss-transfer/</guid><description>I did a vehicle ownership transfer registration through OSS, so here&apos;s a walkthrough of how it works.</description><pubDate>Mon, 01 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Another car post, following on from last time.&lt;/p&gt;
&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;Update (September 2026): After writing this post I started a used car dealership, and I still use OSS for transfer registrations where I’m the applicant. I built &lt;a href=&quot;https://tk.doany.io/?from=blog-oss&quot;&gt;Todoroku&lt;/a&gt;, which turns the paperwork from purchase to sale into a ledger. Two things in it are relevant to this post: scanning the QR code on a vehicle inspection certificate with your phone lets you export the vehicle inspection certificate import file mentioned below, and when you apply for a parking space certificate at the same time, you can draw the location map and layout diagram on top of a map. I’ve added notes at the relevant spots. I also reviewed the steps as of September 2026 and added notes on what has changed (the abolition of the parking space sticker, the supported environment, and link targets).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;section&gt;&lt;h2 id=&quot;what-is-oss&quot;&gt;What is OSS?&lt;a class=&quot;anchor&quot; href=&quot;#what-is-oss&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;OSS (ワンストップサービス, “One Stop Service”) is a service from the Ministry of Land, Infrastructure, Transport and Tourism that lets you handle vehicle registration procedures, such as a transfer of ownership registration (iten toroku), over the internet.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;how-it-differs-from-the-regular-procedure&quot;&gt;How it differs from the regular procedure&lt;a class=&quot;anchor&quot; href=&quot;#how-it-differs-from-the-regular-procedure&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;These days car dealerships and the like adopt it to streamline their paperwork.
It differs from the regular procedure in the ways below, but for personal use there aren’t many benefits.&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;You can apply for the parking space certificate (shako shomei, proof that you have somewhere to park the car) at the same time, so you make fewer trips to the police station&lt;/li&gt;
&lt;li&gt;Fees can be paid via Pay-easy, credit card, and so on, so even if the regional transport bureau (rikuunkyoku) with jurisdiction doesn’t accept cashless payments, you can pay without cash.&lt;/li&gt;
&lt;li&gt;If you apply with an electronic certificate, you don’t need your own seal registration certificate (inkan shomei), so you save the cost of getting one&lt;/li&gt;
&lt;li&gt;You don’t have to fill in paper forms (except the transfer certificate and power of attorney)&lt;/li&gt;
&lt;li&gt;You can use the electronic vehicle inspection certificate (shakensho) to cut down the time spent entering vehicle details&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;the-actual-process&quot;&gt;The actual process&lt;a class=&quot;anchor&quot; href=&quot;#the-actual-process&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you bought the car from a dealer that uses OSS, the transfer certificate (joto shomeisho) and other documents may already be registered electronically, but that’s rare, so here I’ll go through the case where you received a paper power of attorney (ininjo) and a paper transfer certificate.&lt;/p&gt;&lt;section&gt;&lt;h3 id=&quot;advance-preparation&quot;&gt;Advance preparation&lt;a class=&quot;anchor&quot; href=&quot;#advance-preparation&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4 id=&quot;oss-browser-add-on&quot;&gt;OSS browser add-on&lt;a class=&quot;anchor&quot; href=&quot;#oss-browser-add-on&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;You need to install a browser add-on to use OSS.&lt;br&gt;
Install the add-on and the Chrome extension from the links below.&lt;br&gt;
In my case I used Microsoft Edge, and it works the same in Edge.&lt;br&gt;
&lt;a href=&quot;https://www.oss.mlit.go.jp/secure/beginner/jizen-junbi/pc-kankyou/add-on-kankyou-chrome/kyodaku-add-on/index.html&quot;&gt;OSS browser add-on&lt;/a&gt; (Japanese)&lt;br&gt;
&lt;a href=&quot;https://chrome.google.com/webstore/detail/%E8%87%AA%E5%8B%95%E8%BB%8A%E4%BF%9D%E6%9C%89%E9%96%A2%E4%BF%82%E6%89%8B%E7%B6%9A%E3%83%AF%E3%83%B3%E3%82%B9%E3%83%88%E3%83%83%E3%83%97%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%83%97%E3%83%A9/medknhfnoeebjofagappbmbpcgeffpca?hl=ja&quot;&gt;Chrome extension&lt;/a&gt;&lt;/p&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;Update (September 2026): Officially supported environments are now only Edge or Chrome on Windows 11 (24H2 and 25H2). Windows 10 is not supported. &lt;a href=&quot;https://www.oss.mlit.go.jp/portal/beginner/jizen-junbi/pc-kankyou/index.html&quot;&gt;Supported environments&lt;/a&gt; (Japanese)&lt;/p&gt;
&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h4 id=&quot;my-numberrelated-setup&quot;&gt;My Number–related setup&lt;a class=&quot;anchor&quot; href=&quot;#my-numberrelated-setup&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Some applications require the digital signature function of your My Number Card (Japan’s national ID card). If you’ll read the card with a card reader connected to your PC, install &lt;code&gt;利用者クライアントソフト&lt;/code&gt; (“User Client Software”); if you’ll read it with your smartphone, install &lt;code&gt;マイナポータルアプリ&lt;/code&gt; (“Mynaportal app”).&lt;br&gt;
Here I’ll go with the PC route.&lt;/p&gt;&lt;p&gt;You can install the User Client Software from the link below. Download it from &lt;code&gt;利用者クライアントソフトのダウンロード&lt;/code&gt; (“Download the User Client Software”). You don’t need to install the browser version.&lt;br&gt;
&lt;a href=&quot;https://www.jpki.go.jp/download/win.html&quot;&gt;Public Certification Service for Individuals (JPKI) portal site&lt;/a&gt; (Japanese)&lt;/p&gt;&lt;p&gt;The Mynaportal app is available here.&lt;br&gt;
&lt;a href=&quot;https://play.google.com/store/apps/details?id=jp.go.cas.mpa&quot;&gt;android&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://apps.apple.com/jp/app/%E3%83%9E%E3%82%A4%E3%83%8A%E3%83%9D%E3%83%BC%E3%82%BF%E3%83%AB/id1476359069&quot;&gt;iOS&lt;/a&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4 id=&quot;cashless-payment-registration&quot;&gt;Cashless payment registration&lt;a class=&quot;anchor&quot; href=&quot;#cashless-payment-registration&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;If you want to use cashless payment such as a credit card, you need to register via the link below.&lt;br&gt;
&lt;a href=&quot;https://www.car-cashless.mlit.go.jp/cashless-web/register&quot;&gt;Payment information registration service&lt;/a&gt; (Japanese)&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4 id=&quot;reading-the-electronic-vehicle-inspection-certificate&quot;&gt;Reading the electronic vehicle inspection certificate&lt;a class=&quot;anchor&quot; href=&quot;#reading-the-electronic-vehicle-inspection-certificate&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;You can use the electronic vehicle inspection certificate to skip some of the data entry.&lt;br&gt;
&lt;a href=&quot;https://apps.microsoft.com/detail/9PFXXK8VGX7N?rtc=1&amp;#x26;hl=ja-jp&amp;#x26;gl=JP&quot;&gt;Vehicle inspection certificate viewer app&lt;/a&gt;&lt;/p&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;Update (September 2026): iPhone and Android versions are now out too, so you can export the vehicle inspection certificate import file from your phone as well. &lt;a href=&quot;https://www.jidoushatouroku-portal.mlit.go.jp/etsuran-app&quot;&gt;About the viewer app&lt;/a&gt; (Japanese)&lt;/p&gt;
&lt;p&gt;Update (September 2026): The vehicle inspection certificate import file that the viewer app exports can also be exported from &lt;a href=&quot;https://tk.doany.io/?from=blog-oss&quot;&gt;Todoroku&lt;/a&gt;. Scan the QR code on the vehicle inspection certificate with your phone to register a car, and you can download JSON in the same format from the vehicle details screen. You can create it even after you’ve put the certificate back in the car.&lt;/p&gt;
&lt;/blockquote&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;creating-the-attorney-information-file&quot;&gt;Creating the attorney information file&lt;a class=&quot;anchor&quot; href=&quot;#creating-the-attorney-information-file&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;You’ll be creating an electronic power of attorney, but before that you need to create a file with your own details called the attorney information file (受任者情報ファイル).&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://www.oss.mlit.go.jp/secure/tetsuduki/principal/kyodaku-junin/index.html#&quot;&gt;Create attorney information file&lt;/a&gt; (Japanese)&lt;/li&gt;
&lt;li&gt;Click “Start application” on the page above&lt;/li&gt;
&lt;li&gt;Select IC card&lt;/li&gt;
&lt;li&gt;Select “Use a card reader”&lt;/li&gt;
&lt;li&gt;Enter your digital signature password and read your My Number Card&lt;/li&gt;
&lt;li&gt;For the item &lt;code&gt;自動車の車台番号をご存知ですか。&lt;/code&gt; (“Do you know the vehicle’s chassis number?”), select No (selecting No here lets you use the file for applications with no restriction on which vehicle you’re applying for)&lt;/li&gt;
&lt;li&gt;Save the attorney information file&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;creating-the-power-of-attorney&quot;&gt;Creating the power of attorney&lt;a class=&quot;anchor&quot; href=&quot;#creating-the-power-of-attorney&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;You’ve probably received a paper power of attorney, but you also need to create one electronically.&lt;br&gt;
You can create it from the link below.&lt;br&gt;
&lt;a href=&quot;https://www.oss.mlit.go.jp/secure/tetsuduki/principal/sentaku-inin/kyodaku-inin2/index.html#&quot;&gt;Create power of attorney&lt;/a&gt; (Japanese)&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;I didn’t know the other party’s phone number, so I entered my own, and that was fine.&lt;/li&gt;
&lt;li&gt;For the attorney information file field, specify the file you created earlier.&lt;/li&gt;
&lt;li&gt;Here you need to enter the chassis number.&lt;/li&gt;
&lt;li&gt;For &lt;code&gt;所有者・使用者の別&lt;/code&gt; (“Owner or user”), select the previous owner. (If you don’t select this correctly, you can’t submit the application.)&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;transfer-of-ownership-registration&quot;&gt;Transfer of ownership registration&lt;a class=&quot;anchor&quot; href=&quot;#transfer-of-ownership-registration&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Apply for the transfer of ownership registration from the link below.&lt;br&gt;
&lt;a href=&quot;https://www.oss.mlit.go.jp/secure/tetsuduki/principal/shinsei/tourokusha/kyodaku-iten/index.html&quot;&gt;Transfer of ownership registration&lt;/a&gt; (Japanese) (*Update (September 2026): The page had moved, so I fixed the link)&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;For the type of transfer certificate, select paper.&lt;/li&gt;
&lt;li&gt;If you select No for &lt;code&gt;すでに取得済みの保管場所証明書を利用した申請ですか。&lt;/code&gt; (“Are you applying with a parking space certificate you’ve already obtained?”), you can apply for the parking space certificate at the same time.
&lt;ul&gt;
&lt;li&gt;*Update (September 2026): A simultaneous application requires attaching images of the location map and layout diagram. You can scan hand-drawn ones, but with &lt;a href=&quot;https://tk.doany.io/?from=blog-oss&quot;&gt;Todoroku&lt;/a&gt; you can pull up an aerial photo from the address, draw the parking space and road width, and download an image that meets OSS’s requirements (JPEG, up to 1024×768, around 100KB). I’ve summarized what the reviewers check (within 2km of the vehicle’s base of use, whether the car fits in the space) in &lt;a href=&quot;https://tk.doany.io/guide/shako-shomei-jibun?from=blog-oss&quot;&gt;How to apply for a parking space certificate yourself&lt;/a&gt; (Japanese).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;For the power of attorney, select the one you created earlier.&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;submitting-the-documents&quot;&gt;Submitting the documents&lt;a class=&quot;anchor&quot; href=&quot;#submitting-the-documents&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Even though you apply electronically, you still have to submit the paper originals of the power of attorney and the transfer certificate.&lt;br&gt;
After completing the application on OSS, you need to go to the regional transport bureau and submit them.&lt;br&gt;
It probably varies by bureau, but at Narashino, where I went, there was a dedicated OSS reception window, and I submitted them there.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;paying-the-fees&quot;&gt;Paying the fees&lt;a class=&quot;anchor&quot; href=&quot;#paying-the-fees&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;You get an email every time the status is updated. Check it as soon as one arrives.&lt;br&gt;
After you submit the documents, the application status updates to “awaiting payment of the parking space certification fee.” You can log in to the application screen from the URL in the email, so log in with the password you set when applying and pay via Pay-easy or a supported financial institution.&lt;br&gt;
After payment a police internal management number is issued, and apparently you can phone that number in to the police station with jurisdiction and ask them to mail you the parking space sticker (hokan basho hyosho).&lt;/p&gt;&lt;p&gt;Then after 2 or 3 days the status changes to “awaiting payment of the parking space sticker fee,” so pay that the same way.&lt;br&gt;
The day after paying, it changes to “awaiting payment of the inspection and registration fee.” Pay this one the same way too, but because the payee is different, the supported financial institutions are different. Pay-easy works for this one as well.&lt;/p&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;Update (September 2026): The parking space sticker (the garage sticker) was abolished on April 1, 2025, so there’s no longer a sticker fee to pay. Now it’s the parking space certification fee, then the inspection and registration fee, then automobile tax if applicable, in that order. The parking space certification fee varies by prefecture.&lt;/p&gt;
&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;picking-up-the-parking-space-sticker-and-vehicle-inspection-certificate&quot;&gt;Picking up the parking space sticker and vehicle inspection certificate&lt;a class=&quot;anchor&quot; href=&quot;#picking-up-the-parking-space-sticker-and-vehicle-inspection-certificate&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;By the time you’ve paid the inspection and registration fee, the parking space sticker has been issued, so go pick it up at the police station with jurisdiction.&lt;/p&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;Update (September 2026): Since the sticker was abolished, the step of going to the police station to pick it up is gone. The parking space certificate result is passed to the District Transport Bureau (unyu shikyoku) within OSS, so the only place you need to go is the regional transport bureau.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;After that, go to the regional transport bureau to pick up the vehicle inspection certificate. If the transfer of ownership changes which regional transport bureau has jurisdiction, you’ll need new license plates, so bring the car with you.&lt;br&gt;
The order above doesn’t matter; it’s fine to go pick up the vehicle inspection certificate first.&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;a class=&quot;anchor&quot; href=&quot;#summary&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;That’s the general flow. Since the paper documents have to be submitted to the regional transport bureau, there isn’t much benefit to doing it over the internet. On top of that, the choices on the application screens are convoluted, so I think it’s hard to use unless you’re reasonably familiar with the procedures. I suspect it’s like this because they digitized the existing process as is, but personally I wish they’d stop doing that.&lt;/p&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;Update (September 2026): Things are different when you’re on the selling side as a dealership. Under the amended Administrative Scriveners Act (Gyoseishoshi-ho) that took effect in January 2026, a dealer preparing the buyer’s application documents is now a violation regardless of what it’s called. The same goes for proxy applications through OSS. I’ve written about how much a dealer can do in &lt;a href=&quot;https://tk.doany.io/guide/gyoseishoshi-ho-2026?from=blog-oss&quot;&gt;What used car dealers can and can’t do under the amended Administrative Scriveners Act&lt;/a&gt; (Japanese).&lt;/p&gt;
&lt;/blockquote&gt;&lt;/section&gt;</content:encoded></item><item><title>Audi Coding Notes</title><link>https://doany.io/en/posts/audi-coding/</link><guid isPermaLink="true">https://doany.io/en/posts/audi-coding/</guid><description>I used VCDS to customize how the electrical systems on my Audi behave (apparently this is commonly called &quot;coding&quot;), so here are the steps and the things you can customize.</description><pubDate>Wed, 08 Jul 2020 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote class=&quot;admonition bdm-caution&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;CAUTION&lt;/span&gt;
&lt;p&gt;I accept no responsibility whatsoever if following the steps below damages your vehicle or causes it to fail its vehicle inspection (shaken).&lt;br&gt;
Do this entirely at your own risk.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I used VCDS to customize how the electrical systems on my Audi behave (apparently this is commonly called “coding”), so here are the steps and the things you can customize.&lt;/p&gt;
&lt;section&gt;&lt;h2 id=&quot;1-getting-a-vcds-cable&quot;&gt;1. Getting a VCDS cable&lt;a class=&quot;anchor&quot; href=&quot;#1-getting-a-vcds-cable&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Search for “VCDS” on Yahoo! Auctions, Rakuten or similar and you’ll find cables; just buy one of those and you’re fine.&lt;/p&gt;&lt;p&gt;The listings mention version numbers and such, but the product detail page lists the supported models and model years. Generally, buying a fairly recent one will do.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;2-setting-up-the-pc&quot;&gt;2. Setting up the PC&lt;a class=&quot;anchor&quot; href=&quot;#2-setting-up-the-pc&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Most cables come with a disc, so read what’s on it and set things up accordingly.&lt;/p&gt;&lt;p&gt;Once you can launch the VCDS application, first grab the map data via Auto-Scan and save it. You can use it for restoring later.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;3-settings-list&quot;&gt;3. Settings list&lt;a class=&quot;anchor&quot; href=&quot;#3-settings-list&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To customize anything, plug the cable into the car’s OBD2 port and the PC, then click Select under Select Control Module. The numbers below are the ones in that menu. Depending on the model, some items may not exist; in that case they may be under a different item, or the car may not support them.&lt;br&gt;
These are for the A5 (B8).&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;text&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:114ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;- Rebooting the MMI&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;Pre-facelift: press SETUP + the center of the joystick + the top-right button at the same time&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;Facelift: press MENU + the center of the joystick + the top-right button at the same time&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;No need to hold them; if the simultaneous press registers properly, it reboots the moment you let go.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;- Enabling the green menu&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;details class=&quot;ec-section github&quot;&gt;&lt;summary&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;expand&quot;&gt;&lt;/span&gt;&lt;span class=&quot;collapse&quot;&gt;&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;11 collapsed lines&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/summary&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;For MMI 2G&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[07-Bed.Paneel/Display]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Aanpassen-10]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Kanaal 08]  enter &apos;1&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;Pre-facelift: press SETUP + CAR at the same time&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;Facelift: press CAR + MENU at the same time&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;For MMI 3G&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[5F-Informatie Electr.]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Aanpassen-10]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Kanaal 06]  enter &apos;1&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;The menu is opened the same way&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/details&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;- Showing the cruise control distance setting screen&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;details class=&quot;ec-section github&quot;&gt;&lt;summary&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;expand&quot;&gt;&lt;/span&gt;&lt;span class=&quot;collapse&quot;&gt;&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;4 collapsed lines&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/summary&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[13-Afstandsregeling]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Aanpassen-10]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Kanaal 07]  set the value to &apos;1&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;It should appear in the MMI under CAR -&gt; ACC.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/details&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;- Setting the speed at which parking assist turns off&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;details class=&quot;ec-section github&quot;&gt;&lt;summary&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;expand&quot;&gt;&lt;/span&gt;&lt;span class=&quot;collapse&quot;&gt;&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;4 collapsed lines&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/summary&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[10-Parkeerhulp]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Aanpassen-10]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;Pre-facelift  [Kanaal 23]  max 20 km/h&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;Facelift  [Kanaal 233]  max 20 km/h&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/details&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;- Recirculation while parked (not an add-on unit, but apparently it&apos;s called parking air conditioning)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;details class=&quot;ec-section github&quot;&gt;&lt;summary&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;expand&quot;&gt;&lt;/span&gt;&lt;span class=&quot;collapse&quot;&gt;&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;5 collapsed lines&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/summary&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[08-Airco/Verwarming]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Hercoderen-07] longcoding&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Byte 01]  [Bit 4]  enable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;Show the green menu -&gt; &apos;Car&apos; -&gt; &apos;cardevicelist&apos; -&gt; &apos;Auxillary heating&apos; enable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;Show the green menu -&gt; &apos;Car&apos; -&gt; &apos;carmenuoperations&apos; -&gt; &apos;Auxillary heating&apos; set this item to 5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/details&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;- Enabling key answer-back &amp;#x26; showing the settings in the MMI (only on cars with an anti-theft system)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;details class=&quot;ec-section github&quot;&gt;&lt;summary&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;expand&quot;&gt;&lt;/span&gt;&lt;span class=&quot;collapse&quot;&gt;&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;6 collapsed lines&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/summary&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[46-Comfortsysteem]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Hercoderen-07] longcoding&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Byte 01]  [Bit 2]  enable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[46-Comfortsysteem]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Aanpassen-10]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Kanaal 63]  default 40; 44 to enable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/details&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;- Enabling the alarm with interior monitoring (not standard on Japanese-market cars, but if you&apos;ve retrofitted it)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;details class=&quot;ec-section github&quot;&gt;&lt;summary&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;expand&quot;&gt;&lt;/span&gt;&lt;span class=&quot;collapse&quot;&gt;&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;8 collapsed lines&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/summary&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[46-Comfortsysteem]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Hercoderen-07] longcoding&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Byte 01]  [Bit 1]  enable anti-theft system&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Byte 01]  [Bit 2]  enable horn alarm&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;45&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Byte 01]  [Bit 3]  enable tilt sensor&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;46&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Byte 01]  [Bit 4]  enable interior monitoring&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;47&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Byte 01]  [Bit 5]  enable rear window sensor&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;48&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Byte 01]  [Bit 6]  enable anti-rod monitoring wireless contact&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/details&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;49&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;- Enabling Autobahn lights&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;details class=&quot;ec-section github&quot;&gt;&lt;summary&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;expand&quot;&gt;&lt;/span&gt;&lt;span class=&quot;collapse&quot;&gt;&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;6 collapsed lines&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/summary&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;50&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;Turns on at 130 km/h; not confirmed whether it passes vehicle inspection (shaken) in Japan&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;51&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[09-Boordnet]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;52&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Hercoderen-07] longcoding&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;53&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;From the dropdown at the top, pick the one that looks like this&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;54&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;&apos;2 -- xxx -- RLS&apos; (Rain and Light Sensor)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;55&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8;--1:#e1e4e8&quot;&gt;[Byte 00]  [Bit 0]  enable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/details&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;</content:encoded></item><item><title>Saving Periodic RDS Dumps to S3 with ECS</title><link>https://doany.io/en/posts/rdsecsdump/</link><guid isPermaLink="true">https://doany.io/en/posts/rdsecsdump/</guid><description>Saving periodic RDS dumps to S3 with ECS</description><pubDate>Thu, 31 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;a class=&quot;anchor&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I wanted to take periodic dumps of RDS on AWS, so I set things up to grab dumps on a schedule using ECS’s cron-like feature.&lt;br&gt;
I also automated the management of the container image and task that ECS runs on a schedule.&lt;br&gt;
This post targets postgres; for mysql and the like, adjust the Dockerfile as needed.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;a class=&quot;anchor&quot; href=&quot;#prerequisites&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;An AWS account&lt;/li&gt;
&lt;li&gt;An RDS instance already set up&lt;/li&gt;
&lt;li&gt;A bitbucket account&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;preparation&quot;&gt;Preparation&lt;a class=&quot;anchor&quot; href=&quot;#preparation&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3 id=&quot;creating-an-iam-user&quot;&gt;Creating an IAM user&lt;a class=&quot;anchor&quot; href=&quot;#creating-an-iam-user&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Here we’ll create a user for deploying from bitbucket pipeline and for uploading the dump data to S3.&lt;br&gt;
Normally you’d want separate users for deployment and S3, but to keep management overhead down I’m creating just one.&lt;/p&gt;&lt;p&gt;Open IAM, go to Users in the left menu, then open Add user, and you’ll get a screen like the one below.&lt;br&gt;
Enter a user name as you like, and limit the access type to programmatic access.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://doany.io/static/images/blog/rdsecsdump0.webp&quot; alt=&quot;console.aws.amazon.com_iam_home_.png&quot;&gt;&lt;/p&gt;&lt;p&gt;Select &lt;code&gt;AmazonECS_FullAccess&lt;/code&gt;, &lt;code&gt;AmazonEC2ContainerRegistryFullAccess&lt;/code&gt;, and &lt;code&gt;AmazonS3FullAccess&lt;/code&gt; (I wanted to use the same user from the CLI, so the permissions are broad. If you know what you’re doing, narrow them down as appropriate.)&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://doany.io/static/images/blog/rdsecsdump1.webp&quot; alt=&quot;console.aws.amazon.com_iam_home_ (1).png&quot;&gt;&lt;/p&gt;&lt;p&gt;There’s nothing else in particular to configure after that, so proceed through to creating the user.&lt;br&gt;
At the end you’ll see a screen like this; make a note of the access key ID and secret access key. You can issue additional secret access keys, but this one can never be displayed again, so be absolutely sure to write it down.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://doany.io/static/images/blog/rdsecsdump2.webp&quot; alt=&quot;console.aws.amazon.com_iam_home_ (2).png&quot;&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;creating-an-iam-role&quot;&gt;Creating an IAM role&lt;a class=&quot;anchor&quot; href=&quot;#creating-an-iam-role&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This is the role used to run tasks. It normally gets created automatically, but if you’ve never used ECS at all it won’t exist yet, so create it manually.&lt;br&gt;
(If you already have a role ending in &lt;code&gt;ecsTaskExecutionRole&lt;/code&gt;, make a note of its ARN.)&lt;br&gt;
Open IAM, go to Roles in the left menu, then open Create role, and you’ll get a screen like the one below.&lt;br&gt;
Select &lt;code&gt;Elastic Container Service&lt;/code&gt;, then select &lt;code&gt;Elastic Container Service Task&lt;/code&gt;, and click Next.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://doany.io/static/images/blog/rdsecsdump3.webp&quot; alt=&quot;console.aws.amazon.com_iam_home_region=ap-northeast-1 (1).png&quot;&gt;&lt;/p&gt;&lt;p&gt;Select &lt;code&gt;AmazonECSTaskExecutionRolePolicy&lt;/code&gt;; there’s nothing else in particular to configure after that, so proceed through to creating the role.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://doany.io/static/images/blog/rdsecsdump4.webp&quot; alt=&quot;console.aws.amazon.com_iam_home_region=ap-northeast-1 (2).png&quot;&gt;&lt;/p&gt;&lt;p&gt;Once it’s created you’ll be taken back to the original screen, so open the role you just created and make a note of the &lt;code&gt;ロール ARN&lt;/code&gt; (“Role ARN”) shown on the screen below.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://doany.io/static/images/blog/rdsecsdump5.webp&quot; alt=&quot;console.aws.amazon.com_iam_home_region=ap-northeast-1 (4).png&quot;&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;creating-a-bucket&quot;&gt;Creating a bucket&lt;a class=&quot;anchor&quot; href=&quot;#creating-a-bucket&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;There’s nothing special to configure. Create it with whatever settings suit you.&lt;br&gt;
Make a note of the name you give it.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;creating-an-ecr-repository&quot;&gt;Creating an ECR repository&lt;a class=&quot;anchor&quot; href=&quot;#creating-an-ecr-repository&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Again there’s nothing special to configure, but since the pipeline overwrites tags, make sure tag immutability is disabled (it’s disabled by default).&lt;br&gt;
After creating it, make a note of the URL-like value in the URI column.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;creating-a-repository-on-bitbucket&quot;&gt;Creating a repository on bitbucket&lt;a class=&quot;anchor&quot; href=&quot;#creating-a-repository-on-bitbucket&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Create a repository as you normally would.&lt;br&gt;
Locally, create the following files.&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;shell&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:26ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;~&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:1ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;├&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;.pgpass&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:1ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;├&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;bitbucket-pipelines.yml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:1ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;├&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;Dockerfile&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:1ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;└&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;docker-entrypoint.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;.pgpass is a password file that lets you access postgres without typing a password. Write it as follows.&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;shell&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:40ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;hostname:port:database:username:password&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;bitbucket-pipelines.yml is the file that defines the bitbucket pipeline. Write it as follows.&lt;br&gt;
Fill in the &lt;code&gt;ロール ARN&lt;/code&gt; (Role ARN) you noted earlier for execution-role-arn.&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;yml&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:281ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#99A0A6;--1:#99A0A6&quot;&gt;# enable Docker for your repository&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;options&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:2ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;pipelines&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:2ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:4ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;step&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:8ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;build-push&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:8ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;image&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;atlassian/pipelines-awscli:latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:8ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;deployment&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:8ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;caches&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;docker&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:8ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;--0:#99A0A6;--1:#99A0A6&quot;&gt;# aws login&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;aws ecr get-login-password --region ${AWS_DEFAULT_REGION} | docker login --username AWS --password-stdin ${AWS_REGISTRY_URL%%/*}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;--0:#99A0A6;--1:#99A0A6&quot;&gt;# docker&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;export BUILD_ID=$BITBUCKET_BRANCH_$BITBUCKET_COMMIT_$BITBUCKET_BUILD_NUMBER&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;docker build -t ${AWS_REGISTRY_URL}:$BUILD_ID -t ${AWS_REGISTRY_URL}:development .&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;docker push ${AWS_REGISTRY_URL}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:4ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;step&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:8ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;deploy&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:8ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;image&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;atlassian/pipelines-awscli:latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:8ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;deployment&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;production&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:8ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;details class=&quot;ec-section github&quot;&gt;&lt;summary&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;expand&quot;&gt;&lt;/span&gt;&lt;span class=&quot;collapse&quot;&gt;&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;9 collapsed lines&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/summary&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;export BUILD_ID=$BITBUCKET_BRANCH_$BITBUCKET_COMMIT_$BITBUCKET_BUILD_NUMBER&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;export IMAGE_NAME=&quot;${AWS_REGISTRY_URL}:$BUILD_ID&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;--0:#99A0A6;--1:#99A0A6&quot;&gt;# ECS variables&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;export ECS_CLUSTER_NAME=&quot;${BITBUCKET_REPO_OWNER}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;export ECS_SERVICE_NAME=&quot;${BITBUCKET_REPO_SLUG}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;export ECS_TASK_NAME=&quot;${BITBUCKET_REPO_SLUG}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;--0:#99A0A6;--1:#99A0A6&quot;&gt;# Create ECS cluster, task, service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;aws ecs list-clusters | grep &quot;${ECS_CLUSTER_NAME}&quot; || aws ecs create-cluster --cluster-name &quot;${ECS_CLUSTER_NAME}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;--0:#99A0A6;--1:#99A0A6&quot;&gt;# Updating the existing cluster, task, service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/details&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;export TASK_VERSION=$(aws ecs register-task-definition&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:12ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;--family &quot;${ECS_TASK_NAME}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:12ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;--execution-role-arn &quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;details class=&quot;ec-section github&quot;&gt;&lt;summary&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;expand&quot;&gt;&lt;/span&gt;&lt;span class=&quot;collapse&quot;&gt;&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;7 collapsed lines&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/summary&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:12ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;--network-mode &quot;awsvpc&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:12ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;--requires-compatibilities &quot;FARGATE&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:12ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;--cpu &quot;256&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:12ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;--memory &quot;512&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:12ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;--container-definitions &quot;[{\&quot;name\&quot;:\&quot;${ECS_TASK_NAME}\&quot;,\&quot;image\&quot;:\&quot;${IMAGE_NAME}\&quot;,\&quot;logConfiguration\&quot;:{\&quot;logDriver\&quot;:\&quot;awslogs\&quot;,\&quot;options\&quot;:{\&quot;awslogs-group\&quot;:\&quot;/ecs\&quot;,\&quot;awslogs-region\&quot;:\&quot;${AWS_DEFAULT_REGION}\&quot;,\&quot;awslogs-stream-prefix\&quot;:\&quot;${ECS_TASK_NAME}\&quot;}}}]&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:12ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;--0:#FDAEB7;--0fs:italic;--1:#FDAEB7;--1fs:italic&quot;&gt; jq --raw-output &apos;.taskDefinition.revision&apos;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:10ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;- echo &quot;Registered ECS Task Definition:&quot; &quot;${TASK_VERSION}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/details&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;Update (October 2026): &lt;code&gt;aws ecr get-login&lt;/code&gt;, which I’d been using to log in, was removed in AWS CLI v2, so I rewrote it to pipe &lt;code&gt;aws ecr get-login-password&lt;/code&gt; into &lt;code&gt;docker login&lt;/code&gt; (this also works on v1 from 1.17.10 onward). &lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/userguide/cliv2-migration-changes.html#cliv2-migration-ecr-get-login&quot;&gt;Changes in AWS CLI v2&lt;/a&gt;&lt;br&gt;
Also, the &lt;code&gt;atlassian/pipelines-awscli&lt;/code&gt; image has been deprecated, and you’re directed to migrate to &lt;a href=&quot;https://hub.docker.com/r/amazon/aws-cli&quot;&gt;amazon/aws-cli&lt;/a&gt;. &lt;a href=&quot;https://hub.docker.com/r/atlassian/pipelines-awscli&quot;&gt;Docker Hub&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;The Dockerfile is the container definition that gets deployed to ECS. Write it as follows.&lt;br&gt;
Fill in the placeholders with the values you noted earlier (&lt;code&gt;{アクセスキーID}&lt;/code&gt; is the access key ID and &lt;code&gt;{シークレットアクセスキー}&lt;/code&gt; is the secret access key).&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;dockerfile&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:51ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;FROM&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; alpine&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; apk --no-cache add postgresql-client aws-cli&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;ENV&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; AWS_DEFAULT_REGION ap-northeast-1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;ENV&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; AWS_ACCESS_KEY_ID {アクセスキーID}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;ENV&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; AWS_SECRET_ACCESS_KEY {シークレットアクセスキー}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; mkdir dump&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;WORKDIR&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; /root/dump&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;ADD&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; .pgpass /root/.pgpass&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; chmod 0600 /root/.pgpass&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;ADD&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; docker-entrypoint.sh /root/docker-entrypoint.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; chmod +x /root/docker-entrypoint.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;CMD&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;&quot;/root/docker-entrypoint.sh&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;Update (October 2026): On recent Alpine you can’t install packages into the system Python with &lt;code&gt;pip3 install&lt;/code&gt; (it stops with externally-managed-environment), so I changed it to install the AWS CLI from Alpine’s package (&lt;a href=&quot;https://pkgs.alpinelinux.org/packages?name=aws-cli&quot;&gt;aws-cli&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;docker-entrypoint.sh is the set of commands run when the container starts; in other words, it’s the body of the cron job.&lt;br&gt;
Fill in the placeholders with the values you noted earlier (&lt;code&gt;{DB名}&lt;/code&gt; is the DB name, &lt;code&gt;{DBのホスト}&lt;/code&gt; the DB host, &lt;code&gt;{ユーザー}&lt;/code&gt; the user, and &lt;code&gt;{S3のバケット}&lt;/code&gt; the S3 bucket).&lt;br&gt;
As for the storage layout, the first prefix is the deletion cycle, then the DB name, and finally the file name prefix, which here holds the dump cycle.&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;shell&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:92ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#99A0A6;--1:#99A0A6&quot;&gt;#!/bin/sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;mkdir&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;-p&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;yearly/{DB名}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;pg_dump&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;-Fc&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;-h&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;{DBのホスト}&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;-U&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;{ユーザー}&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;{DB名}&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;yearly/{DB名}/weekly-`&lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;date&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt; &quot;+%Y%m%d_%H%M%S&quot;`&lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;.custom&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;aws&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;s3&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;sync&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;s3://{S3のバケット}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;Once everything is ready, push to the repository you created earlier as usual.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;setting-up-the-pipeline-on-bitbucket&quot;&gt;Setting up the pipeline on bitbucket&lt;a class=&quot;anchor&quot; href=&quot;#setting-up-the-pipeline-on-bitbucket&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Open the repository you just pushed on bitbucket.&lt;br&gt;
Go to Settings -&gt; PIPELINES -&gt; Settings and turn on &lt;code&gt;Enable Pipelines&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://doany.io/static/images/blog/rdsecsdump6.webp&quot; alt=&quot;bitbucket.org_j-roi_dump_admin_addon_admin_pipelines_settings.png&quot;&gt;&lt;/p&gt;&lt;p&gt;Next, open &lt;code&gt;Repository variables&lt;/code&gt;.&lt;br&gt;
Set them as shown below using the values you noted earlier.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://doany.io/static/images/blog/rdsecsdump7.webp&quot; alt=&quot;bitbucket.org_j-roi_dump_admin_addon_admin_pipelines_repository-variables.png&quot;&gt;&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;deploying-and-setting-up-cron&quot;&gt;Deploying and setting up cron&lt;a class=&quot;anchor&quot; href=&quot;#deploying-and-setting-up-cron&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3 id=&quot;running-the-deployment&quot;&gt;Running the deployment&lt;a class=&quot;anchor&quot; href=&quot;#running-the-deployment&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Still on the bitbucket screen, open &lt;code&gt;Pipelines&lt;/code&gt; from the menu again, then click &lt;code&gt;Run pipeline&lt;/code&gt;, select the options as shown below, and click &lt;code&gt;Run&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://doany.io/static/images/blog/rdsecsdump8.webp&quot; alt=&quot;bitbucket.org_j-roi_dump_addon_pipelines_home.png&quot;&gt;&lt;/p&gt;&lt;p&gt;When that’s done you’ll see something like the screen below; wait for the pipeline to finish.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://doany.io/static/images/blog/rdsecsdump9.webp&quot; alt=&quot;bitbucket.org_j-roi_dump_addon_pipelines_home (1).png&quot;&gt;&lt;/p&gt;&lt;p&gt;From then on it runs automatically every time the branch changes.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;creating-a-schedule-on-aws&quot;&gt;Creating a schedule on AWS&lt;a class=&quot;anchor&quot; href=&quot;#creating-a-schedule-on-aws&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;On AWS ECS, a cluster has been created with the repository owner’s name, so open that cluster (click where the cluster name is shown to open it).&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://doany.io/static/images/blog/rdsecsdump10.webp&quot; alt=&quot;ap-northeast-1.console.aws.amazon.com_ecs_home_region=ap-northeast-1.png&quot;&gt;&lt;/p&gt;&lt;p&gt;Open the Scheduled Tasks tab and click Create.&lt;/p&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;Update (October 2026): The screens are different in the current ECS console. For scheduled runs, the recommended approach now is to create a schedule in Amazon EventBridge Scheduler and choose ECS &lt;code&gt;RunTask&lt;/code&gt; as the target. The cron expression is EventBridge Scheduler’s as well. &lt;a href=&quot;https://docs.aws.amazon.com/AmazonECS/latest/developerguide/tasks-scheduled-eventbridge-scheduler.html&quot;&gt;Using Amazon EventBridge Scheduler to schedule Amazon ECS tasks&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;img src=&quot;https://doany.io/static/images/blog/rdsecsdump11.webp&quot; alt=&quot;ap-northeast-1.console.aws.amazon.com_ecs_home_region=ap-northeast-1 (1).png&quot;&gt;&lt;/p&gt;&lt;p&gt;Set the run interval and so on. For the target, since we created the task for &lt;code&gt;FARGATE&lt;/code&gt; this time, choose &lt;code&gt;FARGATE&lt;/code&gt; as the launch type; for the task definition family there’s a task with the same name as the repository, so select that. Configure the VPC and security group as appropriate.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://doany.io/static/images/blog/rdsecsdump12.webp&quot; alt=&quot;ap-northeast-1.console.aws.amazon.com_ecs_home_region=ap-northeast-1 (2).png&quot;&gt;&lt;/p&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;I got stuck when choosing a cron expression; it seems to differ from the cron expressions of busybox cron and the like. I used the following as a reference.&lt;br&gt;
&lt;a href=&quot;https://qiita.com/da-sugi/items/ef3bb45a8a99a4acacb1&quot;&gt;Wildcards in AWS cron expressions&lt;/a&gt; (Japanese)&lt;/p&gt;
&lt;/blockquote&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>No Network on Ubuntu After Installing a GUI</title><link>https://doany.io/en/posts/guiubuntunet/</link><guid isPermaLink="true">https://doany.io/en/posts/guiubuntunet/</guid><description>No network on Ubuntu after installing a GUI</description><pubDate>Fri, 11 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you’ve set a static IP with netplan on Ubuntu (or another Linux) and then install a GUI such as ubuntu-desktop or network-manager, the interface can end up outside NetworkManager’s control, so the network won’t connect or you can’t configure it from the GUI. When that happens, create a YAML file in &lt;code&gt;/etc/netplan/&lt;/code&gt; and add a setting so that netplan uses NetworkManager. Something like the following should get it working.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;yaml&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:26ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;network&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:2ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;version&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:2ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#85E89D;--1:#85E89D&quot;&gt;renderer&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;NetworkManager&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;</content:encoded></item><item><title>Tripping Up on Level Switching in hls.js</title><link>https://doany.io/en/posts/hlsrate/</link><guid isPermaLink="true">https://doany.io/en/posts/hlsrate/</guid><description>Tripping up on level switching in hls.js</description><pubDate>Tue, 18 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;a class=&quot;anchor&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The reason is pretty embarrassing, but I got stuck implementing level switching (switching between m3u8 files) with hls.js.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;where-i-got-stuck&quot;&gt;Where I got stuck&lt;a class=&quot;anchor&quot; href=&quot;#where-i-got-stuck&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;hls.js provides an API called hls.currentLevel, and you switch levels by changing its value. I tried to build it so that when the select changed, the option’s value would be assigned to it, but whenever I passed -1, playback stopped.&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;js&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:42ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;&apos;.res&gt;select&apos;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;&apos;change&apos;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:4ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;hls.currentLevel &lt;/span&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;val&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;section&gt;&lt;h3 id=&quot;fix&quot;&gt;Fix&lt;a class=&quot;anchor&quot; href=&quot;#fix&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Casting the option’s value with Number fixes it.&lt;/p&gt;&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;js&quot; class=&quot;wrap&quot; style=&quot;--ecMaxLine:45ch&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;&apos;.res&gt;select&apos;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#9ECBFF;--1:#9ECBFF&quot;&gt;&apos;change&apos;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot; style=&quot;--ecIndent:4ch&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;hls.currentLevel &lt;/span&gt;&lt;span style=&quot;--0:#F97583;--1:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;Number&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#79B8FF;--1:#79B8FF&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;--0:#B392F0;--1:#B392F0&quot;&gt;val&lt;/span&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E1E4E8;--1:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;button class=&quot;copy-btn&quot; aria-label=&quot;Copy code&quot;&gt;&lt;div class=&quot;copy-btn-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon copy-icon&quot;&gt;&lt;g fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot;&gt;&lt;rect width=&quot;14&quot; height=&quot;14&quot; x=&quot;8&quot; y=&quot;8&quot; rx=&quot;2&quot; ry=&quot;2&quot;&gt;&lt;/rect&gt;&lt;path d=&quot;M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;copy-btn-icon success-icon&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M20 6L9 17l-5-5&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/button&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;cause&quot;&gt;Cause&lt;a class=&quot;anchor&quot; href=&quot;#cause&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The value you get from val() implicitly ends up as either a string or a number. With the minus sign it became a string, and since hls.js doesn’t validate the value internally, there was no error at all — playback just stopped working.&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;wrap-up&quot;&gt;Wrap-up&lt;a class=&quot;anchor&quot; href=&quot;#wrap-up&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This was pretty basic stuff, but maybe my skill level is just low, because it took me quite a while to solve. It’d be great if there were more information like this out there in Japanese.&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>Testing Magnetic Stripes</title><link>https://doany.io/en/posts/mstripe/</link><guid isPermaLink="true">https://doany.io/en/posts/mstripe/</guid><description>Testing magnetic stripes</description><pubDate>Sun, 26 Aug 2018 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2 id=&quot;what-is-a-magnetic-stripe-card&quot;&gt;What is a magnetic stripe card?&lt;a class=&quot;anchor&quot; href=&quot;#what-is-a-magnetic-stripe-card&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;According to Wikipedia, “A magnetic stripe card is a type of card capable of storing data by modifying the magnetism of tiny iron-based magnetic particles on a band of magnetic material on the card.” Put simply, it’s any card with a magnetic strip on it, like the point cards and credit cards everyone carries around.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;a class=&quot;anchor&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In this post I’ll explain the basic specs of magnetic stripes and share some simple tests I ran to pin down how they work.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;specs&quot;&gt;Specs&lt;a class=&quot;anchor&quot; href=&quot;#specs&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3 id=&quot;data-on-the-magnetic-stripe&quot;&gt;Data on the magnetic stripe&lt;a class=&quot;anchor&quot; href=&quot;#data-on-the-magnetic-stripe&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;First, the data on the magnetic stripe. I read the data using an MSR705 and its bundled software, and put it together with information I found online. For the online information, I referred to &lt;a href=&quot;https://en.wikipedia.org/wiki/Magnetic_stripe_card&quot;&gt;this page&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://doany.io/static/images/blog/mstripe0.webp&quot; alt=&quot;Screenshot&quot;&gt;&lt;/p&gt;&lt;p&gt;The above is the data I read. As a sample I used an Osaifu Ponta card that hadn’t been activated. Credit cards have Track1, Track2, and Track3, arranged one above the other. (*Most credit cards issued in Japan don’t have Track3.) Using the above as an example, here’s a quick breakdown of the numbers on the magnetic stripe.&lt;br&gt;
Track1&lt;br&gt;
B (start sentinel) 3574001005472095 (card number) ^ (field separator) MEMBER/OSAIFUPONTA (name)&lt;br&gt;
^ (field separator) 2110 (expiration date) 121 (service code) 679 (CVC1)&lt;/p&gt;&lt;p&gt;Track2&lt;br&gt;
3574001005472095 (card number) = (field separator) 2110 (expiration date) 121 (service code) 679 (CVC1)&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;about-the-service-code&quot;&gt;About the service code&lt;a class=&quot;anchor&quot; href=&quot;#about-the-service-code&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Next, the service code from the magnetic stripe data above. The service code determines what behavior can be required of the payment terminal, and each digit has the following meanings.&lt;br&gt;
First digit&lt;br&gt;
1: International brand&lt;br&gt;
2: International brand &amp;#x26; IC chip&lt;br&gt;
5: Domestic brand&lt;br&gt;
6: Domestic brand &amp;#x26; IC chip&lt;br&gt;
7: Private use&lt;br&gt;
9: Test use&lt;/p&gt;&lt;p&gt;Second digit&lt;br&gt;
0: Decided by the payment terminal&lt;br&gt;
2: Contact the issuer&lt;br&gt;
4: Contact the issuer, except under bilateral agreements&lt;/p&gt;&lt;p&gt;Third digit
0: No restrictions &amp;#x26; PIN required&lt;br&gt;
1: No restrictions&lt;br&gt;
2: Goods and services only&lt;br&gt;
3: ATM only &amp;#x26; PIN required&lt;br&gt;
4: Cash advance only&lt;br&gt;
5: Goods and services only &amp;#x26; PIN required&lt;br&gt;
6: No restrictions &amp;#x26; PIN required where possible&lt;br&gt;
7: Goods and services only &amp;#x26; PIN required where possible&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;a class=&quot;anchor&quot; href=&quot;#testing&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I tested whether cards can be cloned and whether cloned cards can be used.&lt;/p&gt;&lt;section&gt;&lt;h3 id=&quot;cloning-a-card&quot;&gt;Cloning a card&lt;a class=&quot;anchor&quot; href=&quot;#cloning-a-card&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;To give the conclusion first: cloning is possible. However, even if you know the card number, expiration date, and CVC2, you can’t clone a card, because the CVC1 I explained earlier is unknown. The code usually printed on the back of a credit card is actually the CVC2, which is used for online payments, while the one on the magnetic stripe is the CVC1, used for in-store payments. The CVC1 is random and can’t be guessed, so you can’t forge a card from that information alone. So how can a card be cloned? The well-known method is a skimming device attached to a payment terminal, and a cloned card can also be made if a malicious merchant steals the magnetic stripe data directly. A magnetic stripe has no unique, non-rewritable data, so once someone gets hold of the data on the stripe, it’s game over.&lt;br&gt;
As for countermeasures, ones that have been spreading in Japan in recent years too: having customers operate the payment themselves, and visually checking the payment terminal.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;using-a-cloned-card&quot;&gt;Using a cloned card&lt;a class=&quot;anchor&quot; href=&quot;#using-a-cloned-card&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Again, to give the conclusion first: it can be used. But it’s also true that it has become harder in recent years with the move to IC. The reason is the service code I explained earlier: if its first digit is 2 or 6, the IC chip is required (though some terminals don’t require it), so the payment can’t go through. I also tried rewriting the service code, and under the current credit card infrastructure, a payment fails if even a single number differs from what the issuer has, so the payment couldn’t be made. As an aside, here’s something I found out during this test: with no data on Track1 (with the magnetic data wiped), the payment couldn’t be made, but with any card information at all on Track1, it went through. And no matter what card information I put on Track1, the payment was processed using the card information on Track2. The service code, too, was taken from Track2.&lt;br&gt;
The countermeasures are putting IC chips in cards and making payment terminals IC-capable.&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;a class=&quot;anchor&quot; href=&quot;#summary&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As shown above, it turned out that fraudulent use is fairly easy. Given the state of payments in Japan in particular, I think this needs to be addressed quite urgently.&lt;/p&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;Update (October 2026): Just before this post, on June 1, 2018, the amended Installment Sales Act (kappu hanbai hō) came into effect, requiring merchants that accept credit cards to install terminals that can process IC card payments. Terminals that only swipe the magnetic stripe are subject to replacement. &lt;a href=&quot;https://www.jcb.co.jp/merchant/release/kappu_security.html&quot;&gt;Notice on the amendment to the Installment Sales Act | JCB&lt;/a&gt; (Japanese)&lt;/p&gt;
&lt;/blockquote&gt;&lt;/section&gt;</content:encoded></item><item><title>Validating Credit Card Numbers</title><link>https://doany.io/en/posts/number/</link><guid isPermaLink="true">https://doany.io/en/posts/number/</guid><description>Validating credit card numbers</description><pubDate>Fri, 27 Jul 2018 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;a class=&quot;anchor&quot; href=&quot;#introduction&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Credit card numbers are something we see all the time, but there are all sorts of rules and security measures behind them too. In this post I’ll go over those.&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;overview-of-the-number&quot;&gt;Overview of the number&lt;a class=&quot;anchor&quot; href=&quot;#overview-of-the-number&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;First, how a credit card number is structured: credit card numbers are defined in ISO/IEC 7812, and consist of a 6-digit BIN (Bank Identification Number) or IIN (Issuer Identification Number) plus the remaining digits, which are decided by the issuer.&lt;/p&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;Update (October 2026): The ISO/IEC 7812-1:2017 revision extended the IIN to 8 digits, and since April 2022 the card networks only assign new BINs as 8 digits (existing 6-digit BINs remain in use). The discussion below assumes 6 digits, so keep in mind that if you can pin down an 8-digit BIN, there are two fewer unknown digits. &lt;a href=&quot;https://en.wikipedia.org/wiki/ISO/IEC_7812&quot;&gt;ISO/IEC 7812&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;section&gt;&lt;h3 id=&quot;about-bin-and-iin&quot;&gt;About BIN and IIN&lt;a class=&quot;anchor&quot; href=&quot;#about-bin-and-iin&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The BIN/IIN is itself made up of multiple parts. The first digit is called the MII (Major Industry Identifier) and indicates the industry the card is used in; the mapping is as follows.&lt;/p&gt;
















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;MII value&lt;/th&gt;&lt;th&gt;Industry&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;Reserved by ISO&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Airlines&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Airlines / other future industry assignments&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Travel &amp;#x26; entertainment / banking &amp;#x26; financial&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;Banking &amp;#x26; financial&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;Banking &amp;#x26; financial&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;Merchandising / banking &amp;#x26; financial&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;Petroleum / other future industry assignments&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;Healthcare / medical / telecommunications / other future industry assignments&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;Assignable by national standards bodies&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;And below is a list of major BINs, past and present.&lt;/p&gt;


























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Card type&lt;/th&gt;&lt;th&gt;Prefix&lt;/th&gt;&lt;th&gt;Length&lt;/th&gt;&lt;th&gt;Validation&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;American Express&lt;/td&gt;&lt;td&gt;34, 37&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;Luhn algorithm&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;China UnionPay&lt;/td&gt;&lt;td&gt;622126-622925, 624-626, 6282-6288&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;None&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Diners Club International&lt;/td&gt;&lt;td&gt;300-303574, 3095, 36, 38-39&lt;/td&gt;&lt;td&gt;14&lt;/td&gt;&lt;td&gt;Luhn algorithm&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Discover Card&lt;/td&gt;&lt;td&gt;60110, 60112-60114, 601174-601179, 601186-601199, 644-649, 65&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;Luhn algorithm&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;JCB&lt;/td&gt;&lt;td&gt;3528-3589&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;Luhn algorithm&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;MasterCard&lt;/td&gt;&lt;td&gt;510000 - 559999, 222100 - 272099&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;Luhn algorithm&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;UATP&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;Unknown&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Visa&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;13, 16&lt;/td&gt;&lt;td&gt;Luhn algorithm&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;about-the-check-digit&quot;&gt;About the check digit&lt;a class=&quot;anchor&quot; href=&quot;#about-the-check-digit&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Credit card numbers use the Luhn algorithm. My guess is that its main purposes are to avoid issuing numbers sequentially and to prevent someone else’s card from being charged because of a typo. However, since it can be calculated, it isn’t meant to stop malicious payments.&lt;/p&gt;&lt;section&gt;&lt;h3 id=&quot;validation-from-a-security-perspective&quot;&gt;Validation from a security perspective&lt;a class=&quot;anchor&quot; href=&quot;#validation-from-a-security-perspective&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Based on the rules above, here’s what I tested.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;overview-of-the-test&quot;&gt;Overview of the test&lt;a class=&quot;anchor&quot; href=&quot;#overview-of-the-test&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;These days services like Apple Pay display the last 4 digits of the card number for reference, and in Apple Pay’s case you can identify the issuer to some extent from the card design, so I tested whether the card number could be guessed from that.&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;identifying-the-biniin&quot;&gt;Identifying the BIN/IIN&lt;a class=&quot;anchor&quot; href=&quot;#identifying-the-biniin&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;First, I tried identifying the BIN from the card design. BINs aren’t officially published, but you can look them up on sites like the one below. Some card companies have an enormous number of registered BINs, so at this point the issuers whose card numbers could be pinned down are already limited, but from here on I’ll assume the BIN has been identified. &lt;a href=&quot;https://www.bincodes.com/bin-search/&quot;&gt;BIN search&lt;/a&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3 id=&quot;identifying-the-card-number&quot;&gt;Identifying the card number&lt;a class=&quot;anchor&quot; href=&quot;#identifying-the-card-number&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I’ll proceed assuming the BIN has been identified. At this point you know the 6-digit BIN plus the last 4 digits. I then computed every number that satisfies the Luhn algorithm (for 16 digits this time). I checked it on the site below, and there were well over 100 candidates, so identifying the number was impossible. &lt;a href=&quot;https://businer.com/discard_credit_card_generator.php&quot;&gt;credit card number generator&lt;/a&gt;&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;a class=&quot;anchor&quot; href=&quot;#summary&quot;&gt;&lt;span class=&quot;anchor-icon&quot; data-pagefind-ignore=&quot;&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As shown above, the result is that identifying a card number from screenshots, receipt photos, and the like is practically impossible. On top of that, mandatory security codes and the rollout of 3D Secure have been progressing in recent years, so even if a number were cracked, realistically there’d be no merchant where you could actually make a payment with it.&lt;/p&gt;&lt;blockquote class=&quot;admonition bdm-note&quot;&gt;
&lt;span class=&quot;bdm-title&quot;&gt;NOTE&lt;/span&gt;
&lt;p&gt;Update (October 2026): The Credit Card Security Guidelines [version 5.0] (Japan) require, in principle, all e-commerce merchants to adopt EMV 3-D Secure by the end of March 2025. &lt;a href=&quot;https://www.jcb.co.jp/merchant/release/emv3-dsecure.html&quot;&gt;Introduction of the authentication service for card payments (EMV 3-D Secure) | JCB&lt;/a&gt; (Japanese)&lt;/p&gt;
&lt;/blockquote&gt;&lt;/section&gt;</content:encoded></item></channel></rss>