Planet Grep

Planet'ing Belgian FLOSS people

Planet Grep is maintained by Wouter Verhelst. All times are in UTC.

July 19, 2021

It’s been a long time since I last looked for a job myself. At job[-1] (7 years) and job[-2] (2 years), the employers contacted me while I was already working somewhere else, and at job[-3] I worked for 5 years, so all added up, that makes more than 14 years since I last did anything like this.

Job sites

I started with creating or updating a profile on a couple of job sites:

There are a couple more job sites that I know of but haven’t done anything with. Please leave a comment if you think any of them offer benefits over those listed above.

  • Viadeo (mostly French, so probably less useful)
  • Xing (I think they are mostly German-based)
  • StepStone
  • Facebook Job Search (I can’t imagine that any employer on Facebook Job Search wouldn’t also be on LinkedIn, but maybe I’ll try it to see if the search works better there)

I have also updated my CV and I’ve put it online:

A torrent of messages

But then — I think — I made a mistake. The weather was nice, I wanted to be outdoors, trying to unwind a bit from the unusual times of the past months, and I disconnected.

Meanwhile the messages started pouring in, via email, LinkedIn (messages and connection requests), and occasionally a phone call from an unknown number. First just a few, then dozens, and just a few weeks later, already a couple of hundred. Oops.

The thing is, while I was technically available, I wasn’t yet mentally available. I still had to disconnect from the previous job, where I worked for more than 7 years, and I needed to think about what I really want to do next. Should I do something similar as before, because I already have the experience? Or should I try to find something that truly sparks joy? More on that later.


Anyway, I had to come up with some strategies to deal with these high volumes of communication. First of all, not to get completely crazy, I defined a schedule, because otherwise I’d be responding to messages 24/7. There are other important activities too, like actively browsing through the job listings on various sites, or keeping up to date with current technology, or reaching out to my network, or having a social media presence (like this blog), or, you know, being social, having hobbies, and life in general.

One thing I noticed right away in many messages, is that people ask me for a CV — even though my LinkedIn profile is current. But I get it. And a separate document doesn’t confine me to the format of one specific website, and it helps me to emphasize what I think is important. So I made sure that my CV is available on an easy to reach URL:

Then I made two short template messages, one in Dutch and one in English, to thank people for contacting me, where they can find my CV, and — for the LinkedIn people — what my email address is. That’s because I find it easier to track conversations in my mailbox. I can also give labels and flags to conversations, to help me in identifying the interesting ones.


On LinkedIn, it went like this:

  • Read message.
  • Copy contact details to a spreadsheet.
  • Copy/paste the Dutch or English template message, so that they have my CV and email address.
  • If their message was really interesting(*), add an additional message that I’ll get back to them, and close the conversation. That’ll move it to the top of the message queue.
  • If their message wasn’t interesting or unclear, archive the conversation. If they come back after reading my CV, they’ll either end up in my mailbox, or if they use LinkedIn again, they’ll pop back up at the top of the message queue. But I don’t want to worry about the kind of recruiters that are just “fishing”.

This way I reduced my LinkedIn messages from about 150 to about 20. That’s 20 job offers that I want to give a second, more detailed look. Wow. And that’s just LinkedIn.

(*) What makes a message interesting?

  • It’s relevant.
  • The job isn’t too far to commute.
  • They clearly read my LinkedIn profile.
  • There is a detailed job description.
  • My gut feeling.


Email is another huge source of messages. Fortunately Gmail gives me some tools there to help me. One of the first things I had to do, was to clean out my mailbox. Seriously. It was a dumpster fire. My Inbox had thousands (!) of unread emails. I used rules, filters, deleted emails (I think I deleted more than 100 000 emails), archived emails, and unsubscribed from many, many newsletters that had accumulated over the years. I am now at the point where there are currently 3 emails in my Primary Inbox, all 3 of them actionable items that I expect to finish in the next two weeks, and then those emails will be archived too.

Then, for any recent(ish) email about job offers, I labeled them as “jobhunt” and moved them to the Updates Inbox. That’s the Inbox that Gmail already used automatically for most of these emails, so that was convenient. (For those who don’t know: Gmail has 5 inboxes: Primary, Social, Promotions, Updates and Forums.) At this moment, there are 326 emails labeled “jobhunt”. I’m sure that there will be some overlap with LinkedIn, but still. That’s a lot.

I’ll be using Gmail’s stars, “Important” flag, and archive, to classify emails. Again, just like with LinkedIn, if an email isn’t really interesting at first glance, it’ll go to the archive after I’ve send them a short default message.


I get it. Really, I do. For some of you, talking on the phone comes naturally, you do it all the time, and it’s your preferred way of communication. For you it’s the fastest way to do your job.

But for me it’s a tough one. I wouldn’t say that I have outright phone phobia, but phone really is my least favorite communication channel. I need some time to charge myself up for a planned phone call, and afterwards I need some time to process it. Even if it is just writing down some notes about what was discussed and looking up some stuff.

It also has to do with how I process information. Speech is in one direction, always forward, and always at the same speed. You can’t rewind speech. But that’s not how my brain works. I want to read something again and again, or skip a paragraph, or first jump to a conclusion and then jump back to see how we got to that conclusion. Sometimes my thoughts go faster than how I express them, and putting it in writing helps me to see the gaps.

Calls out of the blue? I prefer to avoid those. Really. Especially the ones where people just want to get to know me. In the time it takes for me to do one such phone call (and I do take them seriously), I’m able to process several emails. So I very much prefer to focus first on contacts who have something concrete and actionable.

As mentioned above, I record contact information in a spreadsheet. I then import that information into Google Contacts, so that when someone calls me, I see their name on the screen of my phone, and not just a number. That also helps me to decide to pick up the phone or let it go to voicemail. I will get back to those that go to voicemail, but it’ll just be at my own pace.

Social media presence

I’m starting to put myself a bit more out there, by engaging in conversations on LinkedIn. I have also picked up blogging again, and I’m sharing links to my posts on LinkedIn, Facebook and Twitter. Besides my Facebook profile, I also have a Facebook page, but I’m not using that fanatically, because for myself at this point I don’t see Facebook as a professional tool.

On Twitter I have two accounts: @amedee and @AmedeeVanGasse. The former is mostly for personal stuff, and is mostly in Dutch. The latter is one that I created to tweet at tech conferences, but we all know how many tech conferences there were in the last 1.5 years… 🙂 Most tweets there will be in English.


I feel like this has become a very long blog post. Maybe too long, I don’t know. Maybe I should have split it up in several parts? But for me it felt like one story I had to tell.

If any of you social media gurus out there have some opinions to share, that’s what the comment box below is for. 🙂

The post So, how is the jobhunt going? appeared first on

In my job as domain architect for "infrastructure", I often come across stakeholders that have no common understanding of what infrastructure means in an enterprise architecture. Since then, I am trying to figure out a way to easily explain it - to find a common, generic view on what infrastructure entails. If successful, I could use this common view to provide context on the many, many IT projects that are going around.

Of course, I do not want to approach this solely from my own viewpoint. There are plenty of reference architectures and frameworks out there that could assist in this. However, I still have the feeling that they are either too complex to use for non-architect stakeholders, too simple to expose the domain properly, or just don't fit the situation that I am currently faced with. And that's OK, many of these frameworks are intended for architects, and from those frameworks I can borrow insights left and right to use for a simple visualization, a landscaping of some sort.

So, let's first look at the frameworks and references out there. Some remarks though that might be important to understand the post:

  • When I use "the infrastructure domain", I reflect on how I see it. Within the company that I work for, there is some guidance on what the scope is of the infratructure domain, and that of course strongly influences how I look at "the infrastructure domain". But keep in mind that this is still somewhat organization- or company oriented. YMMV.
  • While I am lucky enough to have received the time and opportunity to learn about enterprise architecture and architecture frameworks (and even got a masters degree for it), I also learned that I know nothing. Enterprises are complex, enterprise architecture is not a single framework or approach, and the enterprise architecture landscape is continuously evolving. So it is very well possible that I am missing something (and I will gladly learn from feedback on this).

The Open Group Architecture Framework (TOGAF)

If you would ask for one common framework out there for enterprise architecture, then TOGAF is probably it. It is a very exhaustive framework that focuses on various aspects of enterprise architecture: the architecture development methodology, techniques to use, the content of an architecture view (like metamodel descriptions), and the capabilities that a mature organization should pursue.

A core part of TOGAF is the Architecture Development Method cycle, which has several phases, including phases that are close to the infrastructure domain: "Technology Architecture (D)" as well as areas of "Opportunities and Solutions (E)" and "Information Systems Architectures (C)". Infrastructure is more than 'just' technology, but the core of it does fit within the technology part.

TOGAF Cycle The ADM cycle, taken from The Open Group, TOGAF 9.2, Chapter 4

With TOGAF, you can support a full enterprise architecture view from the strategy and vision up to the smallest change and its governance. However, the key word here is support, as TOGAF will not really give you much food for simply representing the scope of infrastructure.

I'm not saying it isn't a good framework, on the contrary. Especially with ArchiMate as modeling language (also from The Open Group), using TOGAF and its meta model is a good way to facilitate a mature architecture practice and enterprise-wide view within the organization. But just like how application architecture and design requires a bit more muscle than TOGAF's metamodel supports, the same is true for infrastructure.

There are also plenty of other enterprise frameworks out there that can easily be mapped to TOGAF. Most of these focus mainly on the layering (business, information, application, technology), processes (requirement management and the like) and viewpoints (how to visualize certain insights) and, if you're fluent in TOGAF, you can easily understand these other frameworks as well. I will not be going through those in detail, but I also do not want to insinuate that they are not valid anymore if you compare them with TOGAF: TOGAF is very extensive and has a huge market adoption, but sometimes an extensive and exhaustive framework isn't what a company needs...

TOGAF Technical Reference Model / Integrated Information Infrastructure Reference Model

As TOGAF is extremely extensive, it has parts that can be used to reference or visualize infrastructure a bit better. In TOGAF 9.1, we had the TOGAF Technical Reference Model (TRM) and TOGAF Integrated Information Infrastructure Reference Model (III-RM) where you might feel that this is closer to what I am seeking (for instance, III-RM).

TOGAF III-RM Focus of the III-RM, taken from The Open Group, TOGAF 9.1, Chapter 44

While it does become a bit more tangible, TOGAF does not expand much on this reference model. Instead, it is more meant as a starting point for organizations to develop their own reference models.

Information Technology Infrastructure Library (ITIL)

ITIL 4 is another very common framework, this time owned by AXELOS Limited. The focus of ITIL is on process support, with many processes (sorry, 'practices' as they are called now) being very much close to what I consider to be part of the infrastructure domain. The practices give a good overview of 'what things to think about' when dealing with the infrastructure domain. Now, ITIL is not exclusive to the infrastructure domain, and the company that I work for currently considers many of these practices as processes that need to be tackled across all domains.

ITIL Practices ITIL 4 Practices, taken from Value Insights, The ITIL 4 Practices Overview

Still, I do want to see some of the ITIL practices reflected in the generic infrastructure view as they are often influencing the infrastructure domain and the projects within. The ITIL practices make it possible to explain that it isn't just about downloading and installing software or quickly buying an appliance.

Reference Model of Open Distributed Processing (RM-ODP)

The RM-ODP standard has a strong focus on distributed processing (hence the name), which is a big part of what the infrastructure domain is about. If we ignore the workplace environment for a bit, and focus on hosting of applications, the majority of today's hosting initiatives are on distributed platforms.

RM-ODP Five Viewpoints Five viewpoints of RM-ODP, taken from MDG Technology for ODP - UML for ODP, SparxSystems

Within RM-ODP guidance is given on how to handle requirement management, how to document the processing semantics, how to identify the components, integrations and limitations of the systems, and how to select the most appropriate technology. The intention of RM-ODP is to be as precise and formal as possible, and leave no room for interpretation. To accomplish that, RM-ODP uses an object model approach.

Unlike the more business and information system oriented frameworks, RM-ODP has a strong focus on infrastructure. Its viewpoints include engineering, computational and technology for instance. The challenge that rises here however is that it sticks to the more engineering oriented abstractions, which make perfect sense for people and stakeholders involved in the infrastructure domain, but is often Chinese for others.

Personally, I consider RM-ODP to be a domain-specific standard strongly associated with the infrastructure domain.

Department of Defense Architecture Framework (DoDAF)

DoDAF is an architecture framework that has a strong focus on the definition and visualization of the different viewpoints. It is less tangible than RM-ODP, instead focusing on view definitions: what and how should something be presented to the stakeholders. The intention of DoDAF is that organizations develop and use an enterprise architecture that supports and uses the viewpoints that DoDAF prescribes.

DoDAF viewpoints DoDAF viewpoints, taken from "What is DoDAF Framework", Visual Paradigm

Unlike broad scale architecture frameworks that look at an enterprise in its entirety, my impression is that DoDAF is more towards product architecture. That makes DoDAF more interesting for solution architectures, which often require to be more detailed and thus hit closer to home when looking at the infrastructure domain. However, it is not something I can 'borrow' from to easily explain what infrastructure is about.

NATO's Architecture Framework (NAF) seems to play witin the same realm.

Sherwood Applied Business Security Architecture (SABSA)

The SABSA framework and methodology has a strong security focus, but covers the requirements from full enterprise view up to the solutions themselves. One of the benefits of SABSA is inherent to this security-orientation: you really need to know and understand how things work before you can show that they are secure. Hence, SABSA is a quite complete framework and methodology.

SABSA Metamodel SABSA metamodel, taken from "A Brief History of SABSA: Part 1",

An important focus area in SABSA is the integration between services, which is something I am frequently confronted with at work. Yet unlike the more solution driven frameworks, SABSA retains its business-oriented top-down approach, which places it alongside the TOGAF one in my opinion. Moreover, we can apply TOGAF's development method while using SABSA to receive more direct requirements and design focus.

Its risk and enabler orientation offers a help to not only explain how things are set up, but also why. Especially in the sector I am currently active in (financial sector) having a risk-based, security-conscious approach is a good fit. The supporting list of attributes, metrics, security services, etc. allow for defining more complete architectures.

Control Objectives for IT (CObIT)

In a similar area as ITIL, the CObIT framework focuses less on a complete enterprise architecture framework and more on processes, process maturity, and alignment of the processes within the organization. I am personally a fan of CObIT as it is a more tangible framework, with more clear deliverables and requirements, compared to others. But like with most frameworks, it has received numerous updates to fit in continuously growing environments and cultures which makes it heavy to use.

CObIT Core Model The CObIT 2019 Core Model, taken from "Using CObIT 2019 to plan and execute an organization transformation strategy,"

The framework is less about the content of infrastructure and technology, and more about how to assess, develop, build, maintain, operate and control whatever service is taken up. However, there are references to infrastructure (especially when dealing with non-functionals) or controls that are actively situated in infrastructure (such as backup/restore, disaster recovery, etc.)

IT for IT (IT4IT)

The Open Group has a similar framework like CObIT, called IT4IT. It does have a reference architecture view that attempts to group certain deliverables/services together to create a holistic view on what IT should offer. But unlike the larger enterprise frameworks it focuses strongly on service delivery and its dependencies.

IT4IT Reference Architecture IT4IT Reference Architecture, taken from The Open Group IT4IT Forum

Within the IT4IT reference architecture, a grouping is attempted that maps on a value stream, starting from a strategy over the deployment up to the detection and correction facilities. This value stream orientation is common across frameworks, but often feels like the value is always to "add more", "deliver more". In my opinion, rationalization exercises, decommissioning and custodianship is too much hidden. Sure, it is part of the change management processes and operational maintenance, but those are extremely valuable areas that are not expressively visible in these frameworks. Compare that to the attention that risk and security receives: while security consciousness should be included at all phases of the value stream, security is always explicitly mentioned.

Vendor-specific visualizations

Several vendors have their own visualization methodology that represents more specific requirements from the domain(s) in which those vendors are active. These are generally domain-specific visualizations, even with a vendor-specific view. Such methodologies are nice to use when dealing with specific viewpoints, but I do not believe these should be considered "architecture" frameworks. They don't deal with requirement management, strategy alignment, and often lack functional and non-functional insights. Still, they are a must to know in the infrastructure areas.

If you are active in Amazon AWS for instance, then you've undoubtedly come across drawings like the one visible in "Wordpress: Best Practices on AWS". These drawings provide a deployment viewpoint that lists the main interactions between AWS services.

When you are more network oriented, then you've been immersed in Cisco's network diagrams, like the one visible in "Verizon Business Assessment for: Cisco PCI Solution for Retail". These network diagrams again focus on the deployment viewpoint of the network devices and their main interactions.

There are probably plenty more of these specific visualizations, but the two mentioned above are most visible to me currently.


There are plenty of frameworks out there to learn from, and some of these can be used to find ways of explaining what the infrastructure domain is about. However, they are often all very complete and require an architectural mindset to start from, which is not obvious when trying to convey something to outside or indirect stakeholders.

Few frameworks have a reference that is directly consumable by non-architect stakeholders. The most tangible ones seem to be related to the IT processes, but those still require an IT mindset to interpret.

July 15, 2021

Ik schrijf al meer dan twintig jaar over allerlei technische onderwerpen en ik heb daarbuiten nog heel wat andere interesses. Maar het is nog maar enkele jaren geleden dat ik in een epifanie plots de rode draad in al die onderwerpen zag: decentralisatie.

  • Ik geloof in de kracht van opensourcesoftware omdat de gebruikers daardoor niet alleen consument maar ook producent zijn.

  • Ik ben een groot voorstander van self-hosted software zodat je niet afhankelijk bent van clouddiensten van grote bedrijven.

  • Tien jaar geleden al schreef ik over Bitcoin en ik vind blockchains en andere vormen van distributed ledgers fascinerende technologie om zonder centrale controle transacties te kunnen uitvoeren.

  • In mijn strijd voor meer privacy vind ik het vooral belangrijk dat mensen controle over hun eigen data hebben, en daarom ben ik een grote fan van technologieën zoals Nextcloud, Solid en end-to-end encryptie.

  • Ik vind het belangrijk dat je zelf dingen kunt maken en programmeren, en ik ben dan ook blij dat ik deel uitmaak van de DIY/makersbeweging die dit democratiseert met de Raspberry Pi, ESP32, Arduino, 3d-printers, fablabs enzovoort.

  • Ik vind dat huishoudens zoveel mogelijk zelfvoorzienend zouden moeten zijn, niet alleen voor energie, maar ook voor voedsel.

More Equal Animals - The Subtle Art of True Democracy (Bron: Dan Larimer)

Toen ik dan in het begin van dit jaar Dan Larimers (gratis te downloaden) boek More Equal Animals - The Subtle Art of True Democracy ontdekte, las ik dit bijna in één ruk uit. Op bijna elke pagina las ik wel inzichten die verweven waren met de rode draad in mijn interesses.

In essentie is een blockchain een techniek om zonder centrale aansturing tot consensus te komen. Bij Bitcoin is dat consensus over transacties, maar volgens Larimer kun je de principes van blockchains ook op onze samenleving zelf toepassen. Een succesvolle samenleving implementeert volgens Larimer een proces dat zoveel mogelijk tot consensus leidt.

Democratie is in die ogen een proces om geschillen te beslechten met meerdere partijen en dat is iets waarin blockchains zo goed zijn. Echte democratie gaat volgens Larimer dan ook over het coördineren met andere personen terwijl je nog je persoonlijke autonomie en macht behoudt. En de ideale vorm van de samenleving bestaat dan uit gedecentraliseerde, autonome gemeenschappen op alle niveaus.

Voor PC-Active schreef ik uitgebreider over de visie die Larimer in zijn boek beschreef. Als onderwerpen zoals persoonlijke autonomie, gedecentraliseerd bestuur, een antifragiele samenleving en de gevaren van moral hazard je nauw aan het hart liggen, dan is het boek More Equal Animals een aanrader.

July 12, 2021

Quick public service announcement; Autoptimize 2.9 is almost ready to be released but given the planned release of WordPress 5.8 (July 20th) and the risk of support requests mixing up WordPress core update related issues with the Autoptimize update related issues, Autoptimize 2.9 will probably be released one week after WordPress 5.8, so on or around Tuesday 27th.

If you’re eager to use 2.9 (with better image optimization, improved JS optimization and per page/ post Autoptimize settings) you can off course download the beta here immediately.

July 10, 2021

Ik heb de titel van deze blog gewijzigd naar ‘Paepe Thoon‘, omdat dat mijn favoriete Leuvenaar is.

Récit de 3 jours de bikepacking pas toujours entre les gouttes à travers le Hainaut, le nord de la France et le Namurois

Je tiens ma formation initiale et ma philosophie du bikepacking de Thierry Crouzet, auteur du livre « Une initiation au bikepacking » (dans lequel je fais un peu de figuration) : Partir en autonomie, mais le plus léger possible, éviter les routes à tout prix,préférer l’aventure et la découverte à la performance ou à la distance.

Élève appliqué de Thierry, je me transforme en professeur pour initier mon filleul Loïc. Anecdote amusante : la différence d’âge entre Thierry et moi est la même qu’entre moi et Loïc. L’enseignement se propage, de génération en génération.

Après plusieurs virées dans les magasins de camping et une très grosse sortie de préparation de 112km, rendez-vous est pris pour notre premier trip de bikepacking sur une trace que j’ai dessinée pour traverser la province du Hainaut du Nord au sud, couper à travers la France dans la région de Givet avant de remonter le Namurois.

Jour 1 : le Hainaut sauvage, 103km, 1250d+

Nous nous retrouvons le vendredi matin sur le Ravel de Genappe. Je suis en retard : je connais tellement ce parcours que j’étais persuadé qu’il faisait 10km. Mon compteur indique déjà 15km lorsque je trouve Loïc qui piaffe d’impatience.

Le temps de me présenter sa config bikepack (il a notamment troqué le Camelbak sur le dos pour une ceinture porte-gourde) et nous voilà partis. À peine sorti des routes de Genappe et nous sommes confrontés à des chemins qui viennent de vivre deux mois de pluie quasi permanente. Cela signifie d’énormes flaques et une végétation plus qu’abondante. J’avais été témoin, sur mes sentiers habituels, de chemins se refermant complètement en trois ou quatre jours de beau temps après des semaines de pluie.

De tout côté, nous sommes entourés par les ronces, les orties. Mes bras deviennent un véritable dictionnaire des différentes formes de piqures et de lacérations. Il y’a les pointues, les griffues, celles qui se boursouflent, celles qui grattent, celles qui saignent. Loïc se marre en m’entendant hurler. Car je suis de ceux qui hurlent avant d’avoir mal, un cri rauque à mi-chemin entre banzaï et le hurlement de douleur. Loïc, lui, préfère garder son énergie et souffre en silence.

Le contournement des flaques s’avère parfois acrobatique et, moins agile que Loïc, je glisse sur un léger éperon de boue, les deux pieds et les fesses dans une énorme mare de gadoue.

Le soleil nous aide à prendre l’essorage de chaussettes à la rigolade sous la caméra amusée de Loïc qui filme. Je ne le sais pas encore, mais l’eau sera le thème central de notre épopée.

Nous dépassons enfin Fleurus pour traverser la banlieue de Charleroi par Chatelineau et Châtelet. À travers des rues peu engageantes qui serpentent entre des façades borgnes, nous suivons la trace qui s’engouffre sous un pont d’autoroute, nous conduit entre deux maisons pour nous faire déboucher soudainement sur de magnifiques sentiers à travers les champs. Comme si les habitants tenaient à cacher la beauté de leur région aux citadins et aux automobilistes.

Après des kilomètres assez plats, le dénivelé se fait brusquement sentir. Nous atteignons les bois de Loverval pour continuer parmi la région boisée contournant Nalinnes. Si les paysages sont loin d’être époustouflants, la trace est un véritable plaisir, verte, physique et nous fait déboucher dans le chouette village de Thy-le-Château.

Nous nous arrêtons pour un sandwich dans une boucherie. Le boucher nous explique sillonner la région en VTT électrique et est curieux de savoir quelle application nous utilisons pour nos itinéraires. Il note le nom « Komoot » sur un papier avant de s’offusquer lorsque je lui explique que nous nous relayons pour passer les commandes afin d’avoir toujours quelqu’un près des vélos.

« On ne vole pas à Thy-le-Château ! » nous assène-t-il avec conviction. Le sandwich est délicieux et nous continuons à travers des montées et des descentes abruptes, inondées de flaques ou de torrents. Les passages difficiles se succèdent et j’ai le malheur de murmurer que je rêve d’un kilomètre tout plat sur une nationale.

J’ai à peine terminé ma prière que mon mauvais génie m’exauce. Arrivant au pied de Walcourt, étrange village qui flanque une colline abrupte, la trace nous propose de suivre 500m d’une route nationale. Mais celle-ci se révèle incroyablement dangereuse. Une véritable autoroute ! Pour l’éviter, nous devrions remonter toute la pente que nous venons de descendre et faire une boucle de plusieurs kilomètres. Loïc propose de rouler le long de la nationale, derrière le rail de sécurité. « Ça se tente ! » me fait-il.

Nous sommes de cette manière à plusieurs mètres des véhicules et protégés par la barrière. Cependant, ce terre-plein est envahi de ronces, d’orties et des détritus balancés par les automobilistes. Les 500m dans le hurlement des camions et des voitures lancées à vive allure sont très éprouvants. Moi qui suis parfois réveillé par l’autoroute à plus de 3km de mon domicile, je me dis qu’on sous-estime complètement la pollution sonore du transport automobile.

Cette épreuve terminée, nous attaquons la dernière colline avant d’arriver aux Lacs de l’Eau d’Heure, objectif assumé pour notre première pause.

Juste avant le barrage de la Plate Taille, nous bifurquons vers une zone de balade autour du lac. Nous nous planquons dans un petit bosquet où, malgré les panneaux d’interdiction, j’enfile un maillot pour profiter d’une eau délicieuse à 19°C. Sur la rive d’en face, je pointe l’endroit où Loïc a fait son baptême de plongée en ma compagnie.

Le cuissard renfilé, je remonte sur ma selle et nous repartons. La trace nous conduit dans des petits sentiers qui longent la route du barrage. Nous arrivons sur le parking du spot de plongée où nous sommes censés retrouver la route, séparée de nous par une barrière fermée. Nous continuons un peu au hasard dans les bois avant de tomber sur le village de Cerfontaine.

Nous quittons désormais la civilisation. Plusieurs kilomètres de sentiers escarpés nous attendent. Loïc voit passer un sanglier. Je vois plusieurs biches. La région est sauvage. Deux choses inquiètent Loïc. Le risque d’orage et la question de trouver à manger. Hein chef ?

Heureusement, nous débouchons sur Mariembourg où une terrasse accueillante nous tend les bras au centre du village. Nous mangeons bercés par les cris de quelques villageois se préparant pour le match de foot du soir à grand renfort de canettes de bière.

Nous étudions la trace, occupation principale d’un bikepacker en terrasse. J’avais prévu un zigzag à proximité de Couvin pour aller découvrir le canyon « Fondry des Chiens ». Étant donné l’heure avancée, je suggère de couper à travers la réserve naturelle de Dourbes.

Nous sommes à peine sortis de Mariembourg que Loïc reconnait la gare. Nous sommes sur les terres où Roudou nous avait emmenés lors d’un mémorable week-end VTTnet en 2015.

La réserve naturelle de Dourbes est tout sauf plate. Un régal de vététiste. Un peu moins avec près de 100bornes dans les pattes. Ça fait partie du bikepacking : parler de régal pour ce qui te fait pester au moment même.

Nous arrivons sur les berges du Viroin. La trace nous fait monter vers le château de Haute-Roche, véritable nid d’aigle qui semble inaccessible. La pente est tellement abrupte qu’il faut escalader d’une main en tirant les vélos de l’autre. Loïc vient m’aider pour les derniers mètres.

Les ruines de la tour moyenâgeuse se dressent devant nous. Après cet effort, Loïc décide qu’il a bien mérité de contempler la vue. Il contourne la tour par un étroit sentier qui nécessite même un mètre d’escalade sur le mur médiéval. J’hésite à le suivre puis me laisse gagner par son enthousiasme.

Loïc a découvert une terrasse qui surplombe la vallée de manière majestueuse. Derrière nous, la tour, devant le vide et la vue. C’est magnifique.

Loïc a soudain une idée : » Et si on plantait la tente ici ? »

J’hésite. Nous sommes sur une propriété privée. L’à-pic n’est pas loin. Les sardines ne se planteront peut-être pas dans la terre fine de la terrasse. Mais je vois les yeux de Loïc pétiller. Je propose de tester de planter une sardine pour voir si c’est faisable. Loïc propose une manière de disposer les deux tentes sur la terrasse de manière à être le plus éloigné possible du trou. Nous finissons par retourner aux vélos, décrocher tous les sacs pour les amener sur notre terrasse. Il reste à faire passer les vélos eux-mêmes par le même chemin. C’est acrobatique, mais nous y arrivons et bénéficions d’un coucher de soleil sublime alors que nous montons nos tentes.

J’utilise un peu d’eau de mon Camelbak pour improviser une douche rapide. Je tends mes fesses à toute la vallée. Vue pour vue, paysage pour paysage.

De la vallée, les faibles cris nous informent que les Belges perdent le match de foot. Nous nous couchons à l’heure où les multiples camps scouts qui parsèment la vallée décident de se lancer dans des chants qui relèvent plus du cri permanent. Au bruit du matelas pneumatique, je devine que Loïc se retourne et ne trouve pas le sommeil.

Jour 2 : la brousse française, 80km, 1500d+

Les supporters et les scouts ont à peine achevé leur tintamarre que les coqs de la vallée prennent le relais. Il n’est pas encore 7h que j’émerge de ma tente. Loïc a très mal dormi et est abasourdi par l’humidité qui dégouline dans sa tente. J’espérais que l’altitude nous protégerait de l’humidité du Viroin, il n’empêche que tout est trempé. Mon Camelbak, mal fermé, s’est vidé dans mon sac de cadre qui, parfaitement étanche, m’offre le premier vélo avec piscine intérieure, comble du luxe.

Heureusement, il fait relativement beau. J’avais prévenu Loïc de compter une grosse heure pour le remballage des affaires, surtout la première fois. Le fait de devoir repasser les vélos en sens inverse le long de la tour complique encore un peu plus la tâche. Nous pratiquons la philosophie « no trace » et Loïc en profite même pour ramasser des vieilles canettes. Au final, il nous faut plus d’1h30 pour être enfin prêts à pédaler. Nous traversons les bois, descendons le long d’une route où nous aidons un scout flamand un peu perdu à s’orienter avant d’accomplir la courte, mais superbe escalade des canons de Vierves. Escalade que nous avions accomplie en 2015 avec Roudou et sa bande sans que j’en aie le moindre souvenir. En pensée, Loïc et moi envoyons nos amitiés et nos souvenirs aux copains de VTTnet.

La trace nous fait ensuite longer la route par un single escarpé avant de nous conduire à Treignes où nous déjeunons sur le parking d’un Louis Delhaize. Je constate que la trace fait un gros détour pour éviter 3km de route et nous fais escalader un énorme mamelon pour en redescendre un peu plus loin en France. La route étant peu fréquentée, je propose d’avancer par la route pour gagner du temps. L’avenir devait révéler ce choix fort judicieux.

Une fois en France, je m’arrange pour repiquer vers la trace. Nous faisons une belle escalade en direction du fort romain du Mont Vireux. Comme le fort en lui-même est au bout d’un long cul-de-sac, nous décidons de ne pas le visiter et de descendre immédiatement sur Vireux où nous traversons la Meuse.

Nous escaladons la ville. Je m’arrête à la dernière maison avant la forêt pour me ravitailler en eau auprès d’habitants absolument charmants et un peu déçus de ne pas pouvoir faire plus pour moi que de me donner simplement de l’eau.

Nous quittons désormais la civilisation pour nous enfoncer dans les plateaux au sud de Givet. Les chemins forestiers sont magnifiques, en montée permanente. Quelques panneaux indiquent une propriété privée. Nous croisons cependant un 4×4 dont le conducteur nous fait un signe amical qui me rassure sur le fait que le chemin soit public. Mais, au détour d’un sentier, une grande maison se dresse, absurde en un endroit aussi reculé. La trace la contourne et nous fait arriver devant une barrière un peu bringuebalante. Je me dis que nous sommes sur le terrain de la maison, qu’il faut en sortir. Nous passons donc la barrière, prenant soin de la refermer, et continuons une escalade splendide et très physique.

Au détour d’un tournant, je tombe sur une harde de sangliers. Plusieurs adultes protègent une quinzaine de marcassins. Les adultes hésitent en me voyant arriver. L’un me fait face avant de changer d’avis et emmener toute la troupe dans la forêt où je les vois détaler. Loïc arrive un peu après et nous continuons pour tomber sur une harde d’un autre type : des humains. Un patriarche semble faire découvrir le domaine à quelques adultes et une flopée d’enfants autour d’un pick-up. Il nous arrête d’un air autoritaire et nous demande ce que nous faisons sur cette propriété privée.

Je lui explique ma méprise à la barrière et la trace GPS en toute sincérité. Il accepte avec bonne grâce mes explications et tente de nous indiquer un chemin qui nous conviendrait. Je promets de tenter de marquer le chemin comme privé sur Komoot (sans réfléchir au fait que c’est en fait sur OpenStreetMap qu’il faut le marquer et que je n’ai pas encore réussi à le faire). Finalement, il nous indique la barrière la plus proche pour sortir du domaine qui se révèle être exactement le chemin indiqué par notre trace. Nous recroisons la harde de sangliers et de marcassins.

Nous escaladons la barrière en remarquant l’immensité de la propriété privée que nous avons traversée et sommes enfin sur un chemin public qui continue sur un plateau avant de foncer vers le creux qui nous sépare de la Pointe de Givet, Pointe que nous devons escalader à travers un single beaucoup trop humide et trop gras pour mes pneus. J’en suis réduit à pousser mon vélo en regardant Loïc escalader comme un chamois. Au cours du périple, les descentes et les montées trop grasses seront souvent à la limite du petit torrent de montagne. Une nouvelle discipline est née : le bikepack-canyoning.

Le sommet nous accueille sous forme de vastes plaines de hautes graminées où le chemin semble se perdre. La trace descend dans une gorge sensée déboucher sur la banlieue est de Givet. Mais la zone a été récemment déboisée. Nous descendons au milieu des cadavres de troncs et de branches dans un paysage d’apocalypse sylvestre. La zone déboisée s’arrête nette face à un mur infranchissable de ronces et de buissons. La route n’est qu’à 200m d’après le GPS, mais ces 200m semblent infranchissables. Nous remontons péniblement à travers les bois pour tenter de trouver un contournement.

Loïc fait remarquer que le paysage ressemble à une savane africaine. Nous roulons à l’aveuglette. Parfois, un souvenir de chemin semble nous indiquer une direction. Nous regagnons l’abri de quelques arbres avant de déboucher sur une vaste prairie de très hautes graminées, herbes et fleurs. Comme nous sommes beaucoup trop à l’ouest, je propose de piquer vers l’est. Une légère éclaircie dans un taillis nous permet de nous faufiler dans une pente boisée que je dévale sur les fesses, Loïc sur les pédales. Le pied de cette raide descente nous fait déboucher sur un champ de blé gigantesque. Du blé à perte de vue et aucun chemin, aucun dégagement. Nous nous résignons à la traverser en suivant des traces de tracteur afin de ne pas saccager les cultures. Les traces nous permettent de traverser le champ en largeur avant de s’éloigner vers l’ouest où la limite du champ n’est même pas visible.

À travers une haie d’aubépines particulièrement touffue, nous apercevons une seconde prairie. Avec force hurlements de douleur et de rage, nous faisons traverser la haie à nos vélos avant de suivre le même passage. De la prairie de pâturage, il devient facile de regagner un chemin desservant l’arrière des jardins de quelques maisons.

Après plusieurs heures de galère et très peu de kilomètres parcourus, nous regagnons enfin la civilisation. Loïc vient de faire son baptême de cet élément essentiel du bikepacking : l’azimut improvisé (autrement connu sous le nom de « On est complètement paumé ! »).

On pourrait croire qu’avec les GPS et la cartographie moderne, se perdre est devenu impossible. Mais la réalité changeante et vivante de la nature s’accommode mal avec la fixité d’une carte. L’état d’esprit du bikepacker passera rapidement du « Trouver le chemin le plus engageant pour arriver à destination » à « Trouver un chemin pour arriver à destination » à « Trouver un chemin praticable » pour finir par un « Mon royaume pour trouver n’importe quoi qui me permet tout simplement de passer ». Après des passages ardus dans les ronces ou les aubépines, après avoir dévalé des pentes particulièrement raides, l’idée de faire demi-tour n’est même plus envisageable. Il faut lutter pour avancer, pour survivre.

Un aphorisme me vient spontanément aux lèvres : « L’aventure commence lorsque tu as envie qu’elle s’arrête ».

Nous pénétrons alors dans Givet par l’ouest alors que j’avais prévu d’éviter la ville. Nous avons faim, nous sommes fatigués et nous n’avons fait qu’une vingtaine de kilomètres. Loïc a du mal de se rendre compte du temps perdu.

Sur une placette un peu glauque où se montent quelques maigres attractions foraines, nous enfilons un sandwich. Pour ma part, un sandwich que je viens d’acheter, mais pour Loïc, un sandwich particulièrement savoureux, car acheté le matin en Belgique et qui a fait toute l’aventure accroché au vélo. Se décrochant même avant une violente descente, emportant la veste de Loïc au passage et nécessitant une réescalade de la pente pour récupérer ses biens.

Hors de Givet, la nature reprend ses droits. Les montées boueuses succèdent aux singles envahis de flaques. Nous retrouvons la Belgique au détour d’un champ. Après quelques patelins typiquement namurois (les différences architecturales entre les bleds hennuyers, français et namurois me sautent aux yeux), nous enchainons de véritables montagnes russes jouant sur les berges de la Lesse.

Alors que j’ai un excellent rythme, une pause impromptue s’impose, le lieu me subjuguant par la beauté un peu irréelle d’une petite cascade. Je m’arrête et m’offre un bain de pieds tandis que Loïc prend des photos. Une fois sortis des gorges de la Lesse, nous nous arrêtons pour étudier la situation.

J’avais prévu un itinéraire initial de 330km, mais, Loïc devant être absolument rentré le 4 au soir, j’ai également concocté un itinéraire de secours de 270km pour le cas où nous aurions du retard. Les itinéraires divergeaient un peu après le retour en Belgique, l’un faisant une boucle par Rochefort, l’autre revenant en droite ligne vers Waterloo.

Par le plus grand des hasards, je constate que je me suis arrêté littéralement au point de divergence. Étant donné le temps perdu le matin, il me semble beaucoup plus sage de prendre l’itinéraire court, au grand dam de Loïc, très motivé, mais très conscient de la deadline.

Le seul problème est que mon itinéraire court ne passe par aucune ville digne de ce nom avant le lendemain, que je n’ai repéré aucun camping. Loïc me demande d’une petite voix inquiète si on va devoir se coucher le ventre vide. Parce qu’il y’aura aussi la question de trouver à manger. Hein chef ?

Je propose d’aviser un peu plus loin. Sur le chemin, quelques moutons échappés de leur enclos me regardent méchamment. Le mâle dominant commence même à gratter du sabot. Je leur crie dessus en fonçant, ils s’écartent.

Arrivés à un croisement, nous consultons les restaurants disponibles dans les quelques villages aux alentours. Un détour par Ciney me semble la seule solution pour s’assurer un restaurant ouvert. Nous sommes au milieu de nos hésitations lorsqu’un vététiste en plein effort s’arrête à notre hauteur. Tout en épongeant la sueur qui l’inonde, il nous propose son aide. Sa connaissance du lieu est bienvenue : il nous conseille d’aller à Spontin pour être sûrs d’avoir à manger puis d’aller dans un super camping au bord du Bocq. Par le plus grand des hasards, il est justement en train de flécher un parcours VTT qui passe tout prêt.

Nous le remercions et nous mettons à suivre ses instructions et ses flèches. Un petit détour assez pittoresque qui nous fait passer dans des singles relativement techniques par moment. C’est vallonné et la journée commence à se faire sentir. Psychologiquement, l’idée d’être presque arrivés rend ces 15km particulièrement éprouvants. Après une grande descente nous débouchons sur un carrefour au milieu de Spontin, carrefour orné, ô miracle, d’une terrasse de restaurant. Nous nous installons sans hésiter. Je commande une panna cotta en entrée.

Loïc est très inquiet à l’idée de ne pas avoir de place au camping recommandé par notre confrère. Le téléphone ne répond pas. De plus, ce camping est à une dizaine de kilomètres, dans un creux qu’il faudra escalader au matin. Alors que nous mangeons, j’aperçois derrière Loïc un panneau au carrefour qui indique un camping à seulement 2km. Un coup d’oeil sur la carte m’apprend que ce camping est à quelques centaines de mètres de notre trace. Je téléphone et le gérant me répond qu’il n’y a aucun souci de place.

Après le repas, nous sautons sur nos montures pour gravir ces 2 derniers kilomètres, le camping étant sur une hauteur. Rasséréné par la certitude d’avoir un logement et le ventre plein, Loïc me lâche complètement dans la côte. Son enthousiasme est multiplié, car il reconnait le camping. C’est une constante de ce tour : alors que je cherche à lui faire découvrir des choses, il reconnait sans cesse les lieux et les paysages pour y être venu à l’une ou l’autre occasion. Parfois même avec moi.

L’emplacement de camping est magnifique, aéré, calme avec une vue superbe. Par contre, les douches sont bouillantes sans possibilité de régler la température, les toilettes sont « à terrasse », sans planche ni papier. Je préfère encore chier dans les bois, mais la douche fait du bien.

La nuit est ponctuée d’épisode de pluie. Je croise les doigts pour qu’il fasse sec au moment de remballer la tente. Je n’ai encore jamais remballé le matériel sous la pluie.

Jour 3 : l’aquanamurois, 97km, 1200d+ 

À 7h30, je commence à secouer la tente de Loïc. Je l’appelle. Pas un bruit. Je recommence, plus fort. Je secoue son auvent. J’espère qu’il est toujours vivant. À ma cinquième tentative, un léger grognement me répond : « Gnnn… »

Loïc a dormi comme un bébé. Il émerge. Nous remballons paisiblement sous un grand soleil et faisons sécher les tentes.

La grande inquiétude de la journée, ce sont les menaces d’orage. Jusqu’à présent, nous sommes littéralement passés entre les gouttes. Nous précédons les gros orages de quelques heures, roulant toujours dans des éclaircies.

Sous un soleil très vite violent, nous nous échappons dans une série de petits singles envahis de végétation avant de commencer l’escalade pour sortir de Crupet. Nous escaladons un magnifique chemin à plus de 15%. Sur la gauche, la vue vers la vallée est absolument à couper le souffle avec des myriades de fleurs bleues au premier plan. À moins que ce ne soit la pente qui coupe le souffle. Des randonneurs nous encouragent, je suis incapable de répondre. Le sommet se profile au bord d’un camp scout. Après quelques centaines de mètres sur la route, un panneau indiquant une église médiévale attire mon attention. Cette fois-ci, c’est moi qui reconnais l’endroit ! Nous sommes à 1km du lieu de mon mariage. J’entraine Loïc dans un bref aller-retour pour envoyer une photo souvenir à mon épouse.

À partir de là, je connais l’endroit pour y être venu de multiples fois à vélo. Après des traversées de champs, nous nous enfonçons dans les forêts du Namurois, forêts aux chemins dévastés par les orages et les torrents de boue. Au village de Sart-Bernard, j’interpelle un habitant pour savoir s’il y’a un magasin ou une boulangerie dans les environs. À sa réponse, je comprends que j’aurais pu tout aussi bien lui demander un complexe cinéma 15 salles, un parc d’attractions et un centre d’affaires.

Nous nous enfonçons donc dans la forêt, zigzaguant entre les chemins privés, pour déboucher finalement sur Dave. Un kilomètre de nationale malheureusement incontournable nous permet d’aller traverser la Meuse sur une écluse juste au moment où celle-ci commence à se remplir pour laisser passer un bateau. Nous continuons le long du fleuve pour aller déguster une crêpe à Wépion. Le temps se couvre, mais reste sec.

La crêpe engloutie, il est temps de sortir du lit de la Meuse. Ma trace passe par une côte que j’ai déjà eu le plaisir d’apprécier : le Fonds des Chênes. Jamais trop pentue ni technique, la côte est cependant très longue et se durcit vers la fin, alors même qu’on a l’impression de sortir du bois et d’arriver dans un quartier résidentiel.

J’arrive au sommet lorsque les premières gouttes commencent à tomber. J’ai à peine le temps d’enfiler ma veste que le déluge est sur nous. Abrité sous un arbre, j’attends Loïc qui, je l’apprendrai après, a perdu beaucoup de temps en continuant tout droit dans une propriété privée.

À partir de ce moment-là, nous allons rouler sous des trombes d’eau incessantes. À travers les bois, nous descendons sur Malonne dont nous escaladons le cimetière à travers des lacets dignes d’un col alpin. La trace traverse littéralement le cimetière au milieu des tombes. Loïc s’étonne. Je réponds que, au moins, on ne dérange personne. C’est ensuite la descente sur Seneffe avant de longer la Sambre.

Lors de notre journée de préparation, nous sommes passés par là dans l’autre sens. Nous sommes en terrain connu, le côté exploration du bikepacking s’estompe pour laisser la place à la douleur psychologique du retour. Étant donné la pluie, je suis heureux de rentrer. Je n’ose imaginer installer une tente sous la pluie, renfiler des vêtements trempés le lendemain.

Nous n’essayons même plus de contourner les flaques qui se sont, de toute façon, transformées en inévitables marigots. Nous roulons des mètres et des mètres avec de l’eau jusqu’aux moyeux, chaque coup de pédale remplissant les chaussures d’eau comme une noria.

Loïc m’a plusieurs fois expliqué être motivé par la pluie. Sous la pluie, il pédale mieux. J’ai en effet observé qu’il supporte assez mal la chaleur alors que, pour moi, rien n’est aussi délectable que d’escalader un col en plein cagnard.

Ses explications se confirment. Loïc fonce, escalade. J’ai de plus en plus de mal à le suivre. L’eau me mine, ma nouvelle selle me torture les fesses. Nous traversons Spy, les plaines de Ligny, probablement tout aussi inondées qu’en 1815 et le golf de Rigenée. La trace traverse le bois Pigeolet, mais je me souviens avoir été bloqué au château de Cocriamont lors d’une de mes aventures antérieures. J’impose un demi-tour et nous gagnons Sart-Dames-Avelines par la route.

Alors que nous arrivons à Genappe, la pluie qui s’était déjà un peu calmée s’arrête tout à fait. Nous en profitons pour prendre un dernier verre en terrasse avant de nous dire au revoir. Nous avons le sentiment d’être à la maison.

Il me reste néanmoins encore 15km à faire. 15km essentiellement de Ravel. Mes chaussures sont presque sèches, l’optimisme est de mise.

C’est sans compter que le Ravel est inondé par endroit, traversé de coulées de boue. Certaines maisons se sont barricadées avec des sacs de sable. Des arbres arrachés rendent le passage compliqué. Alors que je traverse une flaque que je croyais étendue, mais peu profonde, le Ravel étant en théorie essentiellement plat, je m’enfonce jusqu’au moyeu. Je suis recouvert, ainsi que mon vélo et mes sacs, d’une boue jaune, grasse, épaisse et collante.

Il était dit que je ne pouvais pas arriver sec à Louvain-la-Neuve…

280km, près de 4000m de d+ et une expérience mémorable. Je suis enchanté d’avoir pu condenser en 3 jours toutes les expériences d’un trip de bikepacking : camping sauvage, heures perdues à pousser le vélo dans une brousse sans chemin, découragements suivis d’espoirs, pauses imprévues et terrasses délectables.

Maintenant que Loïc a gouté aux joies du bikepacking « extreme », je n’ai qu’une envie : qu’on reparte pour explorer d’autres régions. J’ai une attirance toute spéciale pour les Fagnes… Par contre, cette expérience de la pluie me fait renoncer au rêve de parcourir l’Écosse en bikepacking.

Alors qu’une Grande Traversée du Massif Central (GTMC pour les intimes) se profile avec Thierry, deux inquiétudes restent vives : mes fesses me font toujours autant souffrir (peut-être devrais-je passer le cap du tout suspendu) et je ne me sens pas psychologiquement armé pour affronter un bivouac sous la pluie.

Mais, après tout, l’aventure ne commence-t-elle pas au moment où tu as envie qu’elle s’arrête ?

Je suis @ploum, ingénieur écrivain. Abonnez-vous par mail ou RSS pour ne rater aucun billet (max 2 par semaine). Je suis convaincu que Printeurs, mon dernier roman de science-fiction vous passionnera. Commander et partager mes livres est le meilleur moyen de me soutenir et de m’aider à diffuser mes idées !

Ce texte est publié sous la licence CC-By BE.

July 08, 2021

I published the following diary on “Using Sudo with Python For More Security Controls“:

I’m a big fan of the Sudo command. This tool, available on every UNIX flavor, allows system administrators to provide access to certain users/groups to certain commands as root or another user. This is performed with a lot of granularity in the access rights and logging/reporting features. I’m using it for many years and I’m still learning great stuff about it. Yesterday, at the Pass-The-Salt conference, Peter Czanik presented a great feature of Sudo (available since version 1.9): the ability to extend features using Python modules… [Read more]

The post [SANS ISC] Using Sudo with Python For More Security Controls appeared first on /dev/random.

As I mentioned in An IT services overview I try to keep track of the architecture and designs of the IT services and solutions in a way that I feel helps me keep in touch with all the various services and solutions out there. Similar to how system administrators try to find a balance while working on documentation (which is often considered a chore) and using a structure that is sufficiently simple and standard for the organization to benefit from, architects should try to keep track of architecturally relevant information as well.

So in this post, I'm going to explain a bit more on how I approach documenting service and solution insights for architectural relevance.

July 07, 2021

I did not write any wrap-up for a while because we are all stuck at home and most conference organizers still decided to cancel live events (even if it seems to change by the end of 2021 where some nice events are already scheduled). For the second time, Pass-The-Salt was converted to a virtual event. I like this small event with a great atmosphere. This edition in a few numbers: they received 27 proposals and selected 16 amongst them. They were presented spread across three half-days. Let’s review what was presented.

The first day started with Eloi Benoist-Vanderbeken who talked about jailbreak detection mechanisms in iOS and how to bypass them. iOS is a closed operating system and Apple really takes this seriously. Indeed, when a device has been “jailbroken”, all security measures are bypassed and the user gains full control of the device. Some apps really take this as a major problem and try to detect this. Think about banking applications but also games. To detect how the jailbreak detection is implemented, it’s mandatory to reverse the app to read the code. Also, Apple restricts a lot of operations: only signed code can be executed, side loading is not allowed (you remember the Epic Game problem?) and apps may only use public API’s. One of the best tools to help is debugging an app is Frida. How to debug an iOS app? Without a jailbreak in place, Frida is injected using ptrace but the app must be repackaged, with a lot of side effects. With a jailbreak already in place, Frida can just attach to the process. As a case study, a banking app was taken as an example. When the app is launched on a jailbroken device, it just crashes. A first set of checks did not reveal anything special. Check deeper, the app tried to trigger an invalid entry error. Some Frida usage examples were reviewed, based on hooks. Frida can hook syscalls to get information about the parameters and/or return values or completely replace the syscall with another function. It can also execute a function before any syscall. This is very interesting to follow what’s happening (example: to keep an eye on decrypted data). Another technique is the classic usage of breakpoints. Some techniques to detect jailbreaks: access specific files with open(), utime(), stats(), …), try to detect the presence of a debugger, check the parent PID, is the root filesystem writable?

The second presentation was the one of Esther Onfroy. She’s an Android expert, hacktivist and speaker. The title of Esther’s presentation was “Pithus: let’s open the Android pandora’s box”. Pithus is a project that focus on analyzing Android applications (APK). Why a new project? According to Esther, threat intelligence should not be the proprietary of a private company. For some, the price is a subscription is crazy high so the idea to develop an open-source tool based on free software and self-hostable. Pithus is able to check domains, certificates, the application time line, behavioral analysis and is linked to 3rd party services like MalwareBazaar. A specific function was presented deeper: The CFG (“Control Flow Graph”) dissection using AndroGuard. This is an interesting feature to search for share code:

Benoit Forgette presented “Hook as you want it“. Android is very popular in the mobile landscape. It provides a standard bootloader, an API for hardware, an ARM trustzone, High-level language (easy to write apps) and IPC. Review of the Android permission enforcement mechanism. AST is a tree and a hook is a programming trick that helps to intercept system calls. Once again here Frida will be used.

The next talk was “PatrowlHears and Survival tips for prioritizing threats” by Nicolas Mattiocco. It starts with a quick overview of Patrowl with themotto “demotivate attackers”. In vulnerability management, process is key: “How to manage vulnerabilities?”. It’s not only running a tool at regular interval:

One of the problems is the changing landscape, a lack of skilled people, budget pressure, … Automation is also a key point. And when running the tool more often, you get more findings and more alerts How to improve this? Then Nicolas explained how to prioritise findings. Some ideas: check the CVSS score, are we vulnerable? Is it exposed to the Internet? Is it a critical asset? Functional exploit? Patch or work around? etc… Most important: does it have a nice logo? 🙂 To help in this process, PatrolHears was introduced. It provides scalable, free and open-source solutions for orchestrating Security Operations and providing Threat Intelligence feeds. PatrowlHears is an advanced and real-time Vulnerability Intelligence platform, including CVE, exploits and threats news. Its also a feed aggregator (Packetstorm, Tenable Nessus DB, Metasploit, Exploitdb, ZDI, … Great tool to check if you’re dealing with vulnerability management.

To wrap-up the first day, we had a pretty funny presentation by Michael Hamm from The Luxembourg CERT produces always nice content and it was, once again, the case. Michael presented three live demos (yes, live) of USB devices alterations from a forensic point of view. The first one was about changing the content of a USB disk mounted in “read-only” mode. Yes, it’s possible. That’s why using a physical write-blocker is always highly recommended. The second one, was a kind of DoS attack: By modifying only a few bytes on the filesystem, a Linux system will, once the modified disk is inserted, mount it up to 250 times 😉 Finally, the last demo was even more impressive: by altering the MDR (Master Boot Record) and creating a “Polyglot boot sector”, Michael demonstrated how different files can be seen by different operating systems. Mounting the drive on Linux, some files will be listed, on Windows, other files will be listed.

Day two started with “Fedora CoreOS, a container focused OS to securely deploy and run applications” by Timothée Ravier. Intro to CoreOS: automatic updates, automatic provisioning (Ignition is used to provision nodes on 1st boot), immutable infrastructure and finally huge use of containers. What about security? “Softwares have bugs” so reduce the OS footprint (-> reduce the attack surface). Next, use safer languages: Go & Rust (memory safe languages). They use rpm-ostree to update the system (think about a “git for operating system”. Uses read-only filesystems and a clear split across important directories (/usr, /etc, /var). Everything runs in containers (podman, docker). Confinement with SELinux. Demos! The first one was to run a Matrix server More about CoreOS here.

The second talk was provided by Clément Oudot, a regular PTS speaker: “Hosting Identity in the Cloud with free softwares“. After a nice live song, Clément did a quick introduction/recap about IAM (“Identity and Access Management”) and a review of the market: big players are expensive – pay per user – and complex to install/manage.

So what about FOSS? A lot of tools exist but don’t cover the complete set of IAM features. FusionIAM is a project to propose a unified platform based on different tools: OpenLDAP, LDAP ToolBox, Fusion Directory, LDAP Synchronization Connector, and LemonLDAP:NG. From a features point of view, it provides whites pages, access manager, sync connector, service desk, directory server, directory manager. A great solution to investigate if you need to deploy IAM in a cloud environment.

The next presentation was “Biscuit: pubkey signed token with offline attenuation and Datalog authz policies” by Geoffroy Couprie. Geoffroy works for a hosting company and is often facing authentication issues (example for API’s). A classic way to authenticate users is a JWT (JSON Web Token). They are usually signed using a public key and contain a lot of data. They are not perfect therefore Geoffroy developed a new authentication and authorization token called Biscuit. You can find more information about this project here (code) and here.

Ange Albertini presented “Generating Weird Files“. Ange is known to play a lot with file formats. He already presented several pieces of research where he demonstrated how files can be built in a way that, depending on the program used to open them, they will present different types of content. Today, Ange presented his tool called Mitra. The open-source tool takes 2 files, checks their types, and generates possible polyglots.

What are the strategies used by Mitra?

  • Concatenation: easy because some file format do not need to start at offset 0
  • Cavities (filling empty spaces)
  • Parasite (abusing comments in metadata)
  • Zipper

Ange performed some demos. It was funny to see a reference to 1 file that identified up to 190 different file formats! The conclusion to this? These techniques could be prevented by creating new file formats that starts at offset 0 (magic bytes).

The next time slot was assigned to … me! I presented “Home-Made Distributed Blocklist“. More to come soon on this blog!

We wrapped the afternoon with a very interesting talk by Peter Czanik: “Security alerting made easy using Python“. Peter is a also a regular contributor to the conference and is leading open source products like syslog-ng or Sudo. Even if I’m an old Sudo user (probably for more than 15 years), I’m always learning new stuff about this wonderful tool and it was again the case. Peter explained how to interconnect Sudo and Python. Sudo is a modular tool that accepts plugins in C but also in Python to extend features and controls. You can filter data, alert, sanitize them etc… Peter reviewed some examples of integration.

Day 3 started with some Kunernetes security stuff: “ATT&CKing Kubernetes: A technical deep dive into the new ATT&CK for Containers” by Magno Login. Magno is the maintainer of the following github project: awesome-k8s-security. Then, he talked about the MITRE ATT&CK and more precisely, the matrix dedicated to containers (link). This matrix was created for a while but was updated recently with plenty of new content (thanks to the contribution of volunteers)

The second part of the talk was a suite of attacks scenarios based on a vulnerable Drupal CMS instance. Magno presented many techniques from the MITRE matrix. Scripts described in demos were collected from honeypots or found on C2 servers.

Then we switched to the wonderful world of shellcodes with Yashdeep Saini and Harpreet Singh who presented “Revisiting the Art of Encoder-Fu for novel shellcode obfuscation techniques“. Back to the roots with a review of the differences between x86, x64 and ARM registers and instructions (comparisons, shifting, control flows, stack, data movement and arithmetic operations.

They reviewed how encoders affect the shell code (XOR, Shikata_ga_nai, etc). Another comparison was to show comparative graphs based on the distributions of instructions:

Other observations: encoder types adding laters and branches(mode control, call changes). Other add transformation (more data movements). Why encode? The shell code can be transformed as per transport supported by the target applications. Bad characters can be replaced and more obfuscation.

In Search of Lost Time: A Review of JavaScript Timers in Browsers” by Thomas Rokicki. Timing attacks exploit timing differences to infer secrets from the sanbox.Attacks can be classified in 4 classes: Harare-contention-base attack (Ex: rawhammer.js), Transcient execution attacks (Spectre), Attacks based on system resources (Keystroke attacks, memory deduplication attacks ) and attacks based on browser resources(History sniffing or fingerprinting). This is a topic that remains always difficult to follow for me… I just kept this in mind as a recap to the talk: “If you are concerned by timing attacks, use the TOR browser, they are the strictest on timers countermesures

Then Nils Amiet and Tommaso Gagliardoni presented “ORAMFS: Achieving Storage-Agnostic Privacy“. The important word here is “privacy” or how to store data on a 3rd party service in a safe way. The classic control is to encrypt the complete set of data. But even if data can’t be read more info can be leaked: The size of data, some fields of database will change, etc… Think about an employees db. Example with the good old Truecrypt software: Create a TC volume, encrypted then mounted by the user. A cool feature was the “hidden volume” but can be detected even without the passphrase:

Keep in mind that “Encryption alone does not hide access patterns“. That’s why oramfs (Oblivious Random Access Machines) has been developed. Storage agnostic, written in Rust, resizing supported, multiple encryption cyphers, the tool was demonstrated live. Nice demo!

Finally, to wrap-up day 3, Damien Cauquil presented “Meet Piotr, a firmware emulation tool for trainers and researchers“. Damien performs a lot of IoT trainings and was looking for a nice solution to setup labs. Indeed, even more with the pandemic, it has been challenging to prepare labs. For example, some IoT device might be discontinued or their firmware upgraded to a non-vulnerable version. Another requirement was the size of the lab (to not download gigabytes of data). That’s why Piotr has been developed. It is based on Qemu, Firmadyne and ARM-X. After describing the architecture (how those components are interconnected), Damien performed some demos and booted/exploited some IoT devices. It looks to be a great solution also to researchers. If interested, the code is available here.

And that’s over for this virtual edition of Pass-The-Salt! Let’s hope that the next one will be in-person. Thanks to the organisers for keeping the event up’n’running! All slides are available online as well as videos.

The post Pass-The-Salt 2021 Virtual Wrap-Up appeared first on /dev/random.

I published the following diary on “Python DLL Injection Check“:

They are many security tools that inject DLL into processes running on a Windows system. The classic examples are anti-virus products. They like to inject plenty of code that, combined with API hooking, implements security checks. If DLLs are injected into processes, they can be detected and it’s a common anti-debugging or evasion technique implemented by many malware samples. If you’re interested in such techniques, they are covered in the FOR610 training. The detection relies on a specific API call GetModuleFileName()… [Read more]

The post [SANS ISC] Python DLL Injection Check appeared first on /dev/random.

July 05, 2021

Axl staring at the Boston skyline, waiting for the Fourth of July fireworks to start.
Axl staring at the Boston skyline, waiting for the Fourth of July fireworks to start. It had been years since he saw the fireworks so it was extra special, despite the fog and rain.
Cover Image


It is actually pretty easy to build a mediocre headless React today, i.e. an implementation of React that isn't hooked directly into anything else.

react-reconciler is an official package that lets you hook up React to anything already. That's how both React-DOM and React-Native share a run-time.

Most third-party libraries that use it (like react-three-fiber) follow the same approach. They are basically fully wrapped affairs: each notable Three.js object (mesh, geometry, material, light, ...) will tend to have a matching node in the React tree. Three.js has its own scene tree, like the browser has a DOM, so react-reconciler will sync up the two trees one-to-one.

The libraries need to do this, because the target is a retained in-memory model. It must be mutated in-place, and then re-drawn. But what would it look like to target an imperative API directly, like say 2D Canvas?

You can't just call an imperative API directly in a React component, because the idea of React is to enable minimal updates. There is no guarantee every component that uses your imperative API will actually be re-run as part of an update. So you still need a light-weight reconciler.

Implementing your own back-end to the reconciler is a bit of work, but entirely doable. You build a simple JS DOM, and hook React into that. It doesn't even need to support any of the fancy React features, or legacy web cruft: you can stub it out with no-ops. Then you can make up any <native /> tags you like, with any JS value as a property, and have React reconcile them.

Then if you want to turn something imperative into something declarative, you can render elements with an ordinary render prop like this:

<element render={(context) => {
  context.fillStyle = "blue";
}} />

This code doesn't run immediately, it just captures all the necessary information from the surrounding scope, allowing somebody else to call it. The reconciler will gather these multiple "native" elements into a shallow tree. They can then be traversed and run, to form a little ad-hoc program. In other words, it's an Effect-like model again, just with all the effects neatly arranged and reconciled ahead of time. Compared to a traditional retained library, it's a lot more lightweight. It can re-paint without having to re-render any Components in React.

You can also add synthetic events like in React-DOM. These can be forwarded with conveniences like event.stopPropagation() replicated.

I've used this with great success before. Unfortunately I can't show the results here—maybe in the future—but I do have something else that should demonstrate the same value proposition.

React works hard to synchronize its own tree with a DOM-like tree, but it's just a subset of the tree it already has. If you remove that second tree, what's left? Does that one tree still do something useful by itself?

I wagered that it would and built a version of it. It's pretty much just a straight up re-implementation of React's core pattern, from the ground up. It has some minor tweaks and a lot of omissions, but all the basics of hook-driven React are there. More importantly, it has one extra superpower: it's designed to let you easily collect lambdas. It's still an experiment, but the parts that are there seem to work fine already. It also has tests.

Yeet Reduce

As we saw, a reconciler derives all its interesting properties from its one-way data flow. It makes it so that the tree of mounted components is also the full data dependency graph.

So it seems like a supremely bad idea to break it by introducing arbitrary flow the other way. Nevertheless, it seems clear that we have two very interesting flavors just asking to be combined: expanding a tree downstream to produce nodes in a resumable way, and yielding values back upstream in order to aggregate them.

Previously I observed that trying to use a lambda in a live DFG is equivalent to potentially creating new outputs out of thin air. Changing part of a graph means it may end up having different outputs than before. The trick is then to put the data sinks higher up in the tree, instead of at the leaves. This can be done by overlaying a memoized map-reducer which is only allowed to pass things back in a stateless way.

yeet reduce

The resulting data flow graph is not in fact a two-way tree, which would be a no-no: it would have a cycle between every parent and child. Instead it is a DFG consisting of two independent copies of the same tree, one forwards, one backwards, glued together. Though in reality, the second half is incomplete, as it only needs to include edges and nodes leading back to a reducer.

chain of fibers in the forwards direction turns down and back to yield values in the backwards direction

Thus we can memoize both the normal forward pass of generating nodes and their sinks, as well as the reverse pass of yielding values back to them. It's two passes of DFG, one expanding, one contracting. It amplifies input in the first half by generating more and more nodes. But it will simultaneously install reducers as the second half to gather and compress it back into a collection or a summary.

When we memoize a call in the forward direction, we will also memoize the yield in the other direction. Similarly, when we bust a cache on the near side, we also bust the paired cache on the far side, and keep busting all the way to the end. That's why it's called Yeet Reduce. Well that and yield is a reserved keyword.

when 'yeeting' a value, you throw it into a bin on the far side and knock everything down after it

What's also not obvious is that this process can be repeated: after a reduction pass is complete, we can mount a new fiber that receives the result as input. As such, the data flow graph is not a single expansion and contraction, but rather, many of them, separated by a so-called data fence.

This style of coding is mainly suited for use near the top of an application's data dependency graph, or in a shallow sub-tree, where the number of nodes in play is typically a few dozen. When you have tons of tiny objects instead, you want to rely on data-level parallelism rather than mounting each item individually.

Horse JS

I used to think a generalized solution for memoized data flow would be something crazy and mathematical. The papers I read certainly suggested so, pushing towards the equivalent of automatic differentiation of any code. It would just work. It would not require me to explicitly call memo on and in every single Component. It should not impose weird rules banning control flow. It would certainly not work well with non-reactive code. And so on.

There seemed to be an unbridgeable gap between a DFG and a stack machine. This meant that visual, graph-based coding tools would always be inferior in their ability to elegantly capture Turing-complete programs.

Neither seems to be the case. For one, having to memoize things by hand doesn't feel wrong in the long run. A minimal recomputation doesn't necessarily mean a recomputation that is actually small and fast. It feels correct to make it legible exactly how often things will change in your code, as a substitute for the horrible state transitions of old. Caching isn't always a net plus either, so fully memoized code would just be glacial for real use cases. That's just how the memory vs CPU trade-off falls these days.

That said, declaring dependencies by hand is annoying. You need linter rules for it because even experienced engineers occasionally miss a dep. Making a transpiler do it or adding it into the language seems like a good idea, at least if you could still override it. I also find <JSX> syntax is only convenient for quickly nesting static <Components> inside other <Components>. Normal JS {object} syntax is often more concise, at least when the keys match the names. Once you put a render prop in there, JSX quickly starts looking like Lisp with a hangover.

When your Components are just resources and effects instead of widgets, it feels entirely wrong that you can't just write something like:

live (arg) => {
  let [service, store] = mount [

Without any JSX or effect-like wrappers. Here, mount would act somewhat like a reactive version of the classic new operator, with a built-in yield, except for fiber-mounted Components instead of classes.

I also have to admit to being sloppy here. The reason you can think of a React component as an Effect is because its ultimate goal is to create e.g. an HTML DOM. Whatever code you run exists, in theory, mostly to generate that DOM. If you take away that purpose, suddenly you have to be a lot more conscious of whether a piece of code can actually be skipped or not, even if it has all the same inputs as last time.

This isn't actually as simple as merely checking if a piece of code is side-effect free: when you use declarative patterns to interact with stateful code, like a transaction, it is still entirely contextual whether that transaction needs to be repeated, or would be idempotent and can be skipped. That's the downside of trying to graft statelessness onto legacy tech, which also requires some mutable water in your immutable wine.

I did look into writing a Babel parser for a JS/TS dialect, but it turns out the insides are crazy and it takes three days just to make it correctly parse live / mount with the exact same rules as async / await. That's because it's a chain of 8 classes, each monkey patching the previous one's methods, creating a flow that's impractical to trace step by step. Tower of Babel indeed. It's the perfect example to underscore this entire article series with.

It also bothers me that each React hook is actually pretty bad from a garbage collection point of view:

const memoized = useMemo(() => slow(foo), [foo]);

This will allocate both a new dependency array [foo] and a new closure () => slow(foo). Even if nothing has changed and the closure is not called. This is unavoidable if you want this to remain a one-liner JS API. An impractical workaround would be to split up and inline useMemo into into its parts which avoid all GC:

// One useMemo() call
let memoized;
  memoized = useMemoSameDependencies() ? useMemoValue() : slow(foo);

But a language with a built-in reconciler could actually be quite efficient on the assembly level. Dependencies could e.g. be stored and checked in a double buffered arrangement, alternating the read and write side.

I will say this: React has done an amazing job. It got popular because its Virtual DOM finally made HTML sane to work with again. But what it actually was in the long run, was a Trojan horse for Lisp-like thinking and a move towards Effects.


So, headless React works pretty much exactly as described. Except, without the generators, because JS generators are stateful and not rewindable/resumable. So for now I have to write my code in the promise.then(…) style instead of using a proper yield.

I tried to validate it by using WebGPU as a test case, building out a basic set of composable components. First I hid the uglier parts of the WebGPU API inside some pure wrappers (the makeFoo(...) calls below) for conciseness. Then I implemented a blinking cube like this:

export const Cube: LiveComponent<CubeProps> = memo((fiber) => (props) => {
  const {
    device, colorStates, depthStencilState,
    defs, uniforms, compileGLSL
  } = props;

  // Blink state, flips every second
  const [blink, setBlink] = useState(0);
  useResource((dispose) => {
    const timer = setInterval(() => {
      setBlink(b => 1 - b);
    }, 1000);
    dispose(() => clearInterval(timer));

  // Cube vertex data
  const cube = useOne(makeCube);
  const vertexBuffers = useMemo(() =>
    makeVertexBuffers(device, cube.vertices), [device]);

  // Rendering pipeline
  const pipeline = useMemo(() => {
    const pipelineDesc: GPURenderPipelineDescriptor = {
      primitive: {
        topology: "triangle-list",
        cullMode: "back",
      vertex: makeShaderStage(
        makeShader(compileGLSL(vertexShader, 'vertex')),
        {buffers: cube.attributes}
      fragment: makeShaderStage(
        makeShader(compileGLSL(fragmentShader, 'fragment')),
        {targets: colorStates}
      depthStencil: depthStencilState,
    return device.createRenderPipeline(pipelineDesc);
  }, [device, colorStates, depthStencilState]);

  // Uniforms
  const [uniformBuffer, uniformPipe, uniformBindGroup] = useMemo(() => {
    const uniformPipe = makeUniforms(defs);
    const uniformBuffer = makeUniformBuffer(device,;
    const entries = makeUniformBindings([{resource: {buffer: uniformBuffer}}]);
    const uniformBindGroup = device.createBindGroup({
      layout: pipeline.getBindGroupLayout(0),
    return ([uniformBuffer, uniformPipe, uniformBindGroup]
         as [GPUBuffer, UniformDefinition, GPUBindGroup]);
  }, [device, defs, pipeline]);

  // Return a lambda back to parent(s)
  return yeet((passEncoder: GPURenderPassEncoder) => {
    // Draw call
    uploadBuffer(device, uniformBuffer,;

    passEncoder.setBindGroup(0, uniformBindGroup);
    passEncoder.setVertexBuffer(0, vertexBuffers[0]);
    passEncoder.draw(cube.count, 1, 0, 0);

This is 1 top-level function, with zero control flow, and a few hooks. The cube has a state (blink), that it decides to change on a timer. Here, useResource is like a sync useEffect which the runtime will manage for us. It's not pure, but very convenient.

All the external dependencies are hooked up, using the react-like useMemo hook and its mutant little brother useOne (for 0 or 1 dependency). This means if the WebGPU device were to change, every variable that depends on it will be re-created on the next render. The parts that do not (e.g. the raw cube data) will be reused.

This by itself is remarkable to me: to be able to granularly bust caches like this deep inside a program, written in purely imperative JS, that nevertheless is almost a pure declaration of intent. When you write code like this, you focus purely on construction, not on mutation. It also lets you use an imperative API directly, which is why I refer to this as "No API": the only wrappers are those which you want to add yourself.

Notice the part at the end: I'm not actually yeeting a real draw command. I'm just yeeting a lambda that will insert a draw command into a vanilla passEncoder from the WebGPU API. It's these lambdas which are reduced together in this sub-tree. These can then just be run in tree order to produce the associated render pass.

What's more, the only part of the entire draw call that actually changes regularly is the GPU uniform values. This is why uniforms is not an immutable object, but rather an immutable reference with mutable registers inside. In react-speak it's a ref, aka a pointer. This means if only the camera moves, the Cube component does not need to be re-evaluated. No lambda is re-yeeted, and nothing is re-reduced. The same code from before would keep working.

Therefor the entirety of Cube() is wrapped in a memo(...). It memoizes the entire Component in one go using all the values in props as the dependencies. If none of them changed, no need to do anything, because it cannot have any effect by construction. The run-time takes advantage of this by not re-evaluating any children of a successfully memoized node, unless its internal state changed.

The very top of the (reactive) part is:

export const App: LiveComponent<AppProps> = () => (props) => {
  const {canvas, device, adapter, compileGLSL} = props;

  return use(AutoCanvas)({
    canvas, device, adapter,
    render: (renderContext: CanvasRenderingContextGPU) => {

      const {
        width, height, gpuContext,
        colorStates, colorAttachments,
        depthStencilState, depthStencilAttachment,
      } = renderContext;

      return use(OrbitControls)({
        render: (radius: number, phi: number, theta: number) =>

            canvas, width, height,
            radius, phi, theta,
            render: (defs: UniformAttribute[], uniforms: ViewUniforms) =>

                device, gpuContext, colorAttachments,
                children: [

                    device, colorAttachments, depthStencilAttachment,
                    children: [

                      use(Cube)({device, colorStates, depthStencilState, compileGLSL, defs, uniforms}),



This is a poor man's JSX, but also not actually terrible. It may not look like much, but, pretty much everyone who's coded any GL, Vulkan, etc. has written a variation of this.

This tree composes things that are completely heterogeneous: a canvas auto-sizer, interactive controls, camera uniforms, frame buffer attachments, and more, into one neat, declarative structure. This is quite normal in React-land these days. The example above is static to keep things simple, but it doesn't need to be, that's the point.

The nicest part is that unlike in a traditional GPU renderer, it is trivial for it to know exactly when to re-paint the image or not. Even those mutable uniforms come from a Live component, the effects of which are tracked and reconciled: OrbitCamera takes mutable values and produces an immutable container ViewUniforms.

You get perfect battery-efficient sparse updates for free. It's actually more work to get it to render at a constant 60 fps, because for that you need the ability to independently re-evaluate a subtree during a requestAnimationFrame(). I had to explicitly add that to the run-time. It's around 1100 lines now, which I'm happy with.

Save The Environment

If it still seems annoying to have to pass variables like device into everything, there's the usual solution: context providers, aka environments, which act as invisible skip links across the tree:

export const GPUDeviceContext = makeContext();
export const App: LiveComponent<AppProps> = () => (props) => {
  const {canvas, device, adapter, compileGLSL} = props;

  return provide(GPUDeviceContext, device,
    use(AutoCanvas)({ /*...*/ })

export const Cube: LiveComponent<CubeProps> = memo((fiber) => (props) => {
  const device = useContext(GPUDeviceContext);
  /* ... */

You also don't need to pass one variable at a time, you can pass arbitrary structs.

In this situation it is trickier for the run-time to track changes, because you may need to skip past a memo(…) parent that didn't change. But doable.

Yeet-reduce is also a generalization of the chunking and clustering processes of a modern compute-driven renderer. That's where I got it from anyway. Once you move that out, and make it a native op on the run-time, magic seems to happen.

This is remarkable to me because it shows you how you can wrap, componentize and memoize a completely foreign, non-reactive API, while making it sing and dance. You don't actually have to wrap and mount a <WebGPUThingComponent> for every WebGPUThing that exists, which is the popular thing to do. You don't need to do O(N) work to control the behavior of N foreign concepts. You just wrap the things that make your code more readable. The main thing something like React provides is a universal power tool for turning things off and on again: expansion, memoization and reconciliation of effects. Now you no longer need to import React and pretend to be playing DOM-jot either.

The only parts of the WebGPU API that I needed to build components for to pull this off, were the parts I actually wanted to compose things with. This glue is so minimal it may as well not be there: each of AutoSize, Canvas, Cube, Draw, OrbitCamera, OrbitControls and Pass is 1 reactive function with some hooks inside, most of them half a screen.

I do make use of some non-reactive WebGPU glue, e.g. to define and fill binary arrays with structured attributes. Those parts are unremarkable, but you gotta do it.

If I now generalize my Cube to a generic Mesh, I have the basic foundation of a fully declarative and incremental WebGPU toolkit, without any OO. The core components look the same as the ones you'd actually build for yourself on the outside. Its only selling point is a supernatural ability to get out of your way, which it learnt mainly from React. It doesn't do anything else. It's great when used to construct the outside of your program, i.e. the part that loads resources, creates workloads, spawns kernels, and so on. You can use yeet-reduce on the inside to collect lambdas for the more finicky stuff, and then hand the rest of the work off to traditional optimized code or a GPU. It doesn't need to solve all your problems, or even know what they are.

I should probably reiterate: this is not a substitute for typical data-level parallelism, where all your data is of the exact same type. Instead it's meant for composing highly heterogeneous things. You will still want to call out to more optimized code inside to do the heavy lifting. It's just a lot more straightforward to route.

For some reason, it is incredibly difficult to get this across. Yet algorithmically there is nothing here that hasn't been tried before. The main trick is just engineering these things from the point of view of the person who actually has to use it: give them the same tools you'd use on the inside. Don't force them to go through an interface if there doesn't need to be one.

The same can be said for React and Live, naturally. If you want to get nerdy about it, the reconciler can itself be modeled as a live effect. Its actions can themselves become regular nodes in the tree. If there were an actual dialect with a real live keyword, and WeakMaps on steroids, that would probably be doable. In the current implementation, it would just slow things down.

Throughout this series, I've used Javascript syntax as a lingua franca. Some might think it's insane to stretch the language to this point, when more powerful languages exist where effects fit more natively into the syntax and the runtime. I think it's better to provide stepping stones to actually get from here to there first.

I know that once you have gone through the trouble of building O(N2) lines of code for something, and they work, the prospect of rewriting all of them can seem totally insane. It probably won't be as optimized on the micro-level, which in some domains does actually still matter, even in this day and age. But how big is that N? It may actually be completely worth it, and it may not take remotely as long as you think.

As for me, all I had to do was completely change the way I structure all my code, and now I can finally start making proper diagrams.

Source code on GitLab.

July 04, 2021

Mijn vrouw, die bijna vijf jaar geleden de Boekenjagers oprichtte op Facebook, heeft zich naar aanleiding van verjaardag van die grootste FB boekengroep in Vlaanderen (meer dan 72.000 leden) nu ook in de wereld van het uitgeverij-vak gestort om het eerste Boekenjagers-boek uit te geven. Geloof me, als Veerle zich iets voorneemt, gaat ze er volledig voor en dat blijkt uit de indrukwekkende lijst van bijdrages van onder andere Lucinda Riley (!), Diane Broeckhoven, Toon Tellegen, Lize Spit, Patrick De Bruyn, Lectrr, Herr Seele, Boerken …

De pre-order fase loopt straks af, maar als je je haast kun je het boek nu nog aan €20 bestellen i.p.v. de €25 waarvoor het in augustus in de winkel zal liggen. Rep je dus naar en bestel er een paar, de opbrengst gaat naar 3 leesbevorderende goede doelen! :-)

July 03, 2021

Cover Image


If you have a slow function slow(x) in your code, one way to speed it up is to memoize it: you cache the last result inside, so that if it's called again with the same x, you get the same result. If the function is static, this is equivalent to just storing the last input and output in a global variable. If it's dynamic, you can use e.g. a closure as the storage:

let memo = (func) => {
  let input = undefined;
  let output = undefined;
  return (x) => {
    // Return same output for same input
    if (x === input) return output;

    // Store new output for new input
    input = x;
    output = func(x);

    return output;

let slow = memo((x) => { /*...*/ });

This can only buy you time in small code bases with very limited use cases. As soon as you alternate between e.g. slow(a) and slow(b), your cache is useless. The easy solution is to upgrade to a multi-valued cache, where you can retain the outputs for both a and b. Problem is, now you need to come up with an eviction policy to keep your cache from growing indefinitely. This is also assuming that a and b are immutable values that can be compared, and not e.g. mutable trees, or URLs whose remote content you don't even know.

In garbage collected languages, there is a built-in solution to this, in the form of WeakMap. This is a key/value map that can hold data, but which does not own its keys. This means any record inside will be garbage collected unless another part of the program is also holding on to the same key. For this to work, keys must be objects, not primitive values.

let memo = (func) => {
  let cache = new WeakMap();
  return (x) => {
    // Return same output for cached input
    let output = cache.get(x);
    if (output !== undefined) return output;

    // Store new output for new input
    output = func(x);
    cache.set(x, output);

    return output;

let slow = memo((x) => { /*...*/ });

If a source object is removed elsewhere in the program, the associated cache disappears. It allows you to blindly hold on to an entire collection of derived data, while writing zero lines of code to invalidate any of it. Actual O(0) work. That is, assuming your xs are immutable. This is similar to what you get with reference counting, except WeakMap can also collect cycles.

Unfortunately, this only works for functions of one argument, because each WeakMap key must be one object. If you wish to memoize (x, y) => {...}, you'd need a WeakMap whose keys are xs, and whose values are WeakMaps whose keys are ys. This would only work well if y changes frequently but x does not.

I think this points to a very useful insight about caching: the reason it's so hard is because caches are normally one of the least informed parts of the entire system. A cache has zero contextual information to make any decisions with: it's the code that's accessing the cache that knows what's going on. If that code decides to drop a key or reference, the cache should always follow. Aside from that, how is it supposed to know?

If we are looking for a better solution for caching results, and resumability of code in general, then that's a pretty big clue. We are not looking for a better cache. We are looking for better ways to create context-specific storage. Then we use the simplest possible memoizer. This is 100% reliable. Stale data problems just go away even though you have hidden caches everywhere, it's wild.

Data Flow Graphs

At this point I should address the elephant in the room. If we're talking about declared computations with input dependencies and cached results, isn't this what data flow graphs are for? Today DFGs are still only used in certain niches. They are well suited for processing media like video or audio, or for designing shaders and other data processing pipelines. But as a general coding tool, they are barely used. Why?

We do have a zoo of various reactive, pure, stream-based, observable things... but almost all the successful ones work through code. Even with visual tools, much of their power comes from the ability to wire up custom scriptable nodes, which contain non-reactive, non-pure, non-observable code.

(x) => {
  a = A();
  b = B(x);
  return C(a, b);
data flow graph for the previous program

At first it seems like you can trivially take any piece of imperative code and represent it as a DFG. Each function call becomes a node in the graph, and local variables become the edges between function inputs and outputs.

You can also think of a function like B as a sub-graph in a box. It has 1 input and 1 output exposed to the outside in this case.

But there's an important difference between a node in a DFG and a function in a piece of code. In a DFG, each node is a call, not a definition. If you want to call B twice, then you need two unique B nodes, with unique instances of everything inside. You can't just point all the edges to and from the same node.

(x, y) => {
  a  = A();
  b1 = B(x);
  b2 = B(y);
  return C(a, b1, b2);
data flow graph for the previous program

That's because a DFG has no control flow (if/for/while) and no recursion. It represents a stateless computation, without a traditional stack to push and pop. At most you will have e.g. if and match nodes, which select only one of their inputs, as an optimization.

How a DFG is used depends primarily on whether you are editing a DFG or running a DFG. An example of editing a DFG is shader programming: the goal is to write code using a graphical tool. This code will be run on a GPU, applied to each pixel in parallel.

This means the DFG will be transformed into an abstract syntax tree (AST), and compiled down into regular shader code. It's never run directly in its DFG form. If you wish to inspect intermediate results, you need to make a truncated copy of the shader and then run only that.

It is worth noting that this sort of DFG tends to describe computation at a very granular level, i.e. one pixel or audio sample at time, at the very bottom of our program.

live data flow graph with values on the edges

This is all very different from running a live DFG. Here the nodes represent an actual data flow that is being computed. Each edge is a register containing a data value. The values are pushed or pulled through from top to bottom, with intermediate results cached and directly inspectable. This is so the graph can be minimally recomputed in response to changes.

These sorts of graphs tend to operate at the opposite end of the scale, connecting a small number of resources and nodes at the top. An individual edge often carries an entire image or even data set at once, to contain the complexity.

Even then, they get notoriously messy. One reason is that these models often don't have a way to represent a lambda. This causes spaghetti. Think about a piece of code such as:

square = (x) => x * x;
squared = call(square, number);
representing a lambda in a DFG

Here, square is not a definition, but a value, which we assign to a variable. We then pass it to the call function, to actually run the lambda. It is possible to represent this in a DFG, and some tools do support this.

We box in a sub-graph like before. However, we don't connect any of the inputs or outputs. Instead there is a special output â–¸, which represents a value of type Function, which you can connect to another function-valued input.

hooking up one lambda to two separate nodes

The question is what would happen if you connected the same lambda to two different calls.

This represents a stateless computation, so the two calls happen simultaneously, and need to pass along unique values. This means you need two copies of everything inside the lambda too. So in a live DFG, lambdas don't really provide a Function type but rather a FunctionOnce: a function you may only call once. This is a concept that exists in languages with a notion of ownership, like Rust. If you wish to call it more than once, you need to copy it.

This also means that this does not generalize to N elements. Take for example:

square = (x) => x * x;
numbers = [1, 2, 3, 4]
squared = map(square, numbers);

What would the inside of map look like?

implementing map in a DFG

It would need to split the items into individual variables, call the function square on each, and then join them back into a new array. If we add a number to numbers, we would need to add another column, so that we can call the lambda an additional time. If numbers shrinks, the reverse. The DFG must contain N multiply nodes, where N is data-driven and varies at run-time. This is an operation that simply does not exist in a typical DFG environment, at least not as a generic reusable op.

"Scriptable nodes" let you produce any output you like. But they don't let you write code to produce new nodes on the fly. That would mean returning a new piece of DFG instead of a value. You would likely want to attach something to the end so data can flow back to you. If that were possible, it would mean your graph's topology could change freely in response to the data that is flowing through it.

This would have all sorts of implications for maintaining the integrity of data flow. For example, you wouldn't be able to pull data from the bottom of a graph at all if some data at the top changed: you don't even know for sure what the eventual shape will be until you start re-evaluating it in the middle.

Managing a static DFG in memory is pretty easy, as you can analyze its topology once. But evaluating and re-constructing a DFG recursively on the fly is a very different job. If the graph can contain cycles, that's extra hard because now there isn't even a consistent direction of higher or lower anymore.

All this might sound like science fiction but actually it already exists, sort of, almost.


Here's a simple mock program. We run it twice, once with argument true and once with false. I've lined up the two execution traces to show the matching calls:

let main = (x) => {

let A = (x) => {

let B = (x) => {
  let foo = x ? 3 : 2;
  if (x) B(false);
  if (x) D(0);

let C = () => {};
let D = (x) => {};
comparing main(true) with main(false), call by call

None of these functions return anything, but let's just ignore that for now.

The tree shape shows the evolution of the stack over time. When entering a function, it reserves some space for local variables like foo. When exiting, that space is reclaimed. Any two sibling calls share the same position on the stack, overwriting each other's temporary data. A stack frame can only be identified by its position in the trace's tree, and only exists while a function is running.

Suppose then, that you do record such a trace at run-time. Then when the program is run the second time, you compare at every step, to see if it's making the same calls as before. That would help you with caching.

You can start by trivially matching the initial calls to A and B 1-to-1: only their argument differs. But once you enter B, things change. On the left you have the siblings B(false), C(), D(0), D(3) and on the right you have C(), D(2).

The actual changes are:

  • Remove B(false) and its sub-calls
  • Remove D(0)
  • Replace D(3) with D(2)

Figuring this out requires you to do a proper minimal diff. Even then, there is ambiguity, because the following would also work:

  • Remove B(false) and its sub-calls
  • Replace D(0) with D(2)
  • Remove D(3)

From the code you can tell that it should be the former, not the latter. But while this is easy to describe after the fact, it's difficult to imagine how such a thing might actually work at run-time. The only way to know what calls will be made is to run the code. Once you do, it's too late to try and use previous results to save time. Plus, this code only has a few trivial ifs. This becomes even harder if you allow e.g. for loops because now there's a variable number of elements.

In case it's not clear, this is the exact same problem as the live DFG with lambdas in disguise.

We need to introduce the operation of reconciliation: rather than doing work, functions like B must return some kind of data structure that describes the work to be done. Sort of like an effect. Then we can reconcile it with what it returned previously, and map 1-to-1 the calls that are identical. Then we can run it, while reusing cached results that are still valid.

It would also be useful to match calls where only arguments changed, because e.g. B(true) and B(false) share some of the same calls, which can be reused. At least in theory.

Granted, there is a huge constraint here, which the mock scenario obfuscates. Memoizing the calls only makes sense if they return values. But if we passed any returned value into another call, this would introduce a data dependency.

That is, in order to reconcile the following:

let F = (x) => {
  let foo = C(x);
  if (foo) E();

We would need to somehow yield in the middle:

let F = function* (x) {
  let foo = C(x);
  if (foo) E();

That way we can first reconcile foo = C(x), so we can know whether to reconcile D(false) or D(true), E().

Unfortunately our language does not actually have a trace reconciler built into it, so this can't work. We have two options.

First, we can transpile the code to a deferred form:

let F = function* (x) {
  foo = yield call(C)(x)
  yield [
    foo ? call(E)() : null,

Here, call(C)(x) is a value that says we want to call C with the argument x. A deferred function like F returns one or more wrapped calls via a yield. This allows C(x) to be reconciled, obtaining either a cached or fresh value for foo. Then we can reconcile the calls to D(foo) and E().

To make this work would require functions C, D and E to receive the exact same treatment, which we have to be honest, is not a compelling prospect.

Alternatively, we could recognize that C(x) is called first and unconditionally. It doesn't actually need to be reconciled: its presence is always guaranteed if F is called. Let's call such a function a hook.

let F = (x) => {
  let foo = C(x);
  return defer([
    foo ? call(E)() : null

If hooks like C(x) aren't reconciled, they're regular function calls, so is all the code inside. Like a scriptable node in a DFG, it's an escape hatch inside the run-time.

But we're also still missing something: actual memoization. While we have the necessary information to reconcile calls across two different executions, we still don't have anywhere to store the memoized state.

So we'll need to reserve some state when we first call F. We'll put all the state inside something called a fiber. We can pass it in as a bound argument to F:

let F = (fiber) => (x) => {
  let foo = C(fiber)(x);
  return defer([
    foo ? call(E)() : null

We also pass the fiber to hooks like C: this provides the perfect place for C to store a memoized value and its dependencies. If we run the program a second time and call this exact same F again, in the same place, it will receive the same fiber as before.

As long as the execution flow remains the same between two runs, the fiber and the memoized values inside will remain. Because functions like C are likely to receive exactly the same argument next time, memoization works very well here.

Apropos of nothing, here's how you build a UI component on the web these days:

const Component: React.FC<Props> = (props) => {
  const {foo, bar} = props;

  // These are hooks, which can only be called unconditionally
  const [state, setState] = useState('hello world');
  const memoized = useMemo(() => slow(foo), [foo]);

  // And there's also something called useEffect
  useEffect(() => {
    return () => undoThing(foo);
  }, [foo]);

  // Regular JS code goes here
  // ...

  // This schedules a call to D({foo}) and E()
  // They are mounted in the tree inside <Component> recursively
  return <>
    <D foo={foo} />
    {foo ? <E /> : null}

It's all there. Though useEffect is a side-show: the real Effects are actually the <JSX> tags, which seem to have all the relevant qualities:

  • JSX doesn't run any actual code when we evaluate it
  • We can compose multiple JSX elements together in series (nesting) or in parallel (<> aka "fragments")
  • The run-time takes care of all the scheduling, mounting and clean up, in the right order

It will reconcile the before and after calls, and preserve the matching fibers, so functions like useMemo can work.

You can also reconcile variable size sets, by returning an array where every call has a key property. This allows minimal diffing in O(N) time.

You may eventually realize that the JSX at the bottom is really just an obscure dialect of JavaScript which lacks a real return statement: what this is returning is not a value at all. It is also not passed back to the parent component but to the run-time. The syntax is optimized for named rather than positional arguments, but that's about it.

What's more, if you do accept these constraints and manage to shoehorn your code into this form, it's strangely not terrible in practice. Often the opposite. Suddenly a lot of complex things that should be hard seem to just fall out naturally. You can actually begin to become 10x. Compared to O(N2) anyway.

The difference with our hypothetical trace reconciler is that there is no way to yield back to a parent during a deferred render. A common work-around in React land is a so-called render prop, whose value is a lambda. The lambda is called by the child during rendering, so it must be entirely side-effect free.

The code:

x = A();
y = B();
z = C(x, y);

must be turned into:

  (x) => <B>{
    (y) => <C x={x} y={y}>{
      (z) => {}

This is hideous. Because there is no ability for sibling calls to pass data, B must go inside A, or the other way around. This introduces a false data dependency. But, the data flow graph does match the normal execution trace: in code we also have to decide whether to call A or B first, even if it doesn't matter, unless you explicitly parallelize.

It's interesting that a render prop is an injectable lambda which returns new nodes to mount in the tree. Unlike a "scriptable node", this allows the tree to extend itself on the fly in a Turing-complete yet data-driven way.

So don't think of a reconciler as a tree differ. Think of it as a generalized factory which maintains a perfectly shaped tree of caches called fibers for you. You never need to manually init() or dispose() them... and if you re-run the code in a slightly different way, the fibers that can be re-used will be reused. The value proposition should be pretty clear.

When we made the fiber visible as an argument, much of the magic went away: a React Component is merely a function (fiber) => (props) => DeferredCall. The first argument is implicit, binding it to a unique, persistent fiber for its entire lifetime. The fiber is keyed off the actual call site and the depth in the stack. The hooks can work because they just reserve the next spot in the fiber as each one is called. This is why hooks must be called unconditionally in React: it's only way to keep the previous and next states in sync.

Where we go next is hopefully clear: what if you could use these patterns for things that aren't UI widgets in a tree? Could you retain not just the Effect-like nature, but also the memoization properties? Also, without hideous code? That would be pretty nice.

De VRT radio streams, zoals deze ( hebben sinds kort de neiging om reklame te spelen als je ze start. Niet altijd, maar wel regelmatig.

Zonet (21u en enkele seconden) wou ik naar het nieuws luisteren , maar er begon reklame te spelen. Ik laadde de stream opnieuw en de reklame begon opnieuw (terwijl het nieuws bezig was, maar dat kreeg ik dus niet).

Een paar dagen geleden had ik dat ook al met de Radio 1 stream, telkens reklame als je de stream start. Dit is irritant.

Tijd dus om het script aan te passen en de eerste 60 seconden van de stream te muten. De cronjob om het nieuws te spelen kan dan een minuut eerder starten.


UPDATE 22u30: dit lijkt te werken


export HOME=/var/www
pkill mplayer

mplayer -slave -input file=master &

sleep 0.5
echo mute > master
sleep 0.5
echo mute > master
sleep 0.5
echo mute > master
sleep 0.5
echo mute > master

sleep 60
echo mute 0 > master

July 02, 2021

Cover Image

Declarative Code and Effects

This is a series about incrementalism in code and coding. On the one hand, I mean code that is rewindable and resumable. On the other, I mean incremental changes in how we code.

This is not an easy topic, because understanding some of the best solutions requires you to see the deep commonalities in code across different use cases. So I will deliberately start from basic principles and jump a few domains. Sorry, it's unavoidable.

Hopefully by the end, you will look differently at the code you write. If you don't, I hope it's because you already knew all this.

Hello World

If an abstraction is good enough to be adopted, it tends to make people forget why it was necessary to invent it in the first place. Declarative code is such an abstraction.

The meaning of "declarative code" is often defined through contrast: code that is not imperative or OO (aka "retained mode"). This is not very useful, because you can use declarative patterns all over imperative code, and get the exact same benefits. It can also be a great way to tame wild code you don't own, provided you can build the right glue.

OO however is a different story, and this applies just the same to OO-without-classes. Mind you, this has nothing to do with the typical endless debate between FP vs OO, which is the one about extensibility vs abstract data types. That's unrelated.

While seeing wide adoption in certain niches (e.g. UI), the foundational practices are often poorly understood. As a result, coders tend to go by example rather than principle: do whatever the existing code does. If they need to stray from the simple paths, they easily fall into legacy habits, resulting in poorly performing or broken code.

So far the best solution is to use linters to chide them for using things as they were originally intended. The better solution is to learn exactly when to say No yourself. The most important thing to understand is the anti-patterns that declarative code is supposed to remedy. Headache first, then aspirin.

a modernized version of dwarf fortress

For a perfect example, see the patch notes for almost any complex video game sandbox. They often contain curious, highly specific bugs:

  • Fix crouch staying on if used while a bear attacks
  • Fix player getting control-locked if loading an autosave created while lockpicking
  • Fix a bug which lets you immediately cancel a knockdown by swapping ammo types
  • Fixed an issue where leaving and returning after the briefing would block the quest's progress

Some combination of events or interactions corrupts the game's state in some way. Something changed when it shouldn't have, or the other way around. In the best case this results in a hilarious glitch, in the worst case a permanently unfinishable game. Developers can spend months chasing down weird bugs like this. It turns into whack-a-mole, as each new fix risks breaking other things.

This is very different from issues that occur when the game systems do exactly what they should do. Like that time cats were getting alcohol poisoning in Dwarf Fortress, because they had walked through puddles of spilled beer in the pub, and licked themselves clean afterwards. Such a complex chain of causality may be entirely unanticipated, but it is readily apparent why it happens. It is the result of code working too well.

Part of me dies every time I see a game get stuck in the mud instead, when I really want to see them succeed: I have a pretty good idea of the mess they've created for themselves, and why it's probably only going to get worse. Unless they dramatically refactor. Which usually isn't an option.

It's Imperative

So why does this happen?

Imagine an App with 5 views in tabs. Only one view is visible at a time, so there are 5 possible states. Because you can switch from any tab to any other, there are 5 x 4 = 20 possible state changes.

declarative vs imperative - for N states, there are N x (N - 1) possible state transitions

So you might imagine some code that goes:

constructor() {
  this.currentView = HomeView;

onSelect(newView) {
  // Hide selected view (exit old state)
  this.currentView.visible = false;
  // Show new view (enter new state)
  newView.visible = true;
  this.currentView = newView;

This will realize the arrows in the diagram for each pair of currentView and newView.

But wait. Design has decided that when you switch to the Promotions tab, it should start auto-playing a video. And it needs to stop playing if you switch away. Otherwise it will keep blaring in the background, which is terrible:

onSelect(newView) {
  if (this.currentView == newView) return;

  // Exit old state
  this.currentView.visible = false;
  if (this.currentView == PromoView) {;

  // Enter new state
  newView.visible = true;

  if (newView == PromoView) {;

  this.currentView = newView;

No wait, they want it to keep playing in the background if you switch to the Social tab and back.

onSelect(newView) {
  if (this.currentView == newView) return;

  // Exit old state
  this.currentView.visible = false;
  if ((this.currentView == PromoView && newView != SocialView) ||
      (this.currentView == SocialView && newView != PromoView)) {;

  // Enter new state
  newView.visible = true;

  if (newView == PromoView) {;

  currentView = newView;

Ok. Now they want to add a Radio tab with a podcast player, and they want it to auto play too. But only if the promo video isn't already playing.

Just look at how the if statements are popping out of the ground like mushrooms. This is the point at which things start to go terminally wrong. As features are piled on, a simple click handler explodes into a bloated mess. It is difficult to tell or test if it's even right, and there will likely be bugs and fixes. Replace the nouns and verbs, and you will get something very similar to what caused the curious bugs above.

The problem is that this code is written as delta-code (*). It describes state changes, rather than the states themselves. Because there are O(N2) arrows, there will potentially be O(N2) lines of code for dealing with N states. Each new feature or quirk you add will require more work than before, because it can interact with all the existing ones.

So imperative code isn't a problem by itself. The issue is code that becomes less maintainable over time. Ironically, a software industry notorious for snooty whiteboard interviews spent decades writing code like an O(N2) algorithm.

The real reason to write declarative code is to produce code that is O(N) lines instead. This work does not get harder over time. This is generally not taught in school because neither the students nor their teachers have spent enough time in one continuous codebase. You need to understand the O(N2) > O(N) part of the curve to know why it's so terrible, especially in the real world.

It probably seemed attractive in the first place, because it seemed economical to only touch the state that was changing. But this aversion to wasted CPU cycles has to be seriously traded off against the number of wasted human cycles.

Better to write code that makes it impossible to reach a bad state in the first place. Easier said than done, of course. But you should start with something like:

let tabs = [
    view: homeView,
    view: promoView,
    video: 'promo-video.mp4',
    autoplayVideo: true,
    view: socialView, 
    backgroundVideo: true,
    view: newsView, 
    video: 'news-video.mp4',
    autoplayVideo: true,
  // ...

If you need to add a new kind of tab quirk, you define 1 new flag. You set it on a constant number of tabs, and then write the code to handle that quirk in 1 place. O(1) work. This code also tells you exactly where and what the special exceptions are, in one place, which means another engineer can actually understand it at a glance.

The declarative approach to visibility is just to constantly overwrite the state of every tab in a plain old loop. When somebody inevitably wants the tabs to animate in and out, you will discover that is actually what you have to do in the general case anyway.

Same with the video: you declare which one is currently supposed to be playing. Then you compare new with old, to see if you need to replace, pause, or adopt the current video.

The goal is to have most code only declare intent. Then you use other code to reach that target, no matter what the prior state was.

In some circles this is all old hat. But I suspect the people writing the hot frameworks don't quite realize how alien these problems feel from the outside. If the run-time is invisible, how are you ever supposed to figure out how it works, and learn to apply those same tricks? And how do you reach declarative bliss when there isn't a nice, prepackaged solution for your particular domain yet?

Or maybe there is a very capable library, but it's written in a retained style. If you try to fit this into declarative code without a proper adapter, you will end up writing delta-code all over again.

Strange Effects

The underlying challenge here is as banal as it is important and universal: "Have you tried turning it off and on again?"

Code is Turing-complete, so you don't know what it's going to do until you run it. It has the ability to amplify the complexity of its inputs. So if you change some of them, which parts are going to switch on and which will switch off?

Even on the back-end, similar things crop up behind the scenes:

  • connecting and disconnecting to network peers
  • starting and stopping jobs in response to events
  • building indices of data being edited
  • watching file systems for changes
  • allocating and freeing memory for data

These are all state transitions of some kind, which are usually orchestrated by hand.

Take the job of creating and managing a dependent resource inside a class. I swear I have seen this code written in dozens of codebases, each with dozens of files all doing the same thing, including one of my own:

constructor(size) {
  this.size = size;
  this.thing = new Thing(this.size);

onUpdate() {
  if (this.thing.size != this.size) {
    this.thing = new Thing(this.size);

dispose() {
  if (this.thing) this.thing.dispose();

This general pattern is:

constructor() {
  // Enter state - on

onUpdate() {
  // Exit old state - off
  // Enter new state - on

dispose() {
  // Exit state - off

You have to write code in 3 separate places in order to create, maintain and dispose of 1 thing. This code must also have access to this, in order to mutate it. Look around and you will see numerous variations of this pattern. Whenever someone has to associate data with a class instance whose lifecycle they do not fully control, you will likely spot something like this.

The trick to fix it is mainly just slicing the code differently, e.g. using a generator:

let effect1 = function* () {
  // Enter state - on
  yield // Wait
  // Exit state - off

let effect2 = function* () {
  // Enter state - on
  yield // Wait
  // Exit state - off

There are now only 2 identically shaped functions, each of which only refers to 1 state, not the previous nor next. A yield allows the function to be interrupted mid-call. The idea here is to simplify the lifecycle:


  • constructor()
  • onUpdate()
  • onUpdate()
  • onUpdate()
  • dispose()


  • Call effect
  • Cleanup effect
  • Call effect
  • Cleanup effect
  • Call effect
  • Cleanup effect
  • Call effect
  • Cleanup effect
  • Call effect
  • Cleanup effect

It doesn't have fewer calls, just fewer unique parts. It might seem dramatically less useful, but it's actually mostly the opposite. Though this is not obvious at first.

If you don't have a good mental model of a generator, you can pretend that instead it says:

let effect = () => {
  // Enter state - on
  return {
    // I'm not done yet
    done: false,
    // I don't have a value to return
    value: undefined,
    // Call me 🤙
    next: () => {
      // Exit state - off
      return {done: true, value: undefined}

This could actually be described as a self-rewinding effect. We can make a function that produces Thing resources this way:

// This creates an effect that describes the lifecycle of a Thing of size
let makeThingEffect = (size) => function* () {
  // Make thing
  thing = new Thing(size);

  yield thing;

  // Dispose thing

So let's first talk about just effects as a formal type. They are similar to async/await and promises. But effects and promises are subtly different. A promise represents ongoing work. An effect is merely a description of work.

The difference is:

  • In a promise-based API, fetch(url) => Promise will run a new HTTP request
  • In an effect-based API, fetch(url) => Effect will describe an HTTP request

As a first approximation, you can think of an effect-based API as:

fetch(url) => () => Promise

It won't actually start until you call it a second time. Why would you need this?

Suppose you want to implement an auto-retry mechanism for failed requests. If a fetch promise fails, there is no way for you to retry using the promise itself. Each is one-use-only. You have to call fetch(url) again to get a fresh one. You need to know the specific type of promise and its arguments.

But if a fetch effect fails, then you can retry the same effect just by passing it back to your effect run-time. You don't need to know what effect it is. So in an Effect-based API, you can make a universal attemptRetry(effect, N) in a few lines. To emulate this with Promises, you need to use a () => Promise instead.

However, real Effects are supposed to be chained together, passing data from start to end. This is either a value or an error, to another effect, or back to the calling context. e.g.:

let makeAnEffect = (nextEffect) => function* (input, error) {
  if (!error) {
    output = f(input);
    return [nextEffect, output];
  else return [null, null, error];

let combinedEffect = makeAnEffect( makeAnEffect( makeAnEffect() ) );

Here we return a static nextEffect that was decided on effect construction, along with a successful output. combinedEffect means running it 3 times in a row.

You could also return arbitrary effects on the fly. This is a retry combinator:

let attemptRetry = (effect, n) => function* (i, e) {
  // Make request
  const [value, error] = yield [effect, i, e];
  // Success
  if (value) return [null, value];
  // Retry n times
  if (n > 0) return retryEffect(effect, n - 1);
  // Abort
  return [null, null, error];

This is 1 function in 1 place you can use anywhere.

You can focus purely on defining and declaring intended effects, while letting a run-time figure out how to schedule and execute them. In essence, an Effect is a formalization of 🤙 aka (...) =>.

Whether effects are run serially or parallel depends on the use case, just like promises. Whether an effect should actually be disposed of or undone is also contextual: if it represents an active resource, then disposal is necessary to ensure a clean shutdown. But if an effect is part of a transaction, then you should only be rolling it back if the effect chain failed.

Other people have different definitions, and some of their Effects do different or fancier things. So buyer beware. But know that even just () => Promise, () => Generator and () => void can solve a whole bunch of issues elegantly.


Going back to the makeThingEffect above, it might still seem overkill to wrap a "classic OO" class like new Thing this way. What's wrong with just having an interface Disposable that you implement/inherit from? Why should you want to box in OO code with effects even if you don't need to compose them? The difference is subtle.

For reliable operation, it is desirable that completion of one effect or disposal of a resource happens before the next one starts (i.e. to avoid double-allocating memory). But you can't call new on a class without immediately running a constructor. So you can't hold both an old and a new instance in your hands, so to speak, without having already initialized and run the new one. A common workaround is to have an empty constructor and delegate to an init() instead. This means some (or all) of your classes are non-standard to use, and you've reinvented the native new and delete.

Often you wish to retain some resources across a change, for efficiency, which means you need a hand-coded onChange() to orchestrate that. The main way you will take care of a series of possible creations and disposals is to just write new Thing and if (this.thing) and != code repeatedly. That's why I've seen this code a thousand times.

While you can easily create a central mechanism for tracking disposals with classic OO, it is much harder to create a mechanism that handles both creation and updates. Somebody has to invoke specific constructors or reallocators, on specific classes, with specific arguments. This is called a Factory, and they pretty much suck.

It might seem like effects don't address this at all, as the pure form forbids any retention of state from one effect to the next, with each thing created in isolation. But it totally works: if every resource is wrapped in its own effect, you can declare the data dependencies per resource too. You can say "only re-allocate this resource if ____ or ____ changed since last time."

Instead of having an init() and an onChange() that you write by hand, you just have a render() method which spawns N resources and their dependencies. The run-time will figure out which ones are stale, clean them up, and create replacements.

How to actually do this depends on your usage. If the resource needs to be available immediately, that is different from whether it only needs to be returned to other parts of the code. In the latter case, you can just return effects which haven't run yet. In the former case, well, maybe we can fix that later.

"Effect run-time" sounds intimidating but 90% of the benefit here comes from giving yourself the ability to put your "exit" code below your "enter" code, where it belongs. Which ever way you can. Then there only needs to be 1 place in your code where "exit" appears above "enter" (i.e. an onUpdate), which you only have to write once. O(1).

At least that's the dream. I know I'm still handwaving a lot. It's entirely on purpose. In practice, unless you have a fixed structure to hang your effects and resources off of, it is actually quite hard to track all the associated state. You simply don't have anywhere specific to put it. Where does yield thing go, actually?

The potential benefits here are so enormous, that it's absolutely worth to figure this out.

More tomorrow.

(*) hat-tip James Crook for this term

June 30, 2021

Elf jaar geleden blogde ik over mijn Tide bestek. Bij deze een update.

Ik weet totaal niet waarom dit mij nu weer boeit, misschien omdat dit in de jaren 70 ons thuis bestek was? Toch zou ik graag de ontbrekende vleesvork, minivork en vismes hebben, wie wil ruilen?

Hier twee foto's van wat ik nu heb: (De inleg van de vleesvork, het kleinste vorkje en het vismes zijn copyright S.R.)

De geschiedenis van dit bestek: Het was te krijgen via premiebons die bij Tide waspoeder zaten in de jaren 1950 (en jaren 60?). Er bestaat een FB-groep voor Tide bestek, maar ik zit niet op FB. Veel kans hebben ze daar meer foto's en ruilmogelijkheden.


UPDATE 2021-05-20: Joepie 6 dessertvorkjes

UPDATE 2021-06-21: Danku Laura C. voor de vleesvork in uitstekende staat. Foto hieronder van deze grote Tide vork naast het grote mes (in mindere staat).


Nu enkel de vismesjes nog...

TOSCA is an OASIS open standard, and is an abbreviation for Topology and Orchestration Specification for Cloud Applications. It provides a domain-specific language to describe how an application should be deployed in the cloud (the topology), which and how many resources it needs, as well as tasks to run when certain events occur (the orchestration). When I initially came across this standard, I was (and still am) interested in how far this goes. The promise of declaring an application (and even bundling the necessary application artefacts) within a single asset and then using this asset to deploy on whatever cloud is very appealing to an architect. Especially in organizations that have a multi-cloud strategy.

June 25, 2021

Tentative d’analyse rationnelle et d’élargissement d’un débat émotionnel

Le sujet du port du voile par les femmes musulmanes est un sujet récurrent et politicomédiatique un peu trop vendeur. Forcément, car tout le monde à son avis sur le sujet. Mon avis personnel ne me semble pas avoir plus de valeur que n’importe quel autre.

Aussi ai-je envie d’aborder le sujet d’une manière que je n’ai que trop rarement vue : rationnellement et avec autant de rigueur logique que possible bien que n’étant, sensibilité humaine oblige, pas objectif. Le voile est en effet un exemple parfait pour illustrer le point de friction entre la liberté religieuse et la neutralité législative garante de cette liberté.

Le dress code professionnel

Il semble important de préciser que le débat ne porte pas sur l’interdiction du voile. Dans notre pays, le citoyen peut se vêtir comme bon il semble tant qu’une certaine pudeur est respectée. Chaque citoyen peut porter sur la tête le couvre-chef de son choix, le voile ne pose aucun problème sur la voie publique (je m’abstiendrai de parler du voile intégral, hors propos ici).

La question réelle peut donc être formulée en ces termes : « Si un employeur refuse que ses employés portent un voile, est-ce de la discrimination ? ».

Mise en contexte : les employeurs ont toujours disposé d’un droit de regard sur l’habillement de leurs employés. Depuis l’uniforme au port obligatoire de la cravate. Un voisin m’a un jour affirmé que jamais il n’accepterait un travail où on le forcerait de mettre une cravate. J’ai personnellement été une fois renvoyé chez moi pour me changer, car j’avais mis un bermuda, ce qui était interdit dans le règlement de travail (que je n’avais pas lu). Je fais pourtant partie de la sainte église du bermuda (2 membres en Belgique) pour laquelle le port du pantalon long entre mars et octobre est un blasphème.

Pour l’anecdote, dans cette même entreprise de plusieurs centaines de personnes est un jour arrivée une nouvelle employée voilée. Personne n’a émis le moindre commentaire (le voile n’était pas interdit dans le règlement) excepté une collègue qui m’a expliqué être musulmane et opposée au voile, voile qui n’était selon elle ni musulman ni cité dans le coran.

De même, dans le collège où j’ai fait mes études, le règlement stipulait strictement que les élèves étaient tenus d’être nu-tête à l’intérieur des bâtiments. J’ai été témoin de bon nombre de casquettes et chapeaux confisqués par les éducateurs.

Lors d’une embauche ou de l’accès à un établissement public, ces obligations vestimentaires clairement stipulées n’ont jamais été assimilées à une discrimination. Un postulant est libre de refuser un emploi si les conditions de travail ne lui conviennent pas.

Pourtant, le fait de devoir retirer le voile pour accéder à un travail ou à une école (chose que des milliers de musulmanes font quotidiennement, il faut le préciser, et qui serait acceptable selon les préceptes de l’Islam) est perçu par certaines comme une discrimination.

Je ne vois que deux alternatives logiques.

Soit le voile est un simple morceau de tissu vestimentaire, comme l’affirment certains, et il peut être retiré si nécessaire. J’abhorre la cravate, que j’associe à une laisse, mais je la passerai au cou si j’estime que les circonstances l’exigent.

Soit le port du voile est l’expression d’une religiosité profonde ne permettant pas à une croyante de le retirer en public. Auquel cas, il est manifeste que la personne ne peut accéder à un poste où une certaine forme de neutralité est nécessaire. Les postes de représentation publique, par exemple, impliquent une neutralité jusque dans les détails de l’habillement. Un député ne peut pas porter un t-shirt avec un slogan.

Il est important de souligner que les deux situations sont mutuellement exclusives. Soit le voile est un accessoire vestimentaire, auquel cas il peut-être retiré, soit il est le symbole d’une religiosité forte qui peut être considérée comme incompatible avec certaines fonctions, et cela à la discrétion de l’employeur.

Dans les deux cas, remarquons qu’il apparait raisonnable pour un employeur de ne pas engager une personne qui refuse de retirer son voile sur son lieu de travail alors que l’employeur l’estime nécessaire. Il n’y a pas de discrimination sur la couleur de peau, l’origine sociale ou la religion de l’employé, mais simplement un choix de ce dernier de se conformer ou non au règlement de travail. Ce que les plaignantes appellent discrimination dans les affaires où le port du voile leur a été refusé n’est donc que le refus de l’octroi d’un privilège spécifique qui n’est disponible pour personne d’autre.

L’exception religieuse

À cette constatation, la réponse la plus courante est celle de « l’exception religieuse ». L’employeur peut obliger la cravate et interdire le bermuda, car ceux-ci ne sont pas religieux. Le voile bien.

Cette exception est une véritable boîte de Pandore. Il est important de rappeler que la liberté de religion et de conscience garantie par l’article 18 des droits de l’homme porte sur toutes les religions, y compris les religions personnelles. Certains ont peut-être cru que je me moquais en parlant de l’église du bermuda, mais mon coreligionnaire (qui se reconnaitra), confirmera que notre foi est sincère et assortie de rituels (comme la photo du premier bermuda de l’année et l’expiation du port du pantalon long). Si l’état belge propose une liste de religions reconnues, c’est uniquement pour des motifs de financement. Chaque citoyen est libre de suivre les préceptes de la religion de son choix.

L’exception religieuse, par sa simple existence, sépare le corpus législatif en deux types de lois.

Les premières sont celles qui ne souffriront aucune exception religieuse. Si ma religion recommande la consommation de nouveau-nés au petit-déjeuner, l’exception religieuse sera difficilement recevable.

Par contre, mutiler le sexe du même nouveau-né sans raison médicale est couvert par l’exception religieuse.

Cette contradiction est visible dans tous les services communaux chargés d’établir nos cartes d’identité : il est en effet stipulé que la photo doit représenter le demandeur tête nue (point 8 du règlement), mais qu’en cas de motif religieux s’opposant à apparaitre tête nue sur nue sur la photo, ce point ne s’applique pas (point 10 du règlement).

Extrait du règlement téléchargé sur le site du gouvernement en juin 2021

Paradoxalement, la discrimination porte donc sur les personnes non religieuses. Celles-là ne peuvent pas porter de couvre-chef (et personne ne peut sourire, pourtant, ce serait joli une religion du sourire) sur leur photo d’identité ! La discrimination est bénigne et anecdotique, mais ouvre la porte à de dangereux précédents.

C’est pour illustrer ce paradoxe que plusieurs pastafariens à travers le monde ont tenté, avec plus ou moins de succès, de figurer sur leur document d’identité avec une passoire sur la tête ou avec un chapeau de pirate.

En Suède, face à la répression de la copie illicite de films et de musiques, le Kopimisme s’est mis en place. Selon cette religion, la vie nait de la copie d’information et tout acte de copie de l’information est sacré. En ce sens, les kopimistes ont exigé (et obtenu) une exemption religieuse leur permettant de copier tout document informatique sans être poursuivis. La loi « anti-piratage » fait donc partie, en Suède, de la deuxième catégorie des lois.

Ici encore, la discrimination envers les non-religieux est flagrante. Doit-on créer une religion pour tester et contourner chaque loi ?

Preuve est faite que l’exception religieuse est non seulement dangereuse, mais complètement inutile. Soit une loi ne peut accepter aucune exception religieuse (le meurtre par exemple), soit la loi n’a pas lieu d’être ou doit être retravaillée (vu qu’il est acceptable pour une frange arbitraire de citoyens de ne pas la respecter).

Soit il est essentiel d’être nu-tête sur un document d’identité, soit ce n’est pas nécessaire.

Soit un employeur peut imposer certaines règles vestimentaires à ses employés, soit il ne peut en imposer aucune. Toute tentative de compromis est, par essence, arbitraire et entrainera des débats émotionnels sans fin voire des violences.

Comme le disait déjà Thomas Hobbes, cité par le philosophe et député François De Smet dans son livre « Deus Casino », il est impossible à un humain d’obéir simultanément à deux autorités, deux ensembles distincts de lois (sauf si, par miracle, elles sont temporairement compatibles). L’un des ensembles de loi doit donc être supérieur à l’autre. Si c’est la loi religieuse qui est supérieure, on est tout simplement dans le cas d’une théocratie, chose qui n’est possible que pour une seule religion.

On peut tourner le problème dans tous les sens : la coexistence de plusieurs religions implique nécessairement la primauté des lois séculières sans distinction ni exception. Si une personne religieuse accomplit un rite qui enfreint une loi civile, elle doit être poursuivie comme après n’importe quelle infraction !

Cette conclusion, bien que contre-intuitive, est primordiale : le seul garant d’une réelle liberté de pensée et de culte passe par un état qui affirme que tous les citoyens sont égaux, que la loi est identique pour tous et ne tolère aucune exception religieuse !

L’expression de la croyance en une divinité et en la vérité d’un seul livre relève pour l’athée que je suis du blasphème et du non-respect envers les millions d’intellectuels qui ont fait progresser l’étendue du savoir humain. La religiosité m’offense profondément. Pour certaines femmes, le port du voile est une insulte aux féministes des décennies précédentes, un rappel permanent de la fragilité des récents et encore incomplets droits de la femme.

La liberté de culte passe donc nécessairement par la liberté de blasphème et le droit à l’offense.

Un combat politique à peine voilé

Rationnellement et logiquement parlant, la question du port du voile au travail est donc très simple à trancher : soit un employeur n’a aucun droit sur l’habillement de ses employés, soit il peut en avoir. Fin du débat.

Malheureusement, comme le souligne François De Smet, les religions instituées bénéficient d’une immunité contre l’irrationnel qui ne peut être justifiée par des arguments logiques. La religion est par essence irrationnelle, mais elle peut s’appuyer sur des arguments psychologiques, historiques, culturels ou politiques. Argument psychologique que j’ai intitulé « Le coût de la conviction ».

Le port du voile est souvent défendu par les traditionalistes comme un élément culturel et historique. Pourtant, en 1953 Nasser faisait exploser de rire son auditoire en ironisant sur l’impossibilité, n’en déplaise à une minorité, de forcer 10 millions d’Égyptiennes à porter le voile. Les photos de Téhéran dans les années 70 montrent également des femmes libérées, vêtues selon des normes modernes et se promenant en bikini au bord de la plage. Dans les années 90, je crois me souvenir qu’il était relativement rare de voir une femme voilée en Belgique, malgré plus de 30 ans d’immigration marocaine intense.

S’il y’a bien une certitude, c’est que l’interdiction pour une musulmane de se départir de son voile ne faisait, jusqu’à un passé relativement récent, peu ou plus partie du patrimoine culturel et qu’il n’est donc clairement pas « historique ». À l’opposé, la fameuse danse du ventre, tradition égyptienne millénaire, est en passe de disparaitre du pays, illustrant l’hypocrisie de l’argument « défense de la culture et de la tradition ».

La question qui est certainement la plus intéressante à se poser est donc : « Si ce n’est culturel, ni historique, ni rationnel, d’où vient cet engouement soudain pour le voile ? Qu’est-ce qui a fait apparaitre cette intransigeance récente qui pousse des femmes à refuser un emploi voire à attaquer l’employeur en justice plutôt que de retirer temporairement leur voile ou trouver un autre emploi ? ».

Les réponses à cette question pourraient aller de « Les immigrées n’osaient pas ne pas le retirer et osent enfin s’affirmer » à « Il s’agit d’un effet de mode, une pression sociale ». Ne pouvant répondre par une analyse logique, je laisserai le sujet aux sociologues.

À noter que dans un long billet très documenté, Marcel Sel propose une réponse essentiellement politique. Le port du voile ne serait pas une lutte pour les libertés individuelles, mais une volonté d’ingérence politique à peine voilée, si j’ose dire.

Le droit des femmes

Pour les millions de femmes dans le monde victime d’une théocratie et qui mettent en péril leur intégrité pour avoir le droit… de ne pas porter le voile, le combat de quelques-unes pour avoir le droit de le porter doit sembler incroyablement absurde. Mais, dans la vie quotidienne, il me semble important de laisser de côté les considérations politiques. Quand bien même le voile serait, à large échelle, un instrument politique, la femme en face de vous est avant tout un être humain le plus souvent sincère dans ses convictions.

Au risque d’enfoncer des portes ouvertes, j’aimerais rappeler le droit inaliénable au respect. Dans certains quartiers, des femmes se font insulter et se sentent en insécurité, car elles ne sont pas voilées. Autre part, ce sont les femmes voilées qui sont victimes d’insultes racistes voire d’agressions. Dans les deux cas, les femmes et la dignité humaine sont perdantes.

Quelles que soient leur religion et leur origine, beaucoup de femmes connaissent une insécurité permanente pour une raison unique : une culture machiste tolérée.

C’est là où, en tant qu’homme je peux agir. En surveillant mon comportement et celui de mes condisciples, en ne riant pas aux blagues sexistes, en sermonnant un camarade aux mains un peu trop baladeuses ou aux remarques trop sonores. En respectant totalement l’humaine qui est en face de moi, qu’elle se promène nue ou voilée de pied en cap.

Et en me gardant de faire étalage de manière inappropriée de mes convictions philosophiques. On n’a pas besoin d’être d’accord pour s’entendre et se respecter.

Je suis @ploum, ingénieur écrivain. Abonnez-vous par mail ou RSS pour ne rater aucun billet (max 2 par semaine). Je suis convaincu que Printeurs, mon dernier roman de science-fiction vous passionnera. Commander et partager mes livres est le meilleur moyen de me soutenir et de m’aider à diffuser mes idées !

Ce texte est publié sous la licence CC-By BE.

June 23, 2021

Software as a Service (SaaS) solutions are often a quick way to get new capabilities into an organization’s portfolio. Smaller SaaS solutions are simple, web-based solutions which barely integrate with the organization’s other solutions, besides the identity and access management (which is often handled by federated authentication).

More complex or intermediate solutions require more integration focus, and a whole new market of Integration Platform as a Service (iPaaS) solutions came up to facilitate cross-cloud integrations. But even without the iPaaS offerings, integrations are often a mandatory part to leverage the benefits of the newly activated SaaS solution.

In this post I want to bring some thoughts on the integrations that might be needed to support customizing a SaaS solution.

June 17, 2021

Bluetooth Low Energy (BLE) devices have two ways to transfer data:

  • Connectionless: This is called "broadcasting" or "advertising": the device advertises its existence, its capabilities and some data, and every BLE device in the neighbourhood can pick this up.

  • With a connection: This requires the client device to connect to the server device and ask for the data.

Until recently, ESPHome only supported reading BLE data without a connection. In my book, I gave some examples about how to use this functionality. I also referred to pull request #1177 by Ben Buxton, which was only merged in ESPHome 1.18.0, after the book was published.

In this blog post, I'll show you how to read the battery level and button presses from a Gigaset keeper Bluetooth tracker, as well as the heart rate of a heart rate sensor, for instance in your fitness tracker.


These examples only work for ESP32 boards. The ESP8266 doesn't have Bluetooth Low Energy, and external BLE modules aren't supported.

Setting up a BLE client

If you want your ESPHome device to connect to another device using BLE, you first need to add a ble_client component, which requires an esp32_ble_tracker component. For instance:


  - mac_address: FF:EE:DD:CC:BB:AA
    id: gigaset_keeper

Just specify the BLE MAC address of the device you want to connect to, and give it an ID.


If you don't know the device's MAC address, just add the esp32_ble_tracker component and upload the firmware to your ESPHome device. It will start scanning for BLE devices and will show the devices it finds, with their name and MAC address. You can also scan with the mobile app nRF Connect for Android or iOS.

Reading a one-byte characteristic

After connecting to the device, you can create a BLE Client sensor to read one-byte characteristics such as the battery level from your device.

Each BLE server (the device you connect to) has various services and characteristics. The Bluetooth Special Interest Group has published a lot of standard services and their characteristics in their Specifications List. So if you want to read a device's battery level, consult the Battery Service 1.0 document. You see that it defines one characteristic, Battery Level, which "returns the current battery level as a percentage from 0% to 100%; 0% represents a battery that is fully discharged, 100% represents a battery that is fully charged."

Each service and characteristic has a UUID. The standard services and characteristics are defined in the Bluetooth SIG's Assigned Numbers specifications. These are 16-bit numbers. For instance, the Battery service has UUID 0x180f and the Battery Level characteristic has UUID 0x2a19. To read this in ESPHome, you add a sensor of the ble_client platform:

  - platform: ble_client
    ble_client_id: gigaset_keeper
    name: "Gigaset keeper battery level"
    service_uuid: '180f'
    characteristic_uuid: '2a19'
    notify: true
    icon: 'mdi:battery'
    accuracy_decimals: 0
    unit_of_measurement: '%'

Make sure to refer in ble_client_id to the ID of the BLE client you defined before.

If you compile this, your ESPHome device connects to your Bluetooth tracker and subscribes to notifications for the battery level. 1

The Gigaset keeper has another one-byte characteristic that's easy to read, but it's not a standard one: the number of clicks on the button. By exploring the characteristics with nRF Connect, clicking the button and reading their values, you'll find the right one. You can then read this in ESPHome with:

  - platform: ble_client
    ble_client_id: gigaset_keeper
    name: "Gigaset keeper button clicks"
    service_uuid: '6d696368-616c-206f-6c65-737a637a796b'
    characteristic_uuid: '66696c69-7020-726f-6d61-6e6f77736b69'
    notify: true
    accuracy_decimals: 0

Every time you click the button on the Gigaset keeper, you'll get an update with the click count. Of course you're probably not interested in the number of clicks, but just in the event that a click happens. You can act on the click with an on_value automation in the sensor, which can for instance toggle a switch in Home Assistant. This way you can use your Bluetooth tracker as a wireless button for anything you want.

Reading arbitrary characteristic values

In ESPHome 1.18 you could only read the first byte of a characteristic, and it would be converted to a float number. In ESPHome 1.19, David Kiliani contributed a nice pull request (#1851) that allows you to add a lambda function to parse the raw data. All received bytes of the characteristic are passed to the lambda as a variable x of type std::vector<uint8_t>. The function has to return a single float value.

You can use this for example to read the value of a heart rate sensor. If you read the specification of the Heart Rate Service, you'll see that the Heart Rate Measurement characteristic is more complex than just a number. There's a byte with some flags, the next one or two bytes is the heart rate, and then come some other bytes. So if you have access to the full raw bytes of the characteristic, you can read the heart rate like this:

  - platform: ble_client
    ble_client_id: heart_rate_monitor
    id: heart_rate_measurement
    name: "${node_name} Heart rate measurement"
    service_uuid: '180d'  # Heart Rate Service
    characteristic_uuid: '2a37'  # Heart Rate Measurement
    notify: true
    lambda: |-
      uint16_t heart_rate_measurement = x[1];
      if (x[0] & 1) {
          heart_rate_measurement += (x[2] << 8);
      return (float)heart_rate_measurement;
    icon: 'mdi:heart'
    unit_of_measurement: 'bpm'

Note how the heart rate is in the second byte (x[1]), and if the rightmost bit of x[0] is set, the third byte holds the most significant byte of the 16-bit heart rate value.

However, this way you can still only return numbers. What if you want to read a string? For instance, the device name is accessible in characteristic 0x2a00. Luckily, there's a trick. First define a template text sensor:

  - platform: template
    name: "${node_name} heart rate sensor name"
    id: heart_rate_sensor_name

And then define a BLE client sensor that accesses the raw bytes of the Device Name characteristic, converts it to a string and publishes it to the template text sensor:

  - platform: ble_client
    ble_client_id: heart_rate_monitor
    id: device_name
    service_uuid: '1800'  # Generic Access Profile
    characteristic_uuid: '2a00'  # Device Name
    lambda: |-
      std::string data_string(x.begin(), x.end());
      return (float)x.size();

The only weirdness is that you need to return a float in the lambda, because it's a sensor that's expected to return a float value.

After someone on the Home Assistant forum asked how he could read the heart rate of his BLE heart rate monitor, I implemented all this in a project that displays the heart rate and device name of a BLE heart rate sensor on an M5Stack Core or LilyGO TTGO T-Display ESP32, my two go-to ESP32 boards. I published the source code on GitHub as koenvervloesem/ESPHome-Heart-Rate-Display.

This looks like this:


This is just one example of the many new possibilities with Bluetooth Low Energy in ESPHome 1.19.


Subscribing for notifications (with notify: true) uses less energy than continuously polling for a new value. With a notification, the server only pushes an update when the characteristic value changes.

June 15, 2021

PlatformIO supports the Digispark USB development board, a compact board with the ATtiny85 AVR microcontroller. The canonical example code that lets the built-in LED blink looks like this:

digispark_blink_platformio/main.c (Source)

/* Atml AVR native blink example for the Digispark
 * Copyright (C) 2021 Koen Vervloesem (
 * SPDX-License-Identifier: MIT
#include <avr/io.h>
#include <util/delay.h>

// Digispark built-in LED
// Note: on some models the LED is connected to PB0
#define PIN_LED PB1
#define DELAY_MS 1000

int main(void) {
  // Initalize LED pin as output
  DDRB |= (1 << PIN_LED);

  while (1) {
    PORTB ^= (1 << PIN_LED);

  return 0;


This doesn't use the Arduino framework, but directly uses the native AVR registers.

If you've bought your Digispark recently, the Micronucleus bootloader is a recent version that isn't supported by PlatformIO's older micronucleus command.

If you've already upgraded the bootloader on your Digispark, you also have the newest version of the micronucleus command. So the only thing you need is make PlatformIO use this version when uploading your code. You can do this with the following platformio.ini:

platform = atmelavr
board = digispark-tiny
upload_protocol = custom
upload_command = micronucleus --run .pio/build/digispark-tiny/firmware.hex

The platform and board options are just the configuration options the PlatformIO documentation lists for the Digispark USB. By setting the upload_protocol to custom, you can supply your own upload command, and the micronucleus in this command refers to the one you've installed globally with sudo make install in /usr/local/bin/micronucleus.

After this, you can just build the code and upload it:

koan@tux:~/digispark_blink_platformio$ pio run -t upload
Processing digispark-tiny (platform: atmelavr; board: digispark-tiny)
Verbose mode can be enabled via `-v, --verbose` option
PLATFORM: Atmel AVR (3.1.0) > Digispark USB
HARDWARE: ATTINY85 16MHz, 512B RAM, 5.87KB Flash
DEBUG: Current (simavr) On-board (simavr)
 - tool-avrdude 1.60300.200527 (6.3.0)
 - toolchain-atmelavr 1.50400.190710 (5.4.0)
LDF: Library Dependency Finder ->
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 0 compatible libraries
Scanning dependencies...
No dependencies
Building in release mode
Checking size .pio/build/digispark-tiny/firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [          ]   0.0% (used 0 bytes from 512 bytes)
Flash: [          ]   1.4% (used 82 bytes from 6012 bytes)
Configuring upload protocol...
CURRENT: upload_protocol = custom
Uploading .pio/build/digispark-tiny/firmware.hex
> Please plug in the device ...
> Device is found!
connecting: 33% complete
> Device has firmware version 2.5
> Device signature: 0x1e930b
> Available space for user applications: 6650 bytes
> Suggested sleep time between sending pages: 7ms
> Whole page count: 104  page size: 64
> Erase function sleep duration: 728ms
parsing: 50% complete
> Erasing the memory ...
erasing: 66% complete
> Starting to upload ...
writing: 83% complete
> Starting the user app ...
running: 100% complete
>> Micronucleus done. Thank you!
====================================== [SUCCESS] Took 3.99 seconds ======================================

I've created a GitHub project with this configuration, the example code, a Makefile and a GitHub Action to automatically check and build the code: koenvervloesem/digispark_blink_platformio. This can be used as a template for your own AVR code for the Digispark with PlatformIO.

June 14, 2021

The Digispark USB development board has an ATtiny85 microcontroller. (source: Digistump)

Recently I have been playing with the Digispark development board with the ATtiny85 AVR microcontroller. The cool thing is that it's only 2 by 2 cm big and it plugs right into a USB port.

But when I flashed the canonical blink example to the microcontroller, I noticed that the program didn't blink continuously with the fixed interval I had set up. The first five seconds after plugging in the Digispark the LED blinked in a fast heartbeat pattern (which seems to be normal, this is the bootloader staying in the programming mode for five seconds), then my program slowly blinked for a few seconds, then it was five seconds in heartbeat pattern again, then slower blinks again, and so on. It seemed the microcontroller got stuck in a continuous boot loop.

Thinking about the previous time I solved a microcontroller problem by upgrading the bootloader, I decided to try the same approach here. The Digispark is sold with the Micronucleus bootloader, but the boards I bought 1 apparently had an older version. Upgrading the bootloader is easy on Ubuntu.

First build the micronucleus command-line tool:

git clone
cd micronucleus/commandline
sudo apt install libusb-dev
sudo make install

The Micronucleus project offers firmware files to upgrade older versions, so then upgrading the existing firmware was as easy as:

cd ..
micronucleus --run firmware/upgrades/upgrade-t85_default.hex

Then insert the Digispark to write the firmware.

After flashing the blink example, I didn't see the boot loop this time. And the Micronucleus developers apparently got rid of the annoying heartbeat pattern in the newer version.


Only after I received the 5-pack I realized that I had bought a Chinese clone. So it's possible that the original product sold by Digistump doesn't have the issue I'm describing here.

My current role within the company I work for is “domain architect”, part of the enterprise architects teams. The domain I am accountable for is “infrastructure”, which can be seen as a very broad one. Now, I’ve been maintaining an overview of our IT services before I reached that role, mainly from an elaborate interest in the subject, as well as to optimize my efficiency further.

Becoming a domain architect allows me to use the insights I’ve since gathered to try and give appropriate advice, but also now requires me to maintain a domain architecture. This structure is going to be the starting point of it, although it is not the true all and end all of what I would consider a domain architecture.

June 11, 2021

I just upped Autoptimize 2.9 beta to version 4, which is likely to be the last version before the official 2.9 release (eta end June/ early July).

Main new features;

You can download the beta from Github (do disable 2.8.x before activating 2.9-beta-4) and you can log any issues/ bugs over at

Looking forward to your feedback!

I published the following diary on “Keeping an Eye on Dangerous Python Modules“:

With Python getting more and more popular, especially on Microsoft Operating systems, it’s common to find malicious Python scripts today. I already covered some of them in previous diaries. I like this language because it is very powerful: You can automate boring tasks in a few lines. It can be used for offensive as well as defensive purposes, and… it has a lot of 3rd party “modules” or libraries that extend its capabilities. For example, if you would like to use Python for forensics purposes, you can easily access the registry and extract data… [Read more]

The post [SANS ISC] Keeping an Eye on Dangerous Python Modules appeared first on /dev/random.

June 09, 2021

At my workplace, I jokingly refer to the three extra layers on top of the OSI network model as a way to describe the difficulties of discussions or cases. These three additional layers are Financial Layer, Politics Layer and Religion Layer, and the idea is that the higher up you go, the more challenging discussions will be.

June 08, 2021

Fastly had some issues today.

Here is a top 10 of the number of complainers on downdetector of some random internet services...

10. github 199
9. spotify 264
8. stackoverflow 351
7. twitter 368
6. discord 906
5. nytimes 2069
4. hbomax 2245
3. cnn 3432
2. twitch 5438
1. reddit 18628

Does this measure popularity? probably not.

Does it measure anything useful? Nope.

June 06, 2021

I have a couple of Dragino's LoRaWAN temperature sensors in my garden. 1 The LSN50 that I bought came with a default uplink interval of 30 seconds. Because the temperature outside doesn't change that fast and I want to have a long battery life, I wanted to change the uplink interval to 10 minutes.

Dragino's firmware accepts commands as AT commands over a USB to TTL serial line (you need to physically connect to the device's UART pins for that) or as LoRaWAN downlink messages (which you can send over the air via the LoRaWAN gateway). All Dragino LoRaWAN products support a common set of commands explained in the document End Device AT Commands and Downlink Commands.

The Change Uplink Interval command starts with the command code 0x01 followed by three bytes with the interval time in seconds. So to set the uplink interval to 10 minutes, I first convert 600 seconds to its hexadecimal representation:

$ echo 'ibase=10;obase=16;600' | bc

So I have to send the command 0x01000258.

My LoRaWAN gateway is connected to The Things Network, a global open LoRaWAN network. You can send a downlink message on the device's page in The Things Network's console (which is the easiest way), but you can also do it with MQTT (which is a bit harder). The Things Network's MQTT API documentation shows you the latter. 2 The MQTT topic should be in the form <AppID>/devices/<DevID>/down and the message looks like this:

  "port": 1,
  "confirmed": false,
  "payload_raw": "AQACWA=="

Note that you should supply the payload in Base64 format. You can easily convert the hexadecimal payload to Base64 on the command line:

$ echo 01000258|xxd -r -p|base64

Enter this as the value for the payload_raw field.

So then the full command to change the uplink interval of the device to 10 minutes becomes:

$ mosquitto_pub -h -p 8883 --cafile ttn-ca.pem -u AppID -P AppKey -d -t 'AppID/devices/DevID/down' -m '{"port":1,"payload_raw":"AQACWA=="}'

This downlink message will be scheduled by The Things Network's application server to be sent after the next uplink of the device. After it has been received, the device changes its uplink interval.


I highly recommend Dragino. Their products are open hardware and use open source firmware, and their manuals are extremely detailed, covering countless application scenarios.


Note that I'm still using The Things Network v2, as my The Things Indoor Gateway can't be migrated yet to The Things Stack (v3). Consult the MQTT documentation of The Things Stack if you're already using v3.

June 04, 2021

I published the following diary on “Russian Dolls VBS Obfuscation“:

We received an interesting sample from one of our readers (thanks Henry!) and we like this. If you find something interesting, we are always looking for fresh meat! Henry’s sample was delivered in a password-protected ZIP archive and the file was a VBS script called “presentation_37142.vbs” (SHA256:2def8f350b1e7fc9a45669bc5f2c6e0679e901aac233eac63550268034942d9f). I uploaded a copy of the file on MalwareBazaar… [Read more]

The post [SANS ISC] Russian Dolls VBS Obfuscation appeared first on /dev/random.

June 03, 2021

When an organization has an extensively large, and heterogeneous infrastructure, infrastructure architects will attempt to make itless complex and chaotic by introducing and maintaining a certain degree of standardization. While many might consider standardization as a rationalization (standardizing on a single database technology, single vendor for hardware, etc.), rationalization is only one of the many ways in which standards can simplify such a degree of complexity.

In this post, I'd like to point out two other, very common ways to standardize the IT environment, without really considering a rationalization: abstraction and virtualization.

June 01, 2021

Bluetooth Low Energy (BLE) is een dankbaar protocol om als hobbyprogrammeur mee te werken. De specificaties zijn allemaal op de website van de Bluetooth SIG te vinden en zowat elk ontwikkelplatform kent wel een of meerdere BLE-bibliotheken.

In een artikel voor Computer!Totaal leg ik uit hoe je data van BLE-sensors uitleest in je eigen Arduino-code die op een ESP32-microcontroller draait, en het resultaat visualiseert op het scherm van een M5Stack Core.

Een BLE-apparaat kan op twee manieren communiceren: door data te broadcasten naar iedereen in de buurt, of door een één-op-één verbinding te maken met een ander apparaat en daarnaar data te zenden. In het artikel werk ik voor beide communicatiemanieren een voorbeeldprogramma uit.

Broadcasten gebeurt vaak door omgevingssensoren. De RuuviTag is zo'n sensor die temperatuur, luchtvochtigheid, luchtdruk en beweging detecteert. 1 De sensor broadcast zijn data elke seconde via BLE als manufacturer data. Het protocol is volledig gedocumenteerd. In het artikel in Computer!Totaal leg ik uit hoe je de temperatuur hieruit decodeert en op het scherm van de M5Stack Core toont.

Andere apparaten gebruiken verbindingen. De Bluetooth SIG heeft een heel aantal services voor verbindingen gedefinieerd waarvan je de specificaties gewoon kunt raadplegen. Een populaire is de service Heart Rate. In het artikel beschrijf ik hoe je de code schrijft om met behulp van de bibliotheek NimBLE-Arduino met een hartslagsensor te verbinden en realtime je hartslag op het scherm van de M5Stack Core te tonen. Ideaal voor tijdens het sporten! 2


De nRF52832-gebaseerde RuuviTag is open hardware en ook de firmware is opensource.


Ik heb ondertussen ook een versie van de code geschreven voor de nieuwere M5Stack Core2: M5Core2 Heart Rate Display.

May 30, 2021

Ik vind dat het verhuizen van welke rechtbank laster een eerroof behandelt naar de correctionele rechtbanken te laten gaan, een goede zaak is.

Ik begrijp het, dat het feit dat, assisen de rechtbank was die hierover ging, als een soort van buffer speelde om de vrijheid van meningsuiting te vrijwaren. De comma’s staan er omdat het momenten om over na te denken zijn.

Maar dat is en was eigenlijk een misbruik van een soort van overheidsincompentie (of eigenlijk rechtbankenincompetentie). Namelijk dat er bij Assisen te weinig capaciteit was om dit soort misdaden te beoordelen en wanneer (en slechts enkel wanneer) het over de schreef ging, te veroordelen.

In de praktijk betekende het dat eigenlijk bijna alles kon. Terwijl dat nu geen goed idee meer is. Niet alles kan. Je vrijheid om bv. een leugen te spreken is begrenst daar waar die vrijheid schade berokkent. En dat is zo zelfs wanneer wat jij zegt geen leugen is, en voornamelijk wanneer dat wat gezegd wordt geen maatschappelijke meerwaarde heeft.

Het is dus aan een rechter om je dan te stoppen. Wij leven hier in België onder het Belgische recht. Niet onder jouw absolute vrijheid. Want onze geringe vrijheid is afhankelijk van ons Belgische recht. M.a.w. is, hoe controversieel ook (en laat ik duidelijk zijn dat ik duidelijk weet dat het controversieel is), jouw ingebeelde recht om een ander te schaden, niet altijd gelijk aan jouw recht om het ook te uiten. Niet hier in België. Niet altijd. Bijna altijd wel, maar niet altijd.

Context doet er toe (dus, de humor kan terecht zeer ver gaan). Maar niet altijd.

Ik ben wel erg bezorgd hierover. Maar dat maakt niet dat dit niet mag of kan behandeld worden. Het slaat stevig in op de verlichtingsfilosofie. Die zegt dat bijna alles moet kunnen gezegd worden. Maar om alles te kunnen zeggen moeten we ook, zeker met de opkomst van nieuwe media, akkoord gaan dat een overvloed van leugens ook de reputatie van eerbare mensen onherstelbaar zal schaden. Dus was de verlichting als filosofie soms naïef (hoewel ze stelde dat dit normaalgesproken zichelf corrigeert).

De grond van de vrije meningsuiting mogen we toch niet vergeten. Vind ik.

May 28, 2021

I published the following diary on “Malicious PowerShell Hosted on“:

Google has an incredible portfolio of services. Besides the classic ones, there are less known services and… they could be very useful for attackers too. One of them is Google Apps Script. Google describes it like this:

Apps Script is a rapid application development platform that makes it fast and easy to create business applications that integrate with G Suite.

Just a quick introduction to this development platform to give you an idea about the capabilities. If, as the description says, it is used to extend the G Suite, it can of course perform basic actions like… hosting and delivering some (malicious) content… [Read more]

The post [SANS ISC] Malicious PowerShell Hosted on appeared first on /dev/random.

May 27, 2021

The pandemic was a bit of a mess for most FLOSS conferences. The two conferences that I help organize -- FOSDEM and DebConf -- are no exception. In both conferences, I do essentially the same work: as a member of both video teams, I manage the postprocessing of the video recordings of all the talks that happened at the respective conference(s). I do this by way of SReview, the online video review and transcode system that I wrote, which essentially crowdsources the manual work that needs to be done, and automates as much as possible of the workflow.

The original version of SReview consisted of a database, a (very basic) Mojolicious-based webinterface, and a bunch of perl scripts which would build and execute ffmpeg command lines using string interpolation. As a quick hack that I needed to get working while writing it in my spare time in half a year, that approach was workable and resulted in successful postprocessing after FOSDEM 2017, and a significant improvement in time from the previous years. However, I did not end development with that, and since then I've replaced the string interpolation by an object oriented API for generating ffmpeg command lines, as well as modularized the webinterface. Additionally, I've had help reworking the user interface into a system that is somewhat easier to use than my original interface, and have slowly but surely added more features to the system so as to make it more flexible, as well as support more types of environments for the system to run in.

One of the major issues that still remains with SReview is that the administrator's interface is pretty terrible. I had been planning on revamping that for 2020, but then massive amounts of people got sick, travel was banned, and both the conferences that I work on were converted to an online-only conference. These have some very specific requirements; e.g., both conferences allowed people to upload a prerecorded version of their talk, rather than doing the talk live; since preprocessing a video is, technically, very similar to postprocessing it, I adapted SReview to allow people to upload a video file that it would then validate (in terms of length, codec, and apparent resolution). This seems like easy to do, but I decided to implement this functionality so that it would also allow future use for in-person conferences, where occasionally a speaker requests that modifications would be made to the video file in a way that SReview is unable to do. This made it marginally more involved, but at least will mean that a feature which I had planned to implement some years down the line is now already implemented. The new feature works quite well, and I'm happy I've implemented it in the way that I have.

In order for the "upload" processing and the "post-event" processing to not be confused, however, I decided to import the conference schedules twice: once as the conference itself, and once as a shadow version of that conference for the prerecordings. That way, I could track the progress through the system of the prerecording completely separately from the progress of the postprocessing of the video (which adds opening/closing credits, and transcodes to multiple variants of the same video). Schedule parsing was something that had not been implemented in a generic way yet, however; since that made doubling the schedule in that way rather complex, I decided to bite the bullet and (finally) implement schedule parsing in a generic way. Currently, schedule parsers exist for two formats (Pentabarf XML and the Wafer variant of that same format which is almost, but not quite, entirely the same). The API for that is quite flexible, and I'm happy with the way things have been implemented there. I've also implemented a set of "virtual" parsers, which allow mangling the schedule in various ways (by either filtering out talks that we don't want, or by generating the shadow version of the schedule that I talked about earlier).

While the SReview settings have reasonable defaults, occasionally the output of SReview is not entirely acceptable, due to more complicated matters that then result in encoding artifacts. As a result, the DebConf video team has been doing a final review step, completely outside of SReview, to ensure that such encoding artifacts don't exist. That seemed suboptimal, so recently I've been working on integrating that into SReview as well. First tests have been run, and seem to be acceptable, but there's still a few loose ends to be finalized.

As part of this, I've also reworked the way comments could be entered into the system. Previously the presence of a comment would signal that the video has some problems that an administrator needed to look at. Unfortunately, that was causing some confusion, with some people even thinking it's a good place to enter a "thank you for your work" style of comment... which it obviously isn't. Turning it into a "comment log" system instead fixes that, and also allows for better two-way communication between administrators and reviewers. Hopefully that'll improve things in that area as well.

Finally, the audio normalization in SReview -- for which I've long used bs1770gain -- is having problems. First of all, bs1770gain will sometimes alter the timing of the video or audio file that it's passed, which is very problematic if I want to process it further. There is an ffmpeg loudnorm filter which implements the same algorithm, so that should make things easier to use. Secondly, the author of bs1770gain is a strange character that I'd rather not be involved with. Before I knew about the loudnorm filter I didn't really have a choice, but now I can just rip bs1770gain out and replace it by the loudnorm filter. That will fix various other bugs in SReview, too, because SReview relies on behaviour that isn't actually there (but which I didn't know at the time when I wrote it).

All in all, the past year-and-a-bit has seen a lot of development for SReview, with multiple features being added and a number of long-standing problems being fixed.

Now if only the pandemic would subside, allowing the whole "let's do everything online only" wave to cool down a bit, so that I can finally make time to implement the admin interface...

Bye, Freenode

I have been on Freenode for about 20 years, since my earliest involvement with Debian in about 2001. When Debian moved to OFTC for its IRC presence way back in 2006, I hung around on Freenode somewhat since FOSDEM's IRC channels were still there, as well as for a number of other channels that I was on at the time (not anymore though).

This is now over and done with. What's happening with Freenode is a shitstorm -- one that could easily have been fixed if one particular person were to step down a few days ago, but by now is a lost cause.

At any rate, I'm now lurking, mostly for FOSDEM channels, on, under my usual nick, as well as on OFTC.

Today is the first meeting of the LibreBMC project. This project is an initiative of the OpenPOWER Foundation. We are starting this project to build an open source hardware BMC. It will use the POWER ISA as a soft-core running on an FPGA, like the Lattice ECP5 or the Xilinx Artix7. Our goals is to design a board that is OCP DC-SCM compatible and that can easily build on current FPGA’s. While we are starting with these 2 FPGA’s in mind and we’ll be focussing on 1 or 2 soft-cores. But we want it to be modular, so the general design can support different soft-cores, and run at least LSB and OpenBMC. You can read more in the OPF announcement :

I am very happy to be part of this initiative and will be contributing as much as I can. This initiative is from the OpenPOWER Foundation, however it runs as a public SIG, meaning anybody can help out. If you want to contribute, participate or observe, feel free to follow any updates on : We also have a project page available, where we’ll update the git repo links, discussion links and any other information :

A small anecdote, which isn’t mentioned in many articles is how the name came to be, while initial discussions start in the beginning of this year 2021. We, meaning the OpenPOWER Foundation steering committee, were guaging interest and looking for founding members. In our discussions it quickly became very confusing, as we were talking about theBMC, using the term OpenBMC, and then switched to using OpenBMC hardware, or OpenBMC software, which lead to more confusion. I started using the name LibreBMC for the hardware project we wanted to start and OpenBMC for the existing BMC software stack. It quickly was adopted by the rest of the team and made it out into the wild. While we, engineers, often struggle to come up with good names for our projects, this one was easy and inspired by LibreOffice, Libre-SOC, and many other projects that are open/libre and this is what we’re also doing.

May 26, 2021

Last week my new book has been published, Getting Started with ESPHome: Develop your own custom home automation devices. It demonstrates how to create your own home automation devices with ESPHome on an ESP32 microcontroller board.

I always like to look at the big picture. That's why I want to take some time to talk about what ESPHome is, why you should use it and what you need.

Configuring instead of programming

The ESP8266 and its successor, the ESP32, are a series of low-cost microcontrollers with integrated Wi-Fi (for both series) and Bluetooth (for the ESP32), produced by Espressif Systems. The maker community quickly adopted these microcontrollers for tasks where an Arduino didn't suffice. 1

You can program the ESP8266 and ESP32 using Espressif's ESP-IDF SDK, the ESP32 Arduino core, or MicroPython. Arduino and MicroPython lower the bar significantly, but it still takes some programming experience to build solutions with these microcontrollers.

One of the domains in which the ESP8266 and ESP32 have become popular is in the DIY (do-it-yourself) home automation scene. You just have to connect a sensor, switch, LED, or display to a microcontroller board, program it, and there you have it: your customised home automation device.

However, "program it" isn't that straightforward as it sounds. For instance, if you're using the Arduino environment, which has a lot of easy-to-use libraries, you still have to know your way around C++.

Luckily there are a couple of projects to make it easier to create firmware for ESP8266 or ESP32 devices for home automation. One of these is ESPHome.

On its homepage, the ESPHome developers describe it as: "ESPHome is a system to control your ESP8266/ESP32 by simple yet powerful configuration files and control them remotely through Home Automation systems."

The fundamental idea of ESPHome is that you don't program your ESP8266 or ESP32 device, but configure it. Often you only have to configure which pins you have connected to a component, such as a sensor. You don't have to initialize the sensor, read its values in a loop, and process them.

Configuration is a completely different mindset than programming. It lowers the bar even more. With ESPHome, everyone can make home automation devices. 2

Essentially ESPHome creates C++ code based on your configuration. The process looks like this:


So when you write a YAML file with your device's configuration, ESPHome generates C++ code from it. More specifically, ESPHome creates a PlatformIO project using the Arduino framework. PlatformIO then builds the C++ code, and esptool uploads the resulting firmware to your device.

You don't have to know anything about what's happening under the hood. You just have to write the configuration of your device in a YAML file and memorise a small number of commands to let ESPHome do the rest.

The advantages of ESPHome

Why use ESPHome? The first reason is clear from the project's description: because you don't need to be able to program. Even if you're a programmer, ESPHome offers many advantages:

Works completely locally

Many commercial Wi-Fi-based home automation devices need a connection to a cloud service of the manufacturer. In contrast, ESPHome devices work locally and can communicate with a local home automation system such as Home Assistant or an MQTT-based home automation system.

Offers on-device automations

Many home automation systems use a central gateway that contains all the logic, with automations like "if the sun goes down, close the blinds." In contrast, ESPHome offers powerful on-device automations. Your devices can work independently from a home automation gateway, so they keep working if they lose Wi-Fi access or if your home automation gateway crashes.

Offers over-the-air updates

ESPHome includes out-of-the-box over-the-air (OTA) update functionality. This makes it easy to centrally manage your ESPHome devices and update the firmware. This means you don't have to go around your house with your laptop to connect a serial cable to each device and flash the firmware.

Supports a lot of components

ESPHome supports many components out-of-the-box: several types of sensors, switches, and displays (even e-paper displays) are available with just a couple of configuration lines. The list of supported components is growing with every release.

Has extensive documentation

The developers have documented every component in ESPHome, and this documentation is quite good.

Is customisable

Although you create ESPHome firmware by writing a configuration file, ESPHome doesn't hide anything from you. It's still possible to add custom components that you write in C++. You can even look at the C++ code that ESPHome generates and change it.

What hardware do you need?

ESPHome creates custom firmware for the ESP8266 and ESP32 microcontrollers, so you need one of these. There are many types of boards for both microcontrollers, varying in the amount of flash memory, RAM, and available pins. Some of them even come with extras such as a built-in display (OLED, TFT, or e-paper), battery, or camera.

ESPHome doesn't support all features of all boards out-of-the-box. Technically, all ESP8266/ESP32 devices should be able to run ESPHome. Some features just aren't supported yet.

Your first choice is between the ESP8266 or ESP32. If you're buying a device at present, the choice is simple: the ESP32. It is much more capable than its predecessor and has a faster processor, more memory, more peripherals, and adds Bluetooth.

Then comes the choice of board. Espressif has some development boards. Many other companies are making them too. There are even complete kits such as the M5Stack series. These are ESP32 development boards ready to use in your living room in a case with a display, buttons, MicroSD card slot, and speaker. These are currently my favourite devices to use with ESPHome. If you don't need something with a nice finish as the M5Stack devices, the TTGO T-Display ESP32 made by LilyGO is my development board of choice: it has an integrated 1.14 inch TFT display.


Other interesting devices to run ESPHome on are devices from manufacturers such as Sonoff and Shelly. These come with firmware that works with the manufacturer's cloud services. You can however replace the firmware with ESPHome. This unlocks the full potential of these devices and lets you use them in your local home automation system without any link to a cloud system.

What software do you need?

You can use ESPHome to create a fully autonomous microcontroller project --- for example, a plant monitor that turns on an LED if the plant's soil is too dry. However, if you don't publish the plant's status over the network, this would be a waste of the ESP32's capabilities. The main usage cases of ESPHome are:

  • To send a device's sensor measurements to a home automation gateway.

  • To remotely control a device's lights or switches from a home automation gateway.

ESPHome supports two ways of communication between your device and the home automation gateway:

Native API

The ESPHome native API is a highly optimised network protocol using Google's protocol buffers. It's meant to be used with Home Assistant --- a popular open-source home automation system.


MQTT (Message Queuing Telemetry Transport) is an OASIS standard messaging protocol designed with a lightweight publish/subscribe approach for messages. All your ESPHome devices then communicate with an MQTT broker such as Eclipse Mosquitto.

From the beginning (when it was still called esphomeyaml), the ESPHome project has been tightly integrated with Home Assistant, so the ESPHome developers prefer the native API. However, MQTT is fully supported, allowing your devices to communicate with many other home automation gateways, as MQTT is a popular standard. 3

Do you need a development environment?

With ESPHome you don't program your devices but configure them. However, you still need something that looks like a "development" environment. When your device configurations are simple, you could do without, but the more complex they become, you'll need all the help you can get.

This doesn't mean you have to install a full-blown Integrated Development Environment (IDE). You should only need a couple of programs:

An editor

You could make do with a simple text editor such as Notepad (Windows), TextEdit (macOS), or the default text editor on your Linux distribution. However, having an editor with syntax highlighting for YAML files is easier. Some examples are Notepad++ and Sublime Text. If you're a command-line user on Linux, both vim and Emacs work fine. 4 Use whatever you like, because your editor is an important tool to work with ESPHome.

A YAML linter

A linter is a program that checks your file for the correct syntax. An editor with syntax highlighting has this linter built-in, but you can also run this standalone. A good YAML linter is the Python program yamllint. Not only does it check for syntax validity, but also weird things like key repetitions, as well as cosmetic problems such as line length, trailing spaces, and inconsistent indentation. ESPHome includes its own linter, specifically targeted at finding errors in ESPHome configurations. Both linters are complementary.

If you're used to developing in an IDE, an interesting alternative is the ESPHome plugin for Visual Studio Code. 5 This plugin provides validation and completion of what you type in an ESPHome YAML file. It also shows tooltips with help when you hover over keywords in the configuration.


Basic Arduino models don't have network connectivity, which limits their use for home automation and IoT applications.


Note that you can still add your own C++ code to program ESPHome devices if you like.


I prefer MQTT, and my previous book, Control Your Home with Raspberry Pi: Secure, Modular, Open-Source and Self-Sufficient explains how to create a home automation system centered around MQTT. ESPHome fits perfectly in this approach, and this is how I'm using it at home.


I'm an avid vim user.


If you're worried about the telemetry that Microsoft has enabled by default in Visual Studio Code, download VSCodium. This project builds Microsoft's vscode source code without the telemetry.

May 25, 2021

The FOSDEM IRC channels have been moved to the Libera.Chat network. Please join us on the #fosdem channel there. All other #fosdem-* channels and the Matrix bridge will be available soon. As some of our previous channels on the FreeNode network have been taken over by the new staff, these channels are no longer affiliated with FOSDEM. The FOSDEM organisation is saddened by this act of hostility towards the community. We urge our users to leave these channels as soon as possible and move to the new ones on Libera.Chat. We would like to remind you that any channel舰

Got my first Covid-19 vaccine shot today, and apparently also a new wireless device on my home network:

May 22, 2021

I published the following diary on “‘Serverless’ Phishing Campaign“:

The Internet is full of code snippets and free resources that you can embed in your projects. SmtpJS is one of those small projects that are very interesting for developers but also bad guys. It’s the first time that I spot a phishing campaign that uses this piece of JavaScript code.

To launch a phishing campaign, most attackers deploy their phishing kits on servers (most of the time compromised). These kits contain the HTML code, images, CSS files, … but also scripts (often in PHP) to collect the information provided by the victim and store it into a flat file or send them to another service… [Read more]

The post [SANS ISC] “Serverless” Phishing Campaign appeared first on /dev/random.

May 21, 2021

Als we onze hersenen als technologie zouden beschouwen, zou elke ingenieur onder de indruk zijn: met een verbruik van amper 20 watt slaagt ons brein erin om talloze gevarieerde en complexe taken uit te voeren, zoals spraak en beeld herkennen, navigeren in omgevingen waar we nog nooit geweest zijn, nieuwe vaardigheden leren en redeneren over abstracte zaken. Het is dan ook geen wonder dat onze hersenen al sinds jaar en dag als inspiratie dienen om computers 'intelligentie' te geven.

Een belangrijke aanpak in machinaal leren vormen (kunstmatige) neurale netwerken. Ze bootsen de werking van de hersenen na, die een biologisch neuraal netwerk vormen: een kluwen van talloze verbindingen tussen neuronen (hersencellen). Een kunstmatig neuraal netwerk bestaat meestal uit meerdere lagen:

  • Een invoerlaag van neuronen die de invoer van een probleem voorstellen, bijvoorbeeld de pixels in een foto.

  • Een uitvoerlaag van neuronen die de oplossing van het probleem voorstellen. Die herkennen bijvoorbeeld dat er in de foto een hond te zien is.

  • Eén of meer verborgen lagen die berekeningen uitvoeren. Die herkennen bijvoorbeeld vacht, grootte, aantal poten enzovoorts.


Een neuraal netwerk programmeer je niet door expliciet aan te geven hoe het een probleem moet oplossen; je 'traint' het door het vele voorbeelden van een probleem te geven. De parameters van alle neuronen van het neurale netwerk convergeren door die training dan naar de juiste waarden, zodat het de taak leert uit te voeren.

Vooral deep learning maakt het laatste decennium furore in de wereld van machine learning. Bij deep learning maak je gebruik van een neuraal netwerk met een groot aantal lagen tussen invoer en uitvoer. Door dit grote aantal lagen zijn eindelijk heel complexe taken mogelijk. Een neuraal netwerk als GPT-3 gebruikt zo'n honderd lagen. Als je het volledig zelf zou willen trainen, kijk je aan tegen enkele miljoenen euro's aan kosten om gpu-rekenkracht in de cloud te huren. En dan spreken we nog niet over het energieverbruik en de erbij horende CO₂-uitstoot.

Terwijl de neuronen in onze hersenen met pulsen communiceren, is dat aspect bij de klassieke neurale netwerken niet overgenomen omdat discontinue pulsen nu eenmaal wiskundig moeilijker te hanteren zijn dan continue signalen. Toch is er al de hele geschiedenis van AI ook een aanpak geweest om neurale netwerken te modelleren met pulsen. Dat noemen we een gepulst neuraal netwerk (spiking neural network). Door hun wiskundige complexiteit zijn ze nooit doorgebroken.

In een artikel over gepulse neurale netwerken voor PC-Active beschreef ik onlangs recent onderzoek van het Amsterdamse Centrum Wiskunde & Informatica (CWI) en het Eindhovense onderzoekscentrum IMEC/Holst Centre naar een nieuw algoritme dat een factor honderd energiezuiniger zou zijn dan de beste hedendaagse klassieke neurale netwerken. Voorlopig is de techniek nog beperkt tot zo'n duizend neuronen, maar daarmee liggen toepassingen zoals spraakherkenning, de classificatie van elektrocardiogrammen (ecg) en het herkennen van gebaren al in het verschiet.

[Edited: The technique discussed in this diary is not mine and has been used without proper citation of the original author]

I published the following diary on “Locking Kernel32.dll As Anti-Debugging Technique“:

For bad guys, the implementation of techniques to prevent Security Analysts to perform their job is key! The idea is to make our life more difficult (read: “frustrating”). There are plenty of techniques that can be implemented but it’s an ever-ongoing process. Note that this topic is covered in the SANS FOR610 training.

An anti-debugging technique is based on the following steps… [Read more]

The post [SANS ISC] Locking Kernel32.dll As Anti-Debugging Technique appeared first on /dev/random.

May 20, 2021

When I ask people why they fell in love with Drupal, most often they talk about feeling empowered to build ambitious websites with little or no code. In fact, the journey of many Drupalists started with Drupal's low-code approach to site building.

With that in mind, I proposed a new Project Browser initiative in my DrupalCon North America keynote. A Project Browser makes it easy for site builders to find and install modules. You shouldn't need to use the command line!

Making module discovery and module installation easier is long overdue. It's time to kick off this initiative! I will host the first meeting on May 24th between 14:30 UTC and 15:15 UTC. We'll share a Zoom-link on the Project Browser Slack channel before the meeting starts. Join our Slack channel and mark your calendars.

We'll start the meeting with high-level planning, and we need people with all kinds of skills. For example, we'll need help defining requirements, help designing and prototyping the user experience, and much more.

May 19, 2021

Le Titanic était réputé insubmersible. Il était composé de plusieurs compartiments étanches et pouvait flotter même si plusieurs de ces compartiments s’étaient remplis d’eau. Si le gigantesque navire avait foncé droit dans l’iceberg, il y’aurait eu un grand choc, un ou plusieurs compartiments ouverts, des dizaines de blessés suite au choc et un bateau immobilisé, mais en état de flotter. Malheureusement, une vigie a aperçu l’iceberg. Un peu trop tard. En voulant l’éviter, le navire l’a frôlé et a vu sa coque déchirée tout le long, ouvrant des voies d’eau dans chacun des compartiments étanches. Les passagers n’ont rien senti au moment même, mais la catastrophe reste emblématique plus d’un siècle plus tard.

Que ce serait-il passé si, sur le bateau, s’était trouvé un groupe d’industriels voyageant en première classe et dont la spécialité était une hypothétique colle à réparer la coque des bateaux ? Qu’auraient répondu ces richissimes voyageurs voyant arriver à eux un commandant essoufflé et à l’uniforme débraillé, les suppliant de fournir la formule de leur produit pour sauver le navire ?

La crise du réchauffement climatique nous le laissait présager, mais le débat sur l’ouverture des brevets sur les vaccins COVID nous en donne une réponse éclatante.

Les riches industriels auraient simplement roulé de grands yeux en fustigeant l’idée qu’on puisse « piller leur propriété intellectuelle ». Et lorsque le commandant insistera en disant que le bateau coule, ils ricaneront en disant que ce ne sont que les 3e classes qui ont des voies d’eau. Au pire les 2e classes.

Car l’ouverture des vaccins sur le COVID est essentielle. Sans cette ouverture et la possibilité de fabriquer leurs propres vaccins (ce qui est incroyablement simple avec les vaccins basés sur l’ARN messager), l’immense majorité des pays les plus pauvres ne verront pas une goutte de vaccin avant 2023. C’est une catastrophe pour ces populations qui, même en admettant que ce ne sont que des 3e classes, permettrait un véritable bouillon de culture d’où pourrait émerger des variants bien plus puissants et insensibles à nos vaccins actuels. Ne pas voir cela, c’est littéralement penser que les 3e classes vont couler, mais que le pont des 1res classes va continuer sa route comme par miracle.

Didier Pittet, l’homme qui a offert au monde la formule du gel hydroalcoolique dont l’aspect open source a été un atout indéniable dans la lutte contre cette épidémie, l’explique dans son livre « Vaincre les épidémies ». Lors de ses voyages, il a découvert des installations d’une ingéniosité extrême permettant de produire du gel hydroalcoolique dans des régions souffrant d’un grand manque d’infrastructure. Les produits manquants étaient remplacés par des équivalents disponibles tout en gardant voire en améliorant l’efficacité. Parce que, contrairement aux théories racistes qui percolent dans notre colonialisme industriel, ce n’est pas parce qu’une région a un grand déficit en infrastructure que ses habitants n’ont pas de cerveau. Malgré notre vision du monde fondée sur Tintin au Congo, li pti noir li pas complètement crétin et li fabriquer vaccins si li pas empêché par brevets de bwana blanc.

S’il n’y avait que l’aspect humanitaire, la question d’ouverture des brevets COVID ne devrait même pas se poser. Rien que pour cela, tout personne s’opposant à l’ouverture des brevets dans le contexte actuel est un fou dangereux psychopathe.

Mais il y’a pire : les brevets sont une vaste escroquerie mondiale qui a pris des proportions incroyables.

Je vous ai expliqué ma propre expérience avec les brevets, expérience professionnelle durant laquelle on m’a enseigné à écrire un brevet en m’expliquant de but en blanc l’immoralité du système et la manière de l’exploiter.

Lors de son mandat, le parlementaire européen Christian Engström avait largement démontré que l’immense majorité des fonds permettant le développement d’un nouveau médicament étaient publics (de 90% à 99%). La grande majorité du travail de recherche et des travaux préliminaires nécessaires est accomplie dans les universités par des chercheurs payés par de l’argent public. L’industrie du médicament elle-même bénéficie de nombreuses subventions et d’abattements fiscaux.

Au final, un fifrelin du coût final est issu de la firme elle-même, firme qui va obtenir un monopole sur cette recherche pendant 20 ans grâce au brevet. C’est le traditionnel credo financier « Mutualiser les risques, privatiser les profits ».

N’oublions pas que dans l’esprit initial, le brevet est un monopole temporaire (c’était d’ailleurs le nom qu’on lui donnait à l’origine) en échange du fait qu’une invention soit rendue publique. C’est pour cela que le brevet explique l’invention : l’inventeur a 20 ans pour bénéficier de son monopole et s’engage à ce que l’invention devienne un bien public par la suite.

Ce n’est évidemment pas du gout des industries qui ont trouvé une parade : étendre la durée des brevets en modifiant un produit ou en en sortant un nouveau juste avant l’expiration de l’ancien. Ces modifications sont le plus souvent cosmétiques.

Pourquoi croyez-vous que les vaccins sont désormais mélangés en une seule et unique dose malgré les risques d’augmentation des effets secondaires ? Parce qu’il s’agit d’une manière simple de breveter un nouvel emballage pour des vaccins éprouvés qui, sans cela, ne coûterait littéralement plus rien. Et s’il y’a bien une chose que veut éviter l’industrie pharmaceutique, c’est que les gens soient en bonne santé pour pas cher.

Pour résumer, l’industrie pharmaceutique vole littéralement l’argent public pour privatiser des bénéfices plantureux. Et ne peut imaginer remettre en question ses bénéfices alors que la survie de notre société est peut-être en jeu. Le fait qu’il s’agisse du vaccin COVID est d’autant plus ironique, car, depuis 14 mois, l’argent public a afflué sans restriction dans tous les laboratoires du monde. L’industrie pharmaceutique a été payée pour développer un produit garanti de trouver 8 milliards de clients et prétend aujourd’hui privatiser 100% des bénéfices. Dans le cas du vaccin AstraZeneca, l’ironie est encore plus mordante : il a été conçu de bout en bout par une équipe de scientifiques financés par l’argent public et qui souhaitait le rendre open source. La fondation Bill Gates, idéologiquement opposée à toute idée d’open source, a réussi à leur racheter la formule. Tous les scientifiques ne sont pas Didier Pittet.

Un Didier Pittet qui affirme se faire encore régulièrement appeler « L’homme qui nous a fait perdre des milliards » par les représentants d’une industrie pharmaceutique qui ne digère toujours pas la mise open source du gel hydroalcoolique. Cela en dit long sur la mentalité du secteur. Toute possibilité de se soigner ou se protéger à moindre coût est perçue comme « de l’argent perdu ». C’est la pensée typique d’un monopole pour qui l’idée même de compétition est une intolérable agression que les amis politiques doivent bien vite juguler.

On pourrait s’étonner que l’industrie pharmaceutique n’ouvre pas le brevet du vaccin sur le COVID juste pour redorer son blason, pour en faire une belle opération de relations publiques.

Mais il y’a une raison pour laquelle la mise open source du vaccin AstraZeneca devait être empêchée à tout prix, une raison pour laquelle ce brevet ne peut pas, même temporairement, être ouvert.

C’est que le monde comprendrait que ça fonctionne. Que, comme l’a démontré l’aventure du gel hydroalcoolique, ça fonctionne foutrement bien. Cela créerait un précédent. Car si on le fait pour le COVID, pourquoi ne pas le faire pour les médicaments pour le sida ? Pourquoi ne pas le faire sur l’insuline alors qu’aux États-Unis, des diabétiques meurent parce qu’ils ne peuvent simplement pas s’en acheter ? Pourquoi ne pas le faire pour…

Vous imaginez le précédent ? Un monde où les résultats des recherches publiques sont open source ? Où les régions, même les plus pauvres, peuvent développer une indépendance sanitaire avec des chaînes logistiques locales et courtes ?

Non, il faut que l’orchestre continue de jouer. Et tant pis pour les 3e classes. Tant pis pour les 2e classes. Tant pis pour les chaussettes des 1e classes. Le bateau est insubmersible, n’est-ce pas ?

Les vaccins sont l’une des plus belles inventions humaines. N’en déplaise aux conspirationnistes, les vaccins sont la première cause d’augmentation de notre espérance de vie et de notre confort moderne. Je me ferai vacciner contre le COVID à la première occasion par souci de contribuer à une immunité collective (car le vaccin est un médicament altruiste, il ne fonctionne que si une majorité de gens l’utilise). Cela ne m’empêchera pas de pleurer le fait que ce progrès magnifique soit retenu en otage pour contribuer à l’une des plus grandes arnaques économique, idéologique et financière de ce siècle.

Les antivaccins ont raison : il y’a bien un complot qui détruit notre santé et notre tissu social pour maximiser l’enrichissement d’une minorité de monopoles dirigés par des psychopathes à qui des politiciens véreux servent la soupe en se vautrant dans une fange d’immoralité hypocrite.

Mais ce ne sont pas les vaccins eux-mêmes la base du complot, ce sont tout simplement les brevets et les monopoles industriels.

Photo by Ivan Diaz on Unsplash

Je suis @ploum, ingénieur écrivain. Abonnez-vous par mail ou RSS pour ne rater aucun billet (max 2 par semaine). Je suis convaincu que Printeurs, mon dernier roman de science-fiction vous passionnera. Commander et partager mes livres est le meilleur moyen de me soutenir et de m’aider à diffuser mes idées !

Ce texte est publié sous la licence CC-By BE.

May 18, 2021

I published the following diary on “From RunDLL32 to JavaScript then PowerShell“:

I spotted an interesting script on VT a few days ago and it deserves a quick diary because it uses a nice way to execute JavaScript on the targeted system. The technique used in this case is based on very common LOLbin: RunDLL32.exe. The goal of the tool is, as the name says, to load a DLL and execute one of its exported function:

C:\> rundll32.exe sample.dll,InvokedFunction()

Many Windows OS functions can be invoked through RunDLL32… [Read more]

The post [SANS ISC] From RunDLL32 to JavaScript then PowerShell appeared first on /dev/random.

May 16, 2021

If you're only occasionally building ESP32 firmware with Espressif's IoT Development Framework (ESP-IDF), your current build probably fails because the installation from last time is outdated. Then you have to reinstall the newest version of the framework and its dependencies, and you probably still encounter a dependency issue. If it finally works and you have successfully built your firmware, your excitement makes up for the frustrating experience, until the next time you want to build some other ESP32 firmware, and the process repeats.

But there's another way: use the IDF Docker image. It contains ESP-IDF, all the required Python packages, and all build tools such as CMake, make, ninja, and cross-compiler toolchains. In short: a ready-to-use IDF build system.

Building ESP32 firmware with CMake in the container is as easy as:

docker run --rm -v $PWD:/project -w /project espressif/idf build

This mounts the current directory on the host ($PWD) as the directory /project in the container and runs the command build on the code. After the build is finished, you can find your firmware in the current directory.


If you're building a project that needs a specific version of ESP-IDF, just use a tag such as espressif/idf:v4.2.1 instead of espressif/idf. You can find the available tags on Docker Hub.

The same works if the project is using make for its build:

docker run --rm -v $PWD:/project -w /project espressif/idf make defconfig all -j4

You can also use the container interactively.

May 15, 2021

Ou les tribulations d’un auteur bibliophile qui souhaite faire du commerce local de proximité en payant en cryptomonnaies.

Dans ce billet, je vous raconte ma vie de bibliophile, je râle un peu sur les monopoles du monde du livre, je pleure sur la disparition programmée d’un bouquiniste local, je fais la promotion d’Alternalivre, nouvelle plateforme de vente de livres peu ou mal distribués et je vous parle de Print@Home, concept futuriste du livre « téléchargé et imprimé à la maison ». À la fin du billet, vous aurez l’opportunité de commander des livres de mon éditeur pour le tiers ou la moitié du prix normal, selon le cours du Bitcoin. Qu’est-ce que le Bitcoin vient faire dans tout ça ? Mystère !

On entend souvent qu’Amazon ou Facebook ne sont pas des monopoles, car nous ne sommes pas forcés de les utiliser. Après tout, tout le monde peut commander ailleurs que sur Amazon et supprimer son compte Facebook.

Que ce soit clair : si nous étions forcés d’utiliser Amazon ou Facebook, ce ne seraient plus des monopoles, mais des dictatures. Un monopole n’est pas une entreprise impossible à éviter, c’est une entreprise difficile à éviter. Pourquoi ai-je publié un billet annonçant mon retrait de LinkedIn en fanfare ? Parce que cela a été pour moi un choix difficile, un réel risque professionnel. Pourquoi suis-je encore sur Facebook ? Pourquoi est-ce que je passe encore par Amazon ?

Tout simplement parce que c’est très difficile de l’éviter. Dernièrement, voulant éviter de passer par Amazon pour commander un produit particulier, j’ai réussi à trouver un fournisseur différent. Ma commande a nécessité la création d’un énième compte à travers un formulaire bugué qui m’a imposé de changer d’adresse email d’inscription (la première comportant un caractère non toléré par ce site particulier) en cours d’inscription et qui fait que mon compte est désormais inaccessible. Toutes mes données sont dans ce énième silo que je n’utiliserai plus jamais, sans compter les inscriptions non sollicitées à des newsletters. J’ai finalement reçu mon colis sans passer par Amazon, mais à quel prix !

Autre exemple. Grâce à la recommandation d’un lecteur, j’ai voulu acheter le livre « Le Startupisme » d’Antoine Gouritin. Sur le site de l’éditeur, les frais de livraison s’élevaient à 10€. Mais étaient gratuits sur Amazon. Pour un livre à 20€, avouez que ça fait mal de payer 10€. Qu’auriez-vous fait à ma place ? Et je ne vous parle pas des livres en anglais, introuvables partout y compris sur et que je commande… sur (allez comprendre !).

Amazon est donc très difficile à contourner. C’est pourquoi j’apprécie quand les sites reconnaissent que je ne vais pas les utiliser tous les jours et cherchent à me rendre l’achat le plus simple possible, notamment en n’obligeant pas à la création d’un compte (fonctionnalité à laquelle travaille mon éditeur).

Car, dès le début du projet d’édition de Printeurs, mon éditeur et moi sommes tombés d’accord sur le fait d’éviter Amazon autant que possible. Mais, dans l’édition du livre, il n’y a pas qu’Amazon qui abuse de sa position. Un acteur invisible contrôle le marché entre les éditeurs et les libraires : le distributeur.

Mon roman Printeurs a reçu de bonnes critiques et commence a exister sur Babelio, Senscritique et Goodreads.

Je suis extrêmement reconnaissant aux lecteurs qui prennent le temps de noter mes livres ou de mettre une critique, même brève. Il semble que certains lecteurs aient découvert Printeurs grâce à vous ! J’ai néanmoins un conflit moral à vous recommander d’alimenter ces plateformes propriétaires à visée monopolistique. Cela rend certaines critiques postées sur des blogs personnels encore plus savoureuses (surtout celle-là, merci Albédo !).

Malgré cet accueil initial favorable et de bonnes ventes dans les librairies suisses, aucun distributeur belge ou français n’a été jusqu’à présent intéressé par distribuer le catalogue de mon éditeur. Les librairies, elles, ne souhaitent pas passer directement par les éditeurs.

Pire : être dans un catalogue de distributeur n’offre pas toujours la garantie d’être trouvable en libraire. Du moins près de chez moi.

Dans ma ville, riante cité universitaire et pôle intellectuel majeur du pays, il n’existe que deux librairies (!), faisant toutes deux partie de grandes chaines (Fnac et Furet du Nord). Bon, il y’a aussi mon dealer de bandes dessinées devant la vitrine duquel je me prosterne tous les jours et deux bouquineries d’occasion. Enfin, bientôt plus qu’une. La plus grande des deux (et la seule qui fait également de la BD de seconde main) va en effet disparaître, l’université, à travers son organisme de gestion immobilière, ayant donné son congé au gérant. Le gérant m’a fait observer qu’en rénovant la place des Wallons (où est située la bouquinerie), les ouvriers ont installé devant chez lui des emplacements pour parasols. Il semble donc qu’il soit prévu de longue date de remplacer la bouquinerie par un commerce alimentaire. Une pétition a été mise en place pour sauver la bouquinerie.

Mais le gérant n’y croit plus. Il a commencé à mettre son stock en caisse, les larmes plein les yeux, ne sachant pas encore où aller ni que faire, espérant revenir. Deux librairies et bientôt une seule et minuscule bouquinerie pour toute une cité universitaire. Mais plusieurs dizaines de magasins de loques hors de prix cousues dans des caves par des enfants asiatiques. Heureusement qu’il reste mon temple bédéphile, mais je commence à m’en méfier : les vendeurs m’y appellent désormais par mon nom avec obséquiosité, déroulent un tapis rouge à mon arrivée dans la boutique, m’offrent boissons et mignardises en me vantant les dernières nouveautés et en me félicitant de mes choix. Lorsqu’un vendeur débutant ne me reconnait pas, l’autre lui montre sur l’écran ma carte de fidélité ce qui entraine un mouvement machinal de la main et un sifflement. Je ne sais pas trop comment interpréter ces signes…

Mais trêve de digression sentimentalo-locale, abandonnons les moutons de l’Esplanade (le centre commercial climatisé du cru qui tond lesdits ovins pour remplacer leur laine par les loques suscitées) pour revenir aux nôtres.

Souhaitant acquérir le roman Ecce Homo de l’autrice Ingid Aubry, j’ai découvert qu’il était affiché sur le site du Furet du Nord. Je me suis donc rendu dans l’enseigne de ma ville et j’ai demandé à une libraire de faction de le commander. Malgré son empressement sincère, elle n’a jamais trouvé le livre dans ses bases de données. Déjà, le fait qu’elle ait dû regarder dans pas moins de trois bases de données différentes (avec des interfaces très disparates) m’a semblé absurde. Mais le résultat a été sans appel : le livre, pourtant référencé sur le site de la librairie, était incommandable. (livre pourtant distribué par le plus grand distributeur en francophonie, Hachette, quasi-monopole).

Ingrid a finalement fini par m’envoyer le livre par la poste. Son mari Jean-François m’a révélé qu’ils avaient tenté de créer, à deux reprises, une boutique Amazon pour vendre son livre en ligne à moindre prix (il est en effet disponible sur Amazon, mais avec des frais de livraison de… 40€ !). À chaque fois, leur compte a été suspendu. La raison ? Ils vendaient un livre déjà listé sur Amazon. Le livre d’Ingrid est donc littéralement impossible à acheter à un prix décent !

Ingrid et son mari ont pris le problème à bras le corps et lancé leur propre plateforme de vente de livres. Une plateforme dédiée aux livres peu ou mal diffusés. Alternalivre.

Je loue cette initiative en cruel manque de visibilité, étant coincé entre Fnac, Furet du Nord et Amazon pour assouvir ma bibliophilie compulsive (et je déteste acheter mes livres au milieu des tout nouveaux téléviseurs en promotion, ce qui exclut la Fnac). Mon éditeur s’est empressé de rendre Printeurs et toute la collection Ludomire disponible sur Alternalivre (ce qui devrait diminuer les frais d’expédition pour les Français et les Belges). Vous y trouverez également mon livre pour enfant, « Les aventures d’Aristide, le lapin cosmonaute ». Tout en espérant être un jour disponible au Furet du Nord (parce que, de mon expérience, les libraires y sont sympas, compétents et cultivés) voir, honneur suprême, chez Slumberland (qui fait aussi dans le roman de genre, mais je travaille à des scénarios de BD rien que pour être dans leurs rayons).

Écrire un livre et le faire éditer et convaincre les lecteurs de l’acheter n’est donc pas tout. Encore faut-il que ce soit possible pour les lecteurs de l’acquérir. Dans Printeurs, je poussais à l’extrême le concept d’impression 3D jusqu’à inclure les êtres vivants. En 2012, Jaron Lanier imaginait l’impression locale des smartphones et autres gadgets dans son livre « Who owns the future? ». Pourrais-ton l’imaginer pour les livres, floutant de plus en plus la limite entre le livre électronique et le livre papier ?

Oui, m’a répondu mon éditeur en reposant le manuscrit de Printeurs. Et on va l’inventer. Ce sera le Print@home, un concept financé par les contributeurs de la campagne Ulule Printeurs.

Voici donc la première plateforme dédiée aux livres imprimables artisanalement. Cela ne vaut peut-être pas (encore?) une impression professionnelle, mais le concept peut ouvrir la voie à une nouvelle façon de diffuser les livres.

Et le tout, à prix libre bien sûr ! Les livres imprimables étant tous sous publiés sous une licence Creative Commons.

Pour financer cette plateforme, mon éditeur a lancé une campagne de crowdfunding pour le moins originale, car totalement décentralisée. Au lieu de tourner sur le gigantesque serveur d’un acteur quasi monopolistique (comme Ulule), la campagne tourne sur un raspberry dans son bureau. Et au lieu de payer avec des monnaies centralisées, les paiements se font en bitcoins.

Là où ça devient intéressant pour vous, amis lecteurs, c’est que les tarifs en bitcoin sont calculés en faisant l’hypothèse qu’un bitcoin vaut 100.000€. Cela signifie que si le bitcoin est inférieur et vaut, par exemple, 40.000€, vous ne payez que 40% du prix réel des livres commandés. Et cela, y compris pour les livres papier !

Si vous avez quelques centimes de bitcoins et que vous hésitiez à acheter une version papier de Printeurs, des exemplaires à offrir ou la collection complète Ludomire, c’est le moment !

Tout cela sent bon le bricolage et l’expérimentation. Il y’aura des erreurs, des apprentissages. De cette imprécision typiquement humaine dont nous nous sentons inconsciemment privés par les algorithmes perfectionnés des monopoles centralisés. Bonne découverte !

Photo by César Viteri on Unsplash

Je suis @ploum, ingénieur écrivain. Abonnez-vous par mail ou RSS pour ne rater aucun billet (max 2 par semaine). Je suis convaincu que Printeurs, mon dernier roman de science-fiction vous passionnera. Commander et partager mes livres est le meilleur moyen de me soutenir et de m’aider à diffuser mes idées !

Ce texte est publié sous la licence CC-By BE.

May 14, 2021

I published the following diary on “‘Open’ Access to Industrial Systems Interface is Also Far From Zero“:

Jan’s last diary about the recent attack against the US pipeline was in perfect timing with the quick research I was preparing for a few weeks. If core components of industrial systems are less exposed in the wild, as said Jan, there is another issue with such infrastructures: remote access tools. Today, buildings, factories, farms must be controlled remotely or sometimes managed by third parties. If Microsoft RDP is common on many networks (and is often the weakest link in a classic attack like ransomware), there is another protocol that is heavily used to remote control industrial systems: VNC (“Virtual Network Computing”). This protocol works with many different operating systems (clients and servers), is simple and efficient. For many companies developing industrial systems, It is a good candidate to offer remote access… [Read more]

The post [SANS ISC] “Open” Access to Industrial Systems Interface is Also Far From Zero appeared first on /dev/random.

Imagine you want backups of your data (Somehow not everybody wants this, which I don't understand, but there are many things in this world that I don't understand).

Now imagine you want your backups to be encrypted  (Somehow not everybody wants this, which I don't understand, but there are many things in this world that I don't understand).

And imagine you want these backups to be automated. (...)

Now imagine you want these backups in several distinct locations, so they are not lost if your house burns down or if a burglar steals them. (...)

And imagine you want redundancy in case one or more of these remote locations are unavailable.

And of course it should be simple, because nobody wants complex solutions.

What is the best way to have simple personal redundant automated distributed encrypted backups?

A technical solution:

1. Get a Raspberry Pi, attach a USB stick.

2. Rent five VPS spread across five countries.

3. Set up an iSCSI target on all five VPS.

4. Configure the local Raspberry Pi as Initiator.

5. Create an mdadm RAID6 on these five drives and format with LUKS cryptfs?

6. Mirror this device on the USB drive attached to the Pi (So there is a local encrypted copy of the remote distributed encrypted copy).

7. Setup (on the Pi) crontab with rsync to backup certain directories on my personal laptop. Any file copied to that directory will then be encrypted, backed up locally and distributed redundantly in remote.

The only manual thing in this setup is entering the cryptfs key when the Pi needs a reboot (which happens less than once each month but often enough to remember the key).

(I know I can automate the cryptfs key but I refuse. That key is in my head, and nowhere else.)

Note: Maybe the mirroring should happen before the encryption?? Let me sleep on this.

Cost: I think I can get 15GB per VPS for about 15 euro/month (both OVH and Hetzner do this for 3euro/month). So the full backup device will be 45GB (RAID6 of 5x15GB) which should be adequate for personal documents.

I should try this...


The Initiater on the Pi is connected to the five targets. I wonder if this will work...

root@elvire~# ls -l /dev/disk/by-id/ | grep wwn | cut -b1-55,75-
lrwxrwxrwx 1 root root  9 May  2 18:08 wwn-0x60014052b9750 -> ../../sde
lrwxrwxrwx 1 root root  9 May  2 18:08 wwn-0x6001405557bcf -> ../../sdf
lrwxrwxrwx 1 root root  9 May  2 18:08 wwn-0x60014056a1559 -> ../../sdg
lrwxrwxrwx 1 root root  9 May  2 18:08 wwn-0x60014058ec9a4 -> ../../sdd
lrwxrwxrwx 1 root root  9 May  2 18:09 wwn-0x600140598f532 -> ../../sdc
root@elvire~# cat /proc/mdstat
Personalities : [raid6] [raid5] [raid4]
md0 : active raid6 sdc[4] sdd[3] sdg[2] sdf[1] sde[0]
      44012544 blocks super 1.2 level 6, 512k chunk, algorithm 2 [5/5] [UUUUU]
      [>....................]  resync =  1.7% (260284/14670848) finish=434.6min speed=552K/sec
unused devices: <none>

UPDATE 14-MAY-2021: It seems to work.

root@elvire~# cat /proc/mdstat
Personalities : [raid6] [raid5] [raid4]
md0 : active raid6 sdc[4] sdd[3] sdg[2] sdf[1] sde[0]
44012544 blocks super 1.2 level 6, 512k chunk, algorithm 2 [5/5] [UUUUU]

unused devices: <none>
root@elvire~# mount | grep VPS
/dev/mapper/vpsmd0 on /srv/VPS_mirror type ext4 (rw,relatime,stripe=6144)
root@elvire~# crontab -l | tail -1
0 0 * * * rsync -a /srv/VPS_mirror/ /srv/cova/VPS_mirror/
root@elvire~# ls -l /srv/VPS_mirror/
total 24
-rw-r--r-- 1 root root 0 May 11 13:36 VPS_mirror
drwxr-xr-x 2 root root 4096 May 5 11:27 dotfiles
drwxr-xr-x 2 root root 4096 May 5 11:27 etcfiles
drwx------ 2 root root 16384 May 3 17:42 lost+found

May 09, 2021

boot failed

One of the nice new features of FreeBSD 13 is OpenZFS 2.0. OpenZFS 2.0 comes with zstd compression support. Zstd compression can have compression ratios similar to gzip with less CPU usage.

For my backups, I copy the most import data - /etc/, /home, … - first locally to a ZFS dataset. This data gets synced to a backup server. This local ZFS dataset was compressed with gzip, after upgrading the zroot pool and setting zstd as the compress method. FreeBSD failed to boot with the error message:

ZFS: unsupported feature: org.freebsd:zstd
ZFS: pool zroot is not supported
gptzfsboot: failed to mount default pool zroot

As this might help people with the same issue, I decided to create a blog post about it.

Update the boot loader

We need to update the boot loader with the newer version that has zstd compression support.

live CD

Boot from cdrom

Boot your system from FreeBSD 13 installation cdrom/dvd or USB stick and choose <Live CD>. Log in as the root account, the root account doesn’t have a password on the “Live CD”.

Enable ssh

I prefer to update the boot loader over ssh.

I followed this blog post to enable sshd on the live cd:

# ifconfig
# ifconfig <net_interface> up
#	mkdir /tmp/etc
#	mount_unionfs /tmp/etc/ /etc
#	passwd root
#	cd /etc/ssh/
#	vi sshd_config
#	/etc/rc.d/sshd onestart

Log on to the system remotely.

$ ssh

Update the bootloader

The commands to install the bootloader comes from the FreeBSD wiki.

The wiki page page above describes who install FreeBSD on ZFS root pool. This was very useful before the FreeBSD installer had native ZFS support.

List your partitions to get your boot device name and slice number. The example below is on FreeBSD virtual machine, the device name is vtb0 and the slice number is 1. On a physical FreeBSD system, the device name is probably ada0.

root@:~ # gpart show
=>       40  419430320  vtbd0  GPT  (200G)
         40       1024      1  freebsd-boot  (512K)
       1064        984         - free -  (492K)
       2048    8388608      2  freebsd-swap  (4.0G)
    8390656  411037696      3  freebsd-zfs  (196G)
  419428352       2008         - free -  (1.0M)

=>     33  2335913  cd0  MBR  (4.5G)
       33  2335913       - free -  (4.5G)

=>     33  2335913  iso9660/13_0_RELEASE_AMD64_DVD  MBR  (4.5G)
       33  2335913                                  - free -  (4.5G)

root@:~ # 

I use a legacy BIOS on my system. On a system with a legacy BIOS, you can use the following command to update the bootloader.

root@:~ # gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 vtbd0
partcode written to vtbd0p1
bootcode written to vtbd0
root@:~ # 

To update the bootloader on a UEFI system.

# gpart bootcode -p /boot/boot1.efi -i1 ada0

Should to the trick.

Reboot your FreeBSD 13 system and enjoy zstd compression.

root@:~ # sync
root@:~ # reboot

Have fun!


May 06, 2021

I published the following diary on “Alternative Ways To Perform Basic Tasks“:

I like to spot techniques used by malware developers to perform basic tasks. We know the LOLBins that are pre-installed tools used to perform malicious activities. Many LOLBins are used, for example, to download some content from the Internet. Some tools are so powerful that they can also be used to perform unexpected tasks. I found an interesting blog article describing how to use curl to copy files… [Read more]

The post [SANS ISC] Alternative Ways To Perform Basic Tasks appeared first on /dev/random.

May 03, 2021

Bitterzoet (maar zoals goeie fondant chocolade dat is) verhaal van een jonge man die enkel beleefd en onzichtbaar wilt zijn. Over enkel “een goed functionerend radertje” proberen zijn, maar daar niet in slagen. Over prachtig kunnen zingen, maar dat niet willen. Over liefde, verlies en vluchten. Over Noorwegen, de Faeröer eilanden en een heel klein beetje over de Caraïben. En over de Cardigans.

Aanrader; 4,5 ipv 5 sterren, voornamelijk omdat ik “Max, Micha & het Tet-offensief” nog beter vond.

April 30, 2021

Il est assez rare qu’un livre bouleverse votre représentation du monde. Ou mieux, qu’il éclaire votre compréhension dudit monde en reliant sous un modèle unique parfaitement théorisé toute une série d’intuitions que vous aviez dans des domaines forts différents.

C’est exactement l’effet qu’a eu sur moi le livre Monopolized, de David Dayen, malheureusement pas encore traduit en français et que je n’ai pas réussi à obtenir à un prix décent en Europe (je me suis rabattu sur la version électronique pirate, la faute aux monopoles du livre).

L’idée de David Dayen est de nous démontrer que la puissance économique (et donc politique) est de plus en plus concentrée dans un nombre de plus en plus restreint de mains au travers des monopoles et autres oligopoles, de nous expliquer pourquoi, historiquement et économiquement il en est ainsi, pourquoi c’est une mauvaise chose pour tous ceux qui ne sont pas à la tête d’un monopole et en quoi c’est une tendance « mécanique » : la monopolisation dans un domaine entraine l’apparition de monopoles dans les domaines connexes, ce qui fait boule de neige. Pour finir, David Dayen émet la thèse que seule la régulation politique peut enrayer les abus des monopoles (ce qu’elle faisait d’ailleurs à peu près bien jusque dans les années huitante).

Ceux d’entre vous qui suivent ce blog connaissent mon intérêt pour les problématiques liées aux monopoles de haute technologie (Google, Facebook, Microsoft, etc.). Ma fascination pour Monopolized vient du fait que j’ai compris que mon combat se dirigeait contre une simple conséquence anecdotique d’un paradigme beaucoup plus large : la monopolisation.

D’ailleurs, entre nous, pourquoi êtes-vous si nombreux à avoir l’intuition que « la financiarisation » de l’économie est une mauvaise chose alors qu’en soit, la finance voire même le trading ne sont que des échanges économiques entre adultes consentants ? À cause de la monopolisation de cette finance.

Pourquoi y’a-t-il une telle défiance envers l’industrie pharmaceutique entrainant des comportements absurdes comme le refus de la vaccination ? À cause de la monopolisation.

Pourquoi, quand je m’arrête dans une supérette ou une pompe à essence pour acheter un en-cas n’ai-je le choix qu’entre des dizaines de variations du même mauvais chocolat enrobé de mauvais sucre ? La monopolisation.

La monopolisation jusque dans l’art. La planète écoute désormais une vingtaine de musiciens surpayés alors que des millions d’autres tout aussi talentueux ne gagnent pas un sous, tout bénéfice pour les producteurs.

La tentation du monopole

De tout temps, le monopole s’est imposé comme le meilleur moyen de générer des fortunes pharaoniques. Lorsque vous disposez d’un monopole pour un produit quelconque, vous bénéficiez d’une rente immuable tant que ce produit sera consommé. Et comment s’assurer que le produit restera consommé ? Tout simplement en rachetant les jeunes entreprises qui développent des alternatives ou, mieux, qui pourraient être en mesure de le faire.

Un monopole peut augmenter les prix d’un produit à volonté pour maximiser ses rentes. Mais ce serait maladroit, car cela augmenterait d’autant les incitants économiques pour créer de la compétition. Il est donc préférable pour un monopole de garder le prix le plus bas possible pour empêcher toute compétition. Comment faire de la concurrence à Google ou Facebook alors que, pour l’utilisateur final, le produit semble gratuit ?

Au lieu d’augmenter ses tarifs, un monopole va chercher à diminuer ses coûts. Premièrement en exploitant ses fournisseurs qui, généralement, n’ont pas le choix, car pas d’autres clients potentiels. C’est le monopsone, l’inverse du monopole : un marché avec un seul acheteur et beaucoup de vendeurs. Grâce à cet état de fait, le monopole peut augmenter ses marges tout en gardant les mains propres. Le sale travail d’exploitation des travailleurs est transféré à des fournisseurs voire aux travailleurs eux-mêmes, considérés comme indépendants. C’est le phénomène de « chickenization » bien connu aux États-Unis où les éleveurs de poulets sont obligés de suivre des règles très strictes d’élevage, d’acheter leurs graines et d’utiliser le matériel fourni par… leur seul et unique acheteur qui peut fixer le prix d’achat du poulet. Les éleveurs de poulets sont, pour la plupart, endettés auprès de leur propre client qui peut refuser d’acheter les poulets et les ruiner complètement, mais qui se garde bien de le faire, leur laissant juste de quoi avoir l’espoir d’un jour en sortir. Dans « Planètes à gogos » et sa suite, Frederik Pohl et Cyril Kornbluth nous mettaient en garde contre ce genre d’abus à travers une superbe scène où le personnage principal, ex-publicitaire à succès, se retrouve à travailler sur Vénus pour un salaire qui ne lui permet juste pas de payer son logement et sa nourriture fournie par son employeur monopolistique.

Enfin, le dernier facteur permettant à un monopole de faire du profit, c’est de réduire toute innovation voire même d’activement dégrader la qualité de ses produits. Un phénomène particulièrement bien connu des habitants des zones rurales aux États-Unis où la connexion Internet est de très mauvaise qualité et très chère. Preuve s’il en est qu’il s’agit d’une réelle volonté, des villes ont décidé de mettre en place des programmes municipaux d’installation de fibre optique. Il en résulte… des attaques en justice de la part des fournisseurs d’accès Internet traditionnel pour « concurrence déloyale ».

La morbidité des monopoles

Depuis des siècles, la nocivité des monopoles est bien connue et c’est même l’un des rôles premiers des états, quels que soient la tendance politique : casser les monopoles (les fameuses lois antitrust), mettre hors-la-loi les accords entre entreprises pour perturber un marché ou, si nécessaire, mettre le monopole sous la coupe de l’état, le rendre public. Parfois, l’état peut accorder un monopole temporaire et pour un domaine très restreint à un acteur particulier. Cela pouvait être une forme de récompense, une manière de donner du pouvoir à un vassal ou à l’encourager. Les brevets et le copyright sont des monopoles temporaires de ce type.

Mais, en 1980, Robert Bork, conseiller du président Reagan, va émettre l’idée que les monopoles sont, tout compte fait, une bonne chose sauf s’ils font monter les prix. À partir de cet instant, l’idée va faire son chemin parmi les gens de pouvoir qui réalisent qu’ils sont des bénéficiaires des fameux monopoles. Mais comme je l’ai expliqué ci-dessus, un monopole résulte rarement en une augmentation franche et directe du prix. Pire, il est impossible de prévoir. En conséquence de quoi, les administrations américaines vont devenir de plus en plus souples avec les fusions et les acquisitions.

Si IBM et AT&T sont cassés en plein élan dans les années 80, si Microsoft doit mollement se défendre dans les années 90, Google et Facebook auront un boulevard à partir des années 2000, boulevard ouvert par le fait que les acteurs du passé ont encore peur des lois antitrust et que les acteurs du futur ne peuvent plus émerger face à la toute-puissance de ce qu’on appelle désormais les GAFAM, ces entreprises qui ont saisi la fenêtre d’opportunité parfaite. Une dominance entérinée de manière officielle quand, après les attentats du 11 septembre 2001, l’administration américaine stoppe toute procédure visant à interdire à Google d’exploiter les données de ses utilisateurs, procédure annulée en échange d’une promesse, tenue, que Google aidera désormais la défense à détecter les terroristes grâce aux données susnommées (anecdote racontée dans The Age of Surveillance Capitalism, de Shoshana Zuboff).

Fusion, acquisition

Le laxisme face aux monopoles donne le signal d’une course à l’ultra-monopolisation. Pour survivre dans une économie de mastodontes, il n’est d’autre choix que de devenir un mastodonte soi-même. En fusionnant ou en rachetant de plus petits concurrents, on détruit la compétition et on diminue les coûts de production, augmentant de ce fait les bénéfices et construisant autour de son business ce que Warren Buffet appelle une « douve protectrice » qui empêche toute concurrence. Warren Buffet n’a jamais fait un mystère que sa stratégie d’investissement est justement de favoriser les monopoles. Mieux : il en a fait une idéologie positive. Pour devenir riche, à défaut de construire un monopole à partir de rien (ce que bien peu pourront faire après Mark Zuckerberg et Jeff Bezos), investissez dans ce qui pourrait devenir un monopole !

Il faut dire que le business des fusions/acquisitions est particulièrement juteux. Les transactions se chiffrent rapidement en milliards et les cabinets de consultance qui préparent ces fusions sont payés au prorata, en sus des frais administratifs.

Alors jeune ingénieur en passe d’être diplômé, j’ai participé à une soirée de recrutement d’un de ces prestigieux cabinets (un des « Big Three »). Sur la scène, une ingénieure de quelques années mon aînée, décrivait le cas sur lequel elle avait travaillé, sans donner les noms. Les chiffres s’alignaient explicitement avec, dans la colonne « bénéfices », le nombre d’employés que la fusion permettrait de licencier avec peu ou prou d’indemnités, le nombre de sites à fermer, les opportunités de délocalisation pour échapper à certaines régulations financières ou écologiques.

J’ai levé la main et j’ai demandé, naïvement, ce qu’il en était des aspects éthiques. L’oratrice m’a répondu avec assurance que l’éthique était très importante, qu’il y avait une charte. J’ai demandé un exemple concret de la manière dont la charte éthique était appliquée au projet décrit. Elle me répondit que, par exemple, la charte impliquait que l’intérêt du client passait avant toute chose, ce qui impliquait le respect de la confidentialité et l’interdiction pour un employé du cabinet d’être en contact avec les employés du cabinet qui représentaient l’autre côté du deal.

J’ai été surpris d’une telle naïveté et, surtout, de la non-réponse à ma question. Après la conférence, je suis allé la trouver durant le cocktail dinatoire traditionnel. Un verre à la main, j’ai insisté. Elle ne comprenait pas de quoi je voulais parler. J’ai explicité ce que j’entendais par éthique : l’impact de cette fusion sur les travailleurs, sur les conditions économiques, sur l’aspect écologique global. L’éthique quoi !

La brave ingénieure, qui nous avait été présentée comme ayant obtenu le grade le plus élevé à la fin de ses études (le cabinet ne recrutant que parmi les meilleures notes et les doctorats, je n’avais d’ailleurs aucune chance), est devenue blanche. Elle m’a regardé la bouche ouverte et a fini par balbutier qu’elle n’avait jamais pensé à cela.

Il faut bien avouer que, face à un tel pactole, il est tentant de ne voir que des colonnes de chiffres. En théorie, les cabinets spécialistes des fusions/acquisitions sont censés déconseiller les fusions qui ne seraient pas vraiment intéressantes. Mais, sans fusion, pas de pourcentage. Aucun cabinet ne va donc déconseiller ce type d’opération. C’est également particulièrement intéressant pour les individus hautement impliqués. Wikipedia raconte que, entre 2009 et 2013, un jeune banquier d’affaire de la banque Rothschild va gagner plus de deux millions d’euros en travaillant sur des fusions et des rachats controversés. Il faut avouer que, selon ses supérieurs, il est extrêmement doué pour ce métier et pourrait devenir l’un des meilleurs de France. Il va cependant choisir une autre voie, profitant des appuis importants de ce milieu. Son nom ? Emmanuel Macron.

La quête de rendement et la métamorphose du métier d’entrepreneur.

Historiquement, un entrepreneur est une personne qui cherche à créer un business. Plutôt que de travailler pour un patron, l’entrepreneur va travailleur pour des clients. Les entrepreneurs à succès pouvaient espérer gagner très bien leur vie, une société florissante pouvant se permettre de payer un très haut salaire à son patron fondateur. Il n’en reste pas moins qu’il s’agissait d’un salaire lié à un travail. Pour les investisseurs, une entreprise pouvait également verser des dividendes.

Cependant, la quête de rendement élevé a, ironiquement, entrainé la chute des dividendes. À quoi bon gagner quelques pour cent par an sur une somme immobilisée et donc totalement illiquide, ne permettant pas de bénéficier d’autres opportunités ? La plupart des entreprises actuelles ne versent d’ailleurs que peu ou prou de dividendes. Achetez pour 1000€ d’actions et, à la fin de l’année, vous seriez chanceux d’avoir plus de 10€ de dividendes.

Pour un investisseur qui parie sur une jeune entreprise, il n’existe que deux façons de faire du profit et récupérer sa mise (ce qu’on appelle un « exit »). Premièrement si cette entreprise est cotée en bourse, ce qui est extrêmement rare et prend beaucoup de temps ou, et c’est la voie préférée, en voyant cette entreprise rachetée.

C’est également tout bénéfice pour les fondateurs qui au lieu de travailler toute leur vie sur un projet espèrent désormais gagner un pactole après quelques années seulement (et beaucoup de chance). J’ai vu et encadré suffisamment de startups et de levées de fonds dans ma vie professionnelle pour comprendre que le but d’une startup, désormais, n’est plus de faire un produit, mais d’être rachetée. Pas de vendre mais d’être vendu. Les modalités potentielles d’exit sont discutées avant même les premières lignes de code ou le premier client. De cette manière, toute l’énergie entrepreneuriale est dirigée vers un seul et unique objectif : faire croître les géants.

Ces échanges sont facilités par le fait que les investisseurs, les fameux Venture Capitalists, ont généralement des liens étroits avec les actionnaires de ces fameux géants qui rachètent. Dans certains cas, ce sont tout simplement les mêmes personnes. Pour faire simple, si je fais partie du board de Facebook, je vais donner un million à de jeunes entrepreneurs en les conseillant sur la meilleure manière de développer un produit que Facebook voudra racheter puis je m’arrange pour que le-dit Facebook rachète la boîte à un tarif qui valorise mes parts à 10 millions. Un simple trafic d’influence qui me rapporte 9 millions. Si la startup n’a pas développé de produit, ce n’est pas grave, on parlera alors d’acqui-hire (on rachète une équipe, une expertise et on tue le produit).

C’est également tout bénéfice pour Facebook qui tue de cette manière toute concurrence dans l’œuf et qui augmente ses effectifs pour une bouchée de pain. Voire même qui optimise fiscalement certains bénéfices de cette manière.

Ce procédé est tellement efficace qu’il s’est industrialisé sous forme de fonds. Les investisseurs, au lieu de mettre 1 million dans une jeune startup, créent un fonds de manière à mettre 100 millions dans 100 startups. Les 100 millions sont fournis par les riches qui sont en dehors de toutes ces histoires et qui sont du coup taxés avec des frais de gestion et un pourcentage sur les bénéfices (typiquement, 2 ou 3% du capital par an plus entre 20 et 30% des bénéfices reviennent au gestionnaire du fonds. Ce qui reste intéressant : si un gestionnaire transforme votre million en 10  millions, vous pouvez lui donner 3 millions, vous n’en aurez pas moins gagné 6 millions. Une fameuse somme !).

Les fonds de type Private Equity fonctionnent sur le même principe. Les gestionnaires investissent dans diverses entreprises durant 2 ou 3 ans puis se donnent 6 ou 7 ans pour réaliser des exits. L’argent est bloqué pour 10 ans, mais avec la promesse d’avoir été multiplié par 5 au bout de cette période (ce qui fait du 20% par an !).

Comment garantir les exits ? Premièrement grâce à des lobbies auprès des géants du secteur susceptibles d’acheter les petites boîtes. En dernier recours, il restera au gestionnaire du fonds la possibilité de créer un nouveau fonds pour racheter les invendus du premier. Cette opération fera du premier fond un réel succès, asseyant la réputation du gestionnaire et lui permettant de lever encore plus d’argent dans son nouveau fonds.

Le paradoxe du choix

Cette concentration est pourtant rarement perceptible lorsque nous allons faire nos courses. Et pour cause ? Les monopoles ne sont pas bêtes et proposent « de la diversité pour satisfaire tous les consommateurs ». Que vous achetiez des M&Ms, des Maltesers, un Mars, un Milky Way, un Snickers, un Twix, un Bounty, un Balisto ou bien d’autres, seul l’emballage change. Il s’agit des mêmes produits fabriqués dans les mêmes usines.

Du riz Uncle Ben’s, de l’Ebly, des pâtes Miracoli ou Suzi Wan ? Pareil.

Et pour les animaux ? Pedigree, Cesar, Whiskas, Royal Canin, Sheba, Kitekat, Canigou, Frolic ? Pareil.

Passez dans le rayon chewing-gum, toutes les marques sont par le même fournisseur.

D’ailleurs, je n’ai pas choisi ces exemples au hasard. Le fournisseur en question est identique pour toutes les marques que je viens de citer : Mars.

Rendez-vous dans votre supermarché et supprimez les produits Mars, Nestlé et Unilever. Il ne restera plus grand-chose à part quelques produits Kraft, Danone ou Pepsico. Vos magasins bio ne sont pas en reste. Certaines marques bio appartiennent aux même grands groupes, d’autres sont en pleine consolidation, car le marché est encore jeune.

L’exemple de la nourriture est frappant, mais il en est de même dans tous les secteurs lorsqu’on gratte un peu : automobile, hôtellerie, vêtements, voyages, compléments alimentaires naturels et bio… Grâce aux « alliances », il n’existe en réalité plus qu’une poignée de compagnie aérienne en Europe.

Lutter contre les monopoles.

Les monopoles, par leur essence même, sont difficilement évitables. Nous consommons monopoles, nous travaillons pour un monopole ou ses sous-traitants, renforçant chaque jour leur pouvoir.

Intuitivement, nous percevons le danger. Dans un billet précédent, je vous parlais de l’intuition à l’origine des théories du complot. Si l’on applique le filtre « monopole » à ces théories du complot, la révélation est saisissante.

Le monopole de l’industrie pharmaceutique conduit à des problématiques importantes (lobby pour la non-mise en open source du vaccin contre le Covid, fourniture des vaccins mélangés dans des ampoules pour diminuer les coûts même au prix d’une baisse d’efficacité et d’une augmentation des effets secondaires, augmentation des tarifs et lobby pour des brevets absurdes) qui entrainent une méfiance envers le principe même d’un vaccin, surtout développé en un an alors que les entreprises ont toujours dit qu’il fallait des années (afin d’allonger la durée de vie des brevets et créer des pénuries sur le marché).

Le contrôle total des monopoles du web sur nos données entraine une méfiance envers les ondes qui transmettent lesdites données voire même, dans une succulente fusion avec le monopole précédent, la crainte que les vaccins contiennent des puces 5G pour nous espionner (mais n’empêche cependant personne d’installer des espions comme Alexa ou Google Home dans sa propre maison).

Le sentiment profond d’une inégalité croissante, d’une financiarisation nocive, d’une exploitation sans vergogne de la planète et des humains qui s’y trouvent, tout cela est créé ou exacerbé par la prise de pouvoir des monopoles qui n’hésitent pas à racheter des entreprises florissantes avant de les pousser à la faillite afin de liquider tous les avoirs (bâtiments, machines, stocks). Une technique qui permet de supprimer la concurrence tout en faisant du profit au prix de la disparition de certaines enseignes de proximité dans les régions les plus rurales (sans parler du désastre économique des pertes d’emploi massives brutales dans ces mêmes régions).

Heureusement, la prise de conscience est en train de se faire. De plus en plus de scientifiques se penchent sur le sujet. Un consensus semble se développer : il faut une réelle volonté politique de démanteler les monopoles. Volonté difficile à l’heure où les politiciens ont plutôt tendance à se prosterner devant les grands patrons en échange de la promesse de créer quelques emplois et, dans certains cas, la promesse d’un poste dans un conseil d’administration une fois l’heure de la retraite politique sonnée. S’il y a quelques années, un chef d’entreprise était tout fier de poser pour une photo serrant la main à un chef d’État, aujourd’hui, c’est bel et bien le contraire. La fierté brille dans les yeux des chefs d’État et des ministres.

Si l’Europe cherche à imiter à tout prix son grand frère américain, les Chinois semblent avoir bien compris la problématique. Un géant comme Alibaba reste sous le contrôle intimidant de l’état qui l’empêche, lorsque c’est nécessaire, de prendre trop d’ampleur. La disparition, pendant plusieurs mois, de Jack Ma a bien fait comprendre qu’en Chine, être milliardaire ne suffit pas pour être intouchable. Ce qui ne rend pas le modèle chinois désirable pour autant…

Un autre consensus se dessine également : l’idéologie promue par Robert Bork sous Reagan est d’une nocivité extrême pour la planète, pour l’économie et pour les humains. Même pour les plus riches qui sont pris dans une course frénétique à la croissance de peur d’être un peu moins riches demain et qui savent bien, au fond d’eux-mêmes, que cela ne durera pas éternellement. Cette idéologie est également nocive pour tous les tenants d’une économie de marché libérale : les monopoles détruisent littéralement l’économie de marché ! Le capitalisme reaganien a apporté aux Américains ce qu’ils craignaient du communisme : de la pénurie et de la piètre qualité fournie par des monopoles qui exploitent une main-d’œuvre qui tente de survivre.

Avant de lutter, avant même d’avoir des opinions sur des sujets aussi variés que la vie privée sur le web, la finance, la politique ou la malbouffe, il est important de comprendre de quoi on parle. À  ce titre, Monopolized de David Dayen est une lecture édifiante. Certainement trop centré sur les États-Unis d’Amérique (mais qui déteignent sur l’Europe), écrit « à l’américaine » avec force anecdotes et certaines généralités questionnables (par exemple le chapitre sur les Private Equity), le livre n’en reste pas moins une somme parfaitement documentée et argumentée, bourrée de références et de repères bibliographiques.

Ce qui est intéressant également, c’est de constater que notre vision de la politique a été transformée avec, à droite, les tenants de monopoles privés et, à gauche, les tenants de monopoles appartenant à l’état. Une ambiguïté sur laquelle Macron, fort de son expérience, a parfaitement su jouer en proposant un seul et unique parti monopolistique n’ayant que pour seul adversaire le populisme absurde.

Lorsque vous êtes témoin d’une injustice, posez-vous la question : ne s’agit-il pas d’un monopole à l’œuvre ? Et si le futur passait par la désintégration pure et simple des monopoles ? Depuis les plus petits et les plus éphémères comme les brevets et le copyright, transformé en arme de censure massive, jusqu’aux géants bien connus.

Photo by Joshua Hoehne on Unsplash

Je suis @ploum, ingénieur écrivain. Abonnez-vous par mail ou RSS pour ne rater aucun billet (max 2 par semaine). Je suis convaincu que Printeurs, mon dernier roman de science-fiction vous passionnera. Commander et partager mes livres est le meilleur moyen de me soutenir et de m’aider à diffuser mes idées !

Ce texte est publié sous la licence CC-By BE.

April 29, 2021

Als je nas of server op zolder is gecrasht, wil je waarschijnlijk voorkomen dat je het probleem ter plaatse moet oplossen. Met een kvm-over-ip-systeem kun je op afstand ingrijpen via het netwerk. Met Pi-KVM bouw je zoiets zelf met een Raspberry Pi en enkele goedkope componenten.

Op de GitHub-pagina van Pi-KVM staat uitgelegd welke componenten je nodig hebt om je eigen kvm te maken:

  • Raspberry Pi 4

  • hdmi-naar-csi-2-adapterbordje of hdmi-naar-usb-dongel

  • een usb-splitter om de usb-c-aansluiting van de Raspberry Pi 4 zowel voor stroom als usb-otg in te zetten

De Raspberry Pi 4 krijgt dan toegang tot de hdmi-uitvoer van je nas of server en emuleert een toetsenbord, muis en opslag. De opensourcesoftware Pi-KVM biedt dit alles in een webinterface aan, zodat je via je webbrowser op je nas of server kunt werken alsof je ernaast zit:


Voor PCM schreef ik het artikel Pi-KVM: Nas benaderen via Raspberry Pi op afstand. Ik leg er uit hoe je zelf een usb-splitter maakt van twee usb-kabels en hoe je Pi-KVM installeert en instelt en het gebruikt om een iso-image aan te koppelen en op je nas of server te installeren.

De ontwikkelaars hebben ook een eigen HAT ontwikkeld die je op de Raspberry Pi 4 kunt monteren. De verkoop start binnenkort.

I published the following diary on “From Python to .Net“:

The Microsoft operating system provides the .Net framework to developers. It allows to fully interact with the OS and write powerful applications… but also malicious ones. In a previous diary, I talked about a malicious Python script that interacted with the OS using the ctypes library. Yesterday I found another Python script that interacts with the .Net framework to perform the low-level actions… [Read more]

The post [SANS ISC] From Python to .Net appeared first on /dev/random.

April 23, 2021

I published the following diary on “Malicious PowerPoint Add-On: ‘Small Is Beautiful‘”:

Yesterday I spotted a DHL-branded phishing campaign that used a PowerPoint file to compromise the victim. The malicious attachment is a PowerPoint add-in. This technique is not new, I already analyzed such a sample in a previous diary. The filename is “dhl-shipment-notification-6207428452.ppt” (SHA256:934df0be5a13def81901b075f07f3d1f141056a406204d53f2f72ae53f583341) and has a VT score of 18/60.. [Read more]

The post [SANS ISC] Malicious PowerPoint Add-On: “Small Is Beautiful” appeared first on /dev/random.

April 22, 2021

Last week, Drupalists around the world gathered virtually for DrupalCon North America 2021.

In good tradition, I delivered my State of Drupal keynote. You can watch the video of my keynote, download my slides (244 MB), or read the brief summary below.

I gave a Drupal 9 and Drupal 10 update, talked about going back to our site builder roots, and discussed the need to improve Drupal's contributor experience.

Drupal 9 update

People are adopting Drupal 9 at a record pace. We've gone from 0 to 60,000 websites in only one month. In contrast, it took us seven months to reach the same milestone with Drupal 7, and three months for Drupal 8.

A chart that shows that Drupal 9 adoption is much faster than Drupal 7's and Drupal 8's
With Drupal 8, after about 1.5 years, only a third of the top 50 Drupal modules were ready for Drupal 8. Now, only 10 months after the release of Drupal 9, a whopping 90% of top 50 modules are Drupal 9 ready.
A chart that shows the Drupal 9 module ecosystem is pretty much ready

Drupal 10 update

Next, I spoke about the five big initiatives for Drupal 10, which are making progress:

  1. Decoupled menus
  2. Easy out of the box
  3. Automated updates
  4. Drupal 10 readiness
  5. New front-end theme initiative

I then covered some key dates for Drupal 9 and 10:

A timeline that shows Drupal 9.3 will be released in December 2021 and Drupal 10.0.0 in June 2022

Improving the site builder experience with a project browser

A Drupal robot staring in the distance along with a call to action to focus on the site builder experience

When I ask people why they fell in love with Drupal, most often they talk about feeling empowered to build ambitious websites with little or no code. In fact, the journey of many Drupalists started with Drupal's low-code approach to site building. It's how they got involved with Drupal.

This leads me to believe that we need to focus more on the site builder persona. With that in mind, I proposed a new Project Browser initiative. One of the first things site builders do when they start with Drupal is install a module. A Project Browser makes it easier to find and install modules.

If you're interested in helping, check out the Project Browser initiative and join the Project Browser Slack channel.

Modernizing's collaboration tools with GitLab

A small vessel sailing towards a large GitLab boat

Drupal has one of the largest and most robust development communities. And's collaboration tools have been key to that success.

What you might not know is that we've built these tools ourselves over the past 15+ years. While that made sense 10 years ago, it no longer does today.

Today, most Open Source communities have standardized on tools like GitHub and GitLab. In fact, contributors expect to use GitHub or GitLab when contributing to Open Source. Everything else requires too much learning.

For example, here is a quick video that shows of how easy it is to contribute to Symfony using GitHub:

Next, I showed how people contribute to Drupal. As you can see in the video below, the process takes much longer and the steps are not as clear cut.

(This is an abridged version of the full experience; you can also watch the full video.)

To improve Drupal's contributor experience, the Drupal Association is modernizing our collaboration tools with GitLab. So far, this has resulted in some great new features. However, more work is required to give new Drupalists an easier path to start contributing.

Please reach out to Heather Rocker, the Executive Director at Drupal Association, if you want to help support our GitLab work. We are looking for ways to expand the Drupal Association's engineering team so we can accelerate this work.'s goals for Gitlab along with positive attendee feedback in chat

Thank you

I'd like to wrap up with a thank you to the people and organizations who have contributed since we released Drupal 9 last June. It's been pretty amazing to see the momentum!

The names of the 1,152 individuals that contributed to Drupal 9 so far
The logos of the 365 organizations that contributed to Drupal 9 so far

I published the following diary on “How Safe Are Your Docker Images?“:

Today, I don’t know any organization that is not using Docker today. For only test and development only or to full production systems, containers are deployed everywhere! In the same way, most popular tools today have a “dockerized” version ready to use, sometimes maintained by the developers themselves, sometimes maintained by third parties. An example is the Docker container that I created with all Didier’s tools. Today, we are also facing a new threat: supply chain attacks (think about Solarwinds or, more recently, CodeCov). Let’s mix the attraction for container technologies and this threat, we realize that  Docker images are a great way to compromise an organization… [Read more]

The post [SANS ISC] How Safe Are Your Docker Images? appeared first on /dev/random.