10 interesting stories served every morning and every evening.
You’re only seeing part of our work. Create an account to explore all our investigations. Try it for free for 30 days, no strings attached.
You’re only seeing part of our work. Create an account to explore all our investigations. Try it for free for 30 days, no strings attached.
The European messaging service Zivver — which is used for confidential communication by governments and hospitals in the EU and the U. K. — has been sold to Kiteworks, an American company with strong links to Israeli intelligence. Experts have expressed deep concerns over the deal.
With the sale of Amsterdam-based data security company Zivver, sensitive information about European citizens is now in the hands of Kiteworks. The CEO of the American tech company is a former cyber specialist from an elite unit of the Israeli army, as are several other members of its top management. Various institutions in Europe and the U. K. — from hospitals to courts and immigration services — use Zivver to send confidential documents. While Zivver says these documents are encrypted, an investigation by Follow the Money shows that the company is able to read their contents.Why does this matter?Cybersecurity and intelligence experts told Follow the Money that the takeover should either have been prevented or properly assessed in advance. Zivver processes information that could be extremely valuable to third parties, such as criminals or foreign intelligence services. That information is now subject to invasive U.S. law, and overseen by a company with well-documented links to Israeli intelligence.How was this investigated?Follow the Money investigated the acquisition of Zivver and the management of Kiteworks, and spoke to experts in intelligence services and cyber security.
This article is part of an ongoing series.
When the American data security company Kiteworks bought out its Dutch industry peer Zivver in June, CEO Jonathan Yaron described it as “a proud moment for all of us”. The purchase was “a significant milestone in Kiteworks’ continued mission to safeguard sensitive data across all communication channels”, he added in a LinkedIn post. But what Yaron did not mention was that this acquisition — coming at a politically charged moment between the U.S. and the EU — put highly sensitive, personal data belonging to European and British citizens directly into American hands. Zivver is used by institutions including hospitals, health insurers, government services and immigration authorities in countries including the Netherlands, Germany, Belgium and the U.K.Neither did Yaron mention that much of Kiteworks’ top management — himself included — are former members of an elite Israeli Defence Force unit that specialised in eavesdropping and breaking encrypted communications.
Our journalism is only possible thanks to the trust of our paying members. Not a member yet? Sign up now
In addition to this, an investigation by Follow the Money shows that data processed by Zivver is less secure than the service leads its customers to believe. Research found that emails and documents sent by Zivver can be read by the company itself. This was later confirmed by Zivver to Follow the Money.“All of the red flags should have been raised during this acquisition”Zivver maintained, however, that it does not have access to the encryption keys used by customers, and therefore cannot hand over data to U.S. authorities. This is despite independent researchers confirming that the data was — for a brief period — accessible to the company. If U.S. officials wanted access to such communication, Zivver would be legally obligated to provide it.Cybersecurity experts now point to serious security concerns, and ask why this sale seems to have gone through without scrutiny from European authorities.“All of the red flags should have been raised during this acquisition,” said intelligence expert Hugo Vijver, a former long-term officer in AIVD, the Dutch security service.Amsterdam-based Zivver — which was founded in 2015 by Wouter Klinkhamer and Rick Goud — provides systems for the encrypted exchange of information via email, chat and video, among other means. Dutch courts, for example, work with Zivver to send classified documents, and solicitors use the service to send confidential information to the courts. Other government agencies in the Netherlands — including also use Zivver. So do vital infrastructure operators such as the Port of Rotterdam and The Hague Airport.
In the U.K., a number of NHS hospitals and local councils In it is used in major hospitals. The information that Zivver secures for its customers is therefore confidential and sensitive by nature. When approached by Follow the Money, a number of governmental agencies said the company’s Dutch origins were a big factor in their decision to use Zivver. Additionally, the fact that the data transferred via Zivver was stored on servers in Europe also played a role in their decisions. Now that Zivver has been acquired by a company in the United States, that data is This means that the U.S. government can request access to this information if it wishes, regardless of where the data is stored.These laws are not new, but they have become even more draconian since U.S. President Donald Trump’s return to office, according to experts.Bert Hubert, a former regulator of the Dutch intelligence services, warned: “America is deteriorating so rapidly, both legally and democratically, that it would be very naive to hand over your courts and hospitals to their services.” “Trump recently called on Big Tech to ignore European legislation. And that is what they are going to do. We have no control over it,” he added.
In Europe, Hubert said: “We communicate almost exclusively via American platforms. And that means that the U.S. can read our communications and disrupt our entire society if they decide that they no longer like us.”Zivver had offered an alternative — a European platform governed by EU law. “We are now throwing that away. If you want to share something confidential with a court or government, consider using a typewriter. That’s about all we have left,” Hubert said.Beyond American jurisdiction, Kiteworks’ management raises another layer of concern: its links to Israeli intelligence.Several of the company’s top executives, including CEO Yaron, are veterans of Unit 8200, the elite cyber unit of the Israel Defence Force (IDF). The unit is renowned for its code-breaking abilities and feared for its surveillance operations.
In Israel, there is a revolving door between the army, lobby, business and politics
Unit 8200 has been linked to major cyber operations, including the Stuxnet attack on Iranian nuclear facilities in 2007. More recently, it was accused of orchestrating the detonation of thousands of pagers in Lebanon, an incident the United Nations said violated international law and killed at least two children.The unit employs thousands of young recruits identified for their digital skills. It is able to intercept global telephone and internet traffic.International media have reported that Unit 8200 intercepts and stores an average of one million Palestinian phone calls every hour, data that ends up on Some veterans themselves have also objected to the work of the unit. In 2014, dozens of reservists signed a letter to Israeli leaders saying they no longer wanted to participate in surveillance of the occupied territories.“The lines of communication between the Israeli defence apparatus and the business community have traditionally been very short,” said Dutch intelligence expert Vijver. “In Israel, there is a revolving door between the army, lobby, business and politics.”That revolving door is clearly visible in big U.S. tech companies — and Kiteworks is no exception.Aside from Yaron, both Chief Business Officer Yaron Galant and Chief Product Officer served in Unit 8200, according to publicly available information.They played a direct role in negotiating the acquisition of Zivver. Their background was known to Zivver’s directors Goud and Klinkhamer at the time.
What is transpiring within the European Union? What are the goals and aspirations of the EU, and how is the budget allocated?
Other senior figures also have military intelligence backgrounds. Product director Ron Margalit worked in Unit 8200 before serving in the office of Israeli Prime Minister Benjamin Netanyahu. Mergers and acquisitions director Uri Kedem is a former Israeli naval captain.Kiteworks is not unique in this respect. Increasing numbers of U.S. cybersecurity firms now employ former Israeli intelligence officers. This trend, experts say, creates vulnerabilities that are rarely discussed.An independent researcher quoted by U.S. Drop Site News said: “Not all of these veterans will send classified data to Tel Aviv. But the fact that so many former spies work for these companies does create a serious vulnerability: no other country has such access to the Or, as the ex-intelligence regulator Hubert put it: “Gaining access to communication flows is part of Israel’s long-term strategy. A company like Zivver fits perfectly into that strategy.”The information handled by Zivver — confidential communications between governments, hospitals and citizens — is a potential goldmine for intelligence services.According to intelligence expert Vijver, access to this kind of material makes it easier to pressure individuals into cooperating with intelligence agencies. Once an intelligence service has access to medical, financial and personal data, it can more easily pressure people into spying for it, he said.But the gain for intelligence services lies not just in sensitive information, said Hubert: “Any data that allows an agency to tie telephone numbers, addresses or payment data to an individual is of great interest to them.” He added: “It is exactly this type of data that is abundantly present in communications between civilians, governments and care institutions. In other words, the information that flows through a company like Zivver is extremely valuable for intelligence services.”These geopolitical concerns become more pronounced when combined with technical worries about Zivver’s encryption. For years, Zivver presented itself as a European alternative that guaranteed privacy. Its marketing materials claimed that messages were encrypted on the sender’s device and that the company had “zero access” to content. But an investigation by two cybersecurity experts at a Dutch government agency, at the request of Follow the Money, undermines this claim.
The experts, who participated in the investigation on condition of anonymity, explored what happened when that government agency logged into Zivver’s web application to send information.Tests showed that when government users sent messages through Zivver’s web application, the content — including attachments — was uploaded to Zivver’s servers as readable text before being encrypted. The same process applied to email addresses of senders and recipients.“In these specific cases, Zivver processed the messages in readable form,” said independent cybersecurity researcher Matthijs Koot, who verified the findings. “Even if only briefly, technically speaking it is possible that Zivver was able to view these messages,” he said.He added: “Whether a message is encrypted at a later stage makes little difference. It may help against hackers, but it no longer matters in terms of protection against Zivver.”Despite these findings, Zivver continues to insist on its website and in promotional material elsewhere — including on the U.K. government’s Digital Marketplace — that “contents of secure messages are inaccessible to Zivver and third parties”.So far, no evidence has surfaced that Zivver misused its technical access. But now that the company is owned by Kiteworks, experts see a heightened risk.Former intelligence officer Vijver puts it bluntly: “Given the links between Zivver, Kiteworks and Unit 8200, I believe there is zero chance that no data is going to Israel. To think otherwise is completely naive.”The sale of Zivver could technically have been blocked or investigated under Dutch law. According to the Security Assessment of Investments, Mergers and Acquisitions Act, such sensitive takeovers are supposed to be reviewed by a specialised agency. But the Dutch interior ministry declared that Zivver was not part of the country’s “critical infrastructure,” meaning that no review was carried out.That, in Hubert’s view, was “a huge blunder”.
“It’s bad enough that a company that plays such an important role in government communications is falling into American hands, but the fact that there are all kinds of Israeli spies there is very serious,” he said.“The takeover is taking place in an unsafe world full of geopolitical tensions”Experts say the Zivver case highlights Europe’s lack of strategic control over its digital infrastructure.Mariëtte van Huijstee of the Netherlands-based said: “I doubt whether the security of sensitive emails and files … should be left to the private sector. And if you think that is acceptable, should we leave it to non-European parties over whom we have no control?”“We need to think much more strategically about our digital infrastructure and regulate these kinds of issues much better, for example by designating encryption services as vital infrastructure,” she added.Zivver, for its part, claimed that security will improve under Kiteworks. Zivver’s full responses to Follow the Money’s questions can be read here and here.But Van Huijstee was not convinced.“Kiteworks employs people who come from a service that specialises in decrypting files,” she said. “The takeover is taking place in an unsafe world full of geopolitical tensions, and we are dealing with data that is very valuable. In such a case, trust is not enough and more control is needed.”
...
Read the original on www.ftm.eu »
hashcards is a local-first spaced repetition app, along the lines of
Anki or Mochi. Like Anki, it uses FSRS, the most advanced scheduling algorithm yet, to schedule reviews.
The thing that makes hashcards unique: it doesn’t use a database. Rather, your flashcard collection is just a directory of Markdown files, like so:
And each file, or “deck”, looks like this:
Q: What is the role of synaptic vesicles?
A: They store neurotransmitters for release at the synaptic terminal.
Q: What is a neurite?
A: A projection from a neuron: either an axon or a dendrite.
C: Speech is [produced] in [Broca’s] area.
C: Speech is [understood] in [Wernicke’s] area.
You write flashcards more or less like you’d write ordinary notes, with lightweight markup to denote basic (question/answer) flashcards and cloze
deletion flashcards. Then, to study, you run:
This opens a web interface on localhost:8000, where you can review the flashcards. Your performance and review history is stored in an SQLite
database in the same directory as the cards. Cards are content-addressed, that is, identified by the hash of their text.
This central design decision yields many benefits: you can edit your flashcards with your editor of choice, store your flashcard collection in a Git repo, track its changes, share it on GitHub with others (as I have). You can use scripts to generate flashcards from some source of structured data (e.g. a CSV of English/French vocabulary pairs). You can query and manipulate your collection using standard Unix tools, or programmatically, without having to dig into the internals of some app’s database.
Why build a new spaced repetition app? Mostly because I was dissatisfied with both Anki and Mochi. But also, additionally, because my flashcards collection is very important to me, and having it exist either in some remote database, or as an opaque unusable data blob on my computer, doesn’t feel good. “Markdown files in a Git repo” gives me a level of ownership that other approaches lack.
The rest of this post explains my frustrations with Anki and Mochi, and how I landed on the design decisions for hashcards.
Anki was the first SR system I used. It’s open source, so it will be around forever; it has a million plugins; it was the first SR system to use FSRS for scheduling. It has really rich stats, which I think are mostly useless but are fun to look at. And the note types feature is really good: it lets you generate a large number of flashcards automatically from structured data.
The central problem with Anki is that the interface is really bad. This manifests in various ways.
First, it is ugly to look at, particularly the review screen. And this diminishes your enjoyment of what is already an often boring and frustrating process.
Second, doing simple things is hard. A nice feature of Mochi is that when you start the app you go right into review mode. You’re drilling flashcards before you even realize it. Anki doesn’t have a “study all cards due today”, rather, you have to manually go into a deck and click the “Study Now” button. So what I would do is put all my decks under a “Root” deck, and study that. But this is a hack.
And, third: card input uses WYSIWYG editing. So, you’re either jumping from the keyboard to the mouse (which increases latency, and makes flashcard creation more frustrating) or you have to remember all these keybindings to do basic things like “make this text a cloze deletion” or “make this TeX math”.
Finally, plugins are a double-edged sword. Because having the option to use them is nice, but the experience of actually using most plugins is bad. The whole setup feels janky, like a house of cards. Most of the time, if a feature is not built into the app itself, I would rather live without it than use a plugin.
Mochi feels like it was built to address the main complaint about Anki: the interface. It is intuitive, good looking, shortcut-rich. No jank. Instead of WYSIWYG, card text is Markdown: this is delightful.
There’s a few problems. While Markdown is a very low-friction way to write flashcards, cloze deletions in Mochi are very verbose. In hashcards, you can write this:
The equivalent in Mochi is this:
This is a lot of typing. And you might object that it’s only a few characters longer. But when you’re studying from a textbook, or when you’re copying words from a vocabulary table, these small frictions add up. If writing flashcards is frustrating, you’ll write fewer of them: and that means less knowledge gained. Dually, a system that makes flashcard creation as frictionless as possible means more flashcards, and more knowledge.
Another problem is that Mochi doesn’t have an equivalent of Anki’s note
types. For example: you can make a note type for chemical elements, with fields like atomic number, symbol, name, etc., and write templates to generate flashcards asking questions like:
What is the atomic number of [name]?
What is the symbol for [name]?
And so on for other properties. This is good. Automation is good. Less work, more flashcards. Mochi doesn’t have this feature. It has templates, but these are not as powerful.
But the biggest problem with Mochi, I think, is the algorithm. Until very
recently, when they added beta support for FSRS, the algorithm used by Mochi was even simpler than SM-2. It was based on multipliers: remembering a card multiplies its interval by a number >1, forgetting a card multiplies its interval by a number between 0 and 1.
The supposed rationale for this is simplicity: the user can reason about the algorithm more easily. But I think this is pointless. The whole point of an SR app is the software manages the schedule for you, and the user is completely unaware of how the scheduler works. The optimality is to have the most advanced possible scheduling algorithm (meaning the one that yields the most recall for the least review time) under the most intuitive interface possible, and the user just reaps the benefits.
Obviously without an RCT we can’t compare Mochi/SM-2/FSRS, but my subjective experience of it is that the algorithm works well for the short-term, and falters on the long-term. It’s very bad when you forget a mature card: if a card has an interval of sixty days, and you click forget, you don’t reset the interval to one day (which is good, because it helps you reconsolidate the lost knowledge). Rather, the interval is multiplied by the forget multiplier (by default: 0.5) down to thirty days. What’s the use? If I forgot something after sixty days, I surely won’t have better recall in thirty.
You can fix this by setting the forget multiplier to zero. But you have to know this is how it works, and, crucially: I don’t want to configure things! I don’t want “scheduler parameter finetuning” to be yet another skill I have to acquire: I want the scheduler to just work.
In general, I think spaced repetition algorithms are too optimistic. I’d rather see cards slightly more often, and spend more time reviewing things, than get stuck in “forgetting hell”. But developers have to worry that making the system too burdensome will hurt retention.
In Anki, it’s the interface that’s frustrating, but the algorithm works marvelously. In Mochi, the interface is delightful, but it’s the algorithm that’s frustrating. Because you can spend months and months drilling flashcards, building up your collection, but when the cards cross some invisible age threshold, you start to forget them, and the algorithm does not help you relearn things you have forgotten. Eventually I burned out on it and stopped doing my reviews, because I expected to forget everything eventually anyhow. And now they added support for FSRS, but by now I have 1700 cards overdue.
Additionally: Mochi has only two buttons, “Forgot” and “Remembered”. This is simpler for the user, yes, but most SR scheduling algorithms have more options for a reason: different degrees of recall adjust the card parameters by different magnitudes.
What do I want from a spaced repetition system?
The first thing is: card creation must be frictionless. I have learned that the biggest bottleneck in spaced repetition, for me, is not doing the reviews (I am very disciplined about this and have done SR reviews daily for months on end), it’s not even converting conceptual knowledge into flashcards, the biggest bottleneck is just entering cards into the system.
The surest way to shore up your knowledge of some concept or topic is to write more flashcards about it: asking the same question in different ways, in different directions, from different angles. More volume means you see the same information more often, asking in different ways prevents “memorizing the shape of the card”, and it acts as a kind of redundancy: there are multiple edges connecting that bit of knowledge to the rest of your mind.
And there have been many times where I have thought: I would make this more solid by writing another flashcard. But I opted not to because the marginal flashcard is too effortful.
If getting cards into the system involves a lot of friction, you write fewer cards. And there’s an opportunity cost: the card you don’t write is a concept you don’t learn. Integrated across time, it’s entire oceans of knowledge which are lost.
So: the system should make card entry effortless. This was the guiding principle behind the design of the hashcards text format. For example, cloze deletions use square brackets because in a US keyboard, square brackets can be typed without pressing shift (compare Mochi’s curly brace). And it’s one bracket, not two. Originally, the format was one line per card, with blank lines separating flashcards, and question-answer cards used slashes to separate the sides, like so:
What is the atomic number of carbon? / 6
The atomic number of [carbon] is [6].
And this is strictly less friction. But it creates a problem for multi-line flashcards, which are common enough that they should not be a second-class citizen. Eventually, I settled on the current format:
Q: What is the atomic number of carbon?
A: 6
C: The atomic number of [carbon] is [6].
Which is only slightly more typing, and has the benefit that you can easily visually identify where a card begins and ends, and what kind of card it is. I spent a lot of time arguing back and forth with Claude about what the optimal format should be.
Another source of friction is not creating the cards but editing them. The central problem is that your knowledge changes and improves over time. Often textbooks take this approach where Chapter 1 introduces one kind of ontology, and by Chapter 3 they tell you, “actually that was a lie, here’s the real ontology of this subject”, and then you have to go back and edit the old flashcards to match. Because otherwise you have one card asking, e.g., for the undergraduate definition of some concept, while another asks you for the graduate-level definition, creating ambiguity.
For this reason, when studying from a textbook, I create a deck for the textbook, with sub-decks for each chapter. That makes it easy to match the flashcards to their source material (to ensure they are aligned) and each chapter deck only has a few tens of cards usually, keeping them navigable.
Sometimes you wrote multiple cards for the same concept, so you have to update them all at once. Finding the related ones can be hard if the deck is large. In hashcards, a deck is just a Markdown file. The cards immediately above and below a card are usually semantically related. You just scroll up and down and make the edits in place.
But why plain-text files in a Git repo? Why not use the above format, but in a “normal” app with a database?
The vague idea of a spaced repetition system where flashcards are stored as plain-text files in a Git repo had been kicking around my cranium for a long time. I remember asking an Ankihead on IRC circa 2011 if such a thing existed. At some point I read Andy Matuschak’s note on his implementation of an SR system. In his system, the flashcards are colocated with prose notes. The notation is similar to mine: Q and A tags for question-answer cards, and {curly braces} for cloze deletions. And the cards are content-addressed: identified by their hash. Which is an obviously good idea. But his code is private and, besides, I feel that prose notes and flashcards are very different beasts, and I don’t need or want them to mix.
But I think the idea of plain-text spaced repetition got bumped up the priority queue because I spontaneously started using a workflow that was similar to my current hashcards workflow.
When studying from a textbook or a website, I’d write flashcards in a Markdown file. Usually, I used a shorthand like [foo] for cloze deletions. Then I’d use a Python script to transform the shorthand into the {{1::foo}} notation used by Mochi. And I’d edit the flashcards in the file, as my knowledge built up and my sense of what was relevant and important to remember improved. And then, when I was done with the chapter or document or whatever, only then, I would manually import the flashcards into Mochi.
And it struck me that the last step was kind of unnecessary. I was already writing my flashcards as lightly-annotated Markdown in plain-text files. I had
already implemented FSRS out of curiosity. I was looking for a personal project to build during funemployment. So hashcards was by then a very neatly-shaped hole that I just needed to paint inside.
It turns out that using plain-text storage has many synergies:
You can edit the cards using whatever editor you use, build up a library of
card-creating macros, and navigate the collection using the editor’s file
browser.
You can query and update the collection using standard Unix tools, or a
programming language, e.g. using wc to get the total number of words in the
collection, or using awk to make a bulk-update to a set of cards.
You can use Git for version control. Git is infinitely more featureful than
the change-tracking of any SR app: you can edit multiple cards in one commit,
branch, merge, use pull requests, etc.
You can make your flashcards public on GitHub. I often wish people put more of
themselves out there: their blog posts, their dotfiles, their study notes. And
why not their flashcards? Even if they are not useful to someone else, there
is something enjoyable about reading what someone else finds interesting, or
enjoyable, or worth learning.
You can generate flashcards using scripts (e.g., turn a CSV of foreign
language vocabulary into a deck of flashcards), and write a Makefile to tie
the script, data source, and target together. I do this in my
personal deck. Anki’s note types don’t have to be built into hashcards,
rather, you can DIY it using some Python and make.
The result is a system where creating and editing flashcards is nearly frictionless, that uses an advanced spaced repetition scheduler, and which provides an elegant UI for drilling flashcards. I hope others will find it useful.
...
Read the original on borretti.me »
...
Read the original on status.claude.com »
AI and the ironies of automation - Part 2
AI and the ironies of automation - Part 1
It is your fault if your application is down
View more posts
AI and the ironies of automation - Part 2
AI and the ironies of automation - Part 2
In the previous post, we discussed several observations, Lisanne Bainbridge made in her much-noticed paper “The ironies of automation”, she published in 1983 and what they mean for the current “white-collar” work automation attempts leveraging LLMs and AI agents based on LLMs, still requiring humans in the loop. We stopped at the end of the first chapter, “Introduction”, of the paper.
In this post, we will continue with the second chapter, “Approaches to solutions”, and see what we can learn there.
However, before we start: Some of the observations and recommendations made in the paper must be taken with a grain of salt when applying them to the AI-based automation attempts of today. When monitoring an industrial production plant, it is often a matter of seconds until a human operator must act if something goes wrong to avoid severe or even catastrophic accidents.
Therefore, it is of the highest importance to design industrial control stations in a way that a human operator can recognize deviations and malfunctions as easily as possible and immediately trigger countermeasures. A lot of work is put into the design of all the displays and controls, like, e.g., the well-known emergency stop switch in a screaming red color that is big enough to be punched with a flat hand, fist or alike within a fraction of a second if needed.
When it comes to AI-based solutions automating white-collar work, we usually do not face such critical conditions. However, this is not a reason to dismiss the observations and recommendations in the paper easily because, e.g.:
Most companies are efficiency-obsessed. Hence, they also expect AI solutions to increase “productivity”, i.e., efficiency, to a superhuman level. If a human is meant to monitor the output of the AI and intervene if needed, this requires that the human needs to comprehend what the AI solution produced at superhuman speed — otherwise we are down to human speed. This presents a quandary that can only be solved if we enable the human to comprehend the AI output at superhuman speed (compared to producing the same output by traditional means).
Most companies have a tradition of nurturing a culture of urgency and scarcity, resulting in a lot of pressure towards and stress for the employees. Stress is known to trigger the fight-or-flight mode (an ancient survival mechanism built into us to cope with dangerous situations) which massively reduces the normal cognitive capacity of a human. While this mechanism supports humans in making very quick decisions and taking quick actions (essential in dangerous situations), it deprives them of the ability to conduct any deeper analysis (not being essential in dangerous situations). If deeper analysis is required to make a decision, this may take a lot longer than without stress — if possible at all. This means we need to enable humans to conduct deeper analysis under stress as well or to provide the information in a way that eliminates the need for deeper analysis (which is not always possible).
If we let this sink in (plus a few other aspects, I did not write down here but you most likely will add in your mind), we quickly come to the conclusion that also in our AI-related automation context humans are often expected to make quick decisions and act based on them, often under conditions that make it hard (if not impossible) to conduct any in-depth analysis.
If we then also take into account, that depending on the situation a wrong result produced by an AI solution which eluded the human operator may have severe consequences in the worst case (e.g., assume a major security incident due to a missed wrongdoing of the AI solution), the situation is not that far away anymore from the situation in an industrial plant’s control station.
Summarizing, we surely need to add the necessary grain of salt, i.e., ask ourselves how strict the timing constraints in our specific setting are to avoid comparing apples and oranges in the worst case. However, in general we need to consider the whole range of possible settings which will — probably more often than we think — include that humans need to make decisions in a very short time under stressful conditions (which makes things more precarious).
This brings us immediately to Lisanne Bainbridge’s first recommendation:
In any situation where a low probability event must be noticed quickly then the operator must be given artificial assistance, if necessary even alarms on alarms.
In other words, the system must support the human operator as well as possible in detecting a problem, especially if it tends to occur rarely. It is a consequence of the “monitoring fatigue” problem we discussed in the previous post.
Due to the learnings people have made, a lot of effort has been put into the design of the displays, the controls and also the alerting mechanisms of industrial production control stations, making sure the human operators can make their jobs as good, as stress-free and as reliable as possible.
The usual idea is that a single human controls a fleet of AI agents that are designed to do some kind of job, e.g., writing code. Sometimes, most agents are generic “workers”, orchestrated by some kind of supervisor that delegates parts of the work to the worker agents. Sometimes, the different agents are “specialists”, each for a certain aspect of the job to be done, that collaborate using some kind of choreography (or are also orchestrated by a supervisor). While the generic workers are easier to set up, the specialized workers usually produce more accurate results.
Because these AI-based agents sometimes produce errors, a human — in our example a software developer — needs to supervise the AI agent fleet and ideally intervenes before the AI agents do something they should not do. Therefore, the AI agents typically create a plan of what they intend to do first (which as a side effect also increases the likelihood that they do not drift off). Then, the human verifies the plan and approves it if it is correct, and the AI agents execute the plan. If the plan is not correct, the human rejects it and sends the agents back to replanning, providing information about what needs to be altered.
Let us take Lisanne Bainbridge’s recommendation and compare it to this approach that is currently “best practice” to control an AI agent fleet.
Unless we tell them to act differently, LLMs and also AI agents based on them are quite chatty. Additionally, they tend to communicate with an air of utter conviction. Thus, they present to you this highly detailed, multi-step plan of what they intend to do, including lots of explanations, in this perfectly convinced tone. Often, these plans are more than 50 or 100 lines of text, sometimes even several hundred lines.
Most of the time, the plans are fine. However, sometimes the AI agents mess things up. They make wrong conclusions, or they forget what they are told to do and drift off — not very often, but it happens. Sometimes the problem is obvious at first sight. But more often, it is neatly hidden somewhere behind line 123: “… and because 2 is bigger than 3, it is clear, we need to < do something critical >”. But because it is so much text the agents flood you with all the time and because the error is hidden so well behind this wall of conviction, we miss it — and the AI agent does something critical wrong.
We cannot blame the person for missing the error in the plan. The problem is that this is probably the worst UI and UX possible for anyone who is responsible for avoiding errors in a system that rarely produces errors.
But LLM-based agents make errors all the time, you may say. Well, not all the time. Sometimes they do. And the better the instructions and the setup of the interacting agents, the fewer errors they produce. Additionally, we can expect more specialized and refined agents in the future that become increasingly better in their respective areas of expertise. Still, most likely they will never become completely error-free because of the underlying technology that cannot guarantee consistent correctness.
This is the setting we need to ponder if we talk about the user interface for a human observer: a setting where the agent fleet only rarely makes errors but we still need a human monitoring and intervening if things should go wrong. It is not yet clear how such an interface should look like, but most definitely not as it looks now. Probably we could harvest some good insights from our UX/UI design colleagues for industrial production plant control stations. We would need only to ask them …
Lisanne Bainbridge then makes several recommendations regarding the required training of the human operator. This again is a rich section, and I can only recommend reading it on your own because it contains several subtle yet important hints that are hard to bring across without citing the whole chapter. Here, I will highlight only a few aspects. She starts with:
[Some points made in the previous section] make it clear that it can be important to maintain manual skills.
Then she talks about letting the human operator take over control regularly, i.e., do the job instead of the machine as a very effective training option. Actually, without doing hands-on work regularly, the skills of a human expert deteriorate surprisingly fast.
But if taking over the work regularly is not an option, e.g., because we want continuous superhuman productivity leveraging AI agents (no matter if it makes sense or not), we still need to make sure that the human operator can take over if needed. In such a setting, training must take place in some other way, usually using some kind of simulator.
However, there is a problem with simulators, especially if human intervention is only needed (and wanted) if things do not work as expected:
There are problems with the use of any simulator to train for extreme situations. Unknown faults cannot be simulated, and system behaviour may not be known for faults which can be predicted but have not been experienced.
The consequence of this issue is:
This means that training must be concerned with general strategies rather than specific responses […]
It is inadequate to expect the operator to react to unfamiliar events solely by consulting operating procedures. These cannot cover all the possibilities, so the operator is expected to monitor them and fill in the gaps.
Which leaves us with the irony:
However, it is ironic to train operators in following instructions and then put them in the system to provide intelligence.
This is a problem we will need to face with AI agents and their supervising humans in the future, too. The supervising experts are meant to intervene whenever things become messy, whenever the AI agents get stuck, often in unforeseen ways. These are not regular tasks. Often, these are also not the issues we expect an AI agent to run into and thus can provide training for. These are extraordinary situations, the ones we do not expect — and the more refined and specialized the AI agents will become in the future, the more often the issues that require human intervention will be of this kind.
How can we train human operators at all to be able to intervene skillfully in exceptional, usually hard to solve situations?
How can we train a human operator so that their skills remain sharp over time and they remain able to address an exceptional situation quickly and resourcefully?
The questions seem to hint at a sort of paradox, and an answer to both questions is all but obvious. At the moment, we still have enough experienced subject matter experts that the questions may feel of lower importance. But if we only start to address the questions when they become pressing, they will be even harder — if not impossible — to solve.
To end this consideration with the words of Lisanne Bainbridge:
Perhaps the final irony is that it is the most successful automated systems, with rare need for manual intervention, which may need the greatest investment in human operator training.
In other words, we cannot simply take a few available human experts and make them supervise agents that took over their work without any further investments in the humans. Instead, we need to train them continuously, and the better the agents become, the more expensive the training of the supervisors will become. I highly doubt that decision makers who primarily think about saving money when it comes to AI agents are aware of this irony.
As I wrote in the beginning of first part of this blog series, “The ironies of automation” is a very rich and dense paper. We are still only at the end of the second chapter “Approaches to solutions” which is two and a half pages into the paper and there is still a whole third chapter called “Human-computer collaboration” which takes up another page until we get to the conclusion.
While this third chapter also contains a lot of valuable advice that goes well beyond our focus here, I will leave it to you to read it on your own. As I indicated at the beginning, this paper is more than worth the time spent on it.
However, before finishing this little blog series, I would like to mention a new kind of dilemma that Lisanne Bainbridge did not discuss in her paper because the situation was a bit different with industrial production plant automation than with AI-agent-based automation. But as this topic fits nicely into the just-finished training paradox section, I decided to add it here.
The issue is that just monitoring an AI agent fleet doing its work and intervening if things go wrong usually is not sufficient, at least not yet. All the things discussed before apply, but there is more to interacting with AI agents because we cannot simply be reactive with AI agents. We cannot simply watch them doing their work and only intervene if things go wrong. Instead, we additionally need to be proactive with them: We need to direct them.
We need to tell the AI agents what to do, what not to do, which chunks to pick and so on. This is basically a leadership role. While you do not lead humans, the kind of work is quite similar: You are responsible for the result; you are allowed to set directions and constraints, but you do not immediately control the work. You only control it through communicating with the agents and trying to direct them in the right direction with orders, with feedback, with changed orders, with setting different constraints, etcetera.
This is a skill set most people do not have naturally. Usually, they need to develop it over time. Typically, before people are put in a leadership role directing humans, they will get a lot of leadership training teaching them the skills and tools needed to lead successfully. For most people, this is essential because if they come from the receiving end of orders (in the most general sense of “orders”), typically they are not used to setting direction and constraints. This tends to be a completely new skill they need to learn.
This does not apply only to leading humans but also to leading AI agents. While AI agents are not humans, and thus leadership will be different in detail, the basic skills and tools needed are the same. This is, BTW, one of the reasons why the people who praise agentic AI on LinkedIn and the like are very often managers who lead (human) teams. For them, leading an AI agent fleet feels very natural because it is very close to the work they do every day. However, for the people currently doing the work, leading an AI agent fleet usually does not feel natural at all.
However, I have not yet seen anyone receiving any kind of leadership training before being left alone with a fleet of AI agents, and I still see little discussion about the issue. “If it does not work properly, you need better prompts” is the usual response if someone struggles with directing agents successfully.
Sorry, but it is not that easy. The issue is much bigger than just optimizing a few prompts. The issue is that people have to change their approach completely to get any piece of work done. Instead of doing it directly, they need to learn how to get it done indirectly. They need to learn how to direct a group of AI agents effectively, how to lead them.
This also adds to the training irony of the previous topic. Maybe the AI agent fleets will become good enough in the future that we can omit the proactive part of the work and only need to focus on the reactive part of the work, the monitor-and-intervene part. But until then, we need to teach human supervisors of AI agent fleets how to lead them effectively.
We discussed several ironies and paradoxes from Lisanne Bainbridge’s “The ironies of automation” and how they also apply to agentic AI. We looked at the unlearning and recall dilemma and what it means for the next generation of human supervisors. We discussed monitoring fatigue and the status issue. We looked at the UX and UI deficiencies of current AI agents and the training paradox. And we finally looked at the leadership dilemma, which Lisanne Bainbridge did not discuss in her paper but which complements the training paradox.
I would like to conclude with the conclusion of Lisanne Bainbridge:
[…] humans working without time-pressure can be impressive problem solvers. The difficulty remains that they are less effective when under time pressure. I hope this paper has made clear both the irony that one is not by automating necessarily removing the difficulties, and also the possibility that resolving them will require even greater technological ingenuity than does classic automation.
I could not agree more.
I think over time we will become clear on how much “The ironies of automation” also applies to automation done with AI agents and that we cannot ignore the insights known for more than 40 years meanwhile. I am also really curious how the solutions to the ironies and paradoxes will look like.
Until then, I hope I gave you a bit of food for thought to ponder. If you should have some good ideas regarding the ironies and how to address them, please do not hesitate to share them with the community. We learn best by sharing and discussing, and maybe your contribution will be a step towards solving the issues discussed …
AI and the ironies of automation - Part 1
Web site and blog. Writing about IT most of the time.
AI and the ironies of automation - Part 2
AI and the ironies of automation - Part 1
It is your fault if your application is down
View more posts
I’m Uwe, travelling the world of IT for many years. Worked in many roles in and around IT. Sharing my thought, ideas and insights here. Explorer. Connector. Sharer. Coder. Writer. Human. More. Order may vary.
Learn More
...
Read the original on www.ufried.com »
I’ve used GraphQL, specifically Apollo Client and Server, for a couple of years in a real enterprise-grade application.
Not a toy app. Not a greenfield startup. A proper production setup with multiple teams, BFFs, downstream services, observability requirements, and real users.
And after all that time, I’ve come to a pretty boring conclusion:
GraphQL solves a real problem, but that problem is far more niche than people admit. In most enterprise setups, it’s already solved elsewhere, and when you add up the tradeoffs, GraphQL often ends up being a net negative.
This isn’t a “GraphQL bad” post. It’s a “GraphQL after the honeymoon” post.
The main problem GraphQL tries to solve is overfetching.
The idea is simple and appealing:
* the client asks for exactly the fields it needs
* no more, no less
* no backend changes for every new UI requirement
On paper, that’s great. In practice, things are messier.
Most enterprise frontend architectures already have a BFF (Backend for Frontend).
* return exactly what the UI needs
If you’re using REST behind a BFF, overfetching is already solvable. The BFF can scope down responses and return only what the UI cares about.
Yes, GraphQL can also do this. But here’s the part people gloss over.
Most downstream services are still REST.
So now your GraphQL layer still has to overfetch from downstream REST APIs, then reshape the response. You didn’t eliminate overfetching. You just moved it down a layer.
That alone significantly diminishes GraphQL’s main selling point.
There is a case where GraphQL wins here. If multiple pages hit the same endpoint but need slightly different fields, GraphQL lets you scope those differences per query.
But let’s be honest about the trade.
You’re usually talking about saving a handful of fields per request, in exchange for:
That’s a very expensive trade for a few extra kilobytes.
* return what the UI needs
With GraphQL, you now have to:
GraphQL optimizes consumption at the cost of production speed.
In an enterprise environment, production speed matters more than theoretical elegance.
This one doesn’t get talked about enough.
GraphQL has this weird status code convention:
* 400 if the query can’t be parsed
* 200 with an errors array if something failed during execution
* 200 if it succeeded or partially succeeded
* 500 if the server is unreachable
From an observability standpoint, this is painful.
If you filter dashboards by 2XX, you know those requests succeeded.
With GraphQL, a 200 can still mean partial or full failure.
Yes, Apollo lets you customize this behavior. But that’s kind of the point. You’re constantly paying a tax in extra configuration, extra conventions, and extra mental overhead just to get back to something REST gives you out of the box.
This matters when you’re on call, not when you’re reading blog posts.
If you have two queries where only one field differs, Apollo treats them as separate queries. You then have to manually wire things so:
* only the differing field is fetched
* you still have a roundtrip
* debugging cache issues becomes its own problem
Meanwhile, REST happily overfetches a few extra fields, caches the whole response, and moves on. Extra kilobytes are cheap. Complexity isn’t.
Apollo expects every object to have an id or _id field by default, or you need to configure a custom identifier.
That assumption does not hold in many enterprise APIs.
So now the BFF has to generate IDs locally just to satisfy the GraphQL client.
* you’re always fetching one extra field anyway
Which is ironic, considering the original goal was to reduce overfetching.
GraphQL is simply not a good fit for binary data.
In practice, you end up:
* then using REST to fetch the file anyway
Embedding large payloads like PDFs directly in GraphQL responses leads to bloated responses and worse performance.
This alone breaks the “single API” story.
Most frontend and full-stack developers are far more experienced with REST than GraphQL.
That learning curve creates friction, especially when teams need to move fast.
REST is boring, but boring scales extremely well.
* the need to trace which resolver failed and why
All of this adds indirection.
Compare that to a simple REST setup where:
Simple errors are easier to reason about than elegant ones.
But in most enterprise environments:
* you already have BFFs
* overfetching is not your biggest problem
When you add everything up, GraphQL often ends up solving a narrow problem while introducing a broader set of new ones.
That’s why, after using it in production for years, I’d say this:
GraphQL isn’t bad. It’s just niche. And you probably don’t need it.
Especially if your architecture already solved the problem it was designed for.
...
Read the original on johnjames.blog »
On November 25th, 2025, we were on a routine Slack huddle debugging a production issue when we noticed something strange: a PR in one of our internal repos was suddenly closed, showed zero changes, and had a single commit from… Linus Torvalds?
The commit message was just “init.”
Within seconds, our #git Slack channel exploded with notifications. Dozens of force-pushes. PRs closing across multiple repositories. All attributed to one of our engineers.
We had been compromised by Shai-Hulud 2.0, a sophisticated npm supply chain worm that compromised over 500 packages, affected 25,000+ repositories, and spread across the JavaScript ecosystem. We weren’t alone: PostHog, Zapier, AsyncAPI, Postman, and ENS were among those hit.
This is the complete story of what happened, how we responded, and what we’ve changed to prevent this from happening again.
No Trigger.dev packages were ever compromised. The @trigger.dev/* packages and trigger.dev CLI were never infected with Shai-Hulud malware. This incident involved one of our engineers installing a compromised package on their development machine, which led to credential theft and unauthorized access to our GitHub organization. Our published packages remained safe throughout.
On the evening of November 24th, around 20:27 UTC (9:27 PM local time in Germany), one of our engineers was experimenting with a new project. They ran a command that triggered pnpm install. At that moment, somewhere in the dependency tree, a malicious package executed.
We don’t know exactly which package delivered the payload. The engineer was experimenting at the time and may have deleted the project directory as part of cleanup. By the time we investigated, we couldn’t trace back to the specific package. The engineer checked their shell history and they’d only run install commands in our main trigger repo, cloud repo, and one experimental project.
This is one of the frustrating realities of these attacks: once the malware runs, identifying the source becomes extremely difficult. The package doesn’t announce itself. The pnpm install completes successfully. Everything looks normal.
What we do know is that the Shai-Hulud malware ran a preinstall script that:
When the engineer later recovered files from their compromised laptop (booted in recovery mode), they found the telltale signs:
The .trufflehog-cache directory and trufflehog_3.91.1_darwin_amd64.tar.gz file found on the compromised machine. The extract directory was empty, likely cleaned up by the malware to cover its tracks.
The attacker had access to our engineer’s GitHub account for 17 hours before doing anything visible. According to our GitHub audit logs, they operated methodically.
Just over two hours after the initial compromise, the attacker validated their stolen credentials and began mass cloning:
The simultaneous activity from US and India confirmed we were dealing with a single attacker using multiple VPNs or servers, not separate actors.
While our engineer slept in Germany, the attacker continued their reconnaissance. More cloning at 02:56-02:59 UTC (middle of the night in Germany), sporadic activity until 05:32 UTC. Total repos cloned: 669 (527 from US infrastructure, 142 from India).
Here’s where it gets unsettling. Our engineer woke up and started their normal workday:
The attacker was monitoring our engineer’s activity while they worked, unaware they were compromised.
During this period, the attacker created repositories with random string names to store stolen credentials, a known Shai-Hulud pattern:
They also created three repos marked with “Sha1-Hulud: The Second Coming” as a calling card. These repositories were empty by the time we examined them, but based on the documented Shai-Hulud behavior, they likely contained triple base64-encoded credentials.
At 15:27 UTC on November 25th, the attacker switched from reconnaissance to destruction.
The attack began on our cloud repo from India-based infrastructure:
The attack continued on our main repository:
At 15:32:43-46 UTC, 12 PRs on jsonhero-web were closed in 3 seconds. Clearly automated. PRs #47, #169, #176, #181, #189, #190, #194, #197, #204, #206, #208 all closed within a 3-second window.
Our critical infrastructure repository was targeted next:
The final PR was closed on json-infer-types at 15:37:13 UTC.
We got a lucky break. One of our team members was monitoring Slack when the flood of notifications started:
Our #git Slack channel during the attack. A wall of force-pushes, all with commit message “init.”
Every malicious commit was authored as:
An attacked branch: a single “init” commit attributed to Linus Torvalds, thousands of commits behind main.
We haven’t found reports of other Shai-Hulud victims seeing this same “Linus Torvalds” vandalism pattern. The worm’s documented behavior focuses on credential exfiltration and npm package propagation, not repository destruction. This destructive phase may have been unique to our attacker, or perhaps a manual follow-up action after the automated worm had done its credential harvesting.
Within 4 minutes of detection we identified the compromised account, removed them from the GitHub organization, and the attack stopped immediately.
Our internal Slack during those first minutes:
“add me to the call @here”
“Nick could you double check Infisical for any machine identities”
“can someone also check whether there are any reports of compromised packages in our CLI deps?”
Protected branch rejections: 4. Some of our repositories have main branch protection enabled, but we had not enabled it for all repositories at the time of the incident.
npm packages were not compromised. This is the difference between “our repos got vandalized” and “our packages got compromised.”
Our engineer didn’t have an npm publishing token on their machine, and even if they did we had already required 2FA for publishing to npm. Without that, Shai-Hulud would have published malicious versions of @trigger.dev/sdk, @trigger.dev/core, and others, potentially affecting thousands of downstream users.
Production databases or any AWS resources were not accessed. Our AWS CloudTrail audit showed only read operations from the compromised account:
These were confirmed to be legitimate operations by our engineer.
One nice surprise: AWS actually sent us a proactive alert about Shai-Hulud. They detected the malware’s characteristic behavior (ListSecrets, GetSecretValue, BatchGetSecretValue API calls) on an old test account that hadn’t been used in months, so we just deleted it. But kudos to AWS for the proactive detection and notification.
GitHub doesn’t have server-side reflog. When someone force-pushes, that history is gone from GitHub’s servers.
But we found ways to recover.
Push events are retained for 90 days via the GitHub Events API. We wrote a script that fetched pre-attack commit SHAs:
Public repository forks still contained original commits. We used these to verify and restore branches.
Developers who hadn’t run git fetch –prune (all of us?) still had old SHAs in their local reflog.
Within 7 hours, all 199 branches were restored.
During the investigation, our engineer was going through files recovered from the compromised laptop and discovered something concerning: the private key for our GitHub App was in the trash folder.
When you create a private key in the GitHub App settings, GitHub automatically downloads it. The engineer had created a key at some point, and while the active file had been deleted, it was still in the trash, potentially accessible to TruffleHog.
Our GitHub App has the following permissions on customer repositories:
To generate valid access tokens, an attacker would need both the private key (potentially compromised) and the installation ID for a specific customer (stored in our database which was not compromised, not on the compromised machine).
We found no evidence of unauthorized access to any customer repositories. The attacker would have needed installation IDs from our database to generate tokens, and our database was not compromised as previously mentioned.
However, we cannot completely rule out the possibility. An attacker with the private key could theoretically have called the GitHub API to enumerate all installations. We’ve contacted GitHub Support to request additional access logs. We’ve also analyzed the webhook payloads to our GitHub app, looking for suspicious push or PR activity from connected installations & repositories. We haven’t found any evidence of unauthorized activity in these webhook payloads.
We’ve sent out an email to potentially effected customers to notify them of the incident with detailed instructions on how to check if they were affected. Please check your email for more details if you’ve used our GitHub app.
For those interested in the technical details, here’s what we learned about the malware from Socket’s analysis and our own investigation.
When npm runs the preinstall script, it executes setup_bun.js:
Returns immediately so npm install completes successfully with no warnings
The malware runs in the background while you think everything is fine.
The payload uses TruffleHog to scan $HOME for GitHub tokens (from env vars, gh CLI config, git credential helpers), AWS/GCP/Azure credentials, npm tokens from .npmrc, environment variables containing anything that looks like a secret, and GitHub Actions secrets (if running in CI).
Stolen credentials are uploaded to a newly-created GitHub repo with a random name. The data is triple base64-encoded to evade GitHub’s secret scanning.
If an npm publishing token is found, the malware validates the token against the npm registry, fetches packages maintained by that account, downloads each package, patches it with the malware, bumps the version, and re-publishes, infecting more packages.
This is how the worm spread through the npm ecosystem, starting from PostHog’s compromised CI on November 24th at 4:11 AM UTC. Our engineer was infected roughly 16 hours after the malicious packages went live.
If no credentials are found to exfiltrate or propagate, the malware attempts to delete the victim’s entire home directory. Scorched earth.
File artifacts to look for: setup_bun.js, bun_environment.js, cloud.json, contents.json, environment.json, truffleSecrets.json, actionsSecrets.json, .trufflehog-cache/ directory.
This prevents preinstall, postinstall, and other lifecycle scripts from running. It’s aggressive and some packages will break, but it’s the only reliable protection against this class of attack.
We upgraded to pnpm 10. This was significant effort (had to migrate through pnpm 9 first), but pnpm 10 brings critical security improvements. Scripts are ignored by default. You can explicitly whitelist packages that need to run scripts via pnpm.onlyBuiltDependencies. And the minimumReleaseAge setting prevents installing packages published recently.
To whitelist packages that legitimately need build scripts:
This prompts you to select which packages to allow (like esbuild, prisma, sharp).
We switched npm publishing to OIDC. No more long-lived npm tokens anywhere. Publishing now uses npm’s trusted publishers with GitHub Actions OIDC. Even if an attacker compromises a developer machine, they can’t publish packages because there are no credentials to steal. Publishing only happens through CI with short-lived, scoped tokens.
We enabled branch protection on all repositories. Not just critical repos or just OSS repos. Every repository with meaningful code now has branch protection enabled.
We’ve adopted Granted for AWS SSO. Granted encrypts SSO session tokens on the client side, unlike the AWS CLI which stores them in plaintext.
Based on PostHog’s analysis of how they were initially compromised (via pull_request_target), we’ve reviewed our GitHub Actions workflows. We now require approval for external contributor workflow runs on all our repositories (previous policy was only for public repositories).
The ability for packages to run arbitrary code during installation is the attack surface. Until npm fundamentally changes, add this to your ~/.npmrc:
Yes, some things will break. Whitelist them explicitly. The inconvenience is worth it.
pnpm 10 ignores scripts by default and lets you set a minimum age for packages:
Newly published packages can’t be installed for 3 days, giving time for malicious packages to be detected.
Branch protection takes 30 seconds to enable. It prevents attackers from pushing to a main branch, potentially executing malicious GitHub action workflows.
Long-lived npm tokens on developer machines are a liability. Use trusted publishers with OIDC instead.
If you don’t need a credential on your local machine, don’t have it there. Publishing should happen through CI only.
Our #git Slack channel is noisy. That noise saved us.
One of the hardest parts of this incident was that it happened to a person.
“Sorry for all the trouble guys, terrible experience”
Our compromised engineer felt terrible, even though they did absolutely nothing wrong. It could have happened to any team member.
Running npm install is not negligence. Installing dependencies is not a security failure. The security failure is in an ecosystem that allows packages to run arbitrary code silently.
They also discovered that the attacker had made their GitHub account star hundreds of random repositories during the compromise. Someone even emailed us: “hey you starred my repo but I think it was because you were hacked, maybe remove the star?”
Have questions about this incident? Reach out on Twitter/X or Discord.
...
Read the original on trigger.dev »
You know what they say in the world of products: fast, cheap, good; pick two
Which is very similar to my experience when trying to find the most suitable wood finish for my hand carved wooden spoons and coffee cups.
* cures fast (in less than 2 days)
* is food safe (can be used on things you eat or drink from, like spoons and cups)
* is free of solvents (doesn’t smell of nasty chemicals that make you dizzy every time you apply it)
* and with the hard requirement of making wood hydrophobic and leaving a visually pleasant sheen without creating a plasticky layer
Sure, we have pure tung oil which doesn’t need any solvent, has a pleasant nutty smell, and cures into a food-safe hydrophobic polymer inside the wood fibers. But it takes 2 to 4 weeks for the curing to happen depending on oxygen intake, and the finish is quite matte and boring.
While tung oil is natural, that doesn’t mean you can ingest it. It is a powerful irritant in its liquid form, it is only food-safe after curing into its inert polymer state.
There’s also the more rare polymerized (or heat-bodied) tung oil, which is a honey-like viscous oil that cures in a few days because it was heated to very high temperatures in the absence of oxygen. That would be ideal except I can’t find it anywhere around me to buy, and it would most likely need thinning with citrus oil solvent (or D-limonene).
Citrus oil has a nice but way too potent smell of orange peel. It might seem pleasant at first compared to the chemical smell of naphtha and white spirits, but it can be too much if you finish small wooden objects often like I do.
ash cup, in the process of being oiled with tung oil
And there are plenty other drying oils, the most well-known being linseed oil, which is indeed edible but has the unfortunate disadvantages of yellowing over time, taking ages to cure and having a persistent oily-grassy flavor. Polymerized linseed oil (aka stand oil) cures faster but still retains the nasty flavor.
Careful not to confuse it with boiled linseed oil or BLO, which uses metallic drying agents like cobalt or manganese salts.
The boiled name comes from the old practice of heating the linseed oil with lead oxide inside, effectively shortening the oxidation process. But nowadays there’s no heat involved in BLO.
More details in The lowdown on BLO
The oil painting world has experience with many drying oils like safflower oil, walnut oil, poppy seed oil, hemp seed oil. Unfortunately they cure slower and into a less durable film while also being rare and expensive so they aren’t good alternatives for wooden utensils.
For a good few months I used Osmo Polyx Oil which is a solvent-based hardwax oil. Its safety datasheet and full declaration of ingredients mention about 50% of it being white spirit solvent, and the rest is composed of natural oils processed to become drying oils, and a small amount of hard carnauba wax.
It is fast curing in less than a day, leaves a beautiful shimmer on the wood surface, it is food safe after curing, but smells awful because of the solvents.
In the winter months I carve indoors and have to finish the pieces indoors as well, and the horrible solvent smell fills my house for a whole day. I seem to have become sensitized to the smell and now I have almost a full can of Osmo that I can’t use without a mask, even outdoors.
There’s also Rubio Monocoat and other two-component hardwax oils where the base component is usually a solvent-free blend of drying oils and waxes, and the accelerator component is Hexamethylene diisocyanate or HDI. The base component can cure on its own in about 3 weeks and the accelerator shortens the curing time to less than a day.
This alternative cures fast into an inert and food safe polymer, smells like apple pie, and the finish has a slight satin effect. But it’s really hard to mix properly and apply on small wooden objects like spoons and cups. I almost always use too much accelerator, the mixture ends up too viscous and hard to spread on the wood and the constant opening of the bottles and cans makes the components dry prematurely in their containers.
However, this idea of blending drying oils with hard wax seems to be promising and it’s the path I continued on developing.
Food-safe epoxy is another alternative, but I hate both the idea of using synthetic plastics and their plasticky look takes away from the experience of using wooden utensils.
Why even use wood if you’re going to cover it in a layer of clear plastic?
Many people also recommend other edible oils like coconut oil, olive oil etc. But these don’t cure and don’t provide any protection for hot liquids. They either go rancid inside the wood fiber or get washed out after using the wooden object.
Some recommend non-edible petrol-based mineral oil (aka liquid parrafin) because it doesn’t go rancid, but has the same effect of not actually doing much for protection and will leak into hot liquids.
Beeswax is often used by kuksa makers (traditional Scandinavian wooden cups) but wax molecules are too large to penetrate wood fibers well, and it has a low melting point of 60℃ (140°F) so hotter liquids are out of the question.
Carnauba wax has a higher melting point at 80℃ (176°F) and is harder than beeswax, but it has a glossy finish, having a similar look to epoxy resin after buffing.
The ideal finish should combine the durable hydrophobic properties of fiber-penetrating tung oil with the fast drying properties of a top wax layer, in an easy to apply but solvent-free blend.
Some carvers use urushi lacquer which is the sap from a tree common to Japan. The result is a hard and glossy finish that can resist temperatures of over 300℃ (572°F), but the look and process of application is far from what I’m looking for.
To ensure curing is complete, it needs 10 to 20 very thin coats over many months in a warm and humid environment. The wood then takes this dark brown color, regardless of what wood you started with, which is the opposite of what I want.
I work with fruit and flower woods that are less common in woodworking (apple, fig, jasmine etc.) and I want people to know that and experience what that specific wood looks and feels like. I want to keep their original hues and tints and accentuate the wood grain of each piece.
Someone from Israel bought a spoon I made from fig tree wood, but still asked about it a bit incredulously. It had greenish light wood grain, with a bit of untouched bark kept on the handle and a wonky curve that followed the shape of the tree branch it was made from.
They have many fig trees there but had never seen such a thing made from their wood, so they felt a connection to their motherland in that usual object.
My mother-in-law cut a large lilac tree and gave me some thick branches. It’s a wood that cracks very easily while drying, and I couldn’t use most of it. But the few things I made from it, I gifted back to my wife’s mother and sisters in the form of cooking spoons and are still cherished by them today.
Its pastel purple grain accents remind them clearly of the tree that used to flower every spring in their yard.
A brown lacquer would never be able to create this effect, and that is why I’m still trying to find the perfect natural oil based finish.
This is basically polysiloxane dissolved in alcohol and isoparaffin solvent. After 3 weeks it cures into a food-safe, hydrophobic layer which is durable, but not very good looking in my eyes.
It leaves the wood looking bare, without the color enhancing and rippling effect of oil. Some might prefer that, especially people that worked hard on getting a very shiny knife finish on their carving.
In my case, this is not the finish I’m looking for because of the 3-week curing time, the solvent-based solution, the colorless look and the fact that this finish is very expensive and hard to find outside Japan.
Here’s some more information from a woodturner’s experience with the finish: Liquid Glass Wood Finish — Update
For the impatient, here’s a comparison video on how the hardwax oil blends look and perform in a water test on some plum wood blanks.
My experiments started simple, with melting about 10g of carnauba wax flakes into 40g of pure tung oil.
Most people recommend a double-boiler or bain-marie approach to melting the wax. That’s because the heat source would be a container of boiling water which can never get above 100℃ (212°F), a safe temperature to avoid reaching the flash point of these oils.
I just drop a spoonful of carnauba flakes into a glass jar of tung oil and place it in the microwave for a minute until everything becomes liquid.
For small quantities, the microwave works wonders, and there’s no danger of overheating the oil or gelifying it because it cools off rapidly.
* pour the tung oil into a glass jar or ceramic container
* drop the carnauba wax flakes into the oil and stir to disperse the flakes
* place in the microwave for 30 seconds then stir again for a few seconds
* repeat until the wax melts and everything is liquid
* let it cool down until the mix is solid
Brittle paste that’s hard to scoop from the jar and apply on the wood. Doesn’t spread easily, feels sticky.
After the whole wood surface was covered and left for 10 minutes, the excess was wiped with a paper towel. After wiping, the finish is very matte, which means that not much of the wax remained, and the oil didn’t penetrate the fiber enough because of the consistency.
The consistency changes a lot based on the oil-to-wax ratio.
Too much wax and the blend cools into a brittle paste that’s very hard to scoop from the jar. Too little and the blend is too liquid with not enough wax remaining on the wood surface to form a film. A 4:1 oil-to-wax ratio is good, having enough wax for the top layer, but in this simple formulation it is still too hard and brittle.
Instead of increasing the oil quantity which could defeat the purpose of the wax layer, I added a bit of beeswax to soften the blend.
This works, the paste becomes softer but it makes the top layer of wax not durable enough because of its lowered melting point, so I needed an alternative.
My idea is to have the wood absorb the tung oil from the blend while leaving the wax molecules on the wood surface. The wax should be soft enough to not create a glossy layer and not be brittle, but hard enough to protect the wood for the time it takes the tung oil to cure.
To soften the blend without having to add too much beeswax, I found lanolin to be a good addition. It is a highly hydrophobic wax extracted from sheep wool that even in tiny amounts can make the paste consistency softer and easier to apply.
Similar to #1, just immerse the waxes into the oil in a glass jar and microwave in increments of 30 seconds until everything is liquid. Leave to cool until solid.
Softer and more homogenous paste that can be scooped easily from the jar with a finger or rag. Spreads relatively easy onto the wood surface, feels like a more viscous hand cream.
Smells amazing, nutty, vanilla and honey scent, faint enough to not bother anyone.
After covering the whole wood surface, I used a heat gun to liquify the finish and enhance wood fiber penetration. The paste melts very easily and the oil in it gets absorbed quickly in the wood pores, leaving the wax to solidify.
After the wood has cooled, I applied another layer because it seemed that some parts of the wood had little to no wax remaining. I removed the excess with a paper towel and buffed the wax to a satin look.
This finish is not fast curing by any means. It is fast drying, as the oil gets absorbed and the wax on top solidifies into a very thin medium-hard layer.
But the melting point of the wax is about 75℃ (167°F) so still not perfect for very hot liquids. You definitely can’t brew your tea directly into a cup finished this way, not until the oil cures completely after 2-4 weeks.
I tested it by brewing a cup of espresso which seems to be fine, the temperature of the coffee is far below 75℃ (167°F) when it reaches the cup.
Using a spoon for eating soup also works fine, because the soup needs to be below 45℃ (113°F) for me to be able to eat it.
After usage, the wooden spoon and cup could be easily washed with warm water and soap, the wood didn’t stain at all. The wax still worked as I could see the water droplets sliding off the wood.
Visually, it doesn’t leave a very strong sheen. It has a soft waxy look, instead of the shimmer of oiled wood.
That’s because wax doesn’t follow the micro porous surface of the wood, it solidifies into a smooth surface. So instead of scattering light you get mirror-like reflections creating that glossy wax look.
Commercial finishes use flattening agents like fumed silica to get around this, but I’m trying to use as little additives as possible for now.
The look also comes from the carnauba wax being softened by the beeswax and lanolin which also translates into a softer top layer.
The woodworking world has moved on almost completely from natural resins. Almost every finish is either based on polyurethane or epoxy resin if you’re looking for a harder top layer. At best, you’re looking at a shellac layer which doesn’t mix with oils.
Thankfully oil painting forums and blogs still have a trove of information on tree resins, drying oils and siccatives.
That’s how I found out about damar resin, a semi-hard resin extracted from Dipterocarpaceae trees growing in southeast Asia. It is a clear resin, melts at about 120℃ (248°F) which is easily attainable in a microwave, and has a bit of flexibility so that it doesn’t create a cracking layer on wood.
It can be found in the form of crystals or pellets in art shops and it’s pretty cheap. People dissolve it in turpentine to create varnishes, but I wanted to incorporate it into my oil-wax blend through heat instead of solvents.
To increase sheen and make the top layer stronger, I melted damar resin crystals directly into oil, without any solvent. I also added a very small amount of manganese, zirconium and calcium drier to make the curing faster, and some vitamin E to increase shelf life.
Add the carnauba wax, beeswax and lanolin into the tung oil in a glass jar and microwave in increments of 30 seconds until everything is liquid.
Add the damar resin crystals into the coconut oil and microwave in increments of 10 seconds until everything is liquid. Let the mix cool down for about 30 seconds then add the drops of MnZrCa drier and vitamin E. Stir until the mix is homogenous.
Pour the damar resin-oil blend into the tung oil blend and stir until homogenous. Let cool down until solid.
Medium-soft paste that can be scooped relatively easily from the jar with a finger or rag. Spreads easy enough onto the wood surface.
Smells the same as the last experiment: nutty, vanilla and honey scent, faint enough to not bother anyone. The nasty smell of the drier doesn’t transfer into the blend because its quantity is too small. The resin has no smell in its solid form.
After covering the whole wood surface, I used a heat gun to liquify the finish and enhance wood fiber penetration. The paste melts very easily and the oil in it gets absorbed quickly in the wood pores, leaving the wax and resin to solidify into a very thin layer on top.
After that there was almost no excess left and I buffed the top layer to a satin look using a paper towel.
The tung oil inside the wood cured in about 48 hours.
This can be verified by melting the wax layer with a heat gun then removing the liquid wax with a rag. The bare wood then can be sanded with 240 grit to check if the wood dust falls off easily. If the wood dust seems wet and gums on the sandpaper, the oil is still not completely cured.
The satin sheen looks better because of the resin. Regions with medullary rays look wavy and move in the light, regions with straight fibers look exactly between glossy and matte.
Osmo Polyx Oil still has a prettier shimmer that this finish doesn’t achieve, I’ll look into adding trace amounts of fumed silica to see if it makes the finish settle better into the wood fiber microstructure and scatter light instead of being more on the smooth glossy side.
The fumed silica might also help with adding shear thinning capability so that the paste is even easier to scoop from the jar without softening it.
The metallic driers are probably the first thing that jump out, because we always read about how bad they are when seeing people talk about natural wood finishes. Not all driers are bad, and it depends on things like quantity, leaching probability and type of metal used.
Sure, lead and cobalt driers are out of the question, I don’t want them anywhere near my food and beverages no matter how small the risk of leaching. They contain heavy metals that can have catastrophic effects if they accumulate in your body.
I use a drier containing manganese neodecanoate to speed up surface-drying, and zirconium and calcium salts for through-drying.
Zirconium and calcium are known to be safe. Zirconium is the same metal that dental implants are usually made of, even ceramic knives are made using zirconium, it seems to be a pretty inert metal when it comes to contact with our body.
Manganese is toxic if inhaled or ingested in high amounts, but the quantity of manganese in 2 drops of drier is so incredibly low you could take them orally and not feel any effect other than the horrible solvent taste. That, combined with the fact that the drier disperses throughout the oil makes the quantity too low to matter.
The strictest Tolerable Upper Intake Level for manganese is set to 8 mg/day. In fact we get some of that quantity daily through food that is enriched with vitamins and minerals. If the drier were to ever leach completely from the finish, one would get less than 0.1mg per lifetime of object.
You’d probably have to eat the spoon completely for that to happen, in which case the quantity of ingested wood might become more of a problem.
But the whole food-safe standard on drier based finishes is actually based on the fact that a cured finish traps that metal into an inert polymer which can’t leach into food. Just like Teflon pans or aluminum and copper pots, everything is safe until the layer chips off somehow.
I compromised on having a quantity small enough to not pose any safety risk and that wouldn’t shorten shelf life by making the oil cure inside the jar, but high enough to make the oil cure in 48 hours instead of 2-4 weeks. This, in my opinion, makes the finish safer by being more predictable in curing time, and minimizing the probability of food contacting uncured tung oil.
Because damar resin melts at 120℃ (248°F), tung oil is not suitable for melting the resin directly into it because tung oil can gelify at high temperatures, hindering its curing capabilities. That’s why I melt the resin crystals in a small amount of coconut oil, which has a high enough smoking point of 180℃ (356°F), smells amazing and doesn’t go rancid easily.
...
Read the original on alinpanaitiu.com »
Following in Amazon’s footsteps, two student projects independently use ‘collaborative filtering’ to bring recommendations and social networking to online music; soon they will join forces.
What we now know as the “social web” — or Web 2.0 — didn’t arrive until around 2004. But the first inklings of it were emerging a couple of years before. As usual, music was the harbinger.
Last.fm was founded in 2002 by a group of four Austrian and German students from Ravensbourne College of Design and Communication in London. It was fashioned as an internet radio station that allowed a user to build a listening profile and share it with others. The year of its launch, Last.fm won a young talent award at the Europrix, a multimedia awards show based in Vienna. This was how the product was described in a showcase video (embedded below) leading up to the awards ceremony:
“After repeated use, the system builds a listening profile that increasingly reflects the user’s preferences. The sum of all profiles is visualized in the ‘Map of Music,’ a presentation of musical connections and genres determined only by the collaborative effort of Last.fm users.”
When the students went up to receive their award, one of them, Thomas Willomitzer, noted the importance of “collaborative filtering” to the Last.fm system. The idea was that the Last.fm algorithm would recommend music you might like, based on your listening history combined with the listening history of other, similar, users. Willomitzer added that this type of algorithm would be familiar to people who used Amazon.com.
Here’s a video of the Last.fm founders presenting at Europrix 2002, via Thomas Willomitzer:
Collaborative filtering was a common technique in recommender systems, and its history dated back to before the web — for instance, it was the basis for a 1992 Xerox PARC email system called ‘Tapestry.’ But collaborative filtering really came into its own during the web era, and in particular it was popularised by Amazon. By 2002, Amazon users were familiar with the following message: “Customers who bought items in your Shopping Cart also bought…” There was also a “Your Recommendations” list on the Amazon.com homepage. Both of these features were created using an algorithm that Amazon called “item-to-item collaborative filtering.” As explained in a research paper:
“Rather than matching the user to similar customers, item-to-item collaborative filtering matches each of the user’s purchased and rated items to similar items, then combines those similar items into a recommendation list.”
Amazon collaborative filtering examples; via research paper by Greg Linden, Brent Smith and Jeremy York, published by the IEEE Computer Society in January-February 2003 edition.
The key here is that Amazon’s collaborative filtering was done based on the items people bought or rated, not the profiles of its users. This approach was also crucial to how new social web services like Last.fm would develop. The “map of music” that Last.fm created was all about mapping which songs (or genres) were interconnected — so a certain Bob Dylan song might have a strong connection to a certain Joni Mitchell song, based on listener data, and thus the Mitchell song might come up as a recommendation for people who listened to the Dylan song (and vice versa).
By coincidence, another student in the UK was also working on a recommendation system for music in 2002. Audioscrobbler was started as a computer science project by Richard Jones at the University of Southampton. Jones coined the term “audioscrobbling” (later shortened to “scrobbling”) to describe the process of tracking songs that you listen to in order to make a listening profile, which is then used for recommendations.
In an April 2003 interview with his University’s paper, twenty-year old Jones explained how Audioscrobbler worked:
“Users of the system need to download software on to their computer that monitors what artists they listen to. The data is then collated and a pattern emerges by way of a technique known as ‘collaborative filtering.’ The results are then recorded against a username and can be compared with the listening tastes of other members.”
Later, Jones would team up with the Ravensbourne College students and fold his project into Last.fm, but even in 2002 — when they were independent products — it is striking how similar the two systems were. Both used collaborative filtering to create song recommendations, and both aimed to create a kind of social network based around what users listened to.
The key to the emerging social web would be that you discover new content and communities by following other people. For music, the idea was to help you break away from the established broadcast model. At the Europrix event, Last.fm’s Martin Stiksel brought out a 1980s-style transistor radio to illustrate the point. If you want to listen to music on such a device, Stiksel explained, you have to tune the frequency band to find your station. If you don’t like the music playing on that station, you tune the dial to another radio station and try your luck again.
“The inherent problem with broadcast media is that basically, at the end of the day, it’s always somebody else selecting the music for you,” said Stiksel. “So there’s always a bunch of editors or programmers that picked the music and put them into into a program for you.”
Three Last.fm founders in 2002 with a transister radio, “from the 80s, I believe.”
With Last.fm, the stream of music you heard was a mix of manual choice and algorithmic selection. You might start with a song already in your online “record collection” (the term Stiksel kept using), or start from another user’s profile. From then on, songs would be chosen for you based on collaborative filtering. If you played a song through, the Last.fm software automatically added it to your own collection. You could also press a “love” button to add it. But if you didn’t like a certain track, you could press a “hate” button (so it wouldn’t get played again), or click the “skip” button to move to the next song. There was also a “change” button to go to a different user profile.
The early Last.fm user interface was, in truth, a bit cluttered with all these different buttons and various search boxes — but over time it would get more streamlined.
Stiksel explained that the idea for Last.fm came about when the students asked themselves, “how do you look for something that you don’t know?” So in the case of music, how to discover new music when you don’t necessarily know what type of music you’re looking for? The answer, he said, was the social component.
“Then we figured out that it’s the social aspect of music — the best music you always find when you go to your friend’s house and he plays you records. And we’re taking this concept into an online environment here.”
What both Last.fm and Audioscrobbler stumbled onto in 2002 was the collective value of user data in discovering new content — something that Amazon was also taking advantage of at this time. The problem with music, though, was that licensing from record companies was still highly restrictive. The Last.fm founders somewhat glossed over it during their Europrix presentation, but they did admit that “due to legal issues, we’re only allowed to play 30 second samples.” Unless you already owned a piece of music, 30 seconds was all you got.
By the following year, however, Last.fm had begun turning itself into an “online radio” service, by paying licensing fees to the UK collecting societies PRS (Performing Right Society) and MCPS (Mechanical-Copyright Protection Society).
So pre-Web 2.0, the streaming revolution was only just getting started. But with Last.fm and Audioscrobbler, we at least glimpsed the future of the social web.
Last.fm in August 2006. This is the design we now remember, but it took several years to get there. Via Wayback Machine.
Cybercultural is a weekly online magazine about internet history and its cultural impact. Subscribe:
My Web 2.0 memoir, Bubble Blog: From Outsider to Insider in Silicon Valley’s Web 2.0 Revolution, is now available to purchase:
Or search for “Bubble Blog MacManus” on your local online bookstore.
...
Read the original on cybercultural.com »
To use the Mastodon web application, please enable JavaScript. Alternatively, try one of the native apps for Mastodon for your platform.
...
Read the original on mathstodon.xyz »
Apple today released iOS 26.2, iPadOS 26.2, and macOS 26.2, all of which introduce new features, bug fixes, and security improvements. Apple says that the updates address over 20 vulnerabilities, including two bugs that are known to have been actively exploited.
There are a pair of WebKit vulnerabilities that could allow maliciously crafted web content to execute code or cause memory corruption. Apple says that the bugs might have been exploited in an attack against targeted individuals on versions of iOS before iOS 26.
Processing maliciously crafted web content may lead to arbitrary code execution. Apple is aware of a report that this issue may have been exploited in an extremely sophisticated attack against specific targeted individuals on versions of iOS before iOS 26.
Processing maliciously crafted web content may lead to memory corruption. Apple is aware of a report that this issue may have been exploited in an extremely sophisticated attack against specific targeted individuals on versions of iOS before iOS 26.
One of the WebKit bugs was fixed with improved memory management, while the other was addressed with improved validation.
There are several other vulnerabilities that were fixed too, across apps and services. An App Store bug could allow users to access sensitive payment tokens, processing a malicious image file could lead to memory corruption, photos in the Hidden Album could be viewed without authentication, and passwords could be unintentionally removed when remotely controlling a device with FaceTime.
Now that these vulnerabilities have been publicized by Apple, even those that were not exploited before might be taken advantage of now. Apple recommends all users update their devices to iOS 26.2, iPadOS 26.2, and macOS Tahoe 26.2 as soon as possible.
...
Read the original on www.macrumors.com »
To add this web app to your iOS home screen tap the share button and select "Add to the Home Screen".
10HN is also available as an iOS App
If you visit 10HN only rarely, check out the the best articles from the past week.
If you like 10HN please leave feedback and share
Visit pancik.com for more.