deploy: UserNS=keep-id makes every web deploy a 100s outage (regression from #238) #253

Stängd
öppnade 2026-07-30 11:42:54 +00:00 av supernaut · 2 kommentarer
Ägare

Every bitborg-web deploy has taken the portal down for 83–139 seconds since 2026-07-28, up from
about 13 seconds before that. The cause is UserNS=keep-id on the web Quadlet unit, added by
#238 (ADR 0037 Phase 1). This is what the 502s behind #249 / #250 were actually a symptom of.

Measurement

Podman stamps each journald event with a monotonic offset (m=+N) from the start of the command that
caused it, so the phases of a deploy can be priced exactly rather than inferred from probe samples.
Breakdown of the 2026-07-29 22:27 UTC deploy:

Phase Wall clock (UTC) Duration
podman auto-update pull 22:27:10.9 → 22:27:11.6 0.7 s
SIGTERM ignored → SIGKILL 22:27:11.6 → 22:27:21.7 10.1 s
podman create 22:27:21.9 → 22:29:05.1 103.2 s
container init + start 22:29:05.1 → 22:29:05.5 0.5 s
node boot + migrations 22:29:05.5 → 22:29:07.8 2.3 s
migrations → listening 22:29:07.8 → 22:29:08.4 0.5 s
Total unavailability ~117 s

Two things worth stating plainly, because both contradict the assumption made while triaging #249:

  • Migrations are not the cost. In-container startup — node boot, scripts/migrate.mjs, Astro
    listening — is 2.8 s. Migrations are 2 % of the outage.
  • 88 % of the outage happens before the container exists, inside podman create.

It is a regression, not a standing property

container create duration for bitborg-web, every deploy since 07-24:

07-24 09:21   0.05s        07-29 08:17    83.0s
07-26 18:06   0.08s        07-29 12:32    91.9s
07-26 19:03   0.06s        07-29 18:52    86.7s
07-27 12:51   0.07s        07-29 19:38   127.1s
07-28 10:42   0.06s        07-29 21:17    83.6s
07-28 13:37   0.06s        07-29 21:32    86.5s   ← 502 in #249
07-28 16:12   0.06s        07-29 21:42   138.9s   ← 502 in #249
07-28 21:42   0.06s        07-29 21:47   107.6s
        ↑                  07-29 22:29   103.1s
   step change here

Cleanly bimodal: eight deploys at ~0.06 s, then every deploy since at 83–139 s, a ~1500× regression.
The step lands between 07-28 21:42 and 22:16 — which brackets the apply of #238
(adb2fd8, merged 07-28 21:39 UTC), the only change to
ansible/roles/web/templates/bitborg-web.container.j2 in that window.

Cause

That commit added, to give the container's node user (uid 1000) the host gitborg uid so the
ADR 0037 sentinel it writes is owned by gitborg:

UserNS=keep-id:uid=1000,gid=1000

Rootless Podman with keep-id has to produce a rootfs with shifted ownership on every container
creation. For the web image — node:24-trixie-slim plus a production node_modules — that is a
recursive chown across tens of thousands of files.

The cost scales with file count, which is why only this container shows it: gitborg-reconciler
carries the identical UserNS=keep-id:uid=1000,gid=1000 line and creates in 0.05–0.2 s, because its
image is small. So keep-id is not wrong in general — it is wrong on our largest image.

flowchart TD
    A["merge to main"] --> B["CI builds + pushes image"]
    B --> C["podman auto-update pulls<br/>0.7s"]
    C --> D["SIGTERM ignored → SIGKILL<br/>10.1s"]
    D --> E["podman create<br/>keep-id chowns the whole rootfs<br/>103.2s"]
    E --> F["node boot + migrations<br/>2.3s"]
    F --> G["Astro listening"]
    style E fill:#c62828,color:#fff
    style D fill:#ef6c00,color:#fff

Proposed fix

Idmap only the bind mount that needs it, instead of remapping the whole rootfs:

Volume={{ reconcile_trigger_dir }}:{{ reconcile_trigger_dir }}:rw,idmap

and drop UserNS=keep-id from this unit (the reconciler keeps it — small image, and
node_exporter must be able to read its textfile metrics).

The host side of ADR 0037 only ever performs metadata operations on the sentinel — PathExists= in
bitborg-reconcile.path, and stat -c %Y plus rm -f in bitborg-reconcile-kick.sh. None of those
read file contents, and rm needs write permission on the directory, not the file. So the
sentinel's own ownership does not have to match gitborg; only the directory's does, and it is
already bitborg:bitborg 0755.

Prerequisite to verify before committing to this: idmap on a bind mount requires the source
filesystem to support idmapped mounts (kernel ≥ 5.12, ext4/xfs — not overlayfs).
/home/gitborg/reconcile-trigger needs checking on the host. If it is unsupported, the fallback is to
make the sentinel directory writable by the container's mapped subuid — a group or a mode change —
which keeps the default userns and the fast create path.

Acceptance

  • Confirm idmap bind-mount support on gitborg-prod for the sentinel directory's filesystem.
  • Replace UserNS=keep-id with an idmapped volume on the web unit (or apply the fallback).
  • Deploy and confirm container create for bitborg-web is back to sub-second, from the podman
    event m=+ offset — not from probe samples, which lack the resolution to attribute this.
  • Confirm the ADR 0037 event trigger still fires end to end: web writes the sentinel, the .path
    unit kicks, gitborg_reconcile_trigger_lag_seconds is emitted.
  • Re-check the HealthStartPeriod=30s margin against the measured 2.8 s in-container startup.

Notes

  • #249 and #250 tuned Caddy's retry window against an assumed "few seconds" of restart. With the real
    figure at ~117 s, #250's 2 s window is honest about not bridging a deploy — that stays correct
    regardless of this fix, and becomes an adequate mitigation once creation is sub-second again.
  • The 10.1 s SIGTERM slice is a separate defect in the app and is filed against bitborg-web. After
    both fixes, expected deploy unavailability is roughly 3 s.
Every `bitborg-web` deploy has taken the portal down for **83–139 seconds** since 2026-07-28, up from about **13 seconds** before that. The cause is `UserNS=keep-id` on the web Quadlet unit, added by #238 (ADR 0037 Phase 1). This is what the 502s behind #249 / #250 were actually a symptom of. ## Measurement Podman stamps each journald event with a monotonic offset (`m=+N`) from the start of the command that caused it, so the phases of a deploy can be priced exactly rather than inferred from probe samples. Breakdown of the 2026-07-29 22:27 UTC deploy: | Phase | Wall clock (UTC) | Duration | | --- | --- | --- | | `podman auto-update` pull | 22:27:10.9 → 22:27:11.6 | 0.7 s | | SIGTERM ignored → SIGKILL | 22:27:11.6 → 22:27:21.7 | 10.1 s | | **`podman create`** | 22:27:21.9 → 22:29:05.1 | **103.2 s** | | container init + start | 22:29:05.1 → 22:29:05.5 | 0.5 s | | node boot + migrations | 22:29:05.5 → 22:29:07.8 | 2.3 s | | migrations → listening | 22:29:07.8 → 22:29:08.4 | 0.5 s | | **Total unavailability** | | **~117 s** | Two things worth stating plainly, because both contradict the assumption made while triaging #249: - **Migrations are not the cost.** In-container startup — node boot, `scripts/migrate.mjs`, Astro listening — is **2.8 s**. Migrations are 2 % of the outage. - **88 % of the outage happens before the container exists**, inside `podman create`. ## It is a regression, not a standing property `container create` duration for `bitborg-web`, every deploy since 07-24: ``` 07-24 09:21 0.05s 07-29 08:17 83.0s 07-26 18:06 0.08s 07-29 12:32 91.9s 07-26 19:03 0.06s 07-29 18:52 86.7s 07-27 12:51 0.07s 07-29 19:38 127.1s 07-28 10:42 0.06s 07-29 21:17 83.6s 07-28 13:37 0.06s 07-29 21:32 86.5s ← 502 in #249 07-28 16:12 0.06s 07-29 21:42 138.9s ← 502 in #249 07-28 21:42 0.06s 07-29 21:47 107.6s ↑ 07-29 22:29 103.1s step change here ``` Cleanly bimodal: eight deploys at ~0.06 s, then every deploy since at 83–139 s, a ~1500× regression. The step lands between 07-28 21:42 and 22:16 — which brackets the apply of #238 (`adb2fd8`, merged 07-28 21:39 UTC), the only change to `ansible/roles/web/templates/bitborg-web.container.j2` in that window. ## Cause That commit added, to give the container's `node` user (uid 1000) the host `gitborg` uid so the ADR 0037 sentinel it writes is owned by `gitborg`: ```ini UserNS=keep-id:uid=1000,gid=1000 ``` Rootless Podman with `keep-id` has to produce a rootfs with shifted ownership on **every** container creation. For the web image — `node:24-trixie-slim` plus a production `node_modules` — that is a recursive chown across tens of thousands of files. The cost scales with file count, which is why only this container shows it: `gitborg-reconciler` carries the identical `UserNS=keep-id:uid=1000,gid=1000` line and creates in 0.05–0.2 s, because its image is small. So `keep-id` is not wrong in general — it is wrong on our largest image. ```mermaid flowchart TD A["merge to main"] --> B["CI builds + pushes image"] B --> C["podman auto-update pulls<br/>0.7s"] C --> D["SIGTERM ignored → SIGKILL<br/>10.1s"] D --> E["podman create<br/>keep-id chowns the whole rootfs<br/>103.2s"] E --> F["node boot + migrations<br/>2.3s"] F --> G["Astro listening"] style E fill:#c62828,color:#fff style D fill:#ef6c00,color:#fff ``` ## Proposed fix Idmap only the bind mount that needs it, instead of remapping the whole rootfs: ```ini Volume={{ reconcile_trigger_dir }}:{{ reconcile_trigger_dir }}:rw,idmap ``` and drop `UserNS=keep-id` from **this** unit (the reconciler keeps it — small image, and node_exporter must be able to read its textfile metrics). The host side of ADR 0037 only ever performs metadata operations on the sentinel — `PathExists=` in `bitborg-reconcile.path`, and `stat -c %Y` plus `rm -f` in `bitborg-reconcile-kick.sh`. None of those read file contents, and `rm` needs write permission on the *directory*, not the file. So the sentinel's own ownership does not have to match `gitborg`; only the directory's does, and it is already `bitborg:bitborg 0755`. **Prerequisite to verify before committing to this:** `idmap` on a bind mount requires the source filesystem to support idmapped mounts (kernel ≥ 5.12, ext4/xfs — not overlayfs). `/home/gitborg/reconcile-trigger` needs checking on the host. If it is unsupported, the fallback is to make the sentinel directory writable by the container's mapped subuid — a group or a mode change — which keeps the default userns and the fast create path. ## Acceptance - [ ] Confirm `idmap` bind-mount support on `gitborg-prod` for the sentinel directory's filesystem. - [ ] Replace `UserNS=keep-id` with an idmapped volume on the web unit (or apply the fallback). - [ ] Deploy and confirm `container create` for `bitborg-web` is back to sub-second, from the podman event `m=+` offset — not from probe samples, which lack the resolution to attribute this. - [ ] Confirm the ADR 0037 event trigger still fires end to end: web writes the sentinel, the `.path` unit kicks, `gitborg_reconcile_trigger_lag_seconds` is emitted. - [ ] Re-check the `HealthStartPeriod=30s` margin against the measured 2.8 s in-container startup. ## Notes - #249 and #250 tuned Caddy's retry window against an assumed "few seconds" of restart. With the real figure at ~117 s, #250's 2 s window is honest about not bridging a deploy — that stays correct regardless of this fix, and becomes an adequate mitigation once creation is sub-second again. - The 10.1 s SIGTERM slice is a separate defect in the app and is filed against bitborg-web. After both fixes, expected deploy unavailability is roughly 3 s.
Upphovsperson
Ägare

Correction to the proposed fix above — plain ,idmap is not sufficient.

The diagnosis stands, but the one-line fix I suggested does not work as written.

Volume=…:rw,idmap idmaps the bind mount using the container's default user-namespace mapping.
For rootless gitborg (uid 2000) that mapping is: container uid 0 → host 2000, container uids 1–65536
→ the subuid range. So a sentinel directory owned by host uid 2000 appears inside the container as
owned by uid 0, and the app runs as node (uid 1000) — which still cannot write it. Same failure
keep-id was added to avoid.

The candidate that should work is an explicit mapping, scoped to that one mount:

Volume={{ reconcile_trigger_dir }}:{{ reconcile_trigger_dir }}:rw,idmap=uids=@2000-1000-1;gids=@2000-1000-1

@2000-1000-1 maps host uid 2000 to container uid 1000 for a range of 1 — the mount-scoped
equivalent of keep-id:uid=1000, without remapping the rootfs. Two caveats before relying on it:

  • The ; separator and @ syntax have to survive Quadlet's Volume= generation. This unit already
    carries a comment documenting that Quadlet/podman 5.4.2 corrupt embedded double quotes in
    HealthCmd, so the generator's escaping is not to be taken on trust — verify with
    quadlet -dryrun and confirm the resulting --mount in podman inspect.
  • Idmapped bind mounts need kernel ≥ 5.12 and a supporting source filesystem.

Hardcoding uid 2000 also duplicates gitborg_user_uid; it should be templated as
uids=@{{ gitborg_user_uid }}-1000-1, with 1000 matching the node uid in bitborg-web's
Containerfile.

Not committing to this until it is measured on the host. A diagnostic runs six variants — default
userns, keep-id, plain idmap, explicit idmap — timing podman create for each and probing
whether uid 1000 can actually write the sentinel. Options if the explicit mapping does not hold up:

  1. Make the sentinel directory group-writable by a gid the container maps to, keeping the default
    userns and the fast create path.
  2. Check whether keep-id can use idmapped mounts instead of a recursive chown on this host — that
    would fix the cost globally with no unit change. The ~100 s strongly suggests it is currently
    falling back to chown; podman info storage options will say.
  3. Cut the file-based sentinel for the web hop and reach the trigger over the network, as ADR 0037
    Phase 2's shim already does for the webhook path.

Option 2 is worth checking first — it is the only one that fixes the cause rather than working around
it, and it would also protect any future container that needs keep-id.

**Correction to the proposed fix above — plain `,idmap` is not sufficient.** The diagnosis stands, but the one-line fix I suggested does not work as written. `Volume=…:rw,idmap` idmaps the bind mount using the container's **default** user-namespace mapping. For rootless `gitborg` (uid 2000) that mapping is: container uid 0 → host 2000, container uids 1–65536 → the subuid range. So a sentinel directory owned by host uid 2000 appears inside the container as owned by **uid 0**, and the app runs as `node` (uid 1000) — which still cannot write it. Same failure `keep-id` was added to avoid. The candidate that should work is an explicit mapping, scoped to that one mount: ```ini Volume={{ reconcile_trigger_dir }}:{{ reconcile_trigger_dir }}:rw,idmap=uids=@2000-1000-1;gids=@2000-1000-1 ``` `@2000-1000-1` maps host uid 2000 to container uid 1000 for a range of 1 — the mount-scoped equivalent of `keep-id:uid=1000`, without remapping the rootfs. Two caveats before relying on it: - The `;` separator and `@` syntax have to survive Quadlet's `Volume=` generation. This unit already carries a comment documenting that Quadlet/podman 5.4.2 corrupt embedded double quotes in `HealthCmd`, so the generator's escaping is not to be taken on trust — verify with `quadlet -dryrun` and confirm the resulting `--mount` in `podman inspect`. - Idmapped bind mounts need kernel ≥ 5.12 and a supporting source filesystem. Hardcoding uid 2000 also duplicates `gitborg_user_uid`; it should be templated as `uids=@{{ gitborg_user_uid }}-1000-1`, with 1000 matching the `node` uid in bitborg-web's Containerfile. **Not committing to this until it is measured on the host.** A diagnostic runs six variants — default userns, `keep-id`, plain `idmap`, explicit `idmap` — timing `podman create` for each and probing whether uid 1000 can actually write the sentinel. Options if the explicit mapping does not hold up: 1. Make the sentinel directory group-writable by a gid the container maps to, keeping the default userns and the fast create path. 2. Check whether `keep-id` can use idmapped mounts instead of a recursive chown on this host — that would fix the cost globally with no unit change. The ~100 s strongly suggests it is currently falling back to chown; `podman info` storage options will say. 3. Cut the file-based sentinel for the web hop and reach the trigger over the network, as ADR 0037 Phase 2's shim already does for the webhook path. Option 2 is worth checking first — it is the only one that fixes the cause rather than working around it, and it would also protect any future container that needs `keep-id`.
Upphovsperson
Ägare

Measured on the host. The cause is confirmed; both fixes proposed above are wrong. Details below.

The mechanism, now established

The first attempt to reproduce this failed misleadingly: podman create --userns keep-id:uid=1000,gid=1000
against the running image took 113 ms, which looked like a refutation. It was not — the
shifted-ownership copy for that image and that mapping was already cached in the store.

Re-running with a mapping never used before (uid=1001) on the same image forces a cold cache:

Test Result
default userns, no keep-id 129 ms
keep-id:uid=1001 — cold cache 108 786 ms
keep-id:uid=1001 — repeat, now cached 202 ms
keep-id:uid=1000 — prod's mapping, cached 149 ms

108.8 s reproduces the ~103 s seen in prod deploys. So:

keep-id triggers a full recursive chown of the rootfs — 849 MB across 11 layers — cached per
(image digest, mapping). Every deploy ships a new digest, so every deploy pays it cold. A warm-cache
run is 538× faster, which is why this is invisible to any test that reuses the running image.

The reason Podman has to chown at all is in podman info:

Backing Filesystem: extfs   Native Overlay Diff: true
Supports shifting: false    Using metacopy: false

Supports shifting: false — the overlay driver cannot shift UIDs on the fly, so a non-identity
mapping can only be satisfied by physically rewriting ownership. This is also why
gitborg-reconciler carries the identical directive at ~0.1 s: the cost is proportional to image
size, and its image is small. The directive is not wrong in general; it is wrong on our largest image.

Both proposed fixes are dead

Idmapped bind mounts are unavailable on this host for a rootless container:

$ podman run --rm -u 1000:1000 -v …:rw,idmap=uids=2000-1000-1;gids=2000-1000-1 …
Error: crun: mount_setattr `/mnt/rt`: Operation not permitted: OCI permission denied

The relative @-prefixed form fails earlier still, on mapping resolution:

Error: could not find a user namespace mapping for the relative mapping "@2000-1000-1"

So strike the rw,idmap suggestion in the issue body and the explicit-mapping variant in the first
comment. Supports shifting: false predicted this and I should have read it before proposing either.

Surviving options

  1. Drop UserNS=keep-id from the web unit. Recovers the full ~109 s. ADR 0037's web-side trigger
    degrades to the 5-min timer backstop — which is by design: triggerReconcile
    (bitborg-web/src/lib/reconcile-trigger.ts) catches every write failure and never throws, and the
    timer is documented as the fallback. Net effect is pre-#238 behaviour: entitlement changes apply on
    the next tick instead of instantly.

  2. Own the sentinel directory as the container's mapped uid. Keeps both the instant trigger and
    the fast create path. With the default rootless userns, container uid 1000 lands in the subuid range
    (bitborg:165536:65536). Setting the directory owner=<mapped uid>, group=bitborg, mode=0775 lets
    the container write as owner while the host side keeps the group bit it needs — and the host side
    only ever does metadata operations (PathExists=, stat -c %Y, rm -f), never a content read.

    Express it as podman unshare chown 1000:1000 <dir>, which performs the mapping arithmetic itself
    rather than hardcoding 166535 — that number is a function of /etc/subuid and should not be
    embedded in a playbook.

    Fails safe if the mapping ever changes: the write fails, is logged, and the timer reconciles.

  3. Shrink the image. Mitigation only — the chown stays O(image), just smaller.

  4. Replace the file sentinel with a network call, as ADR 0037 Phase 2's shim already does for the
    webhook path. The largest change; worth considering if Phase 2 lands anyway.

Option 2 is the recommendation, pending a host test of the ownership scheme end to end. Option 1 is
available as a one-line mitigation at any time and is worth taking immediately if option 2 needs more
than a moment — a recurring ~2-minute outage on every deploy is worse than 5-minute entitlement latency.

Correction to the acceptance criteria

The first item above ("confirm idmap bind-mount support") is settled: not supported. Replace with
verifying whichever ownership scheme is chosen, and keep the requirement to measure container create
from the podman event m=+ offset after the fix — noting that a warm cache makes any post-deploy
re-test of the same digest meaningless. It has to be measured on a genuinely new image.

**Measured on the host. The cause is confirmed; both fixes proposed above are wrong. Details below.** ## The mechanism, now established The first attempt to reproduce this failed misleadingly: `podman create --userns keep-id:uid=1000,gid=1000` against the running image took **113 ms**, which looked like a refutation. It was not — the shifted-ownership copy for *that* image and *that* mapping was already cached in the store. Re-running with a mapping never used before (`uid=1001`) on the same image forces a cold cache: | Test | Result | | --- | --- | | default userns, no `keep-id` | **129 ms** | | `keep-id:uid=1001` — cold cache | **108 786 ms** | | `keep-id:uid=1001` — repeat, now cached | **202 ms** | | `keep-id:uid=1000` — prod's mapping, cached | **149 ms** | 108.8 s reproduces the ~103 s seen in prod deploys. So: **`keep-id` triggers a full recursive chown of the rootfs — 849 MB across 11 layers — cached per (image digest, mapping). Every deploy ships a new digest, so every deploy pays it cold. A warm-cache run is 538× faster, which is why this is invisible to any test that reuses the running image.** The reason Podman has to chown at all is in `podman info`: ``` Backing Filesystem: extfs Native Overlay Diff: true Supports shifting: false Using metacopy: false ``` `Supports shifting: false` — the overlay driver cannot shift UIDs on the fly, so a non-identity mapping can only be satisfied by physically rewriting ownership. This is also why `gitborg-reconciler` carries the identical directive at ~0.1 s: the cost is proportional to image size, and its image is small. The directive is not wrong in general; it is wrong on our largest image. ## Both proposed fixes are dead Idmapped bind mounts are unavailable on this host for a rootless container: ``` $ podman run --rm -u 1000:1000 -v …:rw,idmap=uids=2000-1000-1;gids=2000-1000-1 … Error: crun: mount_setattr `/mnt/rt`: Operation not permitted: OCI permission denied ``` The relative `@`-prefixed form fails earlier still, on mapping resolution: ``` Error: could not find a user namespace mapping for the relative mapping "@2000-1000-1" ``` So strike the `rw,idmap` suggestion in the issue body and the explicit-mapping variant in the first comment. `Supports shifting: false` predicted this and I should have read it before proposing either. ## Surviving options 1. **Drop `UserNS=keep-id` from the web unit.** Recovers the full ~109 s. ADR 0037's web-side trigger degrades to the 5-min timer backstop — which is by design: `triggerReconcile` (`bitborg-web/src/lib/reconcile-trigger.ts`) catches every write failure and never throws, and the timer is documented as the fallback. Net effect is pre-#238 behaviour: entitlement changes apply on the next tick instead of instantly. 2. **Own the sentinel directory as the container's *mapped* uid.** Keeps both the instant trigger and the fast create path. With the default rootless userns, container uid 1000 lands in the subuid range (`bitborg:165536:65536`). Setting the directory `owner=<mapped uid>, group=bitborg, mode=0775` lets the container write as owner while the host side keeps the group bit it needs — and the host side only ever does metadata operations (`PathExists=`, `stat -c %Y`, `rm -f`), never a content read. Express it as `podman unshare chown 1000:1000 <dir>`, which performs the mapping arithmetic itself rather than hardcoding 166535 — that number is a function of `/etc/subuid` and should not be embedded in a playbook. Fails safe if the mapping ever changes: the write fails, is logged, and the timer reconciles. 3. Shrink the image. Mitigation only — the chown stays O(image), just smaller. 4. Replace the file sentinel with a network call, as ADR 0037 Phase 2's shim already does for the webhook path. The largest change; worth considering if Phase 2 lands anyway. Option 2 is the recommendation, pending a host test of the ownership scheme end to end. Option 1 is available as a one-line mitigation at any time and is worth taking immediately if option 2 needs more than a moment — a recurring ~2-minute outage on every deploy is worse than 5-minute entitlement latency. ## Correction to the acceptance criteria The first item above ("confirm idmap bind-mount support") is settled: **not supported**. Replace with verifying whichever ownership scheme is chosen, and keep the requirement to measure `container create` from the podman event `m=+` offset after the fix — noting that a warm cache makes any post-deploy re-test of the *same* digest meaningless. It has to be measured on a genuinely new image.
Logga in för att delta i denna konversation.
Ingen milstolpe
Inga tilldelade
1 deltagare
Notiser
Förfallodatum
Förfallodatumet är ogiltigt eller utanför gränserna. Använd formatet "åååå-mm-dd".

Inget förfallodatum satt.

Beroenden

Inga beroenden satta

Referens
bitborg/bitborg-infra#253
Ingen beskrivning angiven.