Move TLS to aws-lc-rs with post-quantum key exchange, drop ring via ureq 3, and fix the dependency defects behind it #192

Merged
enricobuehler merged 6 commits from worktree-aws-lc-rs-migration into main 2026-08-13 10:43:38 +00:00
Owner

Six commits. The first two are the substance; the rest are the cleanup they exposed.

Why

The workspace pinned ring everywhere because aws-lc-sys 0.41.0 failed to C-compile on the Windows CI runner. Re-tested on that runner with aws-lc-sys 0.44.0: the params.c cl.exe failure does not reproduce under MSVC 14.44, and rustls's aws_lc_rs feature turns on aws-lc-rs/prebuilt-nasm, so no NASM is needed on the box either.

That unblocked post-quantum TLS. prefer-post-quantum now offers X25519MLKEM768 first on every TLS 1.3 handshake (mgmt API, native control plane, QUIC) — something ring cannot do, since it has no ML-KEM. Classical curves stay in the list, so any client without ML-KEM connects exactly as before. No wire format, ABI or pairing record changes; the session AEAD is a separate mechanism and is untouched.

ring is gone

Not just displaced — removed. cargo tree -i ring is empty for macOS, Windows and Linux. Getting there needed the ureq 2 → 3 upgrade in the same change: ureq 2 named rustls/ring inside its own dependency declaration where no dependent could switch it off. ureq 3 declares rustls with default-features = false and picks no backend.

The only remaining --target all hit is quinn-proto's wasm-only dependency, a target we never build.

⚠️ Spell the ureq dependency features = ["rustls-no-provider", "rustls-webpki-roots"]. ureq 3's convenience rustls feature expands to include _ring and would quietly restore the second backend.

Notable pieces

  • A shared pinning connector. ureq 3's TlsConfig has no hook for a custom ServerCertVerifier, so certificate pinning needed a custom Connector. That glue lives once in punktfunk-core behind an off-by-default ureq-tls feature rather than being hand-rolled twice on a trust boundary. The Apple/Android cdylib embedders do not enable it and pull no HTTP stack.
  • Two behaviour improvements. Over-cap response bodies are now an error rather than ureq 2's silent truncation (which used to surface as a confusing signature failure), and a pin mismatch matches ureq 3's typed Error::Rustls(..) instead of sniffing a substring that could also fire on unrelated cert errors. Conditional requests are unchanged — ureq 3 still returns 304 as Ok.
  • Two CI gates that reported success while scanning nothing. cargo audit only ever read the root lockfile; the drivers lock was already in the job's paths: filter, so edits to it triggered a run that then ignored them. And packaging/windows/pf-vkhdr-layer had no lockfile at all while shipping as a DLL in the host installer. All five Rust lockfiles are now scanned.
  • Security. event-listener 5.4.1 → 5.4.2 (RUSTSEC-2026-0221 unsoundness, invisible because cargo audit reports unsoundness as a warning); spin 0.9.8 → 0.9.9 (yanked, genuinely compiled); wayland-scanner 0.31.11 carries quick-xml 0.41, which is the exact trigger audit.toml documented for the two accepted quick-xml advisories, so both ignores are deleted rather than left permanent.
  • Dependency hygiene. 13 dependencies removed from punktfunk-host that it never referenced (the Wayland stack, xkbcommon, reis, khronos-egl, ash, usbip-sim, parking_lot, bytemuck — all left behind by the subsystem extraction), the high-level wdk crate removed from all five driver crates, five more unused deps elsewhere, and four features that were used but not declared (compiling only by feature unification) made explicit.

Build-visible for packagers and embedders

  • Building from source now needs a working C compiler (aws-lc-sys compiles AWS-LC). No CMake, Go or NASM for the default non-FIPS build.
  • A crate depending on aws-lc-rs directly must name features = ["prebuilt-nasm"], or a package selection that pulls it without rustls's aws_lc_rs feature fails on Windows.
  • Embedders building their own rustls configs should call punktfunk_core::tls::install_default_provider(), or use builder_with_provider.

Verified

Rebased onto current main before opening this.

Target What ran
macOS aarch64 core 385, pf-update-check 32, c_abi 1, clippy -D warnings, fmt
Windows x86_64 (runner, no NASM installed) host clippy at shipping-build settings, encode/win-display/gpu/frame/tray, Windows client + client-core + console-ui + session, host socket tests 6/6 and 7/7, pf-update-check 32/32, drivers build + CI's clippy line
Linux x86_64 cargo check on host, GTK client, pf-inject, pf-capture — the only place the removed Wayland deps and the wayland-scanner bump surface
Android aarch64 + armv7 libaws_lc_0_44_0_crypto.a built for both; core tls check clean
iOS aarch64 libaws_lc_0_44_0_crypto.a built; core tls,ureq-tls check clean
Supply chain cargo audit across all five lockfiles; both cargo about legs, proved non-vacuous with a negative control

Not verified — please read before merging

  • 🛑 GameStream/Moonlight TLS 1.2 interop is untested. The host's GameStream TLS path moved backends with everything else, and no real Moonlight client has connected since. Both backends offer the same standard suites so the risk is low, but this is the one path where a legacy peer meets the new backend.
  • The post-quantum handshake has never been observed on the wireprefer-post-quantum is asserted from the rustls source, not from a capture. Worth confirming over QUIC specifically, where the larger hybrid key share meets the initial-packet constraints.
  • Linux ran checks, not tests. Both Linux boxes were unreachable throughout; the Linux host test suite has not executed against this branch.
  • The Apple xcframework and Android APK were never assembled — blocked locally by a pre-existing audiopus_sys/CMake failure unrelated to crypto. The CI images carry a working toolchain, so these workflows should cover it.
  • punktfunk-webos (separate repo) still compiles both backends and pins core at tag v0.22.3, so nothing here reaches it until that pin moves.

Deliberately out of scope: the version-currency migrations (RustCrypto wave, skia-safe 0.99, jni 0.22, x509-parser, and friends). Two of them carry preconditions — the RustCrypto bump needs aarch64 throughput re-measured, and skia-safe must be gated on prebuilt binaries still existing for the vulkan,textlayout feature set. The safe in-compat cargo update sweep is included here.

Six commits. The first two are the substance; the rest are the cleanup they exposed. ## Why The workspace pinned `ring` everywhere because aws-lc-sys 0.41.0 failed to C-compile on the Windows CI runner. Re-tested on that runner with **aws-lc-sys 0.44.0**: the `params.c` cl.exe failure does not reproduce under MSVC 14.44, and rustls's `aws_lc_rs` feature turns on `aws-lc-rs/prebuilt-nasm`, so no NASM is needed on the box either. That unblocked post-quantum TLS. `prefer-post-quantum` now offers **X25519MLKEM768** first on every TLS 1.3 handshake (mgmt API, native control plane, QUIC) — something ring cannot do, since it has no ML-KEM. Classical curves stay in the list, so any client without ML-KEM connects exactly as before. No wire format, ABI or pairing record changes; the session AEAD is a separate mechanism and is untouched. ## ring is gone Not just displaced — **removed**. `cargo tree -i ring` is empty for macOS, Windows and Linux. Getting there needed the `ureq 2 → 3` upgrade in the same change: ureq 2 named `rustls/ring` inside its own dependency declaration where no dependent could switch it off. ureq 3 declares rustls with `default-features = false` and picks no backend. The only remaining `--target all` hit is quinn-proto's **wasm-only** dependency, a target we never build. ⚠️ Spell the ureq dependency `features = ["rustls-no-provider", "rustls-webpki-roots"]`. ureq 3's convenience `rustls` feature expands to include `_ring` and would quietly restore the second backend. ## Notable pieces - **A shared pinning connector.** ureq 3's `TlsConfig` has no hook for a custom `ServerCertVerifier`, so certificate pinning needed a custom `Connector`. That glue lives once in `punktfunk-core` behind an off-by-default **`ureq-tls`** feature rather than being hand-rolled twice on a trust boundary. The Apple/Android cdylib embedders do not enable it and pull no HTTP stack. - **Two behaviour improvements.** Over-cap response bodies are now an error rather than ureq 2's silent truncation (which used to surface as a confusing signature failure), and a pin mismatch matches ureq 3's typed `Error::Rustls(..)` instead of sniffing a substring that could also fire on unrelated cert errors. Conditional requests are unchanged — ureq 3 still returns 304 as `Ok`. - **Two CI gates that reported success while scanning nothing.** `cargo audit` only ever read the root lockfile; the drivers lock was already in the job's `paths:` filter, so edits to it triggered a run that then ignored them. And `packaging/windows/pf-vkhdr-layer` had no lockfile at all while shipping as a DLL in the host installer. All five Rust lockfiles are now scanned. - **Security.** `event-listener` 5.4.1 → 5.4.2 (RUSTSEC-2026-0221 unsoundness, invisible because cargo audit reports unsoundness as a warning); `spin` 0.9.8 → 0.9.9 (yanked, genuinely compiled); `wayland-scanner` 0.31.11 carries quick-xml 0.41, which is the exact trigger `audit.toml` documented for the two accepted quick-xml advisories, so both ignores are deleted rather than left permanent. - **Dependency hygiene.** 13 dependencies removed from `punktfunk-host` that it never referenced (the Wayland stack, xkbcommon, reis, khronos-egl, ash, usbip-sim, parking_lot, bytemuck — all left behind by the subsystem extraction), the high-level `wdk` crate removed from all five driver crates, five more unused deps elsewhere, and four features that were used but not declared (compiling only by feature unification) made explicit. ## Build-visible for packagers and embedders - Building from source now needs a **working C compiler** (aws-lc-sys compiles AWS-LC). No CMake, Go or NASM for the default non-FIPS build. - A crate depending on `aws-lc-rs` *directly* must name `features = ["prebuilt-nasm"]`, or a package selection that pulls it without rustls's `aws_lc_rs` feature fails on Windows. - Embedders building their own rustls configs should call `punktfunk_core::tls::install_default_provider()`, or use `builder_with_provider`. ## Verified Rebased onto current `main` before opening this. | Target | What ran | |---|---| | macOS aarch64 | core 385, pf-update-check 32, c_abi 1, clippy `-D warnings`, fmt | | Windows x86_64 (runner, **no NASM installed**) | host clippy at shipping-build settings, encode/win-display/gpu/frame/tray, Windows client + client-core + console-ui + session, host socket tests 6/6 and 7/7, pf-update-check 32/32, drivers build + CI's clippy line | | Linux x86_64 | `cargo check` on host, GTK client, pf-inject, pf-capture — the only place the removed Wayland deps and the wayland-scanner bump surface | | Android aarch64 + armv7 | `libaws_lc_0_44_0_crypto.a` built for both; core `tls` check clean | | iOS aarch64 | `libaws_lc_0_44_0_crypto.a` built; core `tls,ureq-tls` check clean | | Supply chain | `cargo audit` across all five lockfiles; both `cargo about` legs, proved non-vacuous with a negative control | ## Not verified — please read before merging - 🛑 **GameStream/Moonlight TLS 1.2 interop is untested.** The host's GameStream TLS path moved backends with everything else, and no real Moonlight client has connected since. Both backends offer the same standard suites so the risk is low, but this is the one path where a legacy peer meets the new backend. - **The post-quantum handshake has never been observed on the wire** — `prefer-post-quantum` is asserted from the rustls source, not from a capture. Worth confirming over QUIC specifically, where the larger hybrid key share meets the initial-packet constraints. - **Linux ran checks, not tests.** Both Linux boxes were unreachable throughout; the Linux host test suite has not executed against this branch. - **The Apple xcframework and Android APK were never assembled** — blocked locally by a pre-existing `audiopus_sys`/CMake failure unrelated to crypto. The CI images carry a working toolchain, so these workflows should cover it. - `punktfunk-webos` (separate repo) still compiles both backends and pins core at tag `v0.22.3`, so nothing here reaches it until that pin moves. Deliberately out of scope: the version-currency migrations (RustCrypto wave, skia-safe 0.99, jni 0.22, x509-parser, and friends). Two of them carry preconditions — the RustCrypto bump needs aarch64 throughput re-measured, and skia-safe must be gated on prebuilt binaries still existing for the `vulkan,textlayout` feature set. The safe in-compat `cargo update` sweep is included here.
enricobuehler added 6 commits 2026-08-13 10:43:03 +00:00
The workspace pinned `ring` everywhere because aws-lc-sys 0.41.0 failed to C-compile
on the Windows CI runner. Re-tested on that runner (.133) with aws-lc-sys 0.44.0: the
`params.c` cl.exe failure does not reproduce under MSVC 14.44, and rustls's `aws_lc_rs`
feature turns on `aws-lc-rs/prebuilt-nasm`, so no NASM is needed on the box either.

That unblocks post-quantum TLS: `prefer-post-quantum` offers X25519MLKEM768 first on
every TLS 1.3 handshake (mgmt API, native control plane, QUIC), which ring cannot do —
it has no ML-KEM. Classical curves stay in the list, so older clients still connect.

rustls, quinn, rcgen and tokio-rustls each select a backend independently, so all four
had to move together; a single dissenter pulls a second crypto stack in via feature
unification. The direct `ring` users (ed25519 in pf-update-check, SHA-256 in the Windows
updater) moved to aws-lc-rs, whose API is ring-compatible.

`ring` does NOT leave the tree: ureq 2 names `features = ["ring"]` in its own rustls
dependency line and cargo features are additive, so no dependent can switch it off. Two
backends compiled in means rustls refuses to infer one, and anything built via
`ClientConfig::builder()` panics instead of picking — which is what ureq's default agent
does on its first HTTPS request. `tls::install_default_provider()` makes the choice
explicit; it runs at each binary's entry point and defensively in pf-client-core, which
several binaries link. Dropping ring entirely needs the ureq 2 -> 3 upgrade (36 call
sites), deliberately left out of this change.

Verified on macOS: pf-update-check 32, punktfunk-core 385, c_abi 1 (the last with
LIBRARY_PATH=/opt/homebrew/opt/opus/lib) — aws-lc-sys links into the C ABI harness, so
the Swift/Kotlin embedders keep working. cargo fmt --all --check clean.
The aws-lc-rs move left `ring` compiled in for one reason: ureq 2 names
`features = ["ring", ...]` inside its own `[dependencies.rustls]` block, and cargo
features are additive, so no dependent could switch it off. ureq 3 declares rustls
with `default-features = false` and selects no backend, which finally makes the
choice ours.

`cargo tree -i ring` is now empty for macOS, Windows and Linux. The one remaining
hit under `--target all` is quinn-proto's wasm-only dependency
(`cfg(all(target_family = "wasm", target_os = "unknown"))`), a target we never build.

⚠ The dependency must be spelled `features = ["rustls-no-provider",
"rustls-webpki-roots"]`. ureq 3's convenience `rustls` feature expands to include
`_ring` — the same shape of trap as rustls's own `features = ["ring"]`, and the
reason punktfunk-webos still carries both backends today.

Ported 9 files. The two pinning call sites (the desktop client's library fetch and
the tray's status poll) needed a custom ureq Connector, because ureq 3's `TlsConfig`
exposes roots/client-cert/off-switch but no hook for a custom ServerCertVerifier.
That glue lives once in punktfunk-core behind a new off-by-default `ureq-tls`
feature rather than being hand-rolled twice on a trust boundary; the Apple/Android
cdylib embedders do not enable it and pull no HTTP stack. The connector is modelled
on ureq's own crate-private RustlsConnector and is transport glue only.

Two behaviour changes worth noting, both improvements:
- Body caps are enforced by the reader, so an over-cap response is now an Err rather
  than ureq 2's silent truncation — which used to surface as a signature failure
  pointing at the wrong thing.
- A pin mismatch matches ureq 3's typed `Error::Rustls(..)` instead of sniffing a
  substring out of a transport message, which could also fire on unrelated cert
  errors.
Conditional requests are unchanged: 304 still arrives as Ok (only 4xx/5xx are Err),
which the existing `ureq_returns_304_as_ok` socket test still pins.

Also removed four now-dead `std::io::Read` imports. One of them (plugin_launch) is
still needed by its test module, so it moved there rather than being deleted: host
CI lints without `--all-targets`, so a top-level import used only under cfg(test)
fails the shipping build.

Verified on macOS: punktfunk-core (quic + ureq-tls) checks clean, pf-update-check
32/32, cargo fmt clean.
`about.toml` carried `OpenSSL` in the global accepted list and a `[ring]` per-crate
acceptance, both there solely because ring's licence is an AND that includes the
OpenSSL terms. The ureq 2 -> 3 upgrade removed ring from every target we build, and
aws-lc-sys 0.44's SPDX (ISC AND (Apache-2.0 OR ISC) AND Apache-2.0 AND MIT AND
BSD-3-Clause AND ... MIT-0) carries no OpenSSL clause, so neither entry has anything
left to permit.

Note about.toml sets no `targets`, so cargo-about still walks quinn-proto's wasm-only
ring edge — removing the exception is safe not because ring is invisible but because
ring 0.17.14 declares `Apache-2.0 AND ISC`, and both are globally accepted already.

Verified by running the gate itself, both legs of what audit.yml runs:
  cargo about generate about.hbs --fail                                    -> 0
  cargo about generate -m packaging/windows/drivers/Cargo.toml -c ...      -> 0
and proved non-vacuous with a negative control: dropping "ISC" from the accepted
list makes the first leg exit 1.

THIRD-PARTY-NOTICES regenerated: 601 -> 580 crates (ureq 3 pulls a smaller tree than
ureq 2 + ring), now listing ureq 3.4.0.
Both failures found running the store + plugin-launch tests on the Windows runner
after the ureq 3 port. Neither is a production defect — the request/response round
trip and the 304 semantics both hold — but both tests were resting on assumptions
that ureq 2 happened to tolerate.

catalog::ureq_returns_304_as_ok: the stub answered without ever reading the request.
Closing a socket that still holds unread received data makes Windows send an RST
rather than a FIN, which discards the response already written, so the client saw a
transport error (os error 10053) instead of the 304 the test exists to pin. The stub
now drains the request first. The pinned behaviour is unchanged and still true:
ureq 3 turns only `is_client_error() || is_server_error()` into Err, so 304 arrives
as Ok exactly as before.

plugin_launch::asks_the_registered_plugin_and_takes_its_answer: hardcoded a cwd of
`/opt/emu`, which has no drive letter and is therefore NOT `Path::is_absolute` on
Windows, so `validate_reply` refused the recipe. This test could never have passed
on Windows, with either ureq version — its sibling
`a_working_directory_must_be_absolute` already had the `cfg!(windows)` split and this
one was simply missed. Confirmed by diagnostic before touching it: the body came back
over ureq 3 byte-perfect, so everything up to validation was working.
Acting on the 2026-08-13 dependency sweep. Every claim below was re-verified against
the tree before acting on it (greps carry a positive control; the advisories were
re-checked with cargo audit 0.22.2).

SECURITY
- event-listener 5.4.1 -> 5.4.2 (RUSTSEC-2026-0221, unsound Send/Sync on StackSlot;
  reaches the tray via zbus and the host via ashpd). This sat unnoticed because
  `cargo audit` reports unsoundness as a WARNING and the job fails only on
  vulnerabilities — audit.toml now says so out loud.
- spin 0.9.8 -> 0.9.9. 0.9.8 is YANKED and was genuinely compiled (flume via mdns-sd
  and relm4, plus lazy_static).
- wayland-scanner 0.31.10 -> 0.31.11, which moves quick-xml 0.39 -> 0.41. That is the
  exact trigger audit.toml documented for RUSTSEC-2026-0194/0195, so both ignores are
  deleted rather than left as permanent exceptions. Only RUSTSEC-2023-0071 (rsa
  Marvin, still unfixed upstream) remains.
- Corrected audit.toml's claim that `paste` arrives "via utoipa-axum": rav1d pulls it
  too, so every client has it through the decode path and dropping utoipa-axum would
  not have cleared it.

TWO CI GATES THAT SCANNED NOTHING
- `cargo audit` only ever reads the ROOT Cargo.lock. The drivers lock was already in
  this job's `paths:` filter, so edits to it triggered a run that then ignored them.
  All four secondary workspaces now get an explicit `--file` (verified: clean, bar the
  known `paste` warning in drivers).
- packaging/windows/pf-vkhdr-layer had NO lockfile at all while shipping as a DLL in
  the host installer, so every build resolved fresh and neither cargo-audit nor
  cargo-about ever saw it. Lockfile generated and committed, and added to `paths:`.

UNUSED / DUPLICATE DECLARATIONS
- punktfunk-host: removed 13 dependencies it never references — the Wayland stack
  (client, protocols{,-wlr,-misc}, scanner, backend), xkbcommon, reis, khronos-egl,
  ash, usbip-sim, parking_lot, bytemuck. The code moved to pf-inject and pf-zerocopy
  in the subsystem extraction and those crates declare them; only the manifest entries
  and their now-false comments stayed. Also dropped four redundant re-declarations
  (tokio/serde_json/futures-util in the Linux block, tower in dev-deps).
- Removed genuinely unused: bytes (punktfunk-core), anyhow (pf-win-display),
  tracing (clients/cli), anyhow (clients/session), serde (clients/windows).
- Removed the high-level `wdk` crate from all five driver crates and the drivers
  workspace: none of them ever referenced `wdk::` (62 `wdk_sys::` uses; pf-umdf-util
  is a full WDF crate that never declared it). `tracing`/`tracing-subscriber` remain
  in that lock afterwards but ONLY as wdk-sys build-dependencies, not in the DLLs.
- pf-win-display took punktfunk-core with `quic` for one type (`Mode`) that lives in
  the ungated `config` module; now `default-features = false`, which keeps
  quinn/tokio/rcgen/opus out of a leaf crate's declared closure.
- pf-encode declared the windows-rs feature `Wdk_Graphics_Direct3D` for a call that
  lives in pf-frame and is resolved via GetProcAddress on gdi32.

LATENT BREAKAGE (compiled only by feature unification)
- pf-inject uses `tokio::select!` without declaring `macros` (borrowed from
  punktfunk-core's quic feature); pf-capture uses `tokio::sync::oneshot` without
  declaring `sync` (borrowed from ashpd->zbus); pf-client-core uses the `minwindef`
  and `winnt` windows-rs headers without declaring them (borrowed from
  clients/windows). Each now declares what it uses, so an unrelated crate changing its
  features cannot break them.
- pf-console-ui took pf-client-core WITHOUT `default-features = false`, unlike every
  other consumer. That default is `pyrowave`, which compiles the vendored PyroWave C++
  — "fatal on Windows ARM64". Only safe today because the ARM64 leg passes
  --no-default-features (which also drops `ui`).

CORRECTED A FALSE INVARIANT
- clients/windows claimed "the workspace builds ONE windows-rs". It does not: wasapi
  pulls the crates.io windows 0.62.2 beside the git-rev copy. The invariant that DOES
  hold is narrower (reactor and that crate share one rev, which is what makes the
  IDXGISwapChain1 hand-off type-check). Comment rewritten, with a warning against
  "fixing" it via a blanket [patch.crates-io] — this rev uses header-named features
  while a dozen other manifests use the old Win32_* namespace ones.

Plus the safe in-compat `cargo update` sweep (no manifest edits).

Verified on macOS: punktfunk-core 385, pf-update-check 32, c_abi 1 (with
LIBRARY_PATH=/opt/homebrew/opt/opus/lib), cargo audit clean bar the two known
unmaintained warnings. Linux and Windows legs follow.
chore(deps): regenerate notices + make audit.yml's header match what it now scans
windows-drivers / driver-build (pull_request) Successful in 1m57s
windows-drivers / probe-and-proto (pull_request) Successful in 35s
apple / swift (pull_request) Successful in 2m29s
apple / screenshots (pull_request) Skipped
ci / bun-nix (pull_request) Successful in 52s
ci / rust-arm64 (pull_request) Failing after 3m22s
ci / web (pull_request) Successful in 3m47s
ci / docs-site (pull_request) Successful in 5m10s
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 7m21s
windows / build (x86_64-pc-windows-msvc) (pull_request) Failing after 24s
ci / rust (pull_request) Failing after 11m30s
nix / flake (pull_request) Failing after 30m26s
android / android (pull_request) Successful in 13m2s
d0a7b262d2
THIRD-PARTY-NOTICES regenerated after the dependency changes (582 crates).

audit.yml's header claimed to cover "EVERY dependency tree the project ships"; it now
actually does, so the note spells out that each Rust lockfile needs its own `--file` —
a bare `cargo audit` reads only the root one, which is how the drivers lock stayed
unscanned while already sitting in this job's `paths:` filter. Also corrected "BOTH
Rust workspaces" for the licence gate, which covers the host + driver workspaces.

Both cargo-about legs re-run after the dependency removals: RC=0.
enricobuehler merged commit c95db8eebc into main 2026-08-13 10:43:38 +00:00
enricobuehler deleted branch worktree-aws-lc-rs-migration 2026-08-13 10:43:47 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/punktfunk#192