Hi Neels,
not sure what kind of answer you expect here, probably just some sort of poll since most of the "rant" was already provided :)
As usual, I'd go for opaque. Since opaque means the implementation can be changed, then it doesn't matter much whether you want to use dynamic or static. It can be a mix of both, use whatever fits more in each place. But I'd definitely make the lib allocate the structs and have them as opaque pointers.
Regarding "benefits of iterating over llist", you can simply have an API return the llist pointer and have some API to get the object from the llist per-item pointer which is internally implemented by means of container_of() or a cast or similar.
Regards, Pau