The key issue was the requirement that a Flow has both a Sink (to process the incoming bytes) and a Source. For this use case the Source does not generate any bytes, but must still be specified.
Source.empty would appear to satisfy the requirement, but it immediately completes the Flow. The above stackoverflow suggests using
Source(List(ByteString.empty)), but that also completes the Flow.
We need an incomplete Source, which is most easily achieved using
Source.maybe, which never completes and explicitly specifies that no data will write to the Flow.
The core code is then
Promise[Option[Nothing]] provides a hook to cleanly terminate the Flow, using
Future[Done] provides a hook to determine the Flow has terminated, e.g.
Await.ready(result._2, 100 seconds).