The YunoHost project decided to sign the open letter published by Sabot.Media and We Will Free Us about the attack from the United States government against Autistici/Inventati, an italian hoster.
YunoHost stands in solidarity with Autistici/Inventati and other hosters. We hope that the United States government will reconsider its position and that decision-makers around the world will fully grasp the implications of this dangerous trend.
The said letter is reproduced below:
Defend Autistici/Inventati and the right to build resistant communication
On August 26, 2026, the United States designated the Italian volunteer technology collective Autistici/Inventati as a Specially Designated Global Terrorist and placed it on the Specially Designated Nationals list under Executive Order 13224. A temporary authorization covering certain wind-down transactions ends at 12:01 a.m. Eastern on September 25.
Autistici/Inventati has provided noncommercial email, websites, mailing lists, Noblogs and other communications services since 2001. It emerged from Italian hacklabs, autonomous media and social movements. Its infrastructure was built in response to censorship, covert surveillance and server seizures. It minimizes identifying data, uses distributed systems and treats privacy as a condition of political participation rather than a product.
The United States’ public announcements point to material and organizations that allegedly used A/I infrastructure. They do not publicly show that A/I planned the cited actions, selected targets, directed users or authored hosted material. The designation therefore raises a question extending far beyond one collective: can maintaining privacy-preserving communications infrastructure for disfavored movements itself be treated as terrorism?
That theory threatens independent hosts, encrypted communications services, radical libraries, movement archives, publishers and small volunteer projects everywhere. It invites banks, registrars, hosting companies and technology providers to sever relationships first and ask legal questions later. It turns data minimization into suspicion, privacy into concealment and infrastructure into guilt by association.
Large commercial platforms are ordinarily distinguished from the speech and conduct of their users through intermediary-liability principles, including Section 230 in the United States. Those protections are not absolute and do not override sanctions law. That is precisely why this designation is so consequential: executive sanctions power can impose economic isolation without resolving the ordinary question of whether a provider is legally responsible for third-party material in court.
Autistici/Inventati is not politically neutral, and neither are many of the publications, libraries, unions and civil-liberties organizations that may sign this letter. Political affinity, however, is not operational control. Providing an email account, publishing platform or server is not the same act as planning everything a user may later say or do.
We therefore call for:
The immediate revocation or prompt, transparent review of the designation, with publication of the factual and legal basis sufficient for meaningful public scrutiny and challenge.
A clear distinction between providing communications infrastructure and directing the conduct of its users.
Privacy, encryption, pseudonymity and refusal to retain unnecessary identifying data must not be treated as evidence of terrorism.
Protection for independent reporting, research, archiving and public advocacy.
Journalists, libraries, researchers and community archivists must be able to document this history without being treated as extensions of a designated entity.
Resistance to institutional over-compliance.
Banks, registrars, hosts, certificate providers and technology companies should disclose relevant restrictions, provide notice where lawful and avoid suppressing material or relationships beyond what the law actually requires.
A public response from digital-rights, free-expression and civil-liberties institutions.
The targeting of autonomous infrastructure cannot be allowed to become a precedent through silence.
We do not ask every signer to endorse every A/I user, publication or political position. We ask signers to defend a foundational distinction: communications infrastructure is not identical to the speech or conduct transmitted through it.
The purpose of economic designation is isolation. Our answer is independent, informed and disciplined solidarity: preserve the public record, examine the government’s claims, resist over-compliance and defend the ability of movements to communicate without surrendering themselves to corporate or state surveillance.
The list of signatories on sabot.media
2 posts - 2 participants
Java concurrency is a powerful feature, but it can be difficult to get right. Writing correct multithreaded code requires a deep understanding of thread pools, synchronization, cancellation, and error propagation. Even experienced developers regularly introduce subtle bugs like thread leaks, swallowed exceptions, and race conditions that only surface under specific circumstances.
Traditionally, Java concurrency has had several limitations:
Project Loom aims to eliminate the tradeoff between simplicity and efficiency in concurrent Java code, making it easier to write, debug, profile, and maintain code that is correct, readable, and scalable. It does this through three features that work together:
In this post, we’ll give you an overview of these features, explain some of the problems they solve, show how they work together, and demonstrate how IntelliJ IDEA supports you along the way.
To illustrate some of the problems with concurrency and how Project Loom can solve them, let’s look at an example of how we could write concurrent code without using any of the features of Project Loom. We’ll then rewrite this code to take advantage of these features and see how they compare.
As an example, we will use an application that loads a customer profile. It fetches order history and product recommendations for a customer in parallel. You can find the project’s source code here.
The method getProfile() in the CustomerProfileService (which you can find here) loads the customer profile using CompletableFuture to run the calls concurrently:
public CustomerProfile getProfile(String customerId) throws OrderServiceException, RecommendationServiceException {
CompletableFuture<List<Order>> orderFuture =
CompletableFuture.supplyAsync(() -> orderServiceClient.getOrders(customerId), executor);
CompletableFuture<List<Recommendation>> recFuture =
CompletableFuture.supplyAsync(() -> recommendationServiceClient.getRecommendations(customerId), executor);
CompletableFuture<Void> allFutures = CompletableFuture.allOf(orderFuture, recFuture);
try {
allFutures.get(2, TimeUnit.SECONDS); // single timeout covering both futures
return new CustomerProfile(customerId, orderFuture.join(), recFuture.join());
}
// catch block
The catch block handles any exceptions. This code does what it is supposed to do: It runs the independent calls in parallel, has a timeout, and makes an effort to cancel remaining tasks on failure. But there are still several potential problems:
cancel(true) marks the future as cancelled, but for CompletableFuture the interrupt flag is ignored and the work already running in the pool continues to completion unless it was explicitly wired to a cancellation signal.ExecutionException wraps the real cause and must be unwrapped manually (shown in the code snippet below and also available here). The unwrapping chain will need to be updated every time a new exception type is introduced. catch (ExecutionException e) {
Throwable cause = e.getCause();
orderFuture.cancel(true);
recFuture.cancel(true);
if (cause instanceof RestClientResponseException ex && ex.getStatusCode().value() == 503) {
if (orderFuture.isCompletedExceptionally()) {
throw new OrderServiceException("Order service unavailable", e.getCause());
}
if (recFuture.isCompletedExceptionally()) {
throw new RecommendationServiceException("Recommendation service unavailable", e.getCause());
}
}
throw new RuntimeException("Unexpected error", e.getCause());
cancel() calls are repeated across both the TimeoutException and ExecutionException catch blocks. If any additional parallel call is added later, it needs a cancel() call in both catch blocks, which is easy to forget. These blocks could drift out of sync as the code evolves.ThreadLocal is fragile. ThreadLocal variables are mutable, their values persist for the lifetime of a thread unless explicitly removed, and child threads do not automatically inherit them unless you use InheritableThreadLocal, which has its own pitfalls.Sidenote: To add the Get Thread Dump button to your Debugger tool window, right-click the Debugger tool window and select Customize Toolbar. In the popup, click Add, search for and select Get Thread Dump, and click OK.
Let’s take a look at how Project Loom addresses these problems.
The first feature of Project Loom is Virtual Threads, which drastically improve throughput in Java applications with blocking code.
Traditionally, the number of available threads in a Java application is limited because platform threads wrap operating system (OS) threads, and the number of OS threads is limited. Platform threads are also expensive; creating them can take milliseconds, each one consumes significant memory, and context switching between them has considerable overhead. To manage these costs, applications use thread pools – a fixed set of reusable threads managed by an ExecutorService.
In contrast, virtual threads are lightweight threads. They are cheap to create (taking microseconds instead of milliseconds), and since they are not tied to OS threads, they are not limited in number; you could run millions of them. When a virtual thread blocks, the underlying platform thread is released for other work and reassigned when the virtual thread is ready to continue. This means that virtual threads can significantly improve throughput for blocking workloads, such as I/O (anything that waits on databases, network calls, or file access), pauses, or synchronization.
In Java 24, an additional improvement was made to improve the scalability of Java code. With JEP 491: Synchronize Virtual Threads without Pinning, virtual threads that block in synchronized methods and statements release their underlying platform threads. You can see this in action in the What’s New in IntelliJ IDEA 2025.2 livestream.
We already briefly discussed virtual threads in Java 25 LTS and IntelliJ IDEA. For more information about using virtual thread dumps, have a look at Thread Dumps and Project Loom (Virtual Threads).
To debug problems with concurrent threads, check out the new, improved logpoint functionality described in Println Debugging Done Right.
The second feature of Project Loom is Scoped Values – a safer, more scalable alternative to ThreadLocal variables, designed with virtual threads in mind. They solve the problem of sharing contextual data across threads cleanly and safely.
To share data between components of an application, we can use thread-local variables, but these have several downsides. A ThreadLocal variable is mutable and, therefore, hard to reason about. Data persists for the thread’s lifetime unless manually removed (risking memory leaks and security issues), and child threads inherit copies that increase memory footprint.
ScopedValue provides a better model: A value is bound once within a defined scope, automatically available to all code running within that scope, and cleaned up automatically when the scope ends. The binding cannot be changed from within the scope, eliminating the risk of accidental mutation and guaranteeing that any code reading the value will see the same one. When used with structured concurrency, scoped values require no explicit propagation to child threads, making context sharing both safer and simpler.
Note that even if your code does not explicitly use ThreadLocal, frameworks like Spring use it under the hood.
For more details, see the section on Scoped Values in Java 25 LTS and IntelliJ IDEA.
The third feature of Project Loom is Structured Concurrency, which is currently still in preview. Java 27 again brings some changes to this preview feature. As we have already added some support for this feature in IntelliJ IDEA, now is the perfect time to try it out.
Structured concurrency is designed to promote a style of concurrent programming that reduces common problems such as thread leaks and cancellation delays, duplicated cancellation logic, and awkward error handling. The core idea is that a group of related concurrent tasks is treated as a single unit of work with a clear owner, a clearly defined lifetime, and clear rules. Subtasks cannot outlive their scope, failures propagate cleanly, and cancellation flows automatically from parent to children.
The StructuredTaskScope lets you break a task down into concurrent subtasks that are coordinated as a single unit. Subtasks are forked to run on their own thread and joined as a unit when the work completes.
StructuredTaskScope has a factory method StructuredTaskScope.open(). This method has several overloads that allow you to provide a Joiner and/or a configuration callback. This lets you define the failure policy, a name for observability, and a timeout all in one place when opening the scope.
In our example, we want both methods (fetchOrders() and fetchRecommendations()) to succeed in order to correctly assemble the customer profile. We can provide a name for our scope and set a timeout for how long we are willing to wait on the results. If either of them fails, the other is cancelled. When a subtask fails or the timeout expires, join() throws an ExecutionException with the underlying cause. We switch on that cause to handle each case explicitly – including CancelledByTimeoutException, which is what the joiner uses to signal a timeout.
What if we don’t need results from all methods called in parallel? For example, imagine recommendations are available from two different caches and you only need the result of one of the calls to succeed. If one task succeeds, the other can be shut down. To accomplish this, we can use a different Joiner, anySuccessfulOrThrow(). As soon as one cache returns a result, the scope shuts down and the other task is cancelled automatically. If both fail, the join() method throws an ExecutionException with the exception of one of the failed subtasks as the cause.
To quickly scaffold a StructuredTaskScope in IntelliJ IDEA, use the built-in live template sts.
Structured concurrency is still a preview feature in Java 27, so it is not yet recommended for production use. That said, the feature has been relatively stable in its broad shape for several preview rounds, with some changes to the API, and now is a great time to experiment with it. To identify where you could use structured concurrency in your code, look for places where the code performs multiple tasks in parallel and awaits the results. This code is a candidate to be rewritten using structured concurrency.
The “modern” branch of our demo project contains the same application rewritten using the features from Project Loom. The structure follows the same pattern as before: Orders and recommendations are fetched in parallel inside the scope.
The updated method getProfile() in the CustomerProfileService (which you can find here) now uses a StructuredTaskScope:
public CustomerProfile getProfile(String customerId) throws InterruptedException, TimeoutException {
try {
return ScopedValue.where(CUSTOMER_ID, customerId).call(() -> {
try (var scope = StructuredTaskScope.open(
Joiner.awaitAllSuccessfulOrThrow(),
config -> config.withName("customer-profile").withTimeout(Duration.ofSeconds(2)))) {
var orderTask = scope.fork(() -> orderServiceClient.getOrders(CUSTOMER_ID.get()));
var recTask = scope.fork(() -> recommendationServiceClient.getRecommendations(CUSTOMER_ID.get()));
scope.join();
return new CustomerProfile(customerId, orderTask.get(), recTask.get());
} catch (ExecutionException e) {
switch (e.getCause()) {
case StructuredTaskScope.CancelledByTimeoutException _ -> throw new TimeoutException("Request timed out");
case OrderServiceException ose -> throw ose;
case RuntimeException rte -> throw rte;
default -> throw new RuntimeException(e.getCause());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted", e);
}
});
} catch (InterruptedException | TimeoutException | RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Notice that we no longer need the duplicated cancel() calls in two catch blocks. If either task fails or the timeout is exceeded, all remaining subtasks are cancelled automatically. There is no longer any need for manual cancel() calls.
The code now clearly expresses its intent: Fetch orders and recommendations in parallel, wait up to two seconds, and fail cleanly if anything goes wrong. Because the pattern of what the code does is clearly captured in the code, this code is easier to read, understand, and reason about.
To see the difference structured concurrency makes, run the updated service in IntelliJ IDEA and take a thread dump while requests are being processed. You can create a thread dump, as described earlier. From IntelliJ IDEA 2026.1, virtual threads forked within a StructuredTaskScope are grouped into containers representing their scopes. The IntelliJ IDEA debugger now shows you the structure in structured concurrency.
To try out the features described in this post, you will need Java 27. You can download it from inside IntelliJ IDEA via Project Structure | Project Settings | Project, and then open the SDK dropdown and select Download JDK. Set Version to 27 and select the Early-Access version.
If you are using a different way to download JDKs, you can point IntelliJ IDEA to your installation. Go to Project Structure | Project Settings | Project, open the SDK dropdown, select Add JDK from disk, and point IntelliJ IDEA to your installation of Java 27.
If you’re using command-line tools like SDKMAN! or asdf, you can use inlay hints to make version management easier. If your .sdkmanrc or .tool-versions file specifies a JDK version that is not yet installed, an inlay hint will appear that allows you to download it directly.
If the JDK is already installed but not configured for the project, you can use the inlay hint to set it as the project JDK.
For more information, see the documentation.
To get support for new language features, like structured concurrency, when using an early access version of the JDK, set the Language level to X – Experimental features.
If Java 27 has already been released when you’re reading this post, download the Java 27 distribution you want to use from IntelliJ IDEA or, if you already have Java 27 installed, point the IDE to your installation. To use structured concurrency, you also need to enable preview features. Set the Language level to 27 (Preview) – Primitive types in patterns, instanceof, and switch (5th preview) in Project Structure. IntelliJ IDEA will flag usage of preview features in the editor, so you are always aware which features are not yet stable.
Virtual threads, scoped values, and structured concurrency are designed as a cohesive system, each addressing a different dimension of the problem:
ThreadLocal (and framework workarounds), giving all tasks in a scope automatic, safe access to shared immutable context.ExecutionException unwrapping.Together, they let you write concurrent code that is much easier to read than traditional concurrent code, while being safe and scalable. The boilerplate that currently may take multiple steps to get right is replaced by code that is more concise and reads exactly like the problem it is solving.
You can use these features in IntelliJ IDEA. If you have questions or feedback, please let us know in the comments below.
Agent Skills have become a key building block of the Agent Harness for AI-driven agentic development. They give AI agents additional capabilities and knowledge, enabling them to complete tasks in a way that aligns with your preferences.
If you are new to Agent Skills, I recommend reading AI-Assisted Java Application Development with Agent Skills first.
IntelliJ IDEA and other JetBrains IDEs include AI Assistant, which helps developers with AI agentic development. AI Assistant provides an elegant and secure way to use and manage Agent Skills.
If you missed the announcement, see Introducing the Skill Manager and Skill Repository.
In this article, we will explore:
AI Assistant supports a wide range of AI agents through ACP (Agent Client Protocol).
The Skills Manager in AI Assistant lets you view the list of available skills and install them.

If you have already installed Agent Skills globally, the Skills Manager detects them and helps you install them as IntelliJ IDEA agent skills.
You can install a skill globally, at the project level, or per agent.

The Skill Repository lets you manage a list of locations where your verified skills are stored.
By default, JetBrains provides a Skill Repository hosted at https://github.com/JetBrains/skills.

These skills are verified by JetBrains for security vulnerabilities.
It is essential to check for security issues before using agent skills downloaded from the internet. A better approach is to maintain an organization-wide Skill Repository, verified by your team, and add it to the Skill Repository list.
Based on the prompt description, the AI agent automatically detects and uses relevant agent skills.
For example, I installed spring-boot-skill, and when I asked the AI agent to write tests for Spring Boot REST API endpoints, it used the spring-boot-skill.

You can also explicitly invoke an agent skill using $skill-name [prompt] with Codex or `/skill-name [prompt]` with Claude.

AI Assistant’s Skills Manager makes agent skills part of your regular IDE workflow. You can discover, install, and manage skills without leaving IntelliJ IDEA, then make them available globally, for a specific project, or only to a particular AI agent. The AI agent can automatically select a relevant skill from your prompt, while explicit invocation gives you control when you need it.
Just as importantly, the Skill Repository provides access to skills verified by JetBrains for security vulnerabilities. Teams can also add their own repositories containing internally reviewed skills. This makes it easier to benefit from reusable agent capabilities while maintaining control over which skills developers use in their projects.