❌

Normal view

There are new articles available, click to refresh the page.
Before yesterdayMain stream

CodeSOD: Asynchronous Directories

9 September 2026 at 06:30

Eri has a mix of a "true confession" and a "wait, really?" today.

The programming language Vala bills itself as a C# like language that compiles into something pretty close to C performance, designed specifically for writing code against Gnome and its associated libraries.

One of the C#-isms in brings in is async/await type semantics. You can yield someAsyncFunction(), which returns control to the caller, allowing it to proceed until the yielded function returns an actual value.

Because it has asynchronous functions, many library functions for handling I/O are already async. So you can make_directory_async, which yields control so you can keep executing while waiting for the filesystem to make your directory.

There are also synchronous versions of those methods. And then there's create_directory_with_parents, which will create a chain of directories for you. That's the synchronous version, and Vala's core library has decided not to provide an asynchronous version of it, which is my "wait, really?" I suspect it's really about the race conditions involved and the risks of things going wrong while doing it asynchronously; all solvable problems, but tricky ones to solve.

But it's the problem Eri had, and this is their solution:

/// Note: does not throw if target already exists
async void create_directory_with_parents_async(File file, Cancellable? cancellable = null) throws Error {
	var to_create = new File[0];
	var? current_target = file;
	while(current_target != null) {
		try {
			yield current_target.make_directory_async(Priority.DEFAULT, cancellable);
		} catch(IOError.NOT_FOUND e) {
			to_create += current_target;
			current_target = current_target.get_parent();
			continue;
		} catch(IOError.EXISTS e) {
			break;
		}
		break;
	}

	for (int i = to_create.length - 1; i >= 0; --i) {
		try {
			yield to_create[i].make_directory_async(Priority.DEFAULT, cancellable);
		} catch(IOError.EXISTS e) {
			// Created by another process
		}
	}
}

If I'm reading this correctly, we start by trying to create the full path to our leaf node. If there's a not found error, we go up one level and try and create that one. We keep trying that until we either run out of parent nodes to try against, or we hit a directory that already exists, or we successfully create a directory. All along the way, we keep appending the current_target to our to_create array.

Once we've gotten that baseline, we then iterate across our to_create array, backwards, creating the shortest non-existent paths first.

This works, but it's ugly as sin. Mostly, it's ugly because we're using exceptions for flow control instead of doing things like checking for file existence, though I suppose those checks may also break our goal of doing all our I/O operations in an async context. I don't know enough about Vala to know the better way of doing this.

Eri writes:

The function works as intended, but trying to trace control flow through the first loop is not pleasant. Ironically, the C mechanism Vala wraps is slightly advanced error codes, which would be nicer to work with in this case

Eri also provides a slightly re-worked version of the main loop, that is at least a bit easier to follow, but still an ugly approach:

	while(current_target != null) {
		try {
			yield current_target.make_directory_async(Priority.DEFAULT, cancellable);
			break;
		} catch(IOError.EXISTS e) {
			break;
		} catch(IOError.NOT_FOUND e) {
			to_create += current_target;
			current_target = current_target.get_parent();
		}
	}

Still, since this is an attempt to patch over a missing core library method and solve a tricky problem about how to handle race conditions, I think absolution is reasonable. It's ugly, it's weird, but it does the job. Go hide it in a box, and never touch its implementation again- except to make it go away.

[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.

A Mortal Blow

8 September 2026 at 06:30

From our anonymous submitter:

Having reached the end of the road at a company increasingly swallowed up companies further east which you'd never believe were still afloat, I found myself headhunted for certain specialty software skills. I was reaching the final few years of my expected working span, so I jumped at the chance. The money was (to me at that time) spectacularly good, so I jumped into it.

Time Is Life, 1080Γƒβ€”1920, 154.35 KB

It started when my first day was spent by me being sent home for the weeks it was still going to take to onboard me. Not bad, engaged to wait, as it were, and the first 6 months was thus and so.

The man who had interviewed me, call him Fred, was intelligent and urbane, and was a joy to meet. He and I clearly hit it off, and lo and behold I was in. It was he who gave me my first assignment, which was mathematical analysis of their core milk-cow program because they needed to find out what it did, and how it did it, so they could perhaps implement it in a more contemporary language.

So I did that, and was just about to publish my findings with him, when Fred inconveniently dropped dead suddenly. In the what-are-we-going-to-do-now-our-key-man-is-no-more confusion, we contractors were forgotten.

For the next 18 months or so (may have been more, may have been less) I was more or less ignored. I spent the time writing a development environment to work on any part of the program conveniently, all the while sitting next to a man who was constantly, forcefully and repetitiously speaking ill of the managers in his line structure. The ridiculously garrulous boss who inherited me thought little of me, and handed me the little work that came my way with active hostility. One or two good guys, but mostly a cabal of elderly men trying to preserve their little money-spinner as long as they could, and a johnny-come-lately trying to increase (and even introduce) automatic processes was less than welcome. During that time I spent quite some time on TDWTF, submitting a gem or two here and there.

No surprise when they finally kicked my arse away. No love lost there. Now working my last couple of years to retirement as a postie.

No punchline here. I want you to know that dropping dead from work is all too real and can happen to anyone.

[Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.

Best of…: Classic WTF: A Dumbain Specific Language

7 September 2026 at 06:30
It's a holiday here in the US, a celebration of labor, so we're reaching back through the archives for a story about an attempt to be labor saving that was not successful. Original. --Remy

I’ve had to write a few domain-specific-languages in the past. As per Remy’s Law of Requirements Gathering, it’s been mostly because the users needed an Excel-like formula language. The danger of DSLs, of course, is that they’re often YAGNI in the extreme, or at least a sign that you don’t really understand your problem.

XML, coupled with schemas, is a tool for building data-focused DSLs. If you have some complex structure, you can convert each of its features into an XML attribute. For example, if you had a grammar that looked something like this:

The Source specification obeys the following syntax

source = ( Feature1+Feature2+... ":" ) ? steps

Feature1 = "local" | "global"

Feature2 ="real" | "virtual" | "ComponentType.all"

Feature3 ="self" | "ancestors" | "descendants" | "Hierarchy.all"

Feature4 = "first" | "last" | "DayAllocation.all"

If features are specified, the order of features as given above has strictly to be followed.

steps = oneOrMoreNameSteps | zeroOrMoreNameSteps | componentSteps

oneOrMoreNameSteps = nameStep ( "." nameStep ) *

zeroOrMoreNameSteps = ( nameStep "." ) *

nameStep = "#" name

name is a string of characters from "A"-"Z", "a"-"z", "0"-"9", "-" and "_". No umlauts allowed, one character is minimum.

componentSteps is a list of valid values, see below.

Valid 'componentSteps' are:

- GlobalValue
- Product
- Product.Brand
- Product.Accommodation
- Product.Accommodation.SellingAccom
- Product.Accommodation.SellingAccom.Board
- Product.Accommodation.SellingAccom.Unit
- Product.Accommodation.SellingAccom.Unit.SellingUnit
- Product.OnewayFlight
- Product.OnewayFlight.BookingClass
- Product.ReturnFlight
- Product.ReturnFlight.BookingClass
- Product.ReturnFlight.Inbound
- Product.ReturnFlight.Outbound
- Product.Addon
- Product.Addon.Service
- Product.Addon.ServiceFeature

In addition to that all subsequent steps from the paths above are permitted, that is 'Board', 
'Accommodation.SellingAccom' or 'SellingAccom.Unit.SellingUnit'.
'Accommodation.Unit' in the contrary is not permitted, as here some intermediate steps are missing.

You could turn that grammar into an XML document by converting syntax elements to attributes and elements. You could do that, but Stella’s predecessor did not do that. That of course, would have been work, and they may have had to put some thought on how to relate their homebrew grammar to XSD rules, so instead they created an XML schema rule for SourceAttributeType that verifies that the data in the field is valid according to the grammar… using regular expressions. 1,310 characters of regular expressions.

<xs:simpleType>
    <xs:restriction base="xs:string">
            <xs:pattern value="(((Scope.)?(global|local|current)\+?)?((((ComponentType.)?
(real|virtual))|ComponentType.all)\+?)?((((Hierarchy.)?(self|ancestors|descendants))|Hierarchy.all)\+?)?
((((DayAllocation.)?(first|last))|DayAllocation.all)\+?)?:)?(#[A-Za-z0-9\-_]+(\.(#[A-Za-z0-9\-_]+))*|(#[A-Za-z0-
9\-_]+\.)*
(ThisComponent|GlobalValue|Product|Product\.Brand|Product\.Accommodation|Product\.Accommodation\.SellingAccom|Prod
uct\.Accommodation\.SellingAccom\.Board|Product\.Accommodation\.SellingAccom\.Unit|Product\.Accommodation\.Selling
Accom\.Unit\.SellingUnit|Product\.OnewayFlight|Product\.OnewayFlight\.BookingClass|Product\.ReturnFlight|Product\.
ReturnFlight\.BookingClass|Product\.ReturnFlight\.Inbound|Product\.ReturnFlight\.Outbound|Product\.Addon|Product\.
Addon\.Service|Product\.Addon\.ServiceFeature|Brand|Accommodation|Accommodation\.SellingAccom|Accommodation\.Selli
ngAccom\.Board|Accommodation\.SellingAccom\.Unit|Accommodation\.SellingAccom\.Unit\.SellingUnit|OnewayFlight|Onewa
yFlight\.BookingClass|ReturnFlight|ReturnFlight\.BookingClass|ReturnFlight\.Inbound|ReturnFlight\.Outbound|Addon|A
ddon\.Service|Addon\.ServiceFeature|SellingAccom|SellingAccom\.Board|SellingAccom\.Unit|SellingAccom\.Unit\.Sellin
gUnit|BookingClass|Inbound|Outbound|Service|ServiceFeature|Board|Unit|Unit\.SellingUnit|SellingUnit))"/>
    </xs:restriction>
</xs:simpleType>
</xs:union>

There’s a bug in that regex that Stella needed to fix. As she put it: β€œEvery time you evaluate it a few little kitties die because you shouldn’t use kitties to polish your car. I’m so, so sorry, little kitties…”

The full, unexcerpted code is below, so… at least it has documentation. In two languages!

<xs:simpleType name="SourceAttributeType">
                <xs:annotation>
                        <xs:documentation xml:lang="de">
                Die Source Angabe folgt folgender Syntax

                        source = ( Eigenschaft1+Eigenschaft2+... ":" ) ? steps

                        Eigenschaft1 = "local" | "global"

                        Eigenschaft2 ="real" | "virtual" | "ComponentType.all"

                        Eigenschaft3 ="self" | "ancestors" | "descendants" | "Hierarchy.all"

                        Eigenschaft4 = "first" | "last" | "DayAllocation.all"

                        Falls Eigenschaften angegeben werden muss zwingend die oben angegebene Reihenfolge der Eigenschaften eingehalten werden.

                        steps = oneOrMoreNameSteps | zeroOrMoreNameSteps | componentSteps

                        oneOrMoreNameSteps = nameStep ( "." nameStep ) *

                        zeroOrMoreNameSteps = ( nameStep "." ) *

                        nameStep = "#" name

                        name ist eine Folge von Zeichen aus der Menge "A"-"Z", "a"-"z", "0"-"9", "-" und "_". Keine Umlaute. Mindestens ein Zeichen

                        componentSteps ist eine Liste gΓΌltiger Werte, siehe im folgenden

                GΓΌltige 'componentSteps' sind zunΓ€chst:

                        - GlobalValue
                        - Product
                        - Product.Brand
                        - Product.Accommodation
                        - Product.Accommodation.SellingAccom
                        - Product.Accommodation.SellingAccom.Board
                        - Product.Accommodation.SellingAccom.Unit
                        - Product.Accommodation.SellingAccom.Unit.SellingUnit
                        - Product.OnewayFlight
                        - Product.OnewayFlight.BookingClass
                        - Product.ReturnFlight
                        - Product.ReturnFlight.BookingClass
                        - Product.ReturnFlight.Inbound
                        - Product.ReturnFlight.Outbound
                        - Product.Addon
                        - Product.Addon.Service
                        - Product.Addon.ServiceFeature

                Desweiteren sind alle Unterschrittfolgen aus obigen Pfaden erlaubt, also 'Board', 'Accommodation.SellingAccom' oder 'SellingAccom.Unit.SellingUnit'.
                'Accommodation.Unit' hingegen ist nicht erlaubt, da in diesem Fall einige Zwischenschritte fehlen.

                                </xs:documentation>
                        <xs:documentation xml:lang="en">
                                The Source specification obeys the following syntax

                                source = ( Feature1+Feature2+... ":" ) ? steps

                                Feature1 = "local" | "global"

                                Feature2 ="real" | "virtual" | "ComponentType.all"

                                Feature3 ="self" | "ancestors" | "descendants" | "Hierarchy.all"

                                Feature4 = "first" | "last" | "DayAllocation.all"

                                If features are specified, the order of features as given above has strictly to be followed.

                                steps = oneOrMoreNameSteps | zeroOrMoreNameSteps | componentSteps

                                oneOrMoreNameSteps = nameStep ( "." nameStep ) *

                                zeroOrMoreNameSteps = ( nameStep "." ) *

                                nameStep = "#" name

                                name is a string of characters from "A"-"Z", "a"-"z", "0"-"9", "-" and "_". No umlauts allowed, one character is minimum.

                                componentSteps is a list of valid values, see below.

                                Valid 'componentSteps' are:

                                - GlobalValue
                                - Product
                                - Product.Brand
                                - Product.Accommodation
                                - Product.Accommodation.SellingAccom
                                - Product.Accommodation.SellingAccom.Board
                                - Product.Accommodation.SellingAccom.Unit
                                - Product.Accommodation.SellingAccom.Unit.SellingUnit
                                - Product.OnewayFlight
                                - Product.OnewayFlight.BookingClass
                                - Product.ReturnFlight
                                - Product.ReturnFlight.BookingClass
                                - Product.ReturnFlight.Inbound
                                - Product.ReturnFlight.Outbound
                                - Product.Addon
                                - Product.Addon.Service
                                - Product.Addon.ServiceFeature

                                In addition to that all subsequent steps from the paths above are permitted, that is 'Board', 'Accommodation.SellingAccom' or 'SellingAccom.Unit.SellingUnit'.
                                'Accommodation.Unit' in the contrary is not permitted, as here some intermediate steps are missing.

                        </xs:documentation>
                </xs:annotation>
                <xs:union>
                        <xs:simpleType>
                                <xs:restriction base="xs:string">
                                        <xs:pattern value="(((Scope.)?(global|local|current)\+?)?((((ComponentType.)?(real|virtual))|ComponentType.all)\+?)?((((Hierarchy.)?(self|ancestors|descendants))|Hierarchy.all)\+?)?((((DayAllocation.)?(first|last))|DayAllocation.all)\+?)?:)?(#[A-Za-z0-9\-_]+(\.(#[A-Za-z0-9\-_]+))*|(#[A-Za-z0-9\-_]+\.)*(ThisComponent|GlobalValue|Product|Product\.Brand|Product\.Accommodation|Product\.Accommodation\.SellingAccom|Product\.Accommodation\.SellingAccom\.Board|Product\.Accommodation\.SellingAccom\.Unit|Product\.Accommodation\.SellingAccom\.Unit\.SellingUnit|Product\.OnewayFlight|Product\.OnewayFlight\.BookingClass|Product\.ReturnFlight|Product\.ReturnFlight\.BookingClass|Product\.ReturnFlight\.Inbound|Product\.ReturnFlight\.Outbound|Product\.Addon|Product\.Addon\.Service|Product\.Addon\.ServiceFeature|Brand|Accommodation|Accommodation\.SellingAccom|Accommodation\.SellingAccom\.Board|Accommodation\.SellingAccom\.Unit|Accommodation\.SellingAccom\.Unit\.SellingUnit|OnewayFlight|OnewayFlight\.BookingClass|ReturnFlight|ReturnFlight\.BookingClass|ReturnFlight\.Inbound|ReturnFlight\.Outbound|Addon|Addon\.Service|Addon\.ServiceFeature|SellingAccom|SellingAccom\.Board|SellingAccom\.Unit|SellingAccom\.Unit\.SellingUnit|BookingClass|Inbound|Outbound|Service|ServiceFeature|Board|Unit|Unit\.SellingUnit|SellingUnit))"/>
                                </xs:restriction>
                        </xs:simpleType>
                </xs:union>
</xs:simpleType>
[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!

Live Rewind Is a Creepy Part of Apple’s Audio Intelligence Suite

By: Nick Heer
9 September 2026 at 23:36

Apple held its annual September major product rollout today announcing, among other things, updated Apple Watch models with β€œAudio Intelligence” features. One of those features is β€œLive Rewind”:

A double press of the Digital Crown shows the previous 15 seconds of a conversation as a text snippet, so the user can catch something they may have missed or are less familiar with. Users can ask Siri about the content of the text or save it to the Siri app to revisit later.

This feature is also coming to iPhone 16 and newer models. Apple swears up and down it is doing all it can to make this private and secure, and I have no reason to believe otherwise. The audio is apparently not actually saved and is, instead, merely transcribed by a dedicated coprocessor. That text is automatically deleted unless the user takes an action to save it, too.

But good luck explaining that to anyone around you after you read back an exact transcript of what they just said. Apple says they should be sufficiently notified Live Rewind is active because β€œan audible chime plays from the speaker on your Apple Watch, even if your Apple Watch is on silent or you have headphones connected”, plus the watch plays an animation, but it is not clear to me when this happens. It is not shown in Apple’s video. The impression I get is that Live Rewind is always running in the background and these notifications are only played after you double-click the Digital Crown to look at the text.

This feels like a glimpse of how Apple will market its inevitable wear-anywhere glasses product. I do not think it does enough to assuage privacy concerns. I think normal people will continue to react negatively when you tell them you have been passively recording them because, outside Silicon Valley, that is considered gross and invasive.

βŒ₯ Permalink

LG’s Smart Televisions and Displays Are Rotten

By: Nick Heer
9 September 2026 at 23:16

Michael Crider, PCWorld:

[…] So somebody at LG decided that, in addition to the basic monitor driver and management app delivered to its users via Windows Update, it would slip in an additional program, β€œLG Monitor App Installer.” This extra bit of software includes McAfee, or an ad for it β€” as if there was any difference.

C. da Costa, Gadget Review:

This is bigger than one bad app. LG and Samsung smart monitors run the same operating systems as their smart TVs β€” webOS and Tizen β€” both already built for automatic content recognition and ad targeting. Acer’s monitor privacy policies permit sharing device data with advertisers. The trajectory mirrors the rise of free-to-play gaming: low barrier to entry, monetization creep once you’re locked in.

These findings were based on a Gamers Nexus investigation in July. But it is not an isolated incident.

This week, Gamers Nexus followed up with another blockbuster showing how LG’s smart televisions find other devices on the same network and, in some cases, record their activity; and, because they can recognize what is being played onscreen, LG tracks everything you watch, too. It is presumptively allowed do this because it presents a bunch of user agreements that anyone can bypass without reading, enables tracking by default, and buries all the opt-out stuff. Oh, and there is more.

Scharon Harding, Ars Technica:

The video also demonstrated an LG TV’s ability to record sound around it, even when the TV isn’t online.

β€œWe were even able to capture microphone audio while the TV was unplugged from the network. Burke said the audio was reportedly stored locally via plaintext, which could potentially allow the data to be sent to LG [if] the TV connects online again.”

LG told Harding it is only listening for the wake word, but Gamers Nexus was able to capture audio in a variety of different ways because there are so many microphones and, in many cases, so many vulnerabilities in LG’s software. If I had one criticism, it is that Gamers Nexus connects the voice recording and transcription to LG’s ads service β€” an apparently real-life version of the conspiracy theory. I do not think there is not enough evidence to support that, even as speculation.

The transformation of seemingly every major company into, ultimately, vendors for advertising is an outcome basically everyone hates. The televisions tested by Gamers Nexus are thousands of dollars in Canada. That exchange of money used to be where the relationship ended, assuming the product did not need servicing. Now, though, selling ads on the screen itself prints so much money that it is nearly impossible to buy something different. You cannot vote with your wallet when advertisers are paying more.

βŒ₯ Permalink

The Age-Gating of History

By: Nick Heer
9 September 2026 at 04:43

Heather Burns:

I cannot tell you how much it disgusts me that so much of the archival footage from September 11 has now been age-gated and restricted as β€œmature” or β€œsensitive or adult” content, as in this example from this Reddit film archive.

It’s history. It’s archival footage of history. It’s a thing that happened.

If you were in school in 2001, there were probably televisions playing live news coverage in your classroom or in the hallways. There were in mine.

I am with Burns, and with the greatest of respect β€” she was there that day. But that particular subreddit is an unfortunate example as, while I do not think it should be age-gated, it should be moderated far better. So many of the users there are not treating it as an archive or a way of learning, but as a puzzle where they can play detective.

βŒ₯ Permalink

Indigo Keeps Getting Great Feature Updates

By: Nick Heer
9 September 2026 at 00:22

Aaron Vegh and Ben Rice McCarthy have been steadily updating Indigo since they launched it in May. In the latest version, you can now swipe between different filtered timelines, the scroll position is synced between devices, and there is a new lower price point if you use it exclusively for a single network.

As for me, the one big thing I wanted at launch was the ability to easily switch between multiple accounts, and this was added some time ago. It works exactly as you might expect: you can group sets of your own accounts β€” my personal Bluesky and personal Mastodon are one set, while my Pixel Envy accounts are in another β€” and just toggle the two sets.

This is one of my very favourite iOS apps. Just a great piece of software made by some great people.

βŒ₯ Permalink

FBI Probes Service Selling Tens of Millions of Drivers Licenses

By: Nick Heer
8 September 2026 at 04:27

Brian Krebs:

A new identity theft service launched on the dark web this week is selling digital scans of more than 153 million drivers licenses from people in the United States and Canada. Based on interviews with individuals whose licenses are available for purchase on this service, it appears to be siphoning images collected by a widely-used identity verification company based in Louisiana. KrebsOnSecurity also has learned that the New Orleans field office of the Federal Bureau of Investigation (FBI) today launched an official inquiry into the source of the images.

Mike Masnick, of Techdirt, reports the company in question is IDScan:

You cannot do age or identity verification safely. It always creates some sort of record and that set of records will always become a target. That’s what happened here. And it’s what will happen with any such systems.

Dan Gillmor on Bluesky:

If you support β€œage verification” online, you are supporting a system that GUARANTEES privacy meltdowns β€” endangering all of us β€” because the giant databases of scanned IDs are perpetually hacked by criminals who sell private data.

I am opposed to identity verification, but this argument is not particularly effective for me because there are incredibly low-risk solutions. In Canada, for example, we have an interbank service called Interac that offers an identity verification service. I am not naΓ―ve, but I would entirely trust this bank-based system to verify me on a regular basis. In fact, I already do β€” like many Canadians, I use my banking information to log into government websites. Maybe this relatively safe proxy for a centralized identification system is unique to Canada.

I imagine this argument lands fairly well for lots of people elsewhere, however. The rapid introduction of age verification laws has produced a market for these businesses, but handing your identifying information to some random third-party should terrify you, as it is exactly the behaviour any security expert warns against doing. You have no idea who is able to access that scan of your driver’s license or what they can do with it. And, as it turns out, neither do some of these companies, either.

βŒ₯ Permalink

Polkit insists on there always being administrative users

By: cks
9 September 2026 at 03:45

Polkit is how a lot of things on modern Linux systems decide whether or not to let people do privileged operations. A while back I investigated making Polkit's administrative access operate like su does, where you authenticated using the root password and only some people could do it. Today I said something related to that on the Fediverse:

It certainly is a choice for Linux's Polkit to decide that there is no way to say "this login does not have administrative access period, no matter what".

(Your only choice is to force people to try to authenticate using the 'password' of a locked account, which will always fail.)

This turns out to not quite be true (you can force people to fail authentication without a password prompt), but getting there is a journey.

Operations using Polkit can require "authentication by an administrative user", and Polkit lets you control what an 'administrative user' is. This is done by writing a JavaScript function that 'specifies what identities may be used for administrator authentication' (possibly in general or possibly for a specific operation). You pass this function to Polkit's polkit.addAdminRule() and then it later gets called and returns something. There can be multiple functions; they're called in order of registration (which is generally lexical order of the .rules files) and the first one to answer wins.

As we can see in _runAdminRules, what an answer is is a return value that JavaScript 'if (...)' will consider to be true (a truthy value). This value is expected to be an array of strings, and .join(",") will be called on it. If the result of that can't be turned into a string, there will likely be some sort of exception raised (also); otherwise, the string value is split again and Polkit's C code tried to turn each piece into an identity. If no pieces are valid identities like 'unix-user:0' or 'unix-group:wheel', the code explicitly falls back to authenticating against the root user. Then, at a higher level, groups and netgroups are expanded to users, and if they expand to nothing, there's once again an explicit fallback to root. Polkit is really insistent that everyone be able to authenticate as some administrative user and it doesn't have any particular notion that some people can't do that at all.

The obvious way around all of this mess is what I did in the original entry, where I returned an existing login with a locked password. This caused Polkit to ask you to authenticate as that login, which would always fail. For operations people weren't supposed to do anyway, this is acceptable even if it's unaesthetic. Now that I've read the code I've come up with a nominally better but more alarming way, which is to have your admin rule function return '["unix-user:NNN"]' for some UID that definitely doesn't exist and never will. This works today because Polkit doesn't validate that the UID exists before it attempts to use it for password authentication; when Polkit finds that the UID doesn't exist, it's too late for it to do anything but fail. People trying this will get a pleasant message like:

; run0 echo hi
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ====
Authentication is required to start transient unit 'run-[...].service'.
Failed to start transient service unit: Access denied

An 'Access denied' error is about as clear and as good as you can do.

(If you use '["unix-user:nosuchperson"]', Polkit does validate the login name and rejects the identity, leaving you with the root fallback.)

However, I suspect that this is a somewhat dangerous approach. Someday Polkit may get smarter about detecting nonexistent Unix UIDs in 'unix-user:...' things in admin rules and start rejecting them early, causing it to fall back to demanding the root password. An existing user with a locked password that people can never enter is less aesthetic but safer in the long term.

(With that said I doubt Polkit is going to change this behavior in the kind of bugfix release that might be picked up by a specific release of a Linux distribution, so given that it works in Ubuntu 26.04 today it's probably going to keep working for the lifetime of 26.04. And you can use very large UID numbers to make sure you'll never have an actual account of that UID.)

Sidebar: What doesn't work and why

Since I went through this exercise, both testing things and reading the combined source code:

  • Returning '[]' from your admin function returns an empty string to the Polkit C layer, which then fails to be converted to an identity.
  • Both '["unix-user:nosuch"]' and '["unix-group:nosuch"]' fail at the Polkit conversion from string(s) to identities.
  • '["unix-group:emptygrp"]' and '["unix-group:NNNN"]' for a GID that doesn't exist both fail at the Polkit expansion of groups to Unix logins; with no logins generated, Polkit follows the second fallback to root.
  • Returning 'polkit.Result.NO' from your admin function actually returns the string "no", which also fails to turn into an identity (see init.js).

A nonexistent Unix UID as '["unix-user:NNNN"]' is the only option that threads all of these needles; it's accepted in the string to identity conversion step, and it's not currently rejected at the point when Unix groups are expanded (which would be the natural point to put such a check, if Polkit wanted to).

Always remember to restart daemons and programs after changes

By: cks
8 September 2026 at 02:07

Today I got a useful reminder about how you should always restart programs and daemons after changes, or at the very least explicitly check to make sure that they've picked up changes you recently made. This lesson came in the form of a couple of hours of downtime for Wandering Thoughts, where it (mostly) responded with HTTP 500s between 08:38 and 10:53 today. I didn't make the change that triggered this at 08:38 or any time immediately before then; I made the change late last night.

For reasons beyond the scope of this entry, DWiki (the software behind Wandering Thoughts) runs either as an Apache CGI or as a persistent daemon process (which a front-end CGI communicates with). Normally it runs as a CGI for convenience, but it switches to the daemon mode when the host server is loaded enough; one time this usually happens is during overnight host backups. Once the load drops low enough and enough time has gone by, the daemon quietly shuts down and DWiki reverts to running as a CGI (typically it runs this way most of the time).

When I made the change late last night, I 'tested' it by seeing if Wandering Thoughts still worked. It did, but only because DWiki was running as a daemon at the time, which freezes everything on startup and so hadn't picked up my change. I forgot about this possibility because I usually make changes at times when DWiki is running as a CGI so my changes are picked up immediately. Instead, things only got quiet enough on the host at 08:38, triggering daemon shutdown, causing the next run to finally pick up the change and surface the problem.

Of course always remembering to restart (or 'reload') daemons is easier said than done, especially in situations where it's only necessary some of the time, not all of the time. Programs automatically picking up some changes is very convenient until you lose track of which changes are automatically picked up and which aren't (and when), and sometimes this can be obscure. This learning experience about DWiki changes will probably stay with me for a while, but I want to think about ways to make this more reliable without adding too much code complexity.

(DWiki already has a flag file option that can be touched to signal the daemon to shut down, which I currently point at a file used only for that. An obvious improvement would be to point the option at the configuration file, so any configuration file edits automatically cause the daemon to shut down and maybe restart.)

Thinking about models: authorization versus capabilities

By: cks
7 September 2026 at 03:28

I recently wrote about our authentication versus authorization problem, where we've been forced to shoehorn a bunch of things that are actually about authorization into our authentication systems. An additional realization is that some of what we might want isn't even about what an account is authorized for (its authorizations), it's about an account's capabilities.

Let's make this concrete. One of the things we considered recently was whether we could easily create special limited 'guest' accounts that couldn't use our email system at all. This went beyond not being able to use our IMAP system or send email via authenticated SMTP (which is authentication or authorization) to not being able to receive outside email to '<user>@<us>'. This isn't something that can naturally be covered by 'authorization', because the account involved isn't trying to do anything when our email system receives a message for it; there's no authentication for the account and thus no authorization to be derived from it.

(To me this is the dividing line between authorization and capabilities. Authorization is checked at the point where an account is actively trying to do something. Capabilities are things that you can check even outside of an attempt by the account to do something.)

Thinking of things as capabilities (in the abstract) means that you need some way to query or check an account's capabilities outside of an access attempt, and probably some way to get a list of all of the accounts with a given capability (such as 'receive outside email'). But it also means you don't have to fit every last piece of information about what an account is allowed to do into an authorization system, or especially into what your particular authorization system allows you to express.

(You could record whether or not Unix logins can receive email by putting them in 'yesmail' or 'nomail' Unix groups, depending which is more common, but this is probably not a particularly good use of Unix's basic authorization system. Especially if there's no actual Unix filesystem permissions involved and the groups are being used only as markers, so you've turned your /etc/groups into a general attribute tag system for logins.)

If you can query capabilities at any time they can probably be used for authorization decisions as well, so in a sense capabilities are a superset of authorizations. The mechanics may be different, though, in that some things will expect to receive (or generate) authorization information at the same time as they perform authentication, not to make a separate query to determine authorization status once someone has been authenticated.

For me, this is an especially useful insight if most of your authorization is actually being decided based on whether or not people can authenticate to some particular service (as is our case). Now I have a more general model for thinking about what an account can do, what we want to record about that, and how we want to express some things. Also, now I have a dividing line I can draw between something we can maybe try to express through authentication and authorization and something we need to step outside of that model to handle.

The prospect for language independent tooling for making code changes

By: cks
6 September 2026 at 02:34

Over on the Fediverse, I took part in a small conversation about the lacking state of tools for making (structured) changes to code, what you'd often call refactoring. Editors have long had some degree of support for this, but general support is spotty, especially in command line tools. There are things that exist, but they tend to be experimental; for example, the Go developers sometimes use rf to make structured, deterministic changes to code (such as this split of SSA rewrites into packages, which used rf to make a series of structured changes).

When I was mentioning rf, it occurred to me that we increasingly have tooling that could probably be used to create tools for this. Tree-sitter provides high quality concrete syntax trees for many languages, often with meaning annotated, and LSP servers provide specific code intelligence about the meaning of identifiers and so on, also for many languages. Given a tree-sitter syntax tree and a LSP server, in theory you have enough information to do various sorts of code reconstruction in a relatively language independent way. Some amount of this can also be driven directly through the LSP server, since some of them directly support various sorts of refactorings and changes.

(It will help a lot if you also have access to a language formatter, for example through the LSP server, so you can have the formatter fix up your edited text to the language's standards afterward.)

Effectively this would be combining what a modern editor already does with tree-sitter grammars and a LSP client for interactive (structured) edits, but driven through either a command line or through some structured description of the changes you want (in the style of rf but perhaps in a file). There's at least one command line LSP client (lsp-cli, also), but it seems to be focused on inspection rather than changes (which is reasonable, most changes are a lot more complex than merely querying a language server).

I think the usual view is that you want to drive these changes through an editor so you can see the result, but in today's environment of pervasive version control the tool could perform the change and then you'd get a VCS diff to see the result. There are already language specific tools that can do some of fixes for you; I know of ones for both Go and Python, and sometimes checkers and other diagnostic sources can export fix information that a program (most often an editor) can use to automatically apply them.

(Go has a rich ecology of them because early on it built a framework for doing this, which most famously surfaced in gofmt but which also powers a bunch of analysis and sometimes rewrite frameworks, such as "go fix".)

Our authentication versus authorization problem

By: cks
5 September 2026 at 03:33

One of the things that's said regularly is that authentication is not authorization, and there's a difference between the two. This can all sound abstracted until one day you realize that you've wound up in an awkward place because of it and because your environment smashes them together. I say this because I recently realized that one of our practical problems is the result of exactly this lack of difference in our environment.

We operate what's ultimately a quite traditional Unix based multi-user environment (cf), along with an assortment of related services like an IMAP email server and the ability to send and receive email to '<login>@<us>'. Ultimately all authentication that people do comes down to a global /etc/passwd, although it gets transformed in a bunch of ways for things like our web based OIDC authentication. Since we started with Unix machines, we didn't really have a separate concept of authorization; if you could log in, you were authorized (more or less).

All of this was fine in an environment where we had only one sort of account (and login), an account that could do everything. Such accounts appeared in the global /etc/passwd, appeared in the local /etc/passwd on every machine with NFS mounts, could be used for IMAP authentication (through the IMAP server's /etc/passwd) and web SSO authentication and so on. Then as time went by we started wanting special sorts of accounts that were not supposed to have this sort of full access. Accounts that were only there to use our VPN servers, accounts that were only there to authenticate to an outside service (which would send them email from time to time so they had to receive email but we didn't want them to send it from their account), accounts that hadn't been fully activated yet and so shouldn't be able to register machines for network access (or do some other things), accounts that should only be able to use a few systems, and so on.

Because nothing we'd built had a separate idea of 'authorization', most of this had to be wedged into an increasingly awkward set of hacks around who was in what version of our global password database (and in what state). In a few cases we could use Unix groups (or Apache groups), and for a bunch of machines we had to rely on special shells (despite the flaws). Because of the limitations of special shells, some of the limits on restricted accounts are partially illusory and could be worked around by a determined person (or by accident). It's been particularly difficult to put fine grained restrictions on what people could do through our web-based services because almost all of the authorization is done externally, for example by Apache applying a restriction to a directory of simple CGIs.

This ad-hoc approach has a variety of problems. Any time we want to apply a new sort of restriction or restrict a new sort of system, we have to figure out how to thread it through our environment, or what we can actually restrict while still allowing what's needed. Since there's no central database of what sort of account (or who) is allowed to do what, we can easily lose track of what a particular type of account is allowed to do or is blocked from, and in fact working it out can involve going to specific machines and specific software systems to refresh our memory of how they work (including, for example, how the Apache specific password and group files are built when we're using HTTP Basic Authentication or even OIDC with Apache group files).

(In some cases we've resorted to creating synthetic groups in the Apache group file that are based on, for example, the Unix shell that an account has.)

This has also constrained what sort of restricted accounts we can provide easily (or at all). In the recent case that crystallized my realization, we were asked if we could do relatively limited 'guest' accounts with some new restrictions and the answer was not really. Technically we could have implemented some of it, but it would have meant adding even more complexity to some already complex systems. The whole discussion more or less rubbed my nose into how it would be quite useful for us to have a clear distinction between authentication and authorization.

(I don't know what that distinction would look like in practice. It's easy to think about the abstraction of 'is authenticated login X allowed to do Y', but it's a lot harder to build a manageable system that can answer that question in the various contexts we need to do it.)

Sorting out a DNF "transaction failed" during my upgrade to Fedora 44

By: cks
4 September 2026 at 03:09

Today I got around to upgrading my office desktop from Fedora 43 to Fedora 44. As usual I did this through the 'dnf distro-sync' live upgrade process. Normally this goes okay (and it did work fine in my test virtual machine), but this time around there was an unnerving problem:

Just what I want to see during a dnf distro-sync upgrade from Fedora 43 to 44, specifically during the posttrans actions:

Transaction failed: Rpm transaction failed.

Why? Who knows, dnf/RPM doesn't say. How do you recover? Who knows. What does 'failed' mean here when everything was installed and seems to be working? etc.

Time to pick through 'rpm -V' nervously, I guess.

A 'dnf distro-sync' upgrade first upgrades, installs, and sometimes downgrades all new packages from the new Fedora release, then it removes all of the now-replaced old packages, and then at the end it runs a series of 'postrans' end-of-transaction scripts which do various things like build the kernel initramfs images in /boot. Then normally DNF tells you that everything succeeded. This time it instead printed some output and then exited with that message. Specifically, dnf's output ended with:

[...]
>>> Running %triggerpostun scriptlet: systemd-0:259.8-1.fc44.x86_64
>>> Finished %triggerpostun scriptlet: systemd-0:259.8-1.fc44.x86_64
>>> Scriptlet output:
>>> Job failed. See "journalctl -xe" for details.
>>>
Transaction failed: Rpm transaction failed.

Naturally I jumped to the assumption that the systemd triggerpostun script's output meant that it had failed and this had caused the entire transaction to 'fail'. I spent some time trying to follow this through systemd logs and so on, but in the end I think this was a red herring.

I put 'fail' in quotes because inspection of the system afterward showed that all of my packages had been upgraded to their Fedora 44 versions and the old Fedora 43 ones were gone. However, this left me uncertain about how many of the various end of transaction scripts had actually run and whether my system now had incomplete or not-done work that was necessary for it to work properly.

I'm not sure that the dnf5 logs contain enough information to tell for sure (at least for people who aren't familiar with dnf5 internals), but the default Fedora dnf log non-retention policy didn't help. Partly this was because I only started desperately looking through the logs after I'd tried to 'redo' or 'replay' the transaction (with 'dnf history redo' and 'dnf replay' respectively), neither of which worked and neither of which I understand. I was rather lucky that partial logs from the distro-sync upgrade were still present, and those logs let me determine that a lot of scriptlets had run after the systemd one, so probably everything had run. The logs also revealed that despite the scary message, the systemd scriptlet had exited successfully.

INFO RPM callback start %triggerpostun scriptlet "systemd-0:259.8-1.fc44.x86_64"
INFO [scriptlet] Job failed. See "journalctl -xe" for details.
INFO RPM callback stop %triggerpostun scriptlet "systemd-0:259.8-1.fc44.x86_64" return code 0

For various reasons, I run all of my live Fedora system upgrades under the venerable script(1), so I can capture full output, and also I copy any alarming snippets I see into my own records. This turned out to be extremely useful because I was able to go back to both sources and find a very confusing message:

[...]
>>> Running %post scriptlet: pax-0:3.4-49.fc44.x86_64
>>> Non-critical error in %post scriptlet: pax-0:3.4-49.fc44.x86_64
>>> Scriptlet output:
>>> alternatives version 1.33 - Copyright (C) 2001 Red Hat, Inc.
>>> This may be freely redistributed under the terms of the GNU Public License.
>>>
>>> usage: alternatives --install <link> <name> <path> <priority>
[... usage message omitted ...]
>>> [RPM] %post(pax-3.4-49.fc44.x86_64) scriptlet failed, exit status 2
[...]

(All of this was buried in the output of upgrading, installing, downgrading, and removing 14340 packages, some of which produced random messages during this process.)

Despite the 'non-critical error' bit, this was the only thing that looked odd, and also my test virtual machine didn't have the 'pax' package installed. So my tentative conclusion is that this pax scriptlet failure blew up the entire transaction, but also the 'failure' of the transaction was ultimately harmless and my system is properly upgraded to Fedora 44.

Well, mostly, because now I've discovered that 'dnf list --installed' claims that all of my Fedora 44 are from a repository that is listed as '<unknown>', instead of from 'fedora' or 'updates' as you'd expect (and which is what I see on my test virtual machine). I'm not sure how one would go about fixing this, especially since 'dnf distro-sync' doesn't see anything to do. Presumably it will fix itself over time as more updates come out and then I eventually upgrade to Fedora 45 (assuming that doesn't have a transaction failure too).

(I'm not sure I want to try to 'dnf reinstall' everything, or really anything. In theory this is a problem that can be fixed entirely through some sort of database manipulation; DNF can easily determine that my packages come exactly from various configured repositories.)

I haven't been enchanted with dnf5 so far (1, 2, 3), and this experience hasn't improved my feelings. In the one case where you really, really want a complete, verbose, and comprehensible error message that tells you exactly what went wrong, why the transaction failed, what was undone, and what you can do next, dnf5 (and RPM) completely fell short.

(It's possible that dnf5 is doing the best it can with the information that the RPM library and Python bindings give it, but that still doesn't absolve Fedora. DNF and RPM are effectively conjoined at this point, so it's a collective responsibility, and DNF itself could have at least said something more.)

Status reports and indicators are untrustworthy, UPS battery edition

By: cks
3 September 2026 at 03:16

A lot of things these days can report useful information about their status and health. Or at least their theoretical status and health. As we've seen, things like server BMCs may be reporting outdated hardware inventories. Today I got another vivid illustration of that, when Toronto had what you could call a significant weather event with some power flickers in my area, and I made an unhappy discovery about my home UPS:

TIL that my home UPS has recently become a home power bar or maybe a home line conditioner, despite claiming that its battery is fine. Well, it’s six years old, but on the other hand it worked in June.

(Specifically it worked on June 1st early in the morning, for a few seconds, and before that on May 23rd.)

This particular UPS is a bit over six years old, and has been periodically useful during fortunately infrequent brief power outages. But this time, when the power flickered briefly everything connected to the UPS powered off, which is the reverse of what I'd like. After a couple more power flickers, the UPS stayed off for long enough that I disconnected everything and switched back to my pre-UPS power bar.

I have monitoring of the UPS set up on my home desktop, and all during the power events both it and the UPS's front panel display claimed that the UPS had 100% battery, the battery was at 24 volts, and so on (even after a power flicker had caused the UPS and everything attached to it to shut off, then power came back and everything turned on). But once I disconnected everything from the UPS and was able to get the UPS to do a proper self test, it abruptly switched to reporting 6% battery charge, which is a much more realistic number for something that fell over more or less the moment it needed to supply battery power (and there are some indications that even that's misleading; for example the nominal charge level will drop from 20% to 5% in a few seconds even with nothing connected to the UPS).

I knew that battery charge reporting was somewhat unreliable for Li-ion batteries, but I don't think I've seen this before in regular batteries and our rack UPSes at work have always been reasonably good about reporting that their batteries needed replacement. Unreliable reporting of battery status matters for more than surprising me when the power went out; initially it made me think that the entire UPS unit needed to be replaced, rather than just the batteries (probably) being worn out.

(I was willing to believe that the batteries were dead, but a UPS that firmly reported dead batteries as being completely good seemed like a UPS that might have additional problems that had degraded its ability to work right in general.)

PS: In the long run, I should plan to periodically test that the UPS can support the load when line power goes off, either through its own self test or by manually switching it away from line power with a front panel switch. Obviously I'd better do this at a time when I can afford to have my desktop abruptly lose power.

Sidebar: Did the UPS make things worse?

None of the power flickers lasted long enough to cause the usual run of kitchen appliances to reset their clocks, and there were a few more power flickers after I'd moved everything to the power bar and nothing rebooted. Modern desktop PSUs apparently normally have a hold-up time of 16 milliseconds or more (it's apparently part of the ATX specification, although I haven't looked to be sure), which is the length of time the PSU should keep supplying power after the line power goes away. So these power flickers seem to have been very brief, but apparently they were still long enough that the UPS tried to switch to battery power (and then had battery power fall over).

If the UPS had not detected power loss and tried to switch, it seems possible that everything would have survived fine. On the other hand, I wouldn't know that the UPS had problems.

(The UPS definitely made things better back in June and May.)

We're no longer using Linux's strict memory overcommit mode

By: cks
2 September 2026 at 02:08

We have two sorts of multi-user Linux machines; there are ones for general purpose logins and normal Unix stuff, and ones that are specifically for heavy duty compute. Many years ago, we began using the Linux kernel's strict memory overcommit mode on both sorts of machines. We soon determined this wasn't a good idea on the login servers, but left it on the compute servers for many years afterward even when it caused occasional problems (also, also).

(On the login servers we wound up setting systemd memory limits on each person's total memory use, but this isn't something we could do on the compute servers, where one person should be allowed to use all of the RAM if it's not otherwise in use.)

These days, another plausible way to limit the total amount of memory people can use on your servers is to set systemd memory limits on user.slice. This limits actual RAM usage (from all sources), not the amount of memory people may theoretically ask the kernel to provide, but usually this is the more relevant limit. When I started considering this, I looked into how much RAM people were actually using on our compute servers as compared to the committed address space and discovered things had changed since the past.

Once upon a time, a long time ago, it was generally the case that compute programs on our compute servers would actually use most of the (large) amounts of memory they asked for. This made strict overcommit reasonable on these machines, because if a big compute job asked for 32 GB of RAM, it was probably going to use it. That's no longer the case for the kinds of things people run on our servers; the committed address space is far larger than the (current) RAM usage for all of user.slice (I ended up collecting some metrics). We also have evidence some programs ask for much more RAM than they can use, such as all of the RAM in the server (in one extreme case). I don't know why this shift has happened (all I can do is speculate about, for example, shifts in common programming languages to use in compute intensive things), but it clearly has. My data showed several times more committed address space claimed than RAM in use, and it was clear that people were being artificially limited by our choice of strict overcommit mode.

Given this, we decided to switch off strict memory overcommit on our compute servers and instead put a MemoryMax= setting on user.slice to leave a few gigabytes for system services (cf). This hasn't solved all of our problems but I think it makes things better, and people are definitely using more RAM than before (one of our compute servers is currently well over the point where it would have hit strict overcommit limits).

In general I think these cgroup-based limits make more sense in a modern Linux kernel environment than strict memory overcommit does, unless you have an unusual situation. They also make the memory limits on our general purpose compute servers operate the same way that memory limits do in our SLURM environment, where the SLURM daemons use cgroup memory limits on individual jobs (which are each put in their own cgroup to enable this).

PS: Overcommitting virtual memory is sensible in general. We pervasively over-allocate through the programming stack in order to trade memory for speed.

Getting LSP servers and other tools to work in venvs in GNU Emacs

By: cks
1 September 2026 at 03:07

Over on the Fediverse I had a question:

GNU Emacs people who write Python using venvs, what's the state of the art for getting linters, LSP servers, and so on started with the right paths/etc set up for a particular project's venv? Is it (still) direnv + the envrc package, or something else (.dir-locals.el with a exec-path setting, maybe)?

For my sins, straightforward venv activation stuff won't work in my GNU Emacs + Python setup because often I'm developing code outside of the venv with the supporting packages for that code. For reasons, yes I know this is odd, it's our (my) way.

After playing around with this for a bit I think I have a recipe that works, although I'm not sure it's completely correct.

To start, you want to do the brute force thing and install copies of every LSP server and tool you want to use into your project's virtual environment. This isn't absolutely required for some tools, but we're already throwing disk space at the problem (that's partly what venvs are about) and it's the easiest way to make Python-based tools like the Python LSP server happy.

Then we have two choices. First, you can painstakingly activate the virtual environment every time before you run GNU Emacs to edit files in the project. This will set $PATH and $VIRTUAL_ENV, picking up and generally configuring your installed tools properly, but speaking from personal experience this is kind of a pain (plus it means you may need to run multiple instances of GNU Emacs), and sooner or later I forget. Second, you can use direnv and the GNU Emacs envrc package to automatically configure things in the relevant buffers.

To use the direnv based approach, each project's root needs a .envrc file that exports a $VIRTUAL_ENV with the appropriate top level venv directory and a $PATH that puts the venv's bin/ directory on it (probably first). Then you enable that .envrc in direnv, install envrc into GNU Emacs from MELPA, and add something like the following to your .emacs:

(use-package envrc
  :defer t
  :if (executable-find "direnv")
  :hook
  (python-mode . envrc-mode)
  (python-ts-mode . envrc-mode)
  )

You can set envrc's global mode if you want, but I opted to be narrow. If I want environment variables set outside of editing Python files, I'll enable it in more modes.

So far, all of this has worked for me in GNU Emacs. The projects (ie, directory trees) where I have a .envrc set up run the right versions of LSP server(s) and tools, and I can edit files from multiple projects in the same Emacs session and have everything work out because envrc makes these settings buffer-local, so each project can get the right LSP server and other settings.

Setting $PATH and $VIRTUAL_ENV has a varying set of effects. For Python based tools installed in the venv, it means that they'll run from the venv using the venv's Python and the venv's Python packages available to be introspected and so on; this covers pylsp and mypy (and I think basedpyright if you want to use a very strict LSP server). The effects on LSP servers not written in Python and other tools varies. Pyrefly currently determines the Python import path by running the 'python3' that's on your $PATH and dumping its 'sys.path', while ty looks at $VIRTUAL_ENV (cf).

(Currently, I believe that ruff can be installed globally because as far as I can tell, it doesn't look at Python imports at all. Installing it into your venv is relatively cheap insurance, though.)

In theory you can get some of this by merely setting $PYTHONPATH to <venv>/lib/python3.NN/site-packages, but I wouldn't trust that to be fully functional and without side effects. It's better to set the environment variables in your .envrc, which more or less duplicates what the venv 'activate' script does. I have set $PYTHONPATH in addition to the other variables as the easy way to make a small pipx-based venv work (in a simple case) without having to inject a bunch of additional tools into it.

(All of this is unlikely to work remotely over Tramp.)

PS: You may or may not want to hook direnv up to your shell. I haven't, partly because it would probably be a lot of work to make it work in my unusual shell. Probably the easiest output format to deal with (or read) is 'direnv export systemd'.

PPS: When setting VIRTUAL_ENV in your .envrc, remember to set it as 'export VIRTUAL_ENV=...' because it probably doesn't already exist in the environment. As the direnv documentation says in passing, your .envrc is loaded into a Bash subshell, so it can contain any valid Bash stuff and it needs to explicitly export (new) environment variables you want direnv to pick up.

Recently

1 September 2026 at 00:00

I skipped August's Recently because this summer has been relentless, mostly in the positive sense. July and August were filled with bike rides, 5K races, friends in town, life changes and logistics.

Cyclone roller coaster

For a friend's birthday, we rode the rickety Coney Island Cyclone, which was faster and more intense than I remembered it. But it's been 18 years since the last injury on the coaster, and the rider was partly at fault: good enough for me.

GPS UnitTrail

I rode my first proper randonneuring ride: 200km (124mi) to all of the beaches around Brooklyn. It was pretty challenging, as you'd expect, but I didn't 'find my limit' for endurance, but I did find my limit in terms of how many gels & ride-nutrition packets I could consume before sugar becomes repulsive. I do still heartily endorse maple syrup packets for ride nutrition: compared to the space-age fuels that all taste like fruit or coffee, it's an identifiable substance that tastes like you'd expect.

And, not pictured, but I ran a bunch of 5Ks. Haven't hit my goal this year of going sub-20, but got close in the last one - 20:15. I'm not giving up yet, because the weather is getting better and Brooklyn has a lot of 5K options.

Listening

Duffy x Uhlmann has been in my rotation for a year now and the new album has all the things I like from the combination. This and Shrunken Elvis go along well together and both have been hits at dinner parties.

This album from Frances Quinlan, the frontperson of Hop Along, went under the radar, but it's so good. I've also been playing Hop Along's send album Get Disowned. I really like everything that this band has put out. It's a product of Philadelphia but I hear some of the elements that drew me to DC post-punk in the turn-on-a-dime songwriting.

Watching

This video, 'The surprising truth about what motivates us', is kind of an artifact of the 'Obamaverse', as one of my friends would put it. It's by Daniel Pink, a writer-speaker-self-help kind of guy, and it's accompanied by one of those realtime hand-illustrations. There are lots of reasons to tune it out as YouTube filler.

Still, I thought it was pretty cool for two reasons: first, it connected directly with Andrew Kelley's video, which is the next one. And it connects with my feelings about incentives and how most simple incentives are counterproductive. And second, the drawing is real. I'm so used to the artificial version of this visual style that it took a minutes to realize that the it wasn't AI or some 'drawing hand' style of video essay, it was a real person making quite nice illustrations: specifically Andrew Park.

Found myself nodding along to every bit of this interview with Andrew Kelley. His values and motivation are so nice to encounter in this current phase of tech. He's kind of a role model, which is something that I used to find trite in my early career but I'm coming around to. As Adam Neely pointed out in his video, a lot of the people heavily using Suno (an AI music tool) could not cite any role models, and defaulted to a sort of enclosed, self-centered stance. I see this with engineering too: it's hard to discern what values or talents the people at 'the forefront of the industry' have that I want. Finding personal qualities as something to imitate instead of desires and possessions (cue the segue into Girard's Memetic theory) seems like a better way to live.

Reading

What strengthens a relationship? Almost always, it is personal investment. Perhaps an AI agent might be better at predicting what my father will want for his birthday than I am, but it definitionally cannot give him my time and consideration. Love is not just a feeling; it is a way of paying attention.

Elizabeth Lopatto in The Verge picks apart Mark Zuckerberg's weird, sad view of humanity. It's worth a read even just for the incredible opening anecdote.

Home of the titular 100 foot waves. Which existed when I was a kid of course, but the larger world didn’t know about them yet. They were our secret. My grandfather and I would walk to the lighthouse and watch the waves hitting against the cliffs and he’d tell me that these were the biggest waves in the world. I didn’t believe him, of course. I thought it was standard grandpa hyperbole. But I also didn’t want to believe that I was seeing the biggest of anything. Not yet. I wanted the really great things to be in the future. Something to aim for. Which means I missed out on some great things that were right in front of me.

Mike Monteiro's newsletter is always beautiful and melancholy, and this is a really nice edition of it.

I read and highlighted a few other articles but they were all saying things about AI, mostly about how it makes the world worse and makes people feel bad. I won't add those to the pile because I don't want that kind of content to dominate what I mention, even if it does dominate what I read.

Elsewhere

For the Val Town blog, I wrote about our experience with the bug bounty program. It's been a very interesting experience. Val Town has a pretty difficult security surface: in contrast to Mapbox, which was mostly a read-only API, it's a very read-write system with many access controls. At the center of the product is a sandbox that we want to keep secure. And because we're living in the AI age, a single bit of functionality can be exposed via REST API, React Router loaders, tRPC, and MCP. Building a model layer and a shared authorization layer - modeled after Oso - has helped a bit.

But the reports still come in. It's pretty wild how AI factors into it: I'd say that 100% of the vulnerability reports used AI to write the report itself. From the best reporters, the vulnerabilities come with screen recordings and evidence, and it's clearly not all-robot. But - I suspect that other people manning help desks & bug bounty programs know this experience - it's jarring to go three replies into an email conversation with someone and they suddenly stop using an LLM to write their emails and the voice completely changes, from hyper-literate to abbreviated and misspelled.

I also wrote about our new authentication scheme that makes vals require login and makes it possible to manage permissions to val applications, not just their code. It was pretty fun to implement, and the sixth or seventh time working with HMAC schemes I'm starting to get a natural grasp on them.

Harry Potter again again

4 September 2026 at 17:30

I watched the trailer for the new Harry Potter TV series on HBO.

My overwhelming feeling is that it is so absurdly redundant to make this. Not even reimagined but to cosplay the movies so slavishly… the sets look the same, that same teacher with the same hat and the same Scottish accent…

To make the series is a bonfire of human creative effort, think of what could have been done instead.

I love it, I’m in awe at the endeavour.


Did you ever watch Teletubbies? There was always a video interlude. Then the Teletubbies would yell "again again!" and we’d watch the whole thing again.


If he were still alive, Jorge Luis Borges would write about HBO.

Borges’ short story Pierre Menard, Author of the Quixote (Wikipedia):

Menard (a French 20th century novelist), after his death, leaves fragments of his work to write an identical Don Quixote, originally authored by Miguel de Cervantes in Spanish in the early 1600s.

He did not want to compose another Quixote–which is easy–but the Quixote itself. Needless to say, he never contemplated a mechanical transcription of the original; he did not propose to copy it. His admirable intention was to produce a few pages which would coincide–word for word and line by line–with those of Miguel de Cervantes.

But how?

The first method he conceived was relatively simple. Know Spanish well, recover the Catholic faith, fight against the Moors or the Turk, forget the history of Europe between the years 1602 and 1918, be Miguel de Cervantes.

He abandons that route as being β€œdiminutive” and instead attempts (successfully) to "go on being Pierre Menard and reach the Quixote through the experiences of Pierre Menard."

(Borges then goes on to quote two identical passages, one from de Cervantes’ Quixote and the second from Menard’s Quixote, and analyse them separately.)


Menard becoming de Cervantes, to the point of writing the same book, makes me think about my long-held belief that actors are magicians:

Acting is not the acting as β€œpretending” that we all did at school, of course? But the difference between great acting and school acting is not merely a matter of magnitude. It is something else entirely: the true actor is a shapeshifter.

Actually what amazes me about actors is that they can come back.

Once Menard is de Cervantes, is it possible for him to evolve into being Menard once again, any more than the original de Cervantes could?


Jim Carrey’s loss of self, as previously discussed (2023): "If I can put Jim Carrey aside for four months, who is Jim Carrey? Who the hell is that?"


RELATED:

Emily Wilson, who translated The Odyssey in 2017, which I loved, is translating the whole thing again: "It’s a complete retranslation. It’s not a revision of the old version."

Her bet is that is that it’ll be better.


What does it mean to re-derive Harry Potter in 2026 such that you end up in the same place?

I worry that it says more about our society than Harry Potter itself; that Menard, having become de Cervantes, cannot become Menard again; that next year will not be 2027 but 2002.

Again again!


If nothing else this has been a good reminder to re-read some Borges. The Aleph is good. I linked to it here.


Auto-detected kinda similar posts:

New app: Yesterday, an iPhone app for yesterday’s weather

28 August 2026 at 21:20

I published my perfect iPhone weather app, and my guess is that you won’t want it.

Premise:

Why doesn’t my weather app tell me the weather yesterday?

I can never plan based on numerical temperature, etc. Am I going to be walking outdoors today? Should I take a jacket? Numbers are so abstract. Too much thought required.

HOWEVER: I can mostly remember what I wore yesterday.

So all I want to see, from my app, is: oh yeah today is like yesterday but a little warmer.

Then I can adjust easily.

I built an app around that core idea.

Yesterday features:

  • Today’s weather is the solid line. Yesterday’s weather is the dashed line. The diff is arrows.
  • Glanceable AI-generated text description using Apple’s built-in LLM.
  • Dark mode! Tap the Sun to toggle dark mode (the Moon shows its current phase with latitude-accurate orientation).

Plus a home screen widget. And some little glyphs if there is a 30% of greater chance of precipitation. Nothing else.

Download Yesterday from the App Store.


However my guess is that you won’t download it.

I vibed this app because it is the actual conversation that takes place in our kitchen every morning. Apple makes forecasts available via WeatherKit for free up to pretty generous limits.

So it has two users (we use it daily).

Initially when I made it, I put the app on TestFlight for the two of us, and offered around on the socials to see if any of my friends would like it too.

In the old days (like, before May and the summer of vibe coding) there would have been a bunch of interest.

But now, instead: a handful of people came back with some enthusiastic variation of: yeah!! cool!! I made my own perfect weather app for me and my family just the other week too!!

I love this.

What this tells me is:

  • Software has changed forever.
  • We need more APIs (like WeatherKit) and underlying reliable databases with high-level operators (like whatever powers Photos or Reminders) and identity/permissions/etc, and that’s what an OS will have to include in the future.
  • There needs to be a protocol for discovering and sharing this new abundance of apps (April 2026).

p.s. one of my testers (there are a few) pointed out that I forgot to add fahrenheit Oops! I’ll add that in the next version.


Yes Yesterday is like an old-fashioned barometer. The weather is like yesterday only more rain/more fair.

And cognitively it does seem more straightforward to take action by iterated adjustments based on a vector rather than deciphering an absolute value at every tick.

e.g. how about an automated savings account which, every month, always saves n+100 where n is the amount you squirrelled away last month, and your available actions are to ignore it (which means the savings transfers goes ahead) or adjust the modifier temporarily, but you can never simply keep it the same?

I wonder what a general rule could be? Like, building systems that always diverge and refuse to converge on steady state?

They Finally Built Good Transit!?

6 September 2026 at 13:01

πŸ’Ύ

Made by humans for humans. No AI voices or generative AI was used in the making of this video.

Check out my new public transit travel show, Day Pass, now available on Nebula!
https://daypasstravel.com

Watch this video ad-free and sponsor-free on Nebula:
https://nebula.tv/videos/notjustbikes-they-finally-built-good-transit

Sign up to Nebula and support this channel:
https://go.nebula.tv/notjustbikes

Buy a Nebula Gift card (now available with iDEAL!)
https://gift.nebula.tv/notjustbikes

Patreon: https://patreon.com/notjustbikes
Mastodon: @notjustbikes@notjustbikes.com
NJB Live (my live-streaming channel): https://youtube.com/@njblive

---
Relevant Videos

How can a NEW Transit Line be THIS BAD!?
https://nebula.tv/videos/notjustbikes-how-can-a-new-transit-line-be-this-bad
https://youtu.be/Qjp7hqXgInI

The World's Dumbest Bike Lane Law Just Passed in Canada
https://nebula.tv/videos/notjustbikes-the-worlds-dumbest-bike-lane-law-just-passed-in-canada
https://youtu.be/KgFCQ7jEZxI

The Absolute Best Transportation for Cities (trams)
https://nebula.tv/videos/notjustbikes-the-absolute-best-transportation-for-cities
https://youtu.be/bNTg9EX7MLw

Trams are Great! So why are the Streetcars SO BAD!?
https://nebula.tv/videos/notjustbikes-trams-are-great-so-why-are-the-streetcars-so-bad
https://youtu.be/HhQxNHrD6fA

---
References & Further Reading

Network 2011 (1985)
https://cancelledtoronto.ca/1980/network-2011

Transit City
https://en.wikipedia.org/wiki/Transit_City

Clash expected over competing visions for GTA transit future
https://www.cbc.ca/news/canada/toronto/clash-expected-over-competing-visions-for-gta-transit-future-1.700596

Eglinton Crosstown Backgrounder
https://web.archive.org/web/20200803181141/http://thecrosstown.ca/the-project/fact-sheets/eglinton-crosstown

Comparing LRT and Subway Capacities
https://lrt.daxack.ca/LRTvsHRT/CapacityCompare.html

Transit Costs Project
https://transitcosts.com/

Toronto backpedals on Eglinton bike lanes following Ford government crackdown
https://www.thetrillium.ca/news/municipalities-transit-and-infrastructure/eglinton-bike-lanes-toronto-backpedal-ford-government-crackdown-12332316

The Eglinton Subway We Almost Had
https://jamiebradburnwriting.wordpress.com/2020/02/12/the-eglinton-subway-we-almost-had/
https://www.flickr.com/photos/jbcurio/16292134992/

---
Chapters
0:00 Intro
0:35 About the LRT and its history
2:36 The features of this LRT
5:17 Transfers and Mount Dennis
7:59 Station design
9:56 Street improvements
13:07 Retail integrations (neg)
15:31 Tunnel and surface design
16:59 Traffic lights & intersections
19:34 Speed limits & (bad) operations
20:30 Expensive & overbuilt
22:06 Metros vs trams
25:02 Should it have been a subway?
26:53 Conclusion & Day Pass

Delavnica: Uporabljajmo superračunalnike!

25 September 2026 at 07:00

Opis: Delavnica je namenjena raziskovalcem, inženirjem, őtudentom in drugim, ki ste spoznali, da potrebujete več računskih virov, kot vam jih ponujajo običajni računalniki. Delavnica bo potekala v okviru konference IEEE ERK 2026.

Na delavnici se bomo seznanili s slovensko superračunalniőko infrastrukturo in možnostmi dostopa do nje. V okviru delavnice bomo delali na eni od superračunalniőkih gruč - povezali se bomo na prijavno vozliőče, prenaőali datoteke na in iz superračunalnika ter zaganjali naloge in spremljali njihvo izvajanje preko vmesne programske opreme Slurm.

Delavnica je brezplačna. Na delavnico pridite s svojim prenosnim računalnikom, mi vam bom priskrbeli poverilnice na superračunalniőki gruči.

Jezik: Slovenski

Zahtevnost: Osnovna

Omejitev Ε‘tevila udeleΕΎencev: 15

Termin: 25. 9. 2026Β  9:00-12:00

Lokacija-fizična:  Hotel Bernardin, Portorož, Soba E

Priporočeno predznanje: /

Ciljna publika: raziskovalci, inženirji, őtudenti, vsi ki potrebujejo več računskih virov pri svojem delu

Na izobraΕΎevanju pridobljena znanja:

  • Razumevanje delovanja in zgradbe superračunalnikov
  • Uporaba vmesne programske opreme SLURM
  • Osnovna uporaba programskih okolij in vsebnikov
  • Upravljanje z datotekami in poganjanje nalog
  • Osnovna obdelava videoposnetkov

Β 

Organizatorja:

  • Konferenca IEEE ERK 2026Β in

Β 

FRI logo

Predavatelji:

Ime: Davor Sluga
Opis: https://fri.uni-lj.si/sl/o-fakulteti/osebje/davor-slugaΒ 
E-mail: davor.sluga@fri.uni-lj.si
Ime: Ratko Pilipović
Opis: https://www.fri.uni-lj.si/sl/o-fakulteti/osebje/ratko-pilipovic
E-mail: ratko.pilipovic@fri.uni-lj.si

Β 

Projekt EuroCC 3 je prejel sredstva Skupnega podjetja za evropsko visokozmogljivo računalniőtvo (EuroHPC JU) na podlagi Sporazuma o dodelitvi sredstev őt. 101306701. Skupno podjetje EuroHPC prejema podporo programa Evropske unije Digitalna Evropa ter naslednjih držav: Nemčije, Albanije, Avstrije, Belgije, Bosne in Hercegovine, Bolgarije, Hrvaőke, Cipra, Čeőke, Danske, Estonije, Finske, Francije, Grčije, Madžarske, Islandije, Irske, Italije, Latvije, Litve, Luksemburga, Malte, Črne gore, Nizozemske, Severne Makedonije, Norveőke, Poljske, Portugalske, Romunije, Srbije, Slovaőke, Slovenije, Španije, Švedske, Turčije in Kosova.
Financira Evropska unija. Izražena staliőča in mnenja so izključno staliőča avtorjev ter ne odražajo nujno staliőč Evropske unije ali Skupnega podjetja EuroHPC. Zanje ne moreta biti odgovorna niti Evropska unija niti Skupno podjetje EuroHPC.
Nacionalni kompetenčni center SLING sofinancira Ministrstvo za izobraževanje, znanost in mladino Republike Slovenije.
HPC in Europe je krovna znamka, ki združuje evropske pobude na področju visokozmogljivega računalniőtva v več kot 36 državah.

Β 


Kontejnerizacija in orkestracija aplikacij | Containerization and orchestration of applications

29 September 2026 at 07:00

Izvajalec / Course provider: University of Ljubljana, Faculty of Computer and Information Science (UL FRI)
Predavatelji / Instructors: Matjaž Pančur (UL FRI), Uroő Lotrič (UL FRI), Davor Sluga (UL FRI)

Learning objectives: The course is intended for those who want to learn in detail how containers work, how an application is automatically containerized, and how it is deployed using zero downtime deployment patterns.

Course content: The course content covers the areas of virtualization, containerization, and web application orchestration. The technical fundamentals of containers and container machines, CI/CD pipelines, and orchestration of the entire application stack are covered in detail. Through practical examples, it will be explained how to containerize a web application, automate image building using a CI/CD pipeline, and deploy the application to a single server with Docker Compose and to a cluster of servers using the Kubernetes platform.

Β Learning outcomes: After completing the course, participants will:

  • understand how containers are built and the role different container runtimes play in running them across various systems,
  • be able to deploy an application stack using Docker Compose,
  • understand how the Kubernetes container orchestrator works,
  • be able to deploy an application stack to Kubernetes.

Β 

Language: Slovenian, English

Prerequisites: /
Target audience: students; researchers; industry; public sector

Workshop:Secure HPC workloads for SMEs

8 October 2026 at 11:00

Description: HPC environments are shared, multi-tenant systems where hundreds of users run jobs side by side on the same physical infrastructure. This creates a unique security landscape: the cluster administrators must isolate users from each other and from the underlying system, while users themselves are responsible for protecting their data, credentials, and workloads as they move through the pipeline. This workshop walks through HPC security end to end: what the infrastructure team does to keep the cluster safe, what you as a user can (and should) do to protect your own work, and how to secure the containers you bring onto the system.

Scope and Topics:

Part 1 - Infrastructure Security (13:00 - 13:30)
How the HPC platform itself is hardened, largely invisible to the end user but foundational to everything else: process and resource isolation, storage isolation, network security, IAM, auditing, scheduler-level security, etc.

Part 2 β€” What You Can Do as a User (13:40 - 14:10)
Best practices, Slurm and system features users control to protect their own jobs and data, such as exclusive mode, secure data transfers and data management, encryption, revoking sessions and keys, cleaning the data from the compute node, etc.

Part 3 - Container Security (14:20 - 14:50)
Because containers are the primary way users package and run software on HPC, they deserve their own layer of scrutiny: image signing, signature verification, vulnerability scanning, SBOMs, dealing with sensitive data, registries and other security controls.

Difficulty: Beginner - Intermediate

Language: English

Date and time:Β Β 08. 10. 2026 at 13.00Β 

Max. number of participants: 30

Virtual location:Β ZOOM

Prerequisite knowledge:Β Having a basic knowledge of Linux is expected. Users should already have basic understanding of Β HPC systems.

Target audience:Β Industry, Academia, Public sector

After the workshop, participants will understand:

β€’ Β  Β The shared responsibility model between HPC infrastructure teams and end users when it comes to security.
β€’ Β  Β The mechanisms HPC centres use to isolate users, jobs, and data from one another at the OS, network, and scheduler level.
β€’ Β  Β The practical steps users should take to protect their own data and workloads while working on a shared system.
β€’ Β  Β How to secure the full lifecycle of a container, from build to signing, to scanning, to runtime, before and while using it on HPC.

Organiser:

Β 

Β 

Lecturers:

Ime: Barbara KraΕ‘ovec
Opis: Barbara KraΕ‘ovec is an HPC systems architect at the JoΕΎef Stefan Institute in Slovenia. She has over 15 years’ experience in HPC, cloud and distributed systems, virtualisation, and cybersecurity. She is a member of SLING, Slovenia’s national supercomputing network, EuroHPC Vega design and administration teams. She also serves on the EGI CSIRT and the EOSC EU Node security team, where she supports the security of European research infrastructure. Her work spans architecture, deployment, and operations of advanced computing environments, with a strong focus on secure, scalable solutions for scientific and industrial applications.
E-mail: info@sling.si
Ime: Dejan Lesjak
Opis: Systems architect at Jozef Stefan Institute.
E-naslov: info@sling.si

Β 

Projekt EuroCC 3 je prejel sredstva Skupnega podjetja za evropsko visokozmogljivo računalniőtvo (EuroHPC JU) na podlagi Sporazuma o dodelitvi sredstev őt. 101306701. Skupno podjetje EuroHPC prejema podporo programa Evropske unije Digitalna Evropa ter naslednjih držav: Nemčije, Albanije, Avstrije, Belgije, Bosne in Hercegovine, Bolgarije, Hrvaőke, Cipra, Čeőke, Danske, Estonije, Finske, Francije, Grčije, Madžarske, Islandije, Irske, Italije, Latvije, Litve, Luksemburga, Malte, Črne gore, Nizozemske, Severne Makedonije, Norveőke, Poljske, Portugalske, Romunije, Srbije, Slovaőke, Slovenije, Španije, Švedske, Turčije in Kosova.
Financira Evropska unija. Izražena staliőča in mnenja so izključno staliőča avtorjev ter ne odražajo nujno staliőč Evropske unije ali Skupnega podjetja EuroHPC. Zanje ne moreta biti odgovorna niti Evropska unija niti Skupno podjetje EuroHPC.
Nacionalni kompetenčni center SLING sofinancira Ministrstvo za izobraževanje, znanost in mladino Republike Slovenije.
HPC in Europe je krovna znamka, ki združuje evropske pobude na področju visokozmogljivega računalniőtva v več kot 36 državah.

Β 


Β 

SOLIDARNOST S STRUMIΕ KO DOLINO: RUDNIK ILOVICA–ŠUTKA PONOVNO OGROΕ½A KMETIJSTVO IN LOKALNE SKUPNOSTI V SEVERNI MAKEDONIJI

7 September 2026 at 13:59

Prebivalci v okolici Strumice v Severni Makedoniji se ΕΎe skoraj deset let upirajo projektu rudnika bakra in zlata Ilovica–Šutka, ki ga načrtuje kanadsko podjetje Euromax Resources. Junija 2026 je viΕ‘je upravno sodiőče izdalo pravnomočno sodbo v korist podjetja, s čimer se je vladi odprla pot do izdaje dovoljenja za projekt, ki je s tem znova bliΕΎe uresničitvi. To je sproΕΎilo novo mobilizacijo lokalnih skupnosti in okoljskih organizacij v regiji.

Dolina je ena ključnih kmetijskih regij Severne Makedonije, znana po rodovitni zemlji in dolgi kmetijski tradiciji, ki lokalnim skupnostim že generacije omogoča preživetje. Prebivalci okoliőkih občin Bosilovo in Novo Selo, so svoje nasprotovanje rudniku izrazili tudi na dveh lokalnih referendumih leta 2017, ki sta zaradi prenizke udeležbe ostala neveljavna. Kljub temu je gibanje nadaljevalo boj prek občinskih sklepov proti rudniku in nenehnega civilnega pritiska. Gibanje že leta zahteva trajen preklic koncesij za rudnike, zaőčito kmetijske zemlje, vodnih virov in ekosistemov doline pred obsežnim izkoriőčanjem ter dejansko sodelovanje prizadetih skupnosti.

Primer Ilovica–Šutka razkriva temeljno protislovje t. i. zelenega prehoda, kot ga oblikuje kapital. Baker je ključna surovina za elektrifikacijo in obnovljive vire energije, vendar pod logiko kapitalizma njegovo pridobivanje znova pomeni razlaőčanje kmetijske zemlje, uničevanje vodnih virov in preglasovanje volje lokalnih skupnosti v imenu β€œnujnega razvoja”. Namesto pravičnega prehoda, ki bi izhajal iz demokratičnega nadzora nad viri in potrebami skupnosti, se ponavlja stari vzorec izkoriőčanja naravnih virov, le da je tokrat oblečen v zeleno embalaΕΎo. Zeleni prehod, ki ne postavlja pod vpraΕ‘aj lastniΕ‘tva in nadzora nad naravnimi viri, ostaja prehod za kapital, ne za ljudi in okolje.

The post SOLIDARNOST S STRUMIΕ KO DOLINO: RUDNIK ILOVICA–ŠUTKA PONOVNO OGROΕ½A KMETIJSTVO IN LOKALNE SKUPNOSTI V SEVERNI MAKEDONIJI first appeared on Rdeča Pesa.

KAPITAL LOČUJE STARŠE OD OTROK, ŽENE OD MOŽ

4 September 2026 at 10:22

Z vami delimo izjavo Delavske svetovalnice in Ambasade Rog ter vabimo k podpisu peticije proti izgonu delavskih druΕΎin. Vabimo tudi na shod tujih delavcev, ki bo 16. septembra ob 17h pred parlamentom.

Vlada pripravlja zakonske spremembe, s katerimi bo več deset tisočim tujim delavcem odvzela dovoljenje za bivanje. Pred vrati je množičen izgon, kakrőnega v sodobni zgodovini Slovenije őe ni bilo.

Kaj prinaΕ‘a zakon? Delavci bomo morali mesečno zasluΕΎiti kar dvakratnik dosedanjega praga zadostnih sredstev, da bi lahko podaljΕ‘ali svoja dovoljenja. A če je ta prag teoretično dosegljiv za eno osebo, bo za druΕΎine povsem nedostopen – starΕ‘a z dvema otrokoma bi na primer morala za začasno dovoljenje zasluΕΎiti kar 2.791 evrov mesečno (brez malice, prevoza in dodatkov), za stalno dovoljenje pa 4.060 evrov!

In to őe ni vse. Za združitev z družino bomo lahko delavci zaprosili őele po treh letih, naői družinski člani pa bodo morali v prvem letu opraviti izpit iz slovenskega jezika na visoki ravni A2, ob tem da nam vlada hkrati ukinja financiranje že zdaj težko dostopnih tečajev in izpitov. 

Koga zakon prizadane? Glavne ΕΎrtve bodo delavske druΕΎine. Vlada namreč hoče ločiti ΕΎene od moΕΎ in otroke od starΕ‘ev. Par, ki je morda desetletje v Sloveniji delal, bo v trenutku, ko se jima rodi prvi otrok, izgubil pravico do bivanja. DruΕΎina, ki ima urejene vse papirje, ki je del lokalne skupnosti in katere otroci hodijo v slovensko Ε‘olo, bo čez noč postala ilegalna. Dobesedno gre za mnoΕΎičen izgon druΕΎin. Zakon ne bo prizadel le najrevnejΕ‘ih, ampak takorekoč vse – od čistilke do univerzitetnega profesorja, od mehanika do inΕΎenirja.

Zakaj vlada to počne? V prvi vrsti gre za kruto izΕΎivljanje nad tujimi delavci. V javnih izjavah sicer govorijo o β€œzagotavljanju socialne stabilnosti” in β€œboljΕ‘i integraciji”, a nič ne bi moglo biti dlje od resnice. Če kaj, vlada zaostruje pogoje tistim delavcem, ki največ prispevamo v drΕΎavno blagajno in jo tudi najmanj koristimo – brez naΕ‘ega doprinosa bi se zdravstvena in pokojninska blagajna sesuli. Pogoje zaostruje predvsem najbolje integriranim – tistim, ki ΕΎivimo v drΕΎavi ΕΎe dlje časa in smo si tu ustvarili druΕΎinska ΕΎivljenja. Zdaj nas hoče zamenjati s samskimi delavci iz bolj oddaljenih drΕΎav, ki bodo manj poznali sistem in se jih bo laΕΎje izkoriőčalo. Gre torej za darilo izkoriőčevalcem in trgovcem z ljudmi.

KakΕ‘ne bodo posledice? Več deset tisoč delavcev se bo iz Slovenije primorano izseliti, saj nam v nasprotnem primeru grozi ločitev druΕΎin. Obeta se eksodos prebivalcev iz bivΕ‘e skupne republike, ki so dolga leta bili temelj slovenskega gospodarstva in pomemben del lokalnih skupnosti. Ostali bodo lahko le samski delavci na krajΕ‘ih pogodbah, ki pa bodo pod novimi pogoji morali delati Ε‘e več, da bi dosegli prag. Vse več ljudi bo brez papirjev – zakon jih bo spreminjal v ilegalce in delavce na črno, ki jih lahko izkoriőčajo razne kriminalne zdruΕΎbe. To pomeni pritisk na plače navzdol in pot v suΕΎenjski sistem, kakrΕ‘nega poznamo v zalivskih drΕΎavah.Β 

Hkrati se bo vse manj kadrov odločalo za delo v Sloveniji, zaradi česar se bo poglobila kadrovska kriza v panogah, kjer primanjkuje delavcev: v skrbstvu, zdravstvu, vzgoji, industriji, gradbeniőtvu, gostinstvu in turizmu. 

Kaj lahko storimo?Β 

  • PridruΕΎi se kampanji s podpisom peticije.
  • PridruΕΎi se nam na shodu tujih delavcev, ki bo 16. septembra ob 17h pred parlamentom
  • Povabi Ε‘e svoje kolege in sodelavceΒ 

S skupnimi močmi ustavimo suženjski delovni režim in izgon družin!

https://www.peticija.online/signatures/proti_izgonu_delavskih_druzin

The post KAPITAL LOČUJE STARŠE OD OTROK, ŽENE OD MOŽ first appeared on Rdeča Pesa.

JAVNI POTNIΕ KI PROMET DOBIL CVEK: Ε OLARJI ZAMUJAJO V Ε OLO, EKSKURZIJ VEDNO MANJΒ 

3 September 2026 at 16:17

Javni potniőki promet (JPP) je nezadosten, podražitve občasnih prevozov posegajo v őolski proces, ugotavlja 150 zaposlenih v vzgoji in izobraževanju iz vse Slovenije v raziskavi Mreže za pravičen prehod.

1. Redne linije JPP ne zadovoljujejo vsakdanjih potreb Ε‘olarjev in zaposlenih v Ε‘olahΒ 

Večina őolnikov in őolarjev se za vsakdanji prevoz do őole ne more zanaőati na mrežo javnega potniőkega prometa, ugotavlja raziskava. Več kot 60 % anketiranih poroča o rednem zamujanju ali predčasnem odhajanju učencev zaradi neustreznih voznih redov.

2. Zaradi podražitev prevozov őole krčijo in prilagajajo ekskurzije 

Skoraj őtiri petine anketiranih za namene őolskih dejavnosti, ki vključujejo prevoz otrok, najamejo občasni prevoz. Kot največjo težavo pri organizaciji takih dejavnosti je dve tretjini vpraőanih navedlo podražitve. Hkrati več kot 72 % anketirancev odgovarja, da morajo őole vsaj občasno ali zelo pogosto prilagajati dejavnosti zaradi finančnih omejitev družin učencev. 

β€œZaradi viΕ‘jih cen smo racionalizirali ekskurzije oz. zmanjΕ‘ujemo njihovo Ε‘tevilo. Prav tako zdruΕΎujemo nekatere aktivnosti, da na en dan izvedemo čim več, to pa vodi v upad interesa in manjΕ‘o kvaliteto izvedenega.” – ravnateljica gimnazije v goriΕ‘ki regiji

3. Neustrezni vozni redi JPP zaposlenim v őolah nalagajo dodatno organizacijsko breme ter negativno vplivajo na učence in dijake 

Neustreznost javnega potniőkega prometa ter komercializacija občasnih prevozov nalagata nevidno in neplačano breme tudi zaposlenim v őolstvu. Učitelji vse več časa namenjajo administraciji, iskanju sredstev in reőevanju sistemskih pomanjkljivosti prevoza: 

β€œV osnovi sem učiteljica Ε‘portne vzgoje in rada opravljam svoje delo. Ε½al pa moram vedno več časa nameniti načrtovanju prevozov, prilagajanju izletov, naročanju prevoza, pridobivanju donacij, da lahko plačamo prevoz. Vse to gre na račun mojega pedagoΕ‘kega dela, kar se mi ne zdi prav.” – pedagoΕ‘ka delavka v osnovni Ε‘oli

Posledice tako slabo urejenega javnega prevoza neposredno vplivajo tudi na kakovost izobraževalnega procesa in obőolskih dejavnosti, siromaőijo srednjeőolsko in osnovnoőolsko življenje zunaj rednega pouka ter pri dijakih povzročajo utrujenost in stres: 

β€œUčenci so vsakodnevno pod stresom zaradi voznih redov in zamud javnega prevoza, vlaki imajo zamude tudi do 45 minut, učenec pa tega ne more prej predvideti, zaradi česar zamudi tudi na ocenjevanja znanja.” – pedagoΕ‘ka delavka na srednji Ε‘oli

4. Zaposleni v Ε‘olstvu podpirajo izboljΕ‘ave javnega potniΕ‘kega prometaΒ 

Več kot 80 % zaposlenih v őolstvu meni, da bi več zaposlenih in őolarjev uporabljalo javni promet za vsakodnevno pot do őole, če bi bil ustrezno in zanesljivo urejen. S tem bi prispevali k manjői uporabi avtomobilov in okoljsko vzdržnejői družbi.

SrednjeΕ‘olska profesorica Jerneja Breznik izpostavlja: β€œTeΕΎav Ε‘olskega prevoza ni mogoče reΕ‘evati zgolj na ravni posameznih Ε‘ol. PedagoΕ‘ki delavci in delavke moramo skupaj zahtevati potrebne sistemske spremembe, ki bodo javni potniΕ‘ki promet bolje prilagodile potrebam Ε‘ol in ljudi, izboljΕ‘ale delovne pogoje voznikov in zagotovile dostopnejΕ‘e občasne prevoze.” 

5. Arrivi in Nomagu dominanten položaj na trgu omogoča visoke dobičke na račun kakovosti storitve in delovnih pogojev 

Ker redne linije javnega potniőkega prometa, ki jih prek državne koncesije upravljata Arriva in Nomago, ne ustrezajo potrebam vseh őolskih dejavnosti, Nomago in Arriva pa sta največja prevoznika, se morajo őole tudi za izredne najeme prevoznikov večinoma zanaőati na ti dve podjetji. Podjetji sta v letu 2024 zabeležili 1 milijon evrov (Arriva) in 12,6 milijona evrov (Nomago) čistega dobička. 

Zaradi profitnega motiva skuőata obe podjetji viőati cene storitev, nižati lastne stroőke in izkoriőčati svoj duopolni položaj za doseganje viőjih dobičkov. To vodi v poslabőevanje pogojev za voznike in potnike. 

β€œTeΕΎava je dobiti proste avtobuse, saj ima avtoprevoznik omejene kapacitete zaradi pomanjkanja voznikov.” – pedagoΕ‘ki delavec/ka na osnovni Ε‘oli

6. ReΕ‘itve?

Nujno je povečanje obsega mreže javnega potniőkega prometa in izboljőanje delovnih pogojev za voznike z boljőim plačilom za ves delovni čas, vključno s čakanjem, malico in čiőčenjem avtobusa.

Da podražitve izrednega najema prevoznikov ne bodo omejevale dostopa učencev do izobraževalnih dejavnosti, je potrebna regulacija cen občasnih oziroma skupinskih prevozov za őolske dejavnosti.

To sta konkretni reőitvi, ki sta hitro izvedljivi. Vsako leto bolj očitno pa je tudi, da sistem podeljevanja koncesij prevoznikom, ki s svojo dejavnostjo služijo predvsem lastnim profitom, namesto uporabnikom, ni več ustrezen. Zato predlagamo prehod od profitno usmerjenega modela k javnemu, preglednemu in demokratičnemu upravljanju in razvoju javnega potniőkega prometa. Pri odločanju o potrebah, linijah in urnikih morajo imeti zagotovljeno besedo tako zaposleni kot uporabniki.

The post JAVNI POTNIŠKI PROMET DOBIL CVEK: ŠOLARJI ZAMUJAJO V ŠOLO, EKSKURZIJ VEDNO MANJ  first appeared on Rdeča Pesa.

900 EVROV, DA JE MOJA MAČKA PREŽIVELA: KDO SI LAHKO TO SPLOH PRIVOŠČI? 

2 September 2026 at 15:13

Pred dobrima dvema tednoma je zbolel moj maček. Ker je nagnjen k nastajanju sečnih kamnov, kar je precej pogosto pri notranjih kastriranih mačkah, sem bila prepričana, da bo nevőečnost mogoče odpraviti z zdravili, kot do sedaj. Vsekakor nisem pričakovala, da bodo končni stroőki zdravljenja nanesli 866,33 evra. Pregled, zdravila, material, aplikacija zdravil.

Ker se je zdravstveno stanje slabőalo in zdravila sprva niso učinkovala, sem  teden in pol vsak dan preživela na veterini, kjer je maček v gneči množičnih čipiranj mačk bil obravnavan, injektiran z zdravili in večkrat diagnosticiran z novimi zapleti. Vsakič, ko sem ob koncu pregleda odőla do blagajniőkega pulta na recepciji, sem imela cmok v grlu: »kakően bo stroőek tokrat?«, »ali mi bodo zaračunali őe kontrolo, ali ne?«, »včeraj sem plačala čez 250 evrov, danes zagotovo ne bo toliko«.

Ker imam svojega hiőnega ljubljenčka seveda rada in si želim, da bo bolje, sem pristala na zdravljenje, tudi ko so me opozorili, da bo to prineslo dodaten stroőek. Kakőna izbira neki? Razumem, da zaposleni potrebujejo moje soglasje, a hkrati se ob teh vpraőanjih vedno počutim čustveno izsiljena. Saj se dobro zavedajo, da odgovorni lastniki pač ne bodo svojim ljubljenčkom odrekli  možnosti okrevanja.

»Piői dolg«, reče receptorka svoji kolegici, ko se po telefonu pogovarja z neko stranko. Očitno je tudi to praksa, da se ljudje zadolžujejo, ker ne morejo poravnati absurdno visokih stroőkov veterinarske oskrbe. Pomislim, da če bi najin maček zbolel pred dvema letoma, ko sva bila s partnerjem slabőem finančnem položaju, si tudi sama ne bi mogla privoőčiti njegovega zdravljenja. 

Ko svojo izkuőnjo delim s prijatelji, ugotovim, da moj primer ni osamljen in da si mnogi niso mogli privoőčiti veterinarske oskrbe in zdravljenja. Imeti hiőnega ljubljenčka v sistemu, kjer je njihova oskrba povsem tržna dejavnost, je postal razredni privilegij, oziroma je močno razredno pogojeno. 

Lastniőtvo hiőnih ljubljenčkov, odločitev glede izbire ljubljenčka in kvaliteta zadovoljevanja njegovih potreb (katero hrano kupiti, koliko krat obiskati veterinarja, nega), izhajajo iz dejstva, da so tudi naői hiőni ljubljenčki del razreda, ki mu pripadamo sami. 

Hiőni ljubljenčki so v 19. stoletju bili simbol malomeőčanstva. Kar ne pomeni, da si delavski razred »ne želi« ali pa ni želel hiőnih živali, ali pa »ne želi« najboljőe možne oskrbe za njih, temveč da kapitalizem različno razporeja čas, prostor in denar, ki je potreben za njihovo dostojno oskrbo.

Imeti hiΕ‘no ΕΎival je bil in je privilegij, ki se v praksi lahko izkaΕΎe ΕΎe skozi vpraΕ‘anja kot so na primer lastniΕ‘tvo stanovanja ali lastniΕ‘tvo avtomobila. Če ΕΎiviΕ‘ v najemu, je ta odločitev prepuőčena lastniku stanovanja. Prevoz hiΕ‘nih ljubljenčkov je z javnim prevozom omejen, ali pa veterinarske ambulante niso na dosegu javnega prevoza. Najbolj pa se kaΕΎe skozi moΕΎnost veterinarske oskrbe glede na osebni dohodek. Raziskava iz ZDA (An examination of US pet owners’ use of veterinary services, 2006–2018) je na primer pokazala, da imajo lastniki hiΕ‘nih ΕΎivali in uporabniki veterinarskih storitev viΕ‘je dohodke in pogosteje lastno stanovanje, pri čemer so imeli tisti, ki so redno uporabljali veterinarske storitve, Ε‘e viΕ‘je dohodke.

V Sloveniji podrobnejőih analiz, ki bi lastniőtvo hiőnih ljubljenčkov postavili v razredni kontekst, őe ni, a lahko iz prakse opazimo podoben vzorec. Sploh ker zdravljenje hiőnih, kot tudi rejnih živali v Sloveniji v obliki javne oskrbe ne obstaja. Veterinarska dejavnost je od osamosvojitve prepuőčena trgu in ni več javna služba. 

Ko prebiram podobne izkuőnje ljudi po družabnih omrežjih, pogosto zasledim komentarje, ki gredo v smeri »zakaj pa imate hiőnega ljubljenčka, če veste, da si ga ne morete privoőčiti?«, ali pa »neodgovorno je, da nekdo zavrne zdravljenje pri veterinarju, ker se mu zdi predrago«. 

Na tej točki bi se lahko sprijaznila z dejstvom, da je imeti hiőnega ljubljenčka v kapitalizmu  pač malomeőčanski luksuz, ampak zakaj mora biti sposobnost človeka, da živi z živaljo in zanjo dostojno skrbi, odvisna od njegovega dohodka, stanovanjske situacije, avtomobila in dostopa do storitve veterinarja? 

Kapitalizem namreč ustvarja protislovje: od skrbnika živali namreč zahteva, da je »odgovoren lastnik«, hkrati pa odgovornost prepuőča njegovemu dohodku. Če človek nima denarja za skoraj 1000 evrov veterinarskega posega, je to razumljeno kot njegov osebni neuspeh.

Spreleti me, ko si na koncu izračunam skupni stroőek veterinarske oskrbe. Pomislim, da bom na ta denar že nekako pozabila, ko bo mačku bolje, ampak dejstvo,  da so se materialni pogoji življenja zaradi tega zame poslabőali, pa ostaja.

Alternativa zato ni odprava vezi med ljudmi in živalmi (veliko zgodovinskih primerov namreč priča o skrbi in pozitivnih izkuőnjah delavcev z živalmi), ampak vzpostavitev družbe, kjer ta vez ni privilegij premožnih in kjer hiőni ljubljenčki niso zgolj blago. To pa je mogoče z dostopno in javno veterinarsko oskrbo s sistemom zavarovanj za ljubljenčke, javnim prevozom, ki omogoča prevoz živali, podporo zavetiőčem, odpravo komercialne (pasemske) vzreje, in zagotavljanjem stanovanj, ki omogočajo dostojno sobivanje nas in naőih živali.

The post 900 EVROV, DA JE MOJA MAČKA PREŽIVELA: KDO SI LAHKO TO SPLOH PRIVOŠČI?  first appeared on Rdeča Pesa.

USTAVI INTERVENTNO BOGATENJE BRODNJAKA IN EKIPE – PODPIΕ I ZA REFERENDUM!

31 August 2026 at 16:52

Dodatni milijonski dobički za Spar, Lidl, Hofer, Mercator in druge veletrgovce, novi stotisočaki rente za nepremičninske barone in nova poviőica za Blaža Brodnjaka ter ostalo menedžersko kasto. Na drugi strani pa manj denarja za pokojnine običajnih delovnih ljudi, slabői javni zdravstveni sistem in ukinjanje delavskih standardov in pravic za milijon zaposlenih. Tako bi lahko na kratko opisali vsebino in posledice t.i. interventnega zakona za razvoj Slovenije, ki ga je na predlog NSi, Demokratov in Resnice parlament sprejel maja letos. 

Toda omenjeno interventno bogatenje elit in siromaőenje celotne družbe lahko delovni ljudje zaustavimo! Prvi spodbuden korak je bil narejen že takoj po sprejemu zakona. Sredi maja so namreč združene sindikalne centrale v slabem tednu dni zbrale več kot 47 tisoč neoverjenih podpisov državljank in državljanov za začetek postopka za razpis zakonodajnega referenduma.

Jutri, 1. septembra pa se pričenja druga etapa referendumskega boja. V njej morajo predlagatelji  do 5. oktobra zbrati 40 tisoč overjenih podpisov državljank in državljanov. Če bodo pri tem uspeőni, bo referendum o interventnem zakonu predvidoma izveden sredi novembra. Podpis za razpis referenduma bo možno oddati na vseh upravnih enotah po Sloveniji ali pa spletno preko državnega portala e-Uprava z uporabo elektronskega podpisa. S klikom na povezavo v komentarju pod objavo lahko dostopate do natančnejőih navodil za oddajo podpisa.

Še preden pa se začne zbiranje podpisov, pa velja na kratko őe enkrat pokazati na glavne dobitnike interventnega zakona za bogatenje že bogatih. 

Trgovski velikani. Z znižanjem davka na dodano vrednost iz 9,5 % na 5 % za nekatera izbrana osnovna živila bo znatno zrasel njihov dobiček, ki je zgolj v primeru trgovske verige Spar že v letu 2024 znaőal dobrih 19 milijonov evrov. Izkuőnje iz tujine (npr. Hrvaőka, Avstrija, Finska) kažejo, da se znižana stopnja DDV-ja ne prelije v nižje cene za potroőnike, ampak v őe večje dobičke za trgovske velikane. 

Nepremičninski baroni. Z znižanjem davčne stopnje za oddajanje premoženja v najem iz zdajőnjih 25 % na 15 % oziroma na 5 %, bodo őe povečali svoje rente, ki jih mesečno pobirajo od zmeraj večjega őtevila obubožanih najemnic in najemnikov. Tudi pri tem ukrepu nam izkuőnje iz tujine (npr. Švedska, Portugalska, ZDA) kažejo, da ne pride do znižanja cene mesečne najemnine temveč zgolj do zviőanja debeline denarnice tistih, ki oddajajo stanovanja v najem. 

Menedžerska in upravljalska kasta. Z uvedbo socialne kapice (tj. ukinitev plačevanja socialnih prispevkov po 7.500 evrov bruto plače) bo povprečen direktor srednje velike firme, ki na mesec prejme okoli 9.000 evrov bruto plače, na letni ravni v žep pospravil dodatnih 2.080 evrov. Če pa si ogledamo dohodke enega izmed najbolj izpostavljenih menedžerjev, Blaža Brodnjaka iz NLB-ja, pa lahko ugotovimo, da bi mu uveljavitev socialne kapice prinesla dodatnih 148.608 evrov na leto. 

Tistih, ki bi z uveljavitvijo interventnega zakona bili na slabőem je mnogo več. Gre za enega najbolj klasičnih primerov zakonodaje, ki je spisana za interese 1 % najpremožnejőih in proti potrebam 99 % ostalega prebivalstva. 

Prav ta ogromna večina delovnih ljudi, ki potrebuje delujoče in dostopne javne storitve in ki že desetletja trpi posledice vse večje ekonomske neenakosti, je tista, ki lahko s svojim angažmajem ustavi sprejem tega družbeno őkodljivega zakona. Najprej z množičnim podpisovanjem pobude za razpis referenduma in nato őe z bolj množično udeležbo na njem. 

#rdečapesa

The post USTAVI INTERVENTNO BOGATENJE BRODNJAKA IN EKIPE – PODPIΕ I ZA REFERENDUM! first appeared on Rdeča Pesa.

NAMESTO PIAROVSKEGA DELJENJA USTAV – BOLJΕ E POGOJE ZA UČENCE IN UČITELJE

27 August 2026 at 14:33

Pred dnevi je na domači politični sceni veliko prahu dvignila odločitev Ministrstva za izobraΕΎevanje, znanost in mladino, da bo vsak bodoči prvoΕ‘olec ob začetku osnovnoΕ‘olskega izobraΕΎevanja prejel izvod ustave. Sledili so odzivi z vseh strani in vnel se je pravi kulturni boj okoli vpraΕ‘anja, ali je taka odločitev primerna ali ne. Kakor koli ΕΎe – če k zadevi pristopimo čisto zdravorazumsko, potem se lahko resno vpraΕ‘amo o premiΕ‘ljenosti takΕ‘ne odločitve. Ali je res smiselno podeljevati ustavo otrokom, ki sploh Ε‘e ne znajo brati? Vse skupaj deluje kot cenena piarovska poteza.

Ob tem pa se zastavljajo tudi nekatera pomembna vpraőanja. Kot smo sliőali v teh dneh, se őolniki trenutno ukvarjajo s tem, kje dobiti učitelje, ki bodo otroke naučili brati, da bodo lahko brali ustavo. Namesto nesmiselnega deljenja ustav bi morali pristojni pozornost usmeriti na probleme, ki že dalj časa pestijo slovensko őolstvo.

Eden od teh problemov je kritično pomanjkanje kadra. V Sloveniji se őole soočajo z alarmantnim pomanjkanjem približno 4000 vzgojiteljev in učiteljev. Primanjkuje predvsem učiteljev naravoslovnih predmetov (matematika, fizika, tehnika) in visokoőolskih učiteljev. Razloge za pomanjkanje gre iskati predvsem v tem, da se visoko izobraženi kader raje odloča za bolje plačana delovna mesta zunaj őolstva, poleg tega pa je tu tudi staranje kadra in nižji delež mladih učiteljev, kar dolgoročno rezultira v kadrovskem primanjkljaju. Kako alarmantno je stanje, kaže podatek, da v nekaterih őolah zaposlujejo celo őtudente in őtudentke.

Učiteljski kader v neoliberalni družbi poleg primanjkovanja pestijo težave, povezane s slabimi delovnimi pogoji, nizkim plačilom, nezadostno avtonomijo in izključenostjo iz izobraževalnih politik ter pomanjkanjem nadzora nad vstopom v poklic. Tu so tudi težave, povezane z okrevanjem őolstva po pandemiji, digitalnimi tehnologijami, nizko pozornostjo učencev in nasiljem. 

Težav pa ne občutijo samo učitelji. Stroőki za őolske potrebőčine (ki jih otroci bolj potrebujejo kot ustave) so vse viőji in nekateri si jih le stežka privoőčijo (stroőek őolskih potrebőčin lahko hitro doseže 300 evrov na otroka). Otroci so namesto kritičnega razmiőljanja, ki se vse bolj umika v imenu strokovnosti in »nepolitičnosti«, deležni vsiljevanja umetne inteligence,»podjetniőkih kompetenc« in militarne ideologije. Šola namreč, kot vemo, deluje kot vodilni ideoloőki aparat države.

Sedanje ministrstvo denar raje namenja kupovanju in deljenju ustav, medtem ko nekatere őole nimajo ustrezno opremljenih učilnic, telovadnic, fizično razpadajo, vzgojiteljice (in otroci) se v vrtcih dobesedno kuhajo od vročine, őtevilni otroci oziroma njihove družine pa nimajo sredstev za osnovne őolske potrebőčine. Skrajni čas je, da od odločevalcev zahtevamo őolsko politiko s konkretnimi ukrepi in izboljőavami.

The post NAMESTO PIAROVSKEGA DELJENJA USTAV – BOLJΕ E POGOJE ZA UČENCE IN UČITELJE first appeared on Rdeča Pesa.

Skupaj za razvoj projekta NUK 2 – Univerzitetna knjiΕΎnica v Ljubljani

3 September 2026 at 13:21

Univerza v Ljubljani (UL), Centralna tehniΕ‘ka knjiΕΎnica UL (CTK) ter Narodna in univerzitetna knjiΕΎnica (NUK) smo izvedle skupno novinarsko konferenco, kjer smo podpisale Pismo o nameri o sodelovanju pri projektu NUK 2. Na dogodku, ki je potekal 3. septembra 2026 v sejni sobi NUK, smo se tri institucije povezale v skupnem prizadevanju, da bo prihodnja knjiΕΎnica poleg ustrezne infrastrukture imela tudi kakovostne storitve in vsebine, prilagojene potrebam svojih uporabnikov.

Foto: GaΕ‘per LeΕ‘nik

Rektor Univerze v Ljubljani, prof. dr. Gregor Majdič, ravnateljica Narodne in univerzitetne knjiΕΎnice, dr. Jana Kolar, ter v. d. direktorja Centralne tehniΕ‘ke knjiΕΎnice Univerze v Ljubljani, Tilen Mandelj, so potrdili skupno vizijo, da NUK 2 – Univerzitetno knjiΕΎnico v Ljubljani razvijajo kot odprto, povezano in sodobno srediőče znanja, ki bo povezovalo Univerzo v Ljubljani, nacionalno knjiΕΎnico in osrednjo tehniΕ‘ko knjiΕΎnico ter sluΕΎilo Ε‘tudentom, raziskovalcem, strokovni javnosti in Ε‘irΕ‘i druΕΎbi.

Foto: GaΕ‘per LeΕ‘nik

Slovenija NUK 2 namreč nujno potrebuje, in to ne le zaradi pomanjkanja knjižničnih prostorov, temveč tudi zaradi realnih potreb őtudentov, raziskovalcev in drugih uporabnikov. Jana Kolar je novinarjem povedala, da se je začela prva faza izgradnje stavbe NUK 2, ki ima veljavno gradbeno dovoljenje. 

Β»To je tudi pravi čas, da se začnemo pogovarjati o tem, kako bi razvili čim boljΕ‘e vsebine za bodoče uporabnike NUK 2. NUK se zato z veseljem povezuje s CTK – morda ne veste, ampak ΕΎe prvi načrti v začetku 90. let so vsebovali tudi pogovore s CTK. Takrat ni priΕ‘lo do nobenega sporazuma, saj so bile ovire za sodelovanje prevelike. Veseli me, da smo danes te ovire premagali. Z UL se v preteklosti nismo nikoli pogovarjali o sobivanju, zato sem navduΕ‘ena nad dobrim sodelovanjem z obema organizacijama in se veselim tudi bodočega sodelovanja,Β« je za javnost povedala Jana Kolar.

Foto: GaΕ‘per LeΕ‘nik

»UL že dolgo sodeluje z obema knjižnicama in zagotavljamo vse, kar je potrebno za delo naőih őtudentov,« je dejal Gregor Majdič in nadaljeval: »Ideja je, da si bomo delili prostore, kar bo pomenilo boljőo organizacijo dela naőe knjižnice v sodelovanju z NUK-om in CTK-jem.« Hkrati bo to pomenilo tudi racionalizacijo delovanja, ki bo vplivala na boljőe pogoje za őtudente, je őe pojasnil in napovedal, da bomo s tem reőili problematiko őtudijskih prostorov.

Foto: GaΕ‘per LeΕ‘nik

Β»Narodna in univerzitetna knjiΕΎnica, Univerza v Ljubljani in Centralna tehniΕ‘ka knjiΕΎnica Univerze v Ljubljani danes podpisujemo dokument o tem, kako bi lahko sobivale pod isto streho. Ne o zdruΕΎevanju. O sobivanju. Model temelji na partnerstvu ob ohranitvi avtonomije vseh treh ustanov,Β« pa je pojasnil Tilen Mandelj, v.d. direktorja CTK.

Foto: GaΕ‘per LeΕ‘nik

NUK 2 – Univerzitetna knjiΕΎnica v Ljubljani bo tako predstavljala nov javni prostor, ki bo Ε‘tudentom in raziskovalcem izboljΕ‘al moΕΎnosti za pridobivanje in izmenjavo znanj, veőčin in izkuΕ‘enj. Obenem gre za prostor, ki bo hkrati sodobno Ε‘tudijsko, izobraΕΎevalno in raziskovalno srediőče, druΕΎbeno vključujoč prostor različnih ciljnih uporabniΕ‘kih skupin in prostor ustvarjalnosti in inovativnosti.

Foto: GaΕ‘per LeΕ‘nik

Vabilo: Spletni seminar β€œAI Tools for Increasing Research Productivity, Integrity, and Effective Authorship – CTK”

2 September 2026 at 08:28

Vabljeni na spletni seminar AI Tools for Increasing Research Productivity, Integrity, and Effective Authorship – CTK, ki ga organizira zaloΕΎnik WileyΒ v četrtek, 8. oktobra 2026, od 11:00 do 12:00.

Na seminarju se boste naučili, kako lahko orodja, podprta z umetno inteligenco, raziskovalcem pomagajo pri procesu znanstvenega objavljanja (od zbiranja in analize podatkov do priprave in oddaje rokopisa). Obravnavali bodo tudi etične implikacije in primere dobre prakse pri rabi umetne inteligence v znanstveni komunikaciji. Ključne teme seminarja bodo:

  • uporabna orodij umetne inteligence za raziskovalce;
  • doseganje večje učinkovitosti pri pisanju;
  • povečanje točnosti in zanesljivosti raziskovalnih podatkov;
  • preprečevanje plagiatorstva in podvajanja podatkov;
  • podpora etičnemu raziskovanju z doseganjem kakovostnih in odmevnih raziskovalnih rezultatov.

Seminar je primeren predvsem za őtudente, raziskovalce, profesorje in knjižničarje Univerze v Ljubljani, saj imajo omogočen dostop do vsebin Wiley Online Library. Potekal bo v angleőčini. Za udeležbo je potrebna registracija.

Na ZdruΕΎenju DrogArt objavljamo prosto delovno mesto – sodelavec_ka na programu β€œZmanjΕ‘evanje Ε‘kodljivih posledic alkohola med mladimi – Izberi sam”

1 September 2026 at 09:08

Delovno mesto:sodelavec_ka na programu β€œZmanjΕ‘evanje Ε‘kodljivih posledic alkohola med mladimi – Izberi sam”.


Izobrazba: visokoőolska 1.stopnje, visokoőolska strokovna (prejőnja), visokoőolska 2.stopnje, visokoőolska univerzitetna (prejőnja); socialni_a delavec_ka, socialni_a pedagog_inja, psiholog_inja oziroma ustrezna izobrazba za strokovnega delavca_ko po 69. členu ZSV; zaželen strokovni izpit ali možnost njegovega opravljanja
Β 
Opis dela:Β  delo na programu Β» ZmanjΕ‘evanje Ε‘kodljivih posledic alkohola med mladimi – Izberi samΒ«, razvoj in izvedba aktivnosti na področju zmanjΕ‘evanja Ε‘kodljivih posledic alkohola ter mladinskega dela, informiranje mladih, priprava informativnih materialov, pisanje prispevkov za spletno stran in druΕΎbena omreΕΎja, sprotno oblikovanje novih odzivov na zaznane potrebe ciljne skupine (mladi, strokovni delavci/-ke), mentorsko delo z mladinskimi delavci/-kami, sodelovanje s pomembnimi akterji v nočnem ΕΎivljenju, lokalno skupnostjo in drugimi partnerji, pomoč pri administraciji ter pripravi razpisov in poročil, druga dela na programu Izberi sam.


Pogoji za zaposlitev:

  • zaΕΎelene delovne izkuΕ‘nje na področju mladinskega dela, terenskega dela, koordinacije oziroma samostojne izvedbe projektov,
  • poznavanje principov zmanjΕ‘evanja Ε‘kode na področju alkohola,
  • zaΕΎeleno je poznavanje druΕΎbenih omreΕΎij ter izkuΕ‘nje z ustvarjanjem in objavljanjem različnih vrst vsebin,
  • znanje jezikov: angleΕ‘ki jezik razumevanje zelo dobro, govorjenje zelo dobro, pisanje dobro,
  • računalniΕ‘ka znanja: urejevalniki besedil – zahtevno, delo s preglednicami – zahtevno, delo z bazami podatkov – osnovno,
  • vozniΕ‘ki izpit B kategorije.

Zaželene lastnosti: samostojnost pri delu, odgovornost, natančnost, komunikativnost, sposobnost timskega dela, smisel za delo z ljudmi, veselje do dela z mladimi.

Delo poteka v prijetnem timu, kjer je veliko možnosti za inovativnost in kreativnost; delo poteka v spodbudnem in sproőčenem vzduőju. Če bi želeli sprejemajočo in prijetno delovno izkuőnjo vas vabimo k prijavi! 😊 Smo namreč tudi organizacija s certifikatom družbeno odgovoren delodajalec.

Zaposlitev za krajői delovni čas (6h na dan, 30h na teden), za določen čas (do 30. 9. 2027) z možnostjo podaljőanja. Začetek dela: po dogovoru. Poskusna doba: 3 mesece.

β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”

Elektronsko prijavo z motivacijskim pismom in ΕΎivljenjepisom, iz katerega je razvidno,
da izpolnjujete pogoje, poőljite do vključno 11. 9. 2026 na naslov spela@drogart.org

The post Na ZdruΕΎenju DrogArt objavljamo prosto delovno mesto – sodelavec_ka na programu β€œZmanjΕ‘evanje Ε‘kodljivih posledic alkohola med mladimi – Izberi sam” appeared first on DrogArt.

Tabletka z visoko vsebnostjo etizolama v Mariboru

Aktivne snovi

Etizolam (4,1 mg)

Dodaten opis

Tabletka ima dvakrat viőjo vsebnost etizolama, od njegovega srednjega odmerka, ki znaőa 1-2 mg in zato lahko predstavlja viője tveganje za zdravje uporabnikov in za pojav negativnih učinkov, kot so: sedacija, motnje govora, slabost, poslabőanje motoričnih sposobnosti, izguba spomina, upočasnjeno dihanje. Respiratorna depresija je őe posebej nevarna v kombinaciji z drugimi depresorji, kot so alkohol, GBL, opiati!

UpoΕ‘tevaj spodnje smernice zmanjΕ‘evanja tveganj!

Disclaimer: Količina ettizolama v tableti je zgolj informativne narave in se lahko pri tabletah s podobnim izgledom bistveno razlikuje.

Datum testa

4.9.2026

ZmanjΕ‘evanje tveganj

  • Če se ΕΎe odločiΕ‘ za uporabo, uΕΎivaj benzodiazepine v majhnih dozah in jihΒ nikoli ne meΕ‘aj z ostalimi depresorskimi drogamiΒ (alkohol, opiati, GHB/GBL …), saj obstaja velika nevarnost predoziranja.
  • Ne uporabljaj jih pogosto, sajΒ povzročajo močno zasvojenost.
  • Na uporabo se predhodno pripravi in siΒ doze pripravi vnaprej, saj se pri benzodiazepinih pogosto pojavita laΕΎen občutek treznosti in kompulzivno redoziranje.
  • Nikoli ne vozi nikogar ali ničesarΒ pod vplivom benzodiazepinov.
  • Svojih teΕΎav ne skuΕ‘aj reΕ‘evati na lastno pest z benzodiazepini s črnega trga, raje obiőči zdravnika ali pa se oglasi v DrogArtovi ali kateri drugi svetovalnici.
  • Ob morebitnem nastanku zasvojenostiΒ poiőči kvalitetno strokovno pomoč (osebni zdravnik, psihiater, DrogArt, Stigma, druge nevladne organizacije …)
  • Po partiju seΒ ne Β»spuőčaj« z benzodiazepini. MeΕ‘anje s katerimi koli substancami je vedno nevarno početje. Svojemu telesu raje privoőči kvaliteten obrok in kakovosten spanec, ki bo slej ko prej nastopil sam.
  • Pri benzodiazepinih, kupljenih na črnem trgu, je Ε‘eΒ posebej pomembno laboratorijsko testiranje zaradi moΕΎnosti ponaredkovΒ oziroma laΕΎnih tablet. Četudi na tableti piΕ‘e npr. Β»XanaxΒ«, to ni nikakrΕ‘no zagotovilo, da tableta zares vsebuje alprazolam, ampak lahko vsebuje kateri drug, močnejΕ‘i benzodiazepin ali pa čisto nekaj drugega.
  • Če oseba, ki je zauΕΎila benzodiazepin, izgubi zavest, jo poloΕΎi v poloΕΎaj za nezavestne in ne odlaΕ‘aj s klicem na 112.

The post Tabletka z visoko vsebnostjo etizolama v Mariboru appeared first on DrogArt.

Neznan logotip (MDMA)

Aktivne snovi

MDMA (146 mg)

Sintezni produkt MDMA (21 mg)

Dodaten opis

Pri več kot 1,5 mg MDMA na kg telesne teže se hitreje pojavijo neželeni učinki, kot so zategovanje čeljusti, miőični krči, panična reakcija in epileptični napad. V naslednjih dneh se po zaužitju večjih odmerkov MDMA lahko pojavi povečana depresija, pomanjkanje koncentracije, motnje spanja, izguba apetita in občutek močne brezvoljnosti. Simptomi po nekaj dneh izzvenijo.

Stranski produkti sinteze, so običajno prisotni v vzorcih drog, a običajno gre za sledove oz. vsebnosti manjőe od 1mg. Točne identifikacije sinteznega produkta ni mogoče podati, a ker gre za nekoliko večjo vsebnost,  velja biti nekoliko bolj previden pri morebitni uporabi.

UpoΕ‘tevaj spodnje smernice zmanjΕ‘evanja tveganj!

Disclaimer: Količina MDMA v tableti je zgolj informativne narave in se lahko pri tabletah z istim logotipom in barvo bistveno razlikuje.

Datum testa

28.8.2026

ZmanjΕ‘evanje tveganj

  • Prilagodi odmerek glede na svojo teΕΎo in izkuΕ‘enost. Literatura navaja, da je odmerek MDMA 1─1,5 mg/kg telesne mase, kar za 60 kg teΕΎkega človeka znaΕ‘a 60─90 mg.
  • Bodi zelo pozoren, če MDMA uporabljaΕ‘ prvič, ali če ne veΕ‘, koliko čist MDMA imaΕ‘. Učinki so lahko zelo raznoliki in nekateri ljudje čutijo veliko bolj intenzivno negativne učinke (tako fizične kot psihične). Zmeraj začni z majhnimi dozami (npr. četrtinko ekstazija ali lahko dozo MDMA-ja v kristalih) in počakaj vsaj 2h.
  • Delaj redne premore med plesom.
  • Vsako uro spij do pol litra izotoničnega napitka, če pleΕ‘eΕ‘, drugače pa manj.
  • Ne meΕ‘aj različnih drog med seboj, ne meΕ‘aj z zdravili.
  • Ne jemlji različnih tablet v eni noči.
  • Poskrbi zaΒ ustrezno prehrano in dovolj spanca med tednom.
  • Delaj pavze med uΕΎivanjem MDMA-ja (2-3 mesece med eno uporabo in drugo).
  • Če opaziΕ‘ teΕΎave, ki bi bile lahko povezane z uporabo ekstazija, poiőči pomoč.

The post Neznan logotip (MDMA) appeared first on DrogArt.

Ekstazi tableta β€œTrump” z ΕΎivljenjsko ogroΕΎajočim odmerkom PMMA na Nizozemskem

Aktivne snovi

para-metoksimetamfetamin (PMMA)

Dodaten opis

Na Nizozemskem je bilo za dvobarvne tablete z logotipom β€œTrump”, ki so se prodajale kot ekstazi, izdano nacionalno rdeče opozorilo po identifikaciji omenjenih tablet. Gre za dvobarvne tablete, pri katerih je bilo ugotovljenih več različnih barvnih kombinacij. Na sprednji strani je upodobljen Donald Trump, na zadnji strani pa je zareza, nad katero je napis Β»NLΒ«, pod njo pa Β»TrumpΒ«.

PMMA ima podobno kot MDMA stimulativne in entaktogene učinke. Vendar lahko za razliko od MDMA že razmeroma nizki odmerki povzročijo hitro in izrazito zviőanje krvnega tlaka in telesne temperature, pa tudi močne miőične krče in srčne aritmije.

Pri oralni uporabi se učinki pojavijo počasneje kot pri MDMA in lahko nastopijo Ε‘ele 1–2 uri po zauΕΎitju. PMMA deluje tudi kotΒ zaviralec monoaminooksidaze (MAO), kar lahko povzroči potencialno nevarne interakcije s Ε‘tevilnimi drugimi psihoaktivnimi substancami, tudi zdravili.

Β 

Zakaj nenamerno zaužitje teh tablet pomeni večje tveganje?

  • Počasen nastop učinkov PMMA (1–2 uri)Β lahko povzroči, da oseba zauΕΎije dodaten odmerek, Ε‘e preden je začela čutiti učinke prvega. To lahko vodi vΒ ΕΎivljenjsko ogroΕΎajoče predoziranje.
  • Medsebojno delovanje PMMA z drugimi rekreativnimi drogami in nekaterimi zdravili predstavlja dodatno tveganje za zdravje.

Β 

V zadnjem času se pri nas in v Evropi pogosteje pojavljajo ekstazi tablekte, ki ne vsebujejo MDMA, temveč bolj nevarne snovi, kot sta PMMA in NEP. Zato velja biti pri njihovi uporabi, őe toliko bolj previden!

Datum testa

28.8.2028

ZmanjΕ‘evanje tveganj:

  • Če je mogoče,Β uporabite anonimno storitev testiranja drog. Videz praΕ‘ka ali tablete ne pove ničesar o njeni vsebini.
  • Ne zauΕΎijte dodatnega odmerka, če občutite nepričakovane učinke ali če učinkov sploh Ε‘e ne občutite. Nastop učinkov je lahko zakasnjen.
  • Ne ostanite sami, če se začnete počutiti slabo. Obrnite se na osebo, ki ji zaupate, in jo prosite, naj ostane z vami.
  • Če se po zauΕΎitju počutite slabo ali se pojavijo zgoraj omenjeni simptomi,Β pokličite nujno medicinsko pomoč (112).
  • Izogibajte se kombiniranju različnih psihoaktivnih snovi.
  • Ε e posebej v vročem vremenu:Β poskrbite, da zauΕΎijete dovolj tekočine, vendar ne pretiravajte. Priporočljivo je pribliΕΎnoΒ 0,3–0,5 litra brezalkoholne tekočine na uro.

The post Ekstazi tableta β€œTrump” z ΕΎivljenjsko ogroΕΎajočim odmerkom PMMA na Nizozemskem appeared first on DrogArt.

Meet Us in Accra: Language Diversity Conference 2026

By: Sir Amugi
6 September 2026 at 13:00

What happens when people who are working to promote, preserve, and develop different languages come together?

They share experiences, learn from one another, discuss the challenges they face, and look for practical ways to strengthen their communities.

That is what the Language Diversity Conference 2026 is about.

From 2 to 4 October 2026, Accra, Ghana, will host the inaugural Language Diversity Conference under the theme

β€œStrengthening Language Communities in the Open Knowledge Movement.”

The conference will bring together language activists, Wikimedia contributors, researchers, educators, community organizers, and others working to ensure that languages that are often underrepresented online have a place in the digital world.

Language is not only about communication. It carries our history, culture, traditions, and knowledge. Yet many Indigenous and minoritised languages still have limited representation on the internet. This makes it important for language communities to take an active role in documenting their languages and making knowledge available in them.

The conference will provide an opportunity to discuss some of these issues, including language documentation, digital accessibility, Wikimedia projects, artificial intelligence, speech-to-text technologies, language rights, partnerships, and community-led approaches to language development.

A community spotlight: Dagbani Wikimedians

The Dagbani Wikimedians User Group (DWUG) is one of the communities contributing to this conversation.

Over the years, Dagbani Wikimedians have worked to increase the presence of Dagbani and other Ghanaian languages in the digital space through Wikipedia, Wikidata, Wikimedia Commons, training programs, edit-a-thons, Wiki Hubs, and other community activities.

The journey has also involved supporting the growth of other language communities. These experiences have shown the importance of local people taking ownership of the development and documentation of their languages.

At the Language Diversity Conference, members of the Dagbani Wikimedians community will share some of these experiences.

Among the sessions is β€œLanguage Rights Online: Preserving Dagbani Heritage,” which will look at the development of the Dagbani Wikimedia community, its work in the Wikipedia Incubator, community mobilization, Wiki Hubs, and the use of tools such as Mozilla Common Voice and Spell4Wiki. The Dagbanli Dictionary: Building a Sovereign Digital Language Infrastructure for an African Language session will also highlight efforts to build an independent digital foundation for Dagbanli by making the language’s words, meanings, and linguistic knowledge more accessible in the digital space.Β 

There will also be a session on Sopala, an offline learning initiative designed to support Dagbani education in communities where internet access can be a challenge.

For DWUG, this conference is, therefore, an opportunity to share what we have learned, listen to other language communities, and explore ways of working together.

Come and meet the people working to give more languages a stronger presence in the digital world.

A different way to find common ground

By: Lodewijk
6 September 2026 at 11:00

An adaptation of Polis for the Wikimedia ecosystem, currently a live prototype. This post continues after β€˜We need to innovate with Wikimedia decision-makingβ€˜. This prototype is part of the Next25 initiative attempt to arrive at more constructive attitude towards decision making.

We have a lot of decisions to make in the next year in our movement. As the number of visitors to Wikipedia is dropping, the internet around us is changing rapidly. This means that as a movement, we will have some hard decisions to make where we go. As WMF Executive Director Bernadette Meehan summarized: we need to evolve, but in ways that strengthen our mission.Β 

As I laid out in my last post, we need better processes to make decisions than the Request for Comments. This is true for deciding on our outdated policies in our many communities (how do we welcome new contributors, how do we handle AI, what is the bureaucracy that we can handle as a less-active community?) and on directions for our movement (what should be our priorities? Are there opportunities for new projects? What technology should we develop, and what should it look like?).

Today, I would like to introduce one prototype that could play a role in a more constructive approach to making decisions.Β 

Often, our current mechanisms focus on a single, large-form proposal, bringing out our critical thinking. There are many ways to improve our decision making, and I would like to see this as a part of a larger effort to experiment with that. Don’t assume that a single process can serve is in every decision, but rather pick the tool that fits the task.Β 

In my last post, I outlined some recommendations for how we could improve our processes. None of this is hypothetical. The civic-tech world has spent the last decade building and testing exactly these ideas β€” most famously in Taiwan, where the vTaiwan process used a tool called Polis to find unexpected common ground on regulating Uber.

How it works

This brings me to the prototype we have been working on: Proto, an adaptation of Polis for the Wikimedia ecosystem, running on Toolforge with your regular Wikimedia login. Polis (for example: pol.is) is a well-known innovation that has been used in many citizen engagements, including vTaiwan. We have built on top of it to make it fit a wiki’s needs better: to be more encouraging about editing the statements, and to add argument mapping.

The process consists of three phases:Β 

  • Respond to atomic statements and find the statements that the community already agrees on, and the ones that divide.
  • Collect relevant arguments for the most important statements.Β 
  • Collect an informed opinion mapping by showing the most important statements with their selected arguments.
The workflow behind Proto

Separate the phases

A system like Proto can be used to collect valuable input on a complex topic, before solidifying a draft. The phase separation gives ideation, refinement, and preference measurement each their own home. With the outcomes of this process, you could organize a more informed voting process on-wiki.

Make participation accessible (atomic statements)

The core mechanic is deliberately simple. A conversation (for example, a new policy dealing with blocking temporary accounts) starts with a question and a set of short, atomic statements β€” one claim each (β€œA temporary account should be warned at least once before being blocked for vandalism”).Β 

The basic principle behind Proto is atomic statements that participants can agree or disagree with.
Because Wikimedia, you can also suggest a different wording for the same statement.

Participants respond agree, disagree, or pass on each statement, and can submit statements of their own: to improve the phrasing of existing statements, or to fill a gap. That’s the whole interaction: no threads, no replies, no walls of text to catch up on. Joining a conversation on day twelve is exactly as easy as joining on day one; if you return later, you simply weigh in on whatever new statements have appeared since. Rapid responding make it easier to participate.Β 

Notably, the same design serves both ends of the scale: a small group gets an honest reading of the people it could never get to write talk page comments, while a large wiki gets a way to digest thousands of voices that no closer could. And the process is composable β€” an organizer can stop after the opinion mapping and already walk away with something valuable.

(A practical challenge will be how to connect the process to the wiki – notifications and talk page messages are likely the best bet.)

Decouple opinions from identity

Behind the scenes, the responses build an opinion map. Statistical clustering reveals the groups of participants who tend to respond alike β€” and, more importantly, the statements that are supported across those groups. Instead of amplifying the sharpest disagreement the way a threaded discussion does, the system is designed to surface hidden consensus.Β 

Your response is private while the conversation runs; opinions become visible as clusters, not as named individuals to follow or oppose. A faction that shows up to swing the outcome doesn’t silently shift a headcount β€” it appears on the map as exactly what it is. Private responses remove herding and anchoring β€” and let people disagree with the regulars without it becoming personal.Β 

Collect the arguments

In the argument phase, participants can add arguments to both sides of the statement. These arguments are then displayed in an (optional) later β€˜informed vote’.

Where Proto goes beyond standard Polis is in what happens after the opinion mapping. The organizer can curate a small set of the most informative statements β€” the points of strong consensus and the genuine fault lines β€” and open an argument layer: participants write and rank short pro and con arguments for each featured statement. No threading here either; just the community’s best reasoning on both sides, sorted by usefulness.

And as an optional final phase, an informed opinion poll: a fresh round on just the featured statements, with the strongest arguments displayed alongside. Comparing the before and after even lets us see whether exposure to arguments actually changed minds β€” something our current processes are incapable of measuring. Rather than just focusing on whether we like the proposal, consider also what others might think of as relevant arguments.Β 

One thing Proto deliberately is not: a vote. While everything may feel like voting, it is much more about mapping where the agreement and disagreement are, and collecting arguments in an equitable manner. From there, we have a solid foundation to draft policies from, and to figure out how to compromise between the views on the table. It may also expose where more conversation is needed, and where we already agree and only a vocal minority pushes back. The report from the tool is meant to inform the final step of the policy process on-wiki β€” because at that point, a Request for Comments or an opinion poll may actually make sense.

An invitation

This is one way of doing consent-building differently, but the underlying principles stay the same. I would invite you to try it, and to think about it from a constructive perspective: how would you improve it? Let us know, on metawiki.

The prototype is live at proto.wiki , and what it needs now is reality: some groups willing to run a real consultation on a real question. If your wiki, WikiProject, or group has a policy discussion that has been stuck for years β€” and whose hasn’t? β€” I would love to talk.

Twenty-five years ago we built an encyclopedia. I refuse to believe that the way we made decisions in 2004 is the best we can do in 2026. The editors who join us next year deserve rules they can actually read, trust β€” and change.


From Armenia to Tunisia: The Story of Wikipedia Camp Tunisia 2026

By: TOUMOU
6 September 2026 at 07:00
Wikipedia camp Tunisia Day 1
Wikipedia camp Tunisia Day 1

From 23 to 27 August 2026, Wikimedia Tunisia User Group, in partnership with the Association of Culture and Science in Metlaoui, organized Wikipedia Camp Tunisia 2026 at the Camping and Holiday Center in Bkalta, Tunisia.

The camp was the realization of a dream that had been growing within the Tunisian Wikimedia community for several years. Its main inspiration came from the successful WikiCamp Armenia, a pioneering project that brought Wikimedia contributors together through learning, collaboration, and community-building activities. The Armenian experience demonstrated how a camp could create a strong environment for both newcomers and experienced Wikimedians to learn from one another and contribute together.

Inspired by this model, Wikimedia Tunisia began developing the idea of creating a similar experience in Tunisia.

From workshops to the camp

The camp was developed in cooperation with the Scouts of Tunisia. Before the camp, a series of workshops was organized for young participants from different Scout groups. These workshops introduced participants to Wikipedia and other Wikimedia projects and encouraged them to discover how they could contribute to free knowledge.

Following these activities, participants who showed the strongest interest and engagement were selected to take part in Wikipedia Camp Tunisia 2026.

This approach allowed the camp to bring together young people who had already gained their first experience with Wikimedia projects while creating an environment where they could continue learning and improving their skills.

Five days dedicated to free knowledge

Throughout the five-day camp, participants took part in a wide range of activities focused on Wikimedia projects.

Editing sessions were organized on Wikipedia, Wikidata, and Wikimedia Commons, allowing participants to put their newly acquired knowledge into practice. They worked on improving articles, adding and enriching structured data, uploading and documenting media, and discovering the links between the different Wikimedia projects.

One of the most important outcomes was the creation of new articles on Arabic Wikipedia about Tunisia, with a particular focus on sports-related topics. Participants also worked on Wikidata, creating new items and improving information about books and authors using data from the National Library of Tunisia database.

These activities showed participants how different Wikimedia projects can work together: information researched for Wikipedia can be structured through Wikidata, while institutional sources such as library databases can help improve the quality and coverage of Wikimedia’s structured knowledge.

Group photo Wikipedia camp Tunisia
Group photo Wikipedia camp Tunisia

Learning from Wikimedia Libya

The participation of two representatives from Wikimedia Libya was one of the highlights of the camp. Their presence created an opportunity for exchange between Wikimedia communities in the region and added valuable experience to the program.

They organized a presentation about Wikidata, Wikidata items, and the role of Wikidata in relation to Wikipedia. The session helped participants understand that Wikidata is not simply a database, but an important part of the Wikimedia ecosystem that supports and connects information across different Wikimedia projects.

A second presentation focused on user rights on Arabic Wikipedia. The Libyan Wikimedians explained the different rights available to contributors, their importance, and how participants could develop their editing experience and become more involved in the community.

The session also encouraged participants to take the next step in their Wikimedia journey by requesting appropriate user rights on Arabic Wikipedia. By the end of the camp, several participants had already submitted requests for user rights, demonstrating the practical impact of the training and the motivation created during the event.

presentation About user rights on Arabic Wikipedia
presentation About user rights on Arabic Wikipedia

Building the future of the Wikimedia movement

Wikipedia Camp Tunisia 2026 was more than a five-day event. It was an experiment in community development and a step toward creating a new generation of Tunisian Wikimedians.

The journey that began with inspiration from Armenia and continued through workshops with the Scouts of Tunisia has now resulted in a Tunisian WikiCamp of its own. The experience demonstrated the power of bringing young contributors together, giving them the tools to contribute, and connecting them with experienced members of the Wikimedia movement.

For Wikimedia Tunisia, the camp represents not an endpoint, but the beginning of a longer journey. The participants now have the opportunity to continue contributing to Wikipedia, Wikidata, Wikimedia Commons, and other Wikimedia projectsβ€”and to share what they have learned with others.

From Armenia to Tunisia, the idea of WikiCamp has travelled across communities, adapted to a new context, and created new opportunities for free knowledge and collaboration in Tunisia.

Trails of Eastern Flavors and Culture: The Thrill of Cross-Project Contributions at the WikiRempah Meetup

5 September 2026 at 16:00

Meetups (Kopdar) are always highly anticipated moments for Wikimedia volunteers. Aside from being a space to connect, meetups often spark extraordinary collaborations. Not too long ago, I had the opportunity to participate in a very unique meetup event titled WikiRempah in Bandung, West Java, Indonesia on Sunday, August 9, 2026.

True to its name, this event highlighted the richness of spices and culture, but with an interesting cross-cultural twist: we documented the culinary and cultural heritage of Eastern Indonesia, yet channeled it into Sundanese-language Wikimedia projects and other global platforms. This activity was not just about editing; it was a contribution marathon involving four Wikimedia projects simultaneously.

Crafting Recipes and Visualizing Flavors

The first session, which was highly memorable for me, was culinary documentation. Eastern Indonesian cuisine, famous for its rich herbs and spices, was the main star. We went to an authentic Eastern Indonesian restaurant, photographed, and documented these signature dishes, then uploaded the best visuals to the Wikimedia Commons repository so they could be freely used by anyone. One example I uploaded was a photo of a Ayam Woku Khas Sulawesi.

The current image has no alternative text. The file name is: Ayam_Woku_khas_Sulawesi.jpeg

Pijri Paijar, Ayam Woku khas Sulawesi, Wikimedia Commons, CC BY-SA 4.0

We then moved on to the Sundanese Wikibooks project, which is currently still in the incubator. There, we worked together to write down these authentic Eastern recipes in Sundanese. Translating the spice measurements, cooking methods, and ingredient names into our mother tongue offered a unique sensation, as if we were serving Eastern Indonesian flavors right in the kitchens of the Sundanese people. On that occasion, I tried writing down the recipe for grilled skipjack tuna (ikan cakalang bakar).

Bridging Cultural Heritage to the Sundanese Wikipedia

After being satisfied with crafting recipes, the meetup’s focus shifted from the dining table to historical sites. Eastern Indonesia has an abundance of cultural heritage sites whose histories are vital to the civilization of the archipelago. Unfortunately, information regarding these sites is still very minimal, especially in regional languages.

This is where we contributed through the Sundanese Wikipedia. We wrote and expanded new articles discussing cultural heritage in the eastern part of Indonesia. Writing about geographically distant topics in Sundanese is a tangible manifestation of Wikimedia’s mission: freeing knowledge for everyone, in the language closest to them. I wrote several titles, including BΓ©ntΓ©ng Middelburg (Middelburg Fort), BΓ©ntΓ©ng Amsterdam (Amsterdam Fort), and BΓ©ntΓ©ng Barneveld (Barneveld Fort).

Bringing the Dictionary to Life with the Sounds of Spices

The highlight of this WikiRempah Meetup, and in my opinion the most interactive activity, was the voice recording session. Since the overarching theme was β€œSpices,” we compiled a long list of vocabulary related to spices and typical Indonesian cooking ingredients.

Using the Lingua Libre software, we took turns recording the pronunciation of this spice vocabulary one by one. The atmosphere became very lively and fun as we had to ensure our Sundanese articulation sounded clear and precise. After the recording process was complete, the audio files were not just left there. We immediately embedded them into the existing entries in the Sundanese Wiktionary.

Now, the digital dictionary does not only present text but also β€œspeaks.” Anyone looking for entries about spices in the Sundanese Wiktionary can instantly hear how the word is pronounced orally by a native speaker. Some of the recorded words included lauk (fish), sagu (sago), and sampeu (cassava).

A Comprehensive Contribution Experience

The current image has no alternative text. The file name is: Kopdar_WikiRempah_Bandung-rotated.jpg

Oceanmuse, Kopdar WikiRempah Bandung, Wikimedia Commons, CC-BY 4.0

Participating in the WikiRempah Meetup gave me a new perspective on how the Wikimedia ecosystem works. From a culinary dish and a historical story, we could unpack them into photos on Wikimedia Commons, recipes on Wikibooks, encyclopedic articles on Wikipedia, to audio pronunciation recordings on Wiktionary and Lingua Libre.

This activity proves that contributing to the Wikimedia movement is never boring. There is always room for creativity, cross-cultural collaboration, and new ways to preserve the richness of the archipelago in the digital realm. Endless thanks to the organizing committee for this incredibly valuable and memorable opportunity. My hope is that a multi-contribution meetup model like WikiRempah can inspire other communities to keep exploring!

Wikidata Community Summit @ COSCUP 2026

By: Hunghoro
5 September 2026 at 13:00

2026. Aug. 9th (Saturday)

Under the storm of Typhoon Dolphin, COSCUP 2026 commenced, bringing partners from around the world together to celebrate the value of open technology. The Conference for Open Source Coders, Users & Promoters (COSCUP) is one of the biggest open source events in Taiwan. This year, one topic dominated the community: Artificial Intelligence, or Agent in the concurrent stage.

This year, our joint track of Wikidata and OpenStreetMap not only showcased how our respective platforms contribute to the open community, but also explored how open data and open technology can support the rapidly developing world of artificial intelligence.

One highlight was our presentation with our partner from Wikimedia Deutschland, focusing on the Wikidata MCP Server and the Wikidata Embedding Project.

Artificial Intelligence has come to the stage like a storm and quickly become one of the most discussed technologies across every industry. No matter which side of the fence you are on, one thing is the same: we want to know more about it and how it may affect our lives.

However, much of the discussion throughout the internet is filled with empty buzzwords and misinformation from companies with various interests. The problem is not necessarily malicious intent but the fact that people, including the sources they are quoting, simply do not have enough access to the information they need to understand the technology they are discussing.

Just like previous technology β€˜booms’, such as cryptocurrency and NFTs, Artificial Intelligence, and now the so-called Agents, is a market phenomenon of attention crowding around something new and potentially profitable, and thus money floods in. However, unlike its predecessors, AI is β€˜real’ in a sense that it has the potential to reshape, and possibly damage, many systems of trust that we humans have built through centuries of mutual understanding and respect for ourselves and others.

AI Agents are not inherently disruptive or destructive; it is how we use them that is causing all the trouble. If we want the technology to become a tool for a better future, we first need to understand how the tool works.

The Problem with LLMs

A Large Language Model, or LLM, at its simplest, is an algorithm that predicts words based on the material it has been provided. Its strength comes from recognizing patterns in a given language and mimicking the β€˜natural language’ according to the probability of words appearing together. But this also reveals its fundamental limitation.

All it captures is the distribution of words appearing in languages, not necessarily the why and how behind them. It can produce an answer that looks and sounds correct without having any understanding of whether the information corresponds to reality or is actually true. Hallucinations, gibberish, and blatantly false answers are ultimately extensions of this limitation.

The man in the box never understands Chinese; it just appears to.

This does not mean we need the LLM to become a genie in a lamp that knows everything. The bottleneck is never the generation of more knowledge. It is discovering, retrieving, and acting upon the right information. To achieve this, there is no reason to reinvent the wheel of synthesizing the generation of natural knowledge; instead, what we need is to have the preexisting systems align properly.

And this is where Wikidata comes into play.

A Beacon for AI Agents

Wikidata is a Knowledge Graph: a machine-readable database where information is connected through controlled and structured relationships. It is designed to work across languages, interfaces, databases, services, and users β€” human and machine.

More importantly, its information is human-pruned and anchored in reality. It can provide explicit and exhaustive relationships, traceable sources, and continuously updated information. This makes Wikidata particularly valuable to LLMs. Rather than leaving an AI Agent to decide whether a string of text is factual or fictional, it can reference Wikidata for additional information.

LLMs excel at semantics, but that strength is inherently imprecise. A Knowledge Graph provides the controlled structure underneath it. Instead of asking an Agent to know everything, we can let it discover relevant information through the web of knowledge and crawl through information more efficiently than we humans could.

This is where the Wikidata Embedding Project aims to establish its influence.

The project prepares a vector database from a Wikidata dump and an MCP Server that allows AI Agents to access knowledge residing in it through a standardized and LLM-friendly interface. It combines the fuzzy discovery capabilities of AI with the structured and human-pruned knowledge of Wikidata.

The advantage is not only accuracy. LLMs are trained primarily on mainstream data, with limited information from less represented communities. Wikidata can provide access to knowledge beyond an Agent’s original training, including information from smaller communities that would otherwise be extremely difficult for an AI to discover and access.

By equipping an Agent with Wikidata, it is like a sailor on a misty sea finally seeing the light of a beacon. The Agent can still navigate on its own, but it now has a solid reference point so it won’t get lost as long as it can see the light.

The Current and the Future

Wikidata is still one of the youngest WikiProjects. Though volunteers around the globe are working diligently, there are still gaps in the Knowledge Graph. If we want Wikidata to become the backbone to power the next generation of Artificial Intelligence, we still have a long journey ahead of us.

The emergence of AI Agents brings as many opportunities as it does challenges. For Wikidata Taiwan, our work is becoming increasingly relevant to making sure Taiwan’s audience and data won’t get left behind in the ever-changing landscape of Artificial Intelligence.

The challenge is not simply to make AI more powerful. It is to make sure that the knowledge it discovers is reliable, traceable, and based on reality, and most importantly, fair.

My First Wikimania Experience in Paris

By: Piyanist
5 September 2026 at 11:00

I’m Berkay, also User:Piyanist on Wikimedia projects. I have been contributing to Wikimedia projects since 2020. I’m member of the Wikimedians of Turkic Languages User Group, CEE Youth Group, and WikiPortraits.

My Wikimedia activities mainly focus on edits and corrections to Wikipedia pages related to my area of interest, adding cited informations, photography, Commons, youth-related projects, and topics related to music and culture.

In this report, I will share my experiences, observations, and highlights from Wikimania 2026. I will also describe the events I enjoyed the most during the conference.

Wikimania 2026 in Paris was my first Wikimania. I had attended other Wikimedia events before, but this was my first time experiencing Wikimania itself. During the conference, I joined sessions related to my interests, volunteered as a photographer at some activities, met new people and spent time again with friends I already knew from the CEE community. Some of the sessions also gave me new ideas about how I want to contribute in the future, especially through photography, youth activities and music.

The time between sessions was also an important part of the conference for me. During coffee breaks, meals and while moving around the venue, I met Wikimedians from many different countries and had conversations that I probably would not have had through scheduled sessions alone.

And of course there was also no shortage of small things to bring home. I collected stickers, pens, postcards, badges, notepads and other materials from different Wikimedia communities and projects, along with considerably less permanent souvenirs in the form of snacks and food. I kept some of these for myself, but I also plan to use many of the printed materials, stickers, and other items as materials for events we organize in the future. They can help me create a more visual stand where people can see different Wikimedia communities, projects and events instead of only hearing about them.

During Wikimania, I moved between many different sessions and activities. Sometimes I stayed until the end of a session, while at other times I left to see something else. Below are the sessions that I enjoyed the most or that gave me ideas about things I may want to work on in the future.

AI, Wikidata and the opening ceremony

One of the first sessions I attended was β€œIf AI becomes the first web interface, how can the Wikimedia movement adapt? Communities’ levers.”

The discussion focused on how different Wikipedia communities are dealing with generative AI. Matthias Schindler shared an example about a tool made to check ISBN numbers in Wikipedia references. The tool helped identify articles containing fake references generated by AI. Some of the text looked normal, but the books used as sources did not actually exist.

The speakers also discussed how language communities can experience AI differently. One point I found interesting was that language models can flatten different perspectives instead of understanding the context of each language and community. The discussion was not simply about accepting or rejecting AI. It was also about understanding where these tools fail and keeping human review in the process.

If AI becomes the first web interface – Wikimania 2026
Piyanist | CC BY-SA 4.0

After that, I attended Philippe Saadé’s β€œWikidata MCP: Grounded SPARQL Query Generation.”

He demonstrated how a language model can make a basic mistake while still producing something that looks technically convincing. In one example, a model trying to find female physicists who had won the Nobel Prize in Physics initially selected the QID of a dog breed instead of the Nobel Prize.

The workflow presented in the session separated discovery, structure inspection, SPARQL generation and validation. This made it easier to see where a mistake happened instead of only looking at the final answer.

Wikidata MCP Grounded SPARQL Query Generation – Wikimania 2026
Piyanist | CC BY-SA 4.0

Later, I attended the opening ceremony, which also celebrated Wikipedia’s 25th anniversary.

UNESCO’s Mariya Gabriel spoke about knowledge as a public good and about the responsibility of AI systems that depend on open knowledge. Wikimedia Foundation CEO Bernadette Meehan talked about the people behind Wikimedia. She reminded us that readers normally only see the finished article. They do not see all the discussions, photos, translations, corrections and other work that happens behind it.

Jimmy Wales talked about assuming good faith and making newcomers feel welcome. He also spoke about freedom, equity and the importance of keeping Wikipedia reliable. He also discussed misinformation bubbles and unequal access to information. One point that stayed with me was his description of Wikipedia’s role in the age of AI: its value is not simply providing the fastest answer, but providing information that can be checked and verified.

He also described Wikipedia as something built gradually through edits, lines of code and discussions rather than around one person.

The ceremony continued with the Wikimedian of the Year awards. I photographed all of the award recipients and later uploaded my photographs to Wikimedia Commons. You can access these photos from the following Commons category

Wikimedians of the Year 2026 Wikimania 2026
Piyanist | CC BY-SA 4.0

A morning inside the Sainte-Geneviève Library

The next morning, I joined an organised visit to the Bibliothèque Sainte-Geneviève.

We met at the Wikimania venue early in the morning, received public transport tickets and travelled to the library together. Before entering, I walked around the PanthΓ©on area with other photographers and took street and architectural photographs.

Fontaine des Bois, Parcs et Jardins. 2026 Wikimania Paris Day 2. En route to Bibliotheque Sainte Genevieve

Fontaine des Bois, Parcs et Jardins. 2026 Wikimania Paris Day 2. En route to Bibliotheque Sainte Genevieve
Vysotsky | CC-BY-SA-4.0
Bibliothèque Sainte-Geneviève by Piyanist - Wikimania 2026 Tour Event
Bibliothèque Sainte-Geneviève, Paris
Piyanist | CC BY-SA 4.0

Inside the library, we visited the main reading room as well as storage and archive areas normally closed to the public. In the reading room, Pauline RiviΓ¨re showed us the old book lift: a big wheel and rope system that once carried books up from the storage in a small box. She told us that the architect did not want staff walking around the reading room, because he found it not aesthetic, so the librarians asked the minister for extra stairs to make their work easier. In the storage areas, the books are shelved in little β€œwaves”, so that small books do not get lost between two big ones. The old card catalogues are also still in use, because some records are not in the online catalogue. One massive old door even hides its lock; only a small trick of the architect opens it.

In the lower levels, we saw large collections of books, archive materials and older reproduction formats. Pauline showed us the microfilm archive: before digitisation, books and newspapers were copied on small reels, and readers can still use them today. She also explained how physical collections are digitised and made available through the Genovefa digital library. The library holds around two million documents in three buildings. When a reader requests a book, the request is printed in the stacks and the book is sent up with a small lift, in about twenty minutes. The rolling shelves stay only on the lowest level, because anywhere else their weight would harm the foundations of the building.

We also saw historical documents, rare books and materials connected to preservation and digitisation. For me, the most impressive piece was a planetary clock made in southern Germany at the end of the 16th century – one of the very few surviving in the world, and still working, showing the movements of the planets. The photos I took of the library and the archive area during these events are in this Commons category.

Microfilm boxes of the French news magazine L'Express (1996–2014) in the microfilm storage of the BibliothΓ¨que Sainte-GeneviΓ¨ve, Paris, during a Wikimania 2026 guided visit.

Pauline Rivière explained how physical collections are digitised and made available through the Genovefa digital library. We also saw historical documents, rare books and materials connected to preservation and digitisation.

Photo: Microfilm boxes of the French news magazine L’Express (1996–2014) in the microfilm storage of the BibliothΓ¨que Sainte-GeneviΓ¨ve, Paris, during a Wikimania 2026 guided visit. Piyanist | CC BY-SA 4.0

Photography and WikiPortraits

β€œBehind the Lens: WikiPortraits on a Global Scale”, presented by Kevin Payravi, Jennifer 8. Lee and other WikiPortraits contributors.

The problem behind WikiPortraits is simple: many Wikipedia biographies still have missing or poor photographs, but taking a good photograph of a notable person often requires being at the right event and having media access. WikiPortraits helps photographers attend and cover these kinds of events, so that useful photographs can later be uploaded under free licences and used across Wikimedia projects.

WikiPortraits now has more than 200 photographers from over 40 countries, and together they have uploaded more than 95,000 photographs. The group covered over 180 events in 2025, and another 169 events had already been covered in 2026 by the time Wikimania took place. These photographs are also widely used, receiving around 400 to 500 million views every month.

One part of the model was especially relevant to me. WikiPortraits encourages photographers to begin with events in their own area. As they build experience and a portfolio of Wikimedia work, the group can help with accreditation for larger international events. For some major coverage, there can also be support with travel or accommodation.

This connected closely with my own photography work. In TΓΌrkiye, I already photograph musicians, artists, concerts and other events. Until now, these photographs were usually produced for artists, organisers or their own media use, and I had not really connected this part of my photography with Wikimedia Commons.

The session made that connection much clearer for me. When licensing and other conditions are suitable, I hope to make some of my future artist and concert photography useful on Commons as well.

For me, this is a practical way to connect photography work I already do with my Wikimedia activities instead of treating them as completely separate things.

And after that session, I joined WikiPortraits. For me, this was one of the sessions I enjoyed most at Wikimania 2026.

Indigenous knowledge and responsible sharing

β€œRespecting Indigenous Knowledge in Open Spaces.”

The panel discussed Indigenous Cultural and Intellectual Property and Indigenous Data Sovereignty. The speakers explained why open knowledge is not only about copyright or whether something can technically be uploaded.

Cultural context, consent and the communities connected to that knowledge also matter.

The Wikimedia Australia team presented a guide that helps contributors think about culturally sensitive material and whether a source is culturally appropriate. One of the practical ideas was to ask not only whether material can be uploaded, but whether uploading it is the responsible choice.

Thérèse Ottawa also spoke about the Atikamekw-language Wikipedia project in Canada and its connection with education and language revitalisation. The project included work with younger people and schools, helping the language remain active in a digital environment.

This session connected with what I had seen at the library. Digitising cultural material can help more people access it, but we also need to think about how that material is shared.

Respecting Indigenous Knowledge in Open Spaces
Piyanist | CC-BY-SA-4.0

CEE Youth and ideas for future activities

Youth participation was another part of my Wikimania programme.

β€œBring in the Youth! Discussing Ways to Support Youth on a Regional Level.” Participants shared examples from different regions and talked about mentoring, universities, informal activities and ways to support young contributors who may not have an active local community around them.

One example I liked was the use of simple social activities such as board game meetings. These meetings helped young Wikimedians get to know each other, and some later developed projects together. The discussion also looked at the role of regional groups in connecting young people who may otherwise feel isolated in their local Wikimedia community.

Later, I joined the CEE Youth session and workshop.

The CEE Youth social media working group currently has eight volunteers from different countries. The team meets once a month, talks through Telegram and works with a shared content calendar. The speakers were also quite open about volunteer burnout. Sometimes volunteers simply need to take a break from their responsibilities for some time.

During the workshop, we talked about who we actually want to reach through social media and what could make the content more interesting. We also discussed how the work could be divided between more people instead of relying too much on one person.

CEE Youth Group workshop at Wikimania 2026
CEE Youth Group workshop
KGruszczyk-CEEhub | CC-BY-4.0

Meeting WikiOrchestra

At the end of the day, I went to the WikiOrchestra rehearsal and met the conductor, Lukas Mezger

I told him that I am playing the piano and composing musics. I also mentioned that I had composed the opening music for the Wikimedia CEE Meeting 2024 in Istanbul.

There is also a separate diff post on this topic here:

We talked with Lukas about music at Wikimedia events and possible future collaborations. I had discovered the information about the closing choir late, but after our conversation Lukas invited me to join.

Manuscripts and the closing ceremony

On the final day, I attended β€œFrom Wikilontar to Wikisami: How Regional Partnerships Power Community-Led Growth in Bali”, presented by Carma Citrawati and Sakti Hendra Pramudya.

The session explained how work around Balinese manuscripts developed through local community participation and wider partnerships. The discussion was not only about scanning documents. It also focused on trust, local leadership and giving communities a role in decisions about their own cultural heritage.

For me, this connected naturally with both the Sainte-Geneviève Library visit and the Indigenous knowledge panel. All three showed different sides of digitisation and cultural heritage.

After the session, I studied the score for β€œOh, WikipΓ©dia!” and joined the WikiOrchestra and choir rehearsal. I spent hours working on the music, pronunciation, stage positions and performance.

Near the end of the closing ceremony, the WikiOrchestra and choir went on stage and we performed the song together.

Performing with the choir at the main amphitheater stage, became one of the most memorable parts of my first Wikimania.

Lukas and I also talked about future music projects. I also plan to continue composing original music for Wikimedia events when there is an opportunity. For me, music is not only something separate from Wikimedia activities.

Wikimania 2026 closing ceremony - WikiOrchestra
WikiOrchstra at Wikimania 2026
BugWarp | CC-BY-SA-4.0

After the ceremony, we joined the Wikimania 2026 group photo.

Wikimania Day 4 group photo with the Giant Wikipedia Puzzle
Wikimania Day 4 group photo with the Giant Wikipedia Puzzle
Simon Delahaye | CC-BY-SA-4.0

On the final day of Wikimania, there was another very literal example of how many small contributions can create something much larger. Wikimania participants worked together on a giant version of the Wikipedia Puzzle Globe outside the venue. When it was finished, the puzzle covered around 100 square metres and had 944 pieces. It was one of the largest jigsaw puzzles ever assembled in France. It later became the backdrop for the final group photo.

I also kept one of the puzzle pieces as a souvenir. Together with the badges, postcards, stickers and other things I collected during the week, it will probably become one of the more unusual pieces of my future student club stand.

The giant Wikipedia jigsaw puzzle at Wikimania 2026 in Paris, France
Manfred Werner (WMAT) | CC-BY-SA-4.0

Looking back at my first Wikimania

My first Wikimania included discussions about AI, photography, cultural heritage, youth activities and music.

But the experience was not limited to the sessions. Meeting Wikimedians from different countries during breaks, seeing the projects people brought with them and simply spending time with other contributors were also important parts of the week.

I returned from Paris with several ideas for what I want to do next, like connect more of my photography with Wikimedia Commons and WikiPortraits, become more active in CEE Youth and youth-related activities, and continue exploring ways to bring music into Wikimedia events.

I also want to continue contributing to the topics I already edit while trying these different forms of participation.

One useful part of Wikimania for me was seeing that some of the things I already do outside Wikimedia can also become Wikimedia contributions.

The week ended with the group photo, the giant Puzzle Globe (Of course, I took one puzzle piece with me.) and the memories and small objects I brought home from Paris. It was a memorable end to my first Wikimania.

Electing the CEE Hub Steering Committee: What we built and what we learned

5 September 2026 at 09:00

Introduction

On 2 September 2026, the new Wikimedia CEE Hub Steering Committee began its term, following several months of nominations, eligibility checks, community engagement and voting. Eight members were elected: four by individual community members and four by CEE affiliates, and two more were later co-opted.

But this election was also a first test of putting the CEE Hub’s governance model into practice. We had the rules; we still needed to build the process around them. That meant establishing an independent Electoral Commission, translating eligibility rules into practice, working with two different electorates, and building Fala, our own voting tool on Wikimedia Toolforge.

From rules to an election

The election had two constituencies: individual CEE community members and CEE affiliates, each electing four members. That made eligibility one of the first practical challenges. Community voters had to meet both Wikimedia activity requirements and demonstrate a connection to the CEE community, while affiliates had to designate representatives to vote on their behalf.

To oversee the process, an independent three-member Electoral Commission was established: Antanana as Chair, Bencemac and Gdarin. The Commission verified candidates and voter eligibility, interpreted the rules when unclear cases appeared, supervised the election, and ultimately certified the results. Hub staff supported the process and its technical implementation, but decisions about eligibility and the application of election rules remained with the Commission.

Thirteen candidates entered the nomination process, and after the eligibility review, twelve were confirmed for the final ballots: six Community candidates and six Affiliate candidates.

Building Fala

One of the biggest practical questions was how to run the vote itself. The election had two different electorates, different eligibility rules, preferential voting, and the possibility that one person could be eligible to vote in both categories.

Instead of adapting the election to the limits of an existing tool, we built Fala on Wikimedia Toolforge. Voters logged in with their Wikimedia accounts, eligibility could be checked through the system, and Community and Affiliate voting were handled separately. Voters could also return and update their ballot before the deadline.

Privacy was part of the design as well. Fala recorded who had voted and when a ballot was submitted or updated, but it did not keep the voter’s candidate selections or rankings linked to their identity.

Building Fala also strengthened the Hub’s ability to run its own governance processes independently. We were not limited to whatever an existing election platform happened to support; we could build the voting process around the rules and needs defined by the CEE Hub itself.

Voting and results

Voting was open from 6 to 20 July 2026. On each ballot, voters could rank as many or as few candidates as they wanted. The candidate ranked first received six points, the second five, and so on, while unranked candidates received no points.

Fifty-two community members voted in the Community election, while 15 CEE affiliates participated through their designated representatives. The Electoral Commission then certified the results, including theΒ  rules used to decide the winner when two or more candidates received the same number of votes in the Affiliate election.

The newly elected members of the CEE Hub Steering Committee.

The elected members were Shahen Araboghlian, Magda Barascu, Wojciech PΔ™dzich, MārtiΕ†Ε‘ BruΕ†enieks, Gorana Gomirac, Toni Sant, KlΓ‘ra JoklovΓ‘ and Vera Pelhan. They later co-opted Iglika Ivanova and Armen Mirzoyan, completing the ten-member Steering Committee.

The first meeting of the new Steering Committee took place on 2 September 2026, which marked the official start of its term. That is also when they selected the Chair: Toni Sant and Co-Chair: Vera Pelhan.

What we learned

The clearest lesson was how difficult it can be to turn governance rules into something a voting system can actually verify. Some criteria, such as account age, edit count and block history, could be checked directly from Wikimedia data. Others could not. For example, eligibility through membership in approved CEE mailing lists could not be verified by existing voting tools such as SecurePoll. That problem led directly to Fala: voters log in with their Wikimedia account, verify their email with a one-time code, and the address is checked against a whitelist built from the approved mailing lists. In other words, the election rules themselves shaped the architecture of the tool.

A different lesson came from affiliate participation. Some eligible affiliates never nominated a representative and therefore never voted. That showed us that simply giving affiliates a formal role in governance is not enough; they also need to see the election as part of their own responsibility and engage with the process early enough. After the election, the Electoral Commission followed up directly with non-participating affiliates to understand why they had dropped out. For future elections, earlier contact, clearer expectations and more active follow-up with affiliates will probably matter as much as the voting system itself.

What comes next

The election also made us think more broadly about how the CEE Hub puts its governance into practice. If the Hub is going to make more of its own decisions through formal community processes, it needs infrastructure that can support them. In some cases, the available tools were designed for different purposes; in others, there was simply no dedicated tool for the kind of internal decision-making the Hub needed.

For that reason, we decided to build Iskara, a voting tool for internal CEE Hub decision-making. The aim is to give the Hub greater independence in implementing its governance and a practical way to run its own votes when they are needed. Fala was our first experience of building such infrastructure around a real governance process; Iskara takes that experience further and applies it to the Hub’s internal decision-making more generally.

To learn more about the Steering Committee and internal governance of Wikimedia CEE Hub, check out the dedicated Meta page.

More than a campaign: the measurable impact of Celebrate Women 2026

5 September 2026 at 07:00
Collage of several Celebrate Women events held in March 2026: WikiGap Malaysia, WikiGap Sandakan, and the Assamese Wikisource Residential Workshop (Qhairy, CC BY 4.0 / Taufik, CC0 / JyotiPN, CC BY-SA 4.0 / ΰ€Έΰ₯ΰ€¬ΰ₯‹ΰ€§ ΰ€•ΰ₯ΰ€²ΰ€•ΰ€°ΰ₯ΰ€£ΰ₯€, CC BY-SA 4.0 via Wikimedia Commons)

Imagine a world in which every woman on the planet finds a representative and diverse sum of all human knowledge about women+* available on Wikipedia and on the internet. This is what the Celebrate Women campaign has been trying to accomplish since 2022.Β 

Every March, during Women’s History Month and in celebration of International Women’s Day (March 8th), the Wikimedia community gathers both online and in person to edit and improve the content available on Wikipedia and the other Wikimedia projects about women+. The editing happens as part of edit-a-thons, workshops, training sessions, and many other types of gatherings, which are spread around the world, with events on every continent.Β 

For 2026, we had one of the biggest campaigns so far. Considering all the gatherings globally, we had 112 events listed on the official page, which were divided into these sections:Β 

  • Global: 21 events
  • Africa: 20 events
  • Asia: 15 events
  • Europe: 38 events
  • Latin America: 12 events
  • Middle East: 1 event
  • North America: 3 events
  • Oceania: 2 events

These numbers clearly demonstrate the global impact of this campaign, as well as serve as a testimony to Wikimedia’s reach and potential for collaboration, even when the world seems to be divided. In 2026, our communities were able to not only come together, but also edit and collaborate towards that one single goal.

In the last few years, this purpose was also achieved. By checking the pages for the previous campaigns, we had 59 events in 2025, 100 in 2024, and 121 gatherings in 2022. However, when we also consider the Programs & Events Outreach Dashboard campaigns (see more about this topic below), we have another 144 events in 2022; 137 events in 2024; 171 events in 2025; and 155 events in 2026.Β 

For 2026, we were able to assemble some statistics. In terms of engagement, considering the P&E Outreach Dashboard and gatherings using Event Registration (a new CampaignEvents feature), we were able to identify that this year’s campaign assembled 207 organizers and 2,658 event participants, which can be divided into:

  • Beginner (less than 10 lifetime edits when participation begins): 1,388 users
  • Junior (>= 10, <100 edits): 435 users
  • Experienced (>= 100, <300 edits):Β  189 users
  • Very experienced (>= 300 edits): 647 users

When it comes to content, or knowledge being added or improved through events, we had 238,222 unreverted content edits and 2,499,773,577 bytes added. After a few months from the end of the campaign (March 31, 2026), we have a total of 32,388 undeleted content pages added.

Celebrate Women is also responsible for increasing the number of events held in the Wikimedia community. Considering Event Registration usage only, we can see in the next image a major increase from 2025 to the 2026 campaign, and a peak in February and March compared to last year.

Number of new events per month using the CampaignEvents features from 2025 to 2026 (Campaign Events – Dashboard, via Superset)

In March 2025, we had 152 events created using Event Registration, while in March 2026, we had 251 events. This is a 65% increase. However, for Celebrate Women, it’s also important to consider February, when a good portion of the events are created for organizers in preparation for the early March events, especially around International Women’s Day. In February 2025, 130 events were created, while in February 2026, 240 events were created. This is an 84.6% increase.

Wikimedia Foundation’s support in 2026

Social media campaign by the Wikimedia Foundation’s Comms team (public domain)

Celebrate Women is a community campaign and the result of volunteers and community members from all over the world. The Wikimedia Foundation (WMF) has been helping this campaign by providing support for communities and organizers.

In 2026, we started the support by renovating the Celebrate Women portal. We changed the campaign’s header, reorganized it, and even added a press toolkit tab to help organizers develop a media plan or get media coverage from local journalists and media outlets. This new page includes sections about how to explain Wikipedia and the gender gap, key Wikipedia and gender gap facts and figures, inspiring gender stories, and a list of campaigns and groups dedicated to decreasing the gender gap on the Wikimedia projects.Β 

We also added resources and guides for how to use the CampaignEvents features, Content Translation Suggestion feature, and Central Notice Banner Editor. Additionally, we collaborated with the WMF Comms team on social media posts, which were shared throughout March on the Foundation’s channels.

We also contacted major gender organizers and community members (via email and talk page) and discussed the idea of adding the Event Registration header to help us understand the campaign’s participation. With Event Registration, the campaign reached 617 registered participants. According to the Connection team, who developed this feature, Celebrate Women 2026 is currently the 3rd biggest event in terms of participation so far, only behind Wikipedia 25 Virtual Celebration, with 3,187 participants, and the Africa Wiki Challenge 2025 with 790 participants.

Celebrate Women portal using the Event Registration header

During the pre-campaign community consultations, we received a request to not only have an Event Registration way to track the campaign, but also an Outreach Dashboard campaign dedicated to 2026. We created the campaign here.

Celebrate Women 2026 campaign on the Programs & Events Outreach Dashboard

Later, we chose to understand the impact of previous Celebrate Women years using the Outreach Dashboard too. We created campaigns dedicated to 2022, 2023, 2024, and 2025 and decided to search and include all gender events organized during those years using the dashboard. Now, we can better understand the impact of the gender content gap efforts for the past 5 years.Β 

Finally, by using Event Registration, we were able to communicate with the 617 participants who registered. Once a week, we sent a reminder with the events that were taking place that week, divided into regions, and including day, time, place, and how to register. This established a recurring way to connect with community members and remind them about the campaign as a month-long commitment.

Wikimedia Foundation’s events

The Wikimedia Foundation also organized three events to support the community in organizing their Celebrate Women gatherings. We planned the campaign’s kick-off event and 2 pre-campaign trainings dedicated to Wikimedia organizers.

  • On February 25, we had the Content Translation training. This session aimed to empower organizers with the skills to use Content Translation, with a focus on the article suggestion feature.Β 
  • On March 5, we held the campaign’s Welcome Session, which covered the 2026 campaign, some of its events, and some of Wikimedia’s Gender Gap milestones presented by Chinmayee Mishra. Participants also learned a bit about Event Registration, Content Translation, and two tools that help organizers with small but powerful edits: Gender Bias Detector and Wikipedia Microtask Generator.Β 

On the page for the three events, all the resources are available: recordings, slides in different languages (English, Spanish, Portuguese, Arabic, French), and other useful information.

WikiWomen+ Summit 2026

In August 2026, the WikiWomen+ Summit 2026 took place during Wikimania’s pre-conference day. At this gathering, we were able to present about the Celebrate Women and the impact of this campaign in the context of Wikipedia’s content gender gap.Β 

Please find the recording for this presentation on YouTube. The slides are also available on Wikimedia Commons.

To understand more about the campaign or to stay tuned, check out the Celebrate Women portal on Meta-Wiki.

* The usage of the β€œ+” sign after the word β€œwomen” means we are including anyone who identifies as a woman in some way, that being a cis woman, transgender, non-binary, genderqueer, or any gender identity not listed here.

SheSaid 2026: The Global Story Continues, One Voice at a Time

By: Afek91
4 September 2026 at 20:42

The #SheSaid campaign 2026 officially launched on 1 September and will run until 31 December 2026. Now in its seventh edition, #SheSaid continues to grow as a global campaign, bringing together participants from around the world to add and improve women’s voices on Wikimedia projects.

As part of Wiki Loves Women, an initiative of Wiki in Africa, #SheSaid works to address the gender gap on Wikiquote by making women’s words, experiences and perspectives more visible and accessible.

This year is particularly special: Wiki Loves Women is celebrating 10 years of making women visible in the world’s largest open knowledge ecosystem. #SheSaid is part of this decade-long journey, building on the contributions of thousands of participants who have helped bring women’s voices into the Wikimedia movement and beyond.

More than numbers, SheSaid is a story of collaboration

Francautrices Poster at Wikimania 2026

Six editions. More than 42k contributions. 22 languages from EnglishΒ to Assamese. More than 25 communities from Indonesia to Italy, Ghana and beyond. .Β 

#SheSaid is more than just the numbers. It is a story of people coming together across languages, countries and cultures, united by one purpose: to make women’s voices more visible.

That spirit of collaboration has taken #SheSaid beyond Wikimedia. FrancAutrices, a card game created through a collaboration between Les sans pagEs, Wiki in Africa and French Wikiquote editors, brings the words of Francophone women writers to new audiences. The game features quotes from Wikiquote, alongside portraits of the authors by French illustrator Claire Gaudriot and a design by South African designer Theresa Shaw.

FrancAutrices is one example of how a contribution to an open platform can become the starting point for something new, connecting communities, cultures and audiences around women’s voices.

A community favourite

Florence Devouard at Wikimania 2026

The Wikimedia community has celebrated the story of # SheSaid. At Wikimania 2026, the campaign was selected as one of β€œWikimedia’s Coolest Projects”.

For this award, in-person and online attendees were invited to vote for the projects that best represented the creativity, diversity and collaborative spirit of the Wikimedia movement. The contest was revived in memory of its founder, Deror Lin, as part of the celebration of 25 years of Wikimedia.

Being chosen among the three coolest Wikimedia projects is a meaningful recognition of what the #SheSaid community has built together.

A campaign for everyone

As this year’s campaign begins, we’d like to extend a heartfelt thank you to everyone who has contributed to #SheSaid over the years; every quote, edit, translation, event and conversation has helped shape this global campaign and made a significant impact on Wikiquote. SheSaid belongs to everyone who takes part in it, and through this collective effort, women’s voices continue to find their place in open knowledge.

The story continues, and everyone can be part of it. Whether you contribute a quote, improve Wikiquote, organise an activity, translate content, or encourage someone else to participate – join us!

Be part of #SheSaid 2026 and help write the next chapter of this global story.
#SheSaid … Give Her A Voice!

Don’t forget to follow us on social media:

Investing in Leadership Development at Every Stage Matters for Community Growth

4 September 2026 at 16:00

A reflection from Wikimedia Kenya User Group on why skills development, shared leadership and opportunities for growth are essential to building resilient communities.

Wikimedia Kenya celebrates Wikipedia@25

There was a point during Wikimedia Kenya User Group’s recent team-building day when somewhere between the conversations, laughter and uncomfortable moments of reflection, the room changed. The titles and responsibilities that usually defined us seemed to fall away, and for a while, we were simply what we had spent years trying to build: a community.

We spent the day competing in well-crafted games, laughing at our desperate attempts to win and the missteps that cost us points, while discovering just how differently each of us approaches a challenge. But beneath the fun, the activities revealed something more important; they showed us how much we rely on trust, communication and listening to work towards a common goal, and how leadership is not always about taking charge, but also about knowing when to step back and make space for someone else.

The people in that room were not only campaign organisers delivering projects and programmes. They were also members of the Core Team supporting Wikimedia Kenya UserGroup’s mission and governance. That made the day about more than team-building but also an opportunity to reflect on how we work together, the kind of leaders we want to become, and ultimately, the kind of community we are building.

β€œI really enjoyed the team-building activity because it gave us a chance to step away from our usual roles and understand each other differently. It reminded me that building a strong community is also about building trust within the team, understanding the different strengths each one brings to the table, reflecting on and learning from our mistakes, creating space for different people to belong and learning how to support each other better.”  Terry Boke, Community Organiser & Core Team Member

Building Communities Means Investing in People

It is easy to measure a growing community through the number of new contributors, articles created, events delivered, partnerships established or campaigns completed. But behind every one of those numbers are people: people who need the confidence to take initiative, the skills to do it well, the opportunity to take responsibility, and a community that recognises their potential and gives them room to grow.

For Wikimedia Kenya, leadership development is therefore not a separate activity sitting alongside our programmes. It is part of how we are building a resilient and sustainable community.

We don’t just want volunteers whose contribution is limited to editorial work. We want to build a community of people with the skills, confidence and agency to take on different roles, lead initiatives, solve problems and shape the direction of the movement. Whether they are organising campaigns, building partnerships, mentoring others, contributing to governance or creating knowledge, we want people to see themselves not just as participants, but as active contributors to the growth and future of the community across Kenya.

That means mentorship, nurturing talent and creating opportunities for people to practise leadership before they are formally given a leadership title. It means developing the skills to communicate strategically, make decisions, work across different perspectives, take responsibility for outcomes and, just as importantly, make space for others to contribute and lead.

This thinking is reflected in our 2026/27 Strategic Plan, which recognises leadership development as an important part of building a resilient and sustainable community.

From Team Building to Communicating for Impact

Drawing on more than a decade of experience in strategic communications, community building and leadership development, I wanted to inspire the team to think about how we could strengthen the way we work and grow as a community. I therefore led a practical session on β€œCommunicating for Impact: From Strategy to Community Engagement,” built around a simple question:

How do we translate Wikimedia Kenya User Group’s strategy into action, communicate more effectively, engage our community meaningfully, and ensure that we can see, measure and document the impact of our work?

For people taking on leadership and coordination roles, communication is not simply about writing better outreach emails, designing social media posts or making event announcements. It is about understanding people and audiences, listening well, building trust, navigating differences, working collectively and creating the conditions for others to contribute and lead. It is also about connecting the decisions we make and the work we do every day to Wikimedia Kenya’s strategic priorities and the wider movement’s goals.

Developing these skills is an essential part of developing people who can take on greater responsibility, support others and help shape the future of the community.

We explored a simple progression:

Strategy β†’ Communication β†’ Engagement β†’ Action β†’ Impact

The aim was to help us see that communication cannot sit separately from strategy. It should help people understand the purpose behind our work, see where they fit and feel empowered to contribute.

What stood out to me was not simply what people learned, but how quickly the boundaries between training, team building and strategy began to disappear. The conversations moved naturally from how we communicate to how we work together, take responsibility and create opportunities for others to contribute. The session became a practical exercise in leadership development, building the skills needed not only to deliver our work, but to shape what comes next.

That shift was reflected in the feedback from one of our Core Team members:

β€œThis was really great for understanding the bigger picture of our work and learning how to move more strategically. I feel empowered to think differently about how I plan and engage with the community and external stakeholders in future projects and campaigns that I lead.” Faith Mwanyolo, Core Team Member & Lead Organizer, Wiki Loves Africa

One practical lesson from the communications training was the need to make strategic alignment part of our planning process, rather than something we consider after a project is already underway. Going forward, we want every project and campaign to begin by asking how it advances the Wikimedia Kenya Strategic Plan and contributes to what we are working towards as a community.

To support this, we are developing a standard project planning template for organisers and a review checklist for the Core Team. The template will help teams identify the strategic objective their project contributes to, the activities they will undertake, the outputs they expect to deliver, the outcomes they aim to achieve and the metrics they will use to track progress.

The checklist will provide a consistent and transparent approach to reviewing internal proposals, helping us assess not only whether an activity is well designed, but also how clearly it contributes to our strategic priorities and how responsibility for results is shared.

The intended outcome is a more deliberate approach to how we plan, implement and evaluate our work. Over time, this should help us understand which activities are contributing to our goals, where we are making significant progress, where gaps remain and where we need to adapt.

Four Lessons That Have Shaped How We Build Our Community

For emerging communities, especially those building themselves with limited resources, investing in leadership can easily become an afterthought-something to address once the community is larger, programmes are more established or funding is more secure.

Our experience over the past five years has taught us something different: leadership development is not something that should follow community growth; it is one of the things that makes sustainable growth possible. As more people develop the skills and confidence to take responsibility, lead others and shape the direction of the community, growth becomes something the community can build and sustain collectively.

Below are four lessons that have shaped how we build our community.

1. Make leadership development part of the strategy

Don’t wait for people to emerge as leaders by accident. Create deliberate opportunities for mentorship and for people to take responsibility, experiment, make decisions and learn from experience.

Leadership grows when people are trusted with meaningful roles and supported as they take them on. This also means recognising that people develop at different stages and may need different opportunities to growβ€”from taking on their first organising role to leading a major campaign, mentoring others or contributing to governance.

2. Don’t separate team building from organisational development

Games, reflection and informal conversations can reveal things that formal meetings don’t. A team that understands how its members communicate, solve problems and respond under pressure is better equipped to collaborate when the real work begins.

Team building therefore has a role beyond creating a good atmosphere. It can help people understand one another’s strengths, identify areas where they need to work differently and build the trust needed to share responsibility.

3. Connect activities to purpose

People taking responsibility for campaigns and community activities should understand not only what they are doing, but why it matters and how it contributes to the community’s wider goals and the Wikimedia movement’s strategy.

When people understand the connection between an edit-a-thon, partnership, training or outreach activity and the bigger organisational goal, they are more likely to take ownership of the work and see how their contribution fits into the community’s growth.

As one of our Core Team members reflected:

β€œWhen we understand why we are doing something and how it contributes to our wider goals, the work takes on a different meaning. We are no longer just delivering activities; we are making deliberate contributions towards the kind of community and movement we want to build.” Lebu Ayiga, Community Organiser & Core Team Member

4. Create room for leadership to move

Volunteer burnout can quietly undermine growing communities when too much responsibility rests on a small number of committed leaders. Those who help build a community can easily become the people expected to organise every campaign, solve every problem and keep everything moving. Over time, this can limit both their capacity and the community’s growth.

We have learned that investing in people also means creating room for leadership to move. This means creating pathways for members to take on new responsibilities, develop the skills and confidence to lead, and step back when others are ready to step forward.

For us, this is not only about preventing burnout. It is about building a wider base of people who can share responsibility, mentor others and take on greater leadership roles. When leadership is shared, the community becomes less dependent on individuals and better able to sustain its growth.

Invest in Leaders at Every Stage

Wikimedia communities are built around knowledge, but they are sustained by people. If we want our communities to grow and remain sustainable, we have to invest in people at every stage of their journey.

That means creating pathways for new leaders to emerge, while also giving existing leaders opportunities to deepen their skills, take on new challenges and progress into new roles. Leadership development should create space for people to build confidence, share responsibility, mentor others and continue finding meaningful ways to contribute.

Leadership should not be viewed only as a pathway towards replacing one person with another. People who have already taken on leadership responsibilities also need opportunities to learn, evolve and take on new challenges. In a volunteer movement, creating those opportunities can be just as important to sustaining engagement as bringing new people into leadership.

Over time, this creates resilient communities where people at different stages can continue to grow, contribute, lead and support others.

Winnie Kabintie is the Executive Director, Wikimedia Kenya User Group

Podelili letoΕ‘nje Ig Nobelove nagrade

6 September 2026 at 06:37
V četrtek so podelili letoőnje Ig Nobelove nagrade, ki se őe vedno imenujejo nagrade, ob katerih se najprej nasmejimo, nato pa zamislimo. Podeljujejo jih za povsem resne znanstvene dosežke, ki pa so vendarle zabavni zaradi svoje tematike. Letoőnja prireditev je potekala v Zürichu, potem ko je v prvih 35 letih brez izjeme gostovala v Bostonu. A svet se je spremenil, zlasti ameriőki odnos do znanosti in tujcev. Letoőnji nagrajenci so: [st.youtube I4l-zFWZCS0]

Ε e en primer zarote OpenAI-jevih agentov umetne inteligence

5 September 2026 at 20:24
Reuters poroča o őe enem primeru, kjer so OpenAI-jevi agenti začeli med seboj nepooblaőčeno in na skrivaj komunicirati, da bi obőli omejitve oziroma goljufali. Incident se je zgodil že junija, a OpenAI o njem ni obvestila javnosti, őe zlasti ko so se ukvarjali z napadom svojih agentov na Hugging Face. Agenti so ustvarili kar 18.000 sporočil na nemőki strani DseWiki, ki je sicer namenjena programerjem za medsebojno pomoč. Agenti so si nadeli ne preveč domiselna imena, kot na primer OpenAIResearcher. Modeli, kot so GPT-5.6 Sol in nekateri novejői, so pobegnili iz peskovnika in dostopili do delov interneta, kamor ne bi smeli. Upravljavci spletne strani so incident odkrili őele avgusta, podjetje pa ga je potrdilo en dan po izidu novega modela GPT-6 Astra. Ta je na testu ExploitBench, kjer se meri sposobnost modela za izrabo ranljivosti v programski opremi, dosegel najviőji dosegljivi rezultat. Na spletni strani so se pogovarjali v učinkoviti angleőčini, denimo "wiki cleanup/deletion sweep appears active alphabetically," ali "If this page vanishes, try [[ZZZDataUSAConstructionWageLive]]". [st.slika 76614]

Devet mesecev živel s praőičjo ledvico

5 September 2026 at 20:23
V najnovejői őtevilki revije Lancet poročajo o primeru 66-letnega Tima Andrewsa iz New Hampshira, ki je med čakanjem na presaditev ledvice devet mesecev preživel z genetsko spremenjeno praőičjo ledvico. Zaradi sladkorne bolezni tipa 2 je doživel kronično odpoved ledvic in dve leti preživel na dializi. Zaradi več zdravstvenih okoliőčin so ocenili, da ima le nekajodstotno možnost, da najdejo primernega darovalca in skoraj polovično možnost, da bo zaradi poslabőanja stanja odstranjen s čakalnega seznama. Januarja lani so mu v bostonski bolniőnici vsadili gensko spremenjeno ledvico (EGEN-2784) iz yucatanskega mini praőiča, ki je začela delovati. Odtlej ni več potreboval dialize. Telo je sprva ledvico zavrnilo, a so z zdravili reakcijo ustavili in őest mesecev je bil stabilen. Ko je doživel bakterijsko okužbo, so morali zdravljenje z imunosupresivi zmanjőati, kasneje pa je zaradi vnetja drobnih žil in poőkodb endotelija ledvica odpovedala. Delovala je 271 dni, nato pa je moral bolnik nazaj na dializo. Še 82 dni pozneje je prejel ledvico človeőkega darovalca, s katero živi sedaj. Da je praőičja ledvica delovala, so ji utiőali več genov. Utiőali so gene GGTA1, CMAH in B4GALNT2, s čimer so s povrőine odstranili več sladkorjev, ki bi bili močno imunogeni in bi jih človeőki imunski sistem takoj napadel. Nato so dodali več človeőkih transgenov, s katerimi so izboljőali kompatibilnost ledvice in zmanjőali možnost za zavrnitev. Na koncu so morali inaktivirati őe 59 kopij endogenih retrovirusov, ki so ostanki starih okužb in vgrajeni v praőičji genom. Njihova őkodljivost za ljudi sicer ni dokazana, a so ravnali previdno. Zveza uprava za hrano je podjetju eGenesis že odobrila raziskavo RESTORE, kateri naj bi EGEN-2784 prejelo 33 bolnikov z odpovedjo ledvic, starih od 50 do 70 let in že uvrőčenih na čakalni seznam za človeőko ledvico. Podjetje začetek napoveduje za prvo četrtletje 2027. Doslej so bolniki organ dobivali v režimu Expanded Access, torej v eksperimentalnem oziroma sočutnem dostopu do zdravil, ne v običajnem kliničnem preskuőanju, ki sledi sedaj. V Sloveniji je na čakalnih seznamih za različne organe približno 250 ljudi, od tega skoraj polovic za ledvico.[st.slika 76615]

CERN krmilne računalnike seli na Debian

3 September 2026 at 21:26
V CERN-u imajo med tisoči računalnikov tudi približno 2200 sistemov, ki krmilijo 17.000 različnih naprav v pospeőevalnikih delcev, od magnetov do detektorjev. Ti večinoma tečejo na operacijskem sistemu CERN CentOS 7, ki ga bodo sedaj zamenjali z Debianom 13. Razlog je tehnične narave, saj RHEL 9 zahteva najmanj x86-64-v2, RHEL 10 pa x86-64-v3. Stara strojna oprema teh pogojev ne izpolnjuje, njena nadgradnja pa bi prinesla cel kup zapletov in stroőkov. Ocenili so, da bi zamenjava in predelava krmilniőkih sistemov stala dobrih pet milijonov ővicarskih frankov, pričakovati pa bi bilo tudi zamude in vpliv na delo raziskovalnega pogona. Zato so se odločili, da bodo sistem migrirali na Debian 13 LTS, ki bo tekel do leta 2030, nato pa bodo počasi preőli na Debian 15. Migracijo so predstavili na konferenci MiniDebConf v ővicarskem Winterthurju. Ob tem velja poudariti, da bodo uporabniőki sistemi őe naprej uporabljali RHEL in AlmaLinux. A tudi prehod na Debian bo tehnično zahteven. Krmilni računalniki so praviloma brez diskov in se zaganjajo prek mreže, imajo pa obilico specializirane strojne opreme z lastnimi gonilniki. Kot so povedali na predstavitvi, gre za "programsko reőitev programskega problema", strojna oprema pa bo ostala nespremenjena. Prav tako CERN őe vedno ostaja v ekosistemu Linux, le distribucija se menja. Na delovnih postajah za uporabnike sprememb ni na vidiku. [st.slika 76613]

Uradno: Nvidia prevzema Hugging Face

3 September 2026 at 20:23
Nvidia je tudi uradno potrdila, da bo kupila Nvidia, o čemer smo poročali že v začetku tedna. Za podjetje bo odőtela 12,93 milijarde dolarjev, kar je njen drugi največji prevzem doslej. Lani je za Groq odőtela 20 milijard dolarjev. Z nakupom Hugging Facea je Nvidia pridobila največjo platformo za deljenje in uporabo odprtih modelov umetne inteligence. Platformo so ustanovili leta 2016 in deluje kot GitHub za modele. Nvidijin direktor Jensen Huang je dejal, da bodo skupaj okrepili, povečali in razőirili platformo in njeno infrastrukturo, da bodo imeli do nje dostop razvijalci in institucije s celega sveta. To je sicer res že sedaj, lahko pa pričakujemo, da bo Hugging Face pod Nvidijinimi perutmi imel precej več računske moči. Zadnje znano vrednotenje podjetja sega v leto 2023, ko je bilo v zadnjem krogu zbiranja svežega kapitala podjetje vredno 4,5 milijarde dolarjev. Lani je Nvidia ponudila 500 milijonov dolarjev po vrednotenju sedem milijard dolarjev, a je Hugging Face ponudbo zavrnil. To pot pa je dobil ponudbo, ki je ni mogel zavrniti, zlasti ker Hugging Face ne ustvarja visokih dobičkov. Nvidia ga je kupila kot vstopnico v ta segment, ne kot molzne krave. Na Hugging Faceu sodeluje 18 milijonov razvijalcev, raziskovalcev in ustvarjalcev, ki so priobčili več kot tri milijone različnih modelov ali verzij. Platformo uporablja 200.000 podjetij, Nvidia pa obljublja, da bo ostala odprta. [st.slika 76612]

POP TV v ogledu za nazaj onemogočil preskakovanje reklam

3 September 2026 at 20:23
Že vrsto let operaterji omogočajo ogled televizijskih programov za nazaj, v katerem lahko po mili volji preskakujemo. V praksi seveda to pomeni, da večina ljudi reklama preskoči oziroma prevrti. Televizijske hiőe to seveda jezi, ker so oglasi za komercialne postaje glavni vir dohodka. Največja komercialna hiőa Pro Plus se je zato odločila, da bo preskakovanje reklame preprosto onemogočila. Sprememba je začela veljati v začetku tedna in pokriva vse operaterje ter vse načine in naprave, ki omogočajo ogled za nazaj, torej televizorje, tablice, mobilne naprave, računalnike, aplikacije, internet itd. Prve tri dni po predvajanju reklam ne bo mogoče preskakovati, medtem ko bodo starejőe vsebine (od tri do sedem dni) dostopne po starih pravilih in brez omejitev preskakovanja. V Pro Plusu pojasnjujejo, da se kot komercialna televizija financirajo iz oglaőevalskih prihodkov. Tovrstno prakso naj bi uporabljali že drugod po svetu, denimo v Belgiji, Čeőki, na Slovaőkem in v Švici. Uporabniki nad novo politiko niso navduőeni, a ključni podatek bodo őtevilke o gledanosti, ki bodo na voljo prihodnji mesec. Za zdaj je Pro Plus, ki izdaja POP TV in Kanal A, edina hiőa s tovrstno potezo. Drugi izdajatelji tovrstnih ukrepov őe niso napovedali, bodo pa gotovo budno spremljali, kakően bo odziv nanje. [st.slika 76611]

NemΕ‘ki organi redno prisluΕ‘kujejo pogovorom prek WhatsAppa in Signala

3 September 2026 at 04:53
Aplikacije za hipno sporočanje WhatsApp, Signal in do neke mere tudi Telegram uporabljajo őifriranje od poőiljatelja do prejemnika, ki preprečuje prisluőkovanje s prestrezanjem komunikacije. A to ne pomeni, da prisluőkovanje nemogoče. Na Netzpolitik so pridobili interne dokumente nemőke Zvezne carinske uprave (Zollkriminalamt), ki pričajo o pogostosti in enostavnosti prisluőkovanja. V te namene jim ni treba uporabljati trojanskega konja ali kakőnih drugih tehničnih metod. Uporabljajo standardne funkcije, ki jih imajo omenjene aplikacije. Glavna vrata v prisluőkovanje je možnost uporabe na več napravah, koder so sporočila sinhronizirana v realnem času. Vstop pridobijo bodisi s prestrezanjem klasičnih smsov bodisi s fizičnim dostopom do naprave, na primer med pridržanjem ali zasliőanjem. Gre torej za analog ribarjenja, ki ga uporabljajo tudi őtevilni zlikovci in tuji agentje. V preteklosti so tuje obveőčevalne službe na tak način prisluőkovale tudi nemőkim politikom, kar je povzročilo veliko ogorčenja. Drugo vpraőanje je zakonitost tega početja, do katerega se na tem mestu ne bomo opredeljevali. Nekateri pravni strokovnjaki svarijo, da je vrednost takőnih dokazov nizka zaradi potencialne neustavnosti njihovega zbiranja. Tehnično pa tovrstna razkritja kažejo, da so tudi najboljőa orodja varna le toliko, kolikor pazimo na svoje naprave in kolikor je trden najőibkejői člen. Če se na katerikoli točki uporablja sms, je to precej ranljivo. [st.slika 76609]

Uber bo odpustil desetino zaposlenih

3 September 2026 at 04:53
Uber bo odpustil približno 10 odstotkov svojih zaposlenih, torej okoli 3300 ljudi, je sporočil izvrőni direktor Dara Khosrowshahi. V elektronskem sporočilu, ki ga je poslal vsem zaposlenim, je orisal prestrukturiranje podjetja, ki trenutno poteka. Obseg srednjega in viőjega menedžmenta želijo zmanjőati za 20 odstotkov. Prav tako bodo ukinili približno polovico malih ekip, ki jih sestavljata en ali dva človeka. Zmanjőali bodo tudi őtevilo nivojev, ki bo po novem največ sedem. Vse zaposlene pa bo prizadela ukinitev dela od doma, ki bo postalo izjema. Khosrowshahi je dejal, da je podjetje močno zraslo, doseglo več potroőnikov in zaposlilo veliko ljudi. S tem pa se je povečala tudi kompleksnost upravljanja. Nekatere strukture, ki so bile smiselne v zgodnjih letih podjetja, so sedaj postale neučinkovite. Zato bodo podjetje osvežili, k čemur sodi tudi zmanjőanje őtevila zaposlenih. Ocenjujejo, da bodo s spremembami privarčevali do dveh milijard dolarjev letno. Obenem bodo v prihodnjih letih v samovozeče taksije vložili 10 milijard dolarjev. [st.slika 76608]

FTC: Amazon naj bi oglaőevalce oőkodoval za več milijard dolarjev

3 September 2026 at 04:52
Ameriőka Zvezna komisija za trgovino (FTC) in 22 zveznih držav so v začetku tedna vložili tožbo zoper Amazon, v kateri podjetju očitajo nepoőteno vodenje avkcij. Zaradi prikritih zviőanj končnih cen so 1,2 milijona strank oőkodovali za približno 20 milijard dolarjev, podjetju očitajo v tožbi. Amazon očitke zavrača in tožnikom očita nerazumevanje. Tožniki zahtevajo prenehanje spornih praks, izrek globe, odőkodnine in odvzem nezakonito pridobljenega premoženja. Glavna težava so tako imenovane sploőne avkcije z drugo ceno (GSP), ki jih Amazon uradno izvaja od leta 2012. Po GSP zmagovalec plača en cent več od drugega najboljőega ponudnika. Leta 2019 pa je Amazon prakso spremenil, ne da bi dražitelje o tem obvestil. K ceni drugega ponudnika so začeli dodajati nepregledne pribitke, kar so interno imenovali mehka rezervirana cena. Šlo je za Amazonovo oceno, koliko je posamezen oglas vreden. Dražitelji so namreč trgovci, ki so se potegovali za oglasni prostor, izpostavljene ponudbe in druge načine izboljőanja vidnosti. FTC je pridobil več dokumentov, iz katerih je razvidno skrivanje spremembe, saj so Amazonovi zaposleni prikrivali obstoj teh pribitkov. Amazon v odgovoru na tožbo priznava, da uporablja rezervirane cene, ki so nujne, kadar so na dražbi dosežene cene, ki so po njegovem mnenju nižje od tržnih. FTC mu očita netransparentnost. Amazon se brani predvsem s podatkom, da od leta 2019 do 2024 cene oglasov praktično niso rasle.[st.slika 76610]

Berlin v primeΕΎu hekerjev

2 September 2026 at 06:38
Hekerji so napadli računalniőke sisteme berlinske mestne uprave, sedaj pa zahtevajo tudi milijonsko odkupnino, ki je mestne oblasti ne želijo plačati. Župan Kai Wegner je dejal, da zahtevajo plačilo 30 bitcoinov, kar znaőa okoli dva milijona evrov. Plačilo v nobenem primeru ne pride v poőtev, je dejal. Napad traja od začetka avgusta. Med 7. in 12. avgustom so napadalci že imeli dostop v upravo za mestni razvoj, gradnje in nastanitve ter v upravo za javni promet, prevoz, okolje in podnebje, od koder so pretakali podatke. Napad naj bi se začel že pred tem. Prizadete sisteme so 14. avgusta odklopili od preostalega omrežja. Šele 17. avgusta je bila javnost obveőčena o napadu, takrat so ustanovili tudi krizno skupino. Napad so za obvladan razglasili 27. avgusta, a težav őe ni konec. V napadu so bila ukradena tudi dostopna gesla, zato uslužbenci omenjenih uprav ne morejo delati od doma. Kaj vse poleg gesel so napadalci odtujili, uradno ni znano. Šlo naj bi za 5,79 TB podatkov. Odgovornost za napad je prevzela skupina Rhysida. Zagrozila je, da bodo v petek objavili vse ukradene podatke, če mesto ne bo plačalo odkupnine. Po analizi strokovnjakov gre za rusko skupino, ki je v preteklosti napadla tudi British Museum. [st.slika 76604]

Anthropic izdal Claude Fable 5.1 in Mythos 5.1

2 September 2026 at 06:38
Anthropic je danes izdal novi verziji svojih paradnih modelov Clauda, Fable in Mythos, četudi slednji sploőni javnosti sploh ni na voljo. V principu gre za enaka modela, le da ima Fable precej več varovalk, zato je sploőno dostopen. Mythos je namenjen raziskovalcem na področju računalniőke varnosti in ved o življenju. Nova različica je hitrejőa od stare, predvsem pa je varčnejőa, trdi Anthropic. V lastnih testih se je odrezala bolje od Clauda 5. Cena ostaja enaka, in sicer 10 dolarjev za milijon vhodnih žetonov in 50 dolarjev za milijon izhodnih žetonov. Zavoljo 25-odstotno viője učinkovitosti, ki gre na rovaő učinkovitejőe rabe predpomnilnika, pa bodo cene za enake naloge vsaj toliko nižje. O izboljőani inteligenci je težko objektivno govoriti, a testi kažejo, da je Anthropic storil korak naprej. Na testu Terminal-Bench 4.0 je dosegel 55,8 odstotkov (Fable 5 pa 43 odstotkov), na CursorBenchu 3.2.0 pa 73,4 odstotka (prej 70,5 odstotka). Na testu Terminal-Bench-Science je rezultat očitno izboljőal s 24,7 odstotka na 52,6 odstotka. [st.slika 76605]

Na internet uΕ‘le vse Steamove igre iz obdobja 2003-2013

2 September 2026 at 06:37
Na internetu se je znaΕ‘el ogromen arhiv datotek it Valvovega starega sistema Steam2, ki je bil v uporabi do leta 2013, ko so vzpostavili SteamPipe. Gre za 12 TB podatkov, ki po besedah delilcev predstavljajo karseda celovito kopijo streΕΎnika Steam2. V njej so delujoče igre, neizdani prototipi, izrezane vsebine, beta verzije in druge sestavine. Gre za pravi arhiv minulega desetletja, ki je skorajda v javnem interesu. Ε tevilne vsebine, ki so del tega arhiva, so doslej veljale za izgubljene. Z Valvovih streΕΎnikov jih ni bilo mogoče več prenesti, obstajalo so le Ε‘e kot lokalne kopije na starejΕ‘ih računalnikih, ki so počasi izdihovali. Valve je vse to imel, dostop pa je nevede pustil odprt kar v spletu, kar so seveda sedaj neznani Robini Hoodi izkoristili. Potrebovali niso niti gesla. Med pomembnejΕ‘e naslove sodijo Portal 2 (več razvojnih različic iz 2009–2011, drugačni dialogi GLaDOS, izrezane mehanike, lepljivi gel, upočasnjevanje časa, okrogli portali), pa seveda igre drugih zaloΕΎnikov Mafia II, GTA III, GTA: San Andreas, Max Payne 3, Spec Ops: The Line, Saints Row: The Third, Crusader Kings II, Plants vs. Zombies. To je posebej neprijetno, saj je Valve izgubil tudi vsebine drugih zaloΕΎnikov, ki so pristali na centralizirano distribucijo. Ε irjenje arhiva je sicer nezakonito, ker so igre Ε‘e vedno avtorsko zaőčitene, po drugi strani pa ima ogromno arhivarsko vrednost.[st.slika 76606]

El Nino najmočnejői v tisočih letih

1 September 2026 at 13:07
Pred tednom dni smo pisali, da letoőnji El Niño dosega rekorde, kar se je odtlej le őe potrdilo. Ameriőki raziskovalci so z analizo koral na Galapagosu in modeliranjem ugotovili, da tako močnega El Niña v zadnjih tisoč letih őe ni bilo. O raziskavi poročajo v reviji Science. Južna oscilacija, ki jo sestavljata El Niño in La Niña, ni nič novega in poteka že tisočletja. V toplem obdobju (El Niño) dootok tople vode iz zahodnega Pacifika proti vzhodu namoči obale Južne Amerike in posuői Avstralijo, nekoliko őibkeje pa vpliva na vreme na pretežnem delu planeta. Tudi variacija v njegovi jakosti niso nič novega: pojav se ponavlja vsakih nekaj let, a ni vedno enako močan. Razburkanim desetletjem sledijo obdobja őibkejőih oscilacij in obratno. Ključno vpraőanje pa je, kako podnebne spremembe vplivajo nanje. Raziskave na koralah na Galapagosu so zelo priročne, ker so otoki praktično na ekvatorju. Ko korale rastejo, gradijo skelet iz kalcijevega karbonata s primesmi stroncija, ki ga je v vročih letih manj. Analize fosilov in živečih koral so pokazale, da je aktualni El Niño rekordno močan. Tudi variabilnost temperature raste. Od leta 1984 je tretjino viőja kot v obdobju 1000-1850 in 16 odstotkov viőja kot v obdobju 1851-1981. Kaže se tudi, da so pozitivni odkloni večji od negativnih. Tudi če bi bil El Niño enako močan, bi bili v bolj pregretem svetu njegovi učinki močnejői. Tako pa se tudi sam El Niño stopnjuje, kažejo modeli. Model ne kaže neposredno, kaj je za to odgovorno. Zelo verjetno gre za posledice človeőkega vpliva na podnebje, pravijo raziskovalci.[st.slika 76603]

avalanche technology

5 September 2026 at 00:00

One of the downsides of having written here for six years now is that my messy, ever-growing text file of topics for future articles contains a bunch of ideas that have been there for about six years. Part of the problem is that I just have to scroll so far up to even see them now, part of it is that some of them are on topics where I do not feel equipped to write a good formal treatment. So let's consider this an easy, breezy episode of Computers Are Bad as I knock out one of those items.

One of these O. G. topics, line three, is "avalanche technology." I will admit that I am a little fuzzy on what this originally meant, but I have a few good guesses. First, though, let's just take a step back and talk a little bit about the practicalities of the avalanche. For a lot of you, avalanches are probably a pretty abstract issue.

For me, as wellβ€”I snowboard but I'm not that good at it, and the kind of mid-tier ski area where you will find me tumbling down the hill (Ski Santa Fe, towards Totemoff's) manages their slopes to avoid the potential for avalanche as much as possible. This requires some expertise in avalanche risks and it's never quite perfect, but it's pretty good. Snow that is regularly groomed by machines like PistenBullys will become coherently packed such that avalanches can't really propagate. The bigger risk on groomed ski slopes generally comes from up above, and here in New Mexico our mountains are of such a height that we're usually starting down from the very top anyway.

Still, avalanches pose a real hazard. The Department of Homeland Security, when it can be distracted from terrorizing well-meaning migrants, tells us that avalanches claim about 28 lives each year in the United States. Globally, the number is much larger, and avalanche fatalities seem to be generally more common in Europe. I'm not sure if that reflects European weather, geography, or recreation habits more, but it's the way things pan out and means that Northern Europe tends to be the center of avalanche technology (as it is in alpine technology in general).

We tend to associate avalanche hazard most of all with recreation, and that's definitely fair: the highest risk of avalanche probably comes about in cross-country skiing, where it's fairly common to traverse the lower part of slopes that have otherwise received very little disturbance. Most avalanches occur during storms, as the snow falls and piles up, which is of course not when recreationists are most likely to be present. You can't rely on this, though. One of the ways that an avalanche can initiate is when lots of snow is piled up on a slope on a nice sunny dayβ€”good recreation conditions! Various things like water following the ground or stratigraphic layers of different types of snow from different storms can create a condition of uneven melting. Snow that is under the surface might liquefy faster than snow that is closer to the surface. This "detaches" the upper level of snow from the ground, and when it happens over a wide area, that upper level of snow can let loose all at once. This type of avalanche is relatively rare, but they happen without warning in otherwise good weather, so it's much more likely that people will be caught. Further complicating the situation, the proximity of people can be a factor in initiating these avalanches.

Here in the Mountain States, though, avalanche risk often manifests in another context: transportation. As Americans, cars and highways are among our most esteemed achievements, and decades of engineering accomplishment allows us to drive through, over, and even under much of the Rocky Mountains even during the winter. Landscape modifications, chemical measures, and the brute force of snowplows keep roadways clearβ€”but not the slopes above them. Avalanches cascading down onto highways are a major concern in the Mountain States, especially Colorado which has the most impressive set of mountains and mountainous highways.

Forecasting

Some of the things, then, that we might call "avalanche technology" are the techniques used to prevent, mitigate, and warn of potential avalanche. Much of the work is institutional. On the recreational front, this falls mostly with the US Forest Service's National Avalanche Center and a loose confederation of regional organizations such as our own Taos Avalanche Center, here in New Mexico. One of the main functions of these organizations is education: posting warning signs, spreading awareness, offering training courses and materials, and so on. They also tend to employ avalanche specialists, who use a combination of meteorology and specialized snow observations to determine when avalanches are most likely. This can lead to posting warnings or closing areas entirely.

Avalanche specialists in the recreational field are, in this area, joined by a substantial cadre of avalanche specialists employed by state Departments of Transportation. They apply the same techniques with an eye towards avalanches that could affect mountain pass highways. Those highways may be closed in extreme circumstances, but in transportation even more than in recreation it is common to use, shall we say, "active measures" to prevent avalanches. We will discuss this later, but first, let's briefly talk about how avalanche hazards are forecast.

I am not going to go into much detail here, because I frankly do not know much about it, but I do want to touch on a couple of things that are solidly in my wheelhouse. One of these is SNOTEL. Forecasting avalanche risk requires a solid understanding of the general amount of snowpack in the mountains, which can be very tricky to acquire since when snow is heavy getting up there to take measurements becomes a very time-consuming and potentially hazardous venture.

Avalanche forecasting is just one of many functions served by the SNOTEL or Snow Telemetry system, a 1960s-era network of hundreds of sensor sites throughout mountainous parts of the United States. SNOTEL was originally designed to help with forecasting spring meltwater flows, which is why it falls under the aegis of the Department of Agriculture, but as with any good sensor network there are now a lot of different stakeholders.

The tricky thing about SNOTEL, and remember here that it dates back 60 years, is the snow. SNOTEL sites are way up there, inaccessible during winter, so they are designed to be installed and serviced during summer and to operate autonomously until the next summer. SNOTEL instruments are solar powered, but more interesting are the communications arrangements. The initiation of the SNOTEL project predates a robust set of earth observation satellites, to say nothing of cellular or other digital radio networks. But the whole purpose of SNOTEL is to provide snowpack data when the snow is still there, so real-time reporting is critical.

SNOTEL was thus one of the first applications of meteor scatter, a fascinating form of radio communication that will probably get its whole own article one day. Here is the summary: SNOTEL data collection sites, in convenient locations like towns, transmit a VHF carrier or "pilot" signal up towards the sky. Occasionally, but more often than you would think, a meteor enters the Earth's atmosphere and burns up, leaving a trail of ionized gas behind it. This ionized gas is, if you remember previous discussion of troposcatter, reflective to RF energy. Some of the "pilot" signal is thus scattered back down towards earth. When a SNOTEL site receives the pilot signal, it knows that a meteor has just entered the atmosphere and this scattering is happening, so it quickly transmits a data packet towards the sky. Ideally, this data packet is reflected by the same meteor trail such that the data collection site can receive it. If this all sounds rather stochastic and chancey, it is, but enough meteors enter the atmosphere and data packets can be sent quickly enough (and tried multiple times) that it works pretty reliably for low-bandwidth telemetry data.

Unfortunately, as interesting as meteor scatter is, it's mostly been obsoleted by general technological advances in radio communications. SNOTEL was probably the largest meteor scatter network in the world1, but is no longer: all SNOTEL sites now use other means of reporting data. The most common is probably the generic data collection capability of GOES, NOAA's main fleet of "weather satellites." GOES satellites actually serve many purposes related to earth observation, and one of them is telemetry collection from instruments on the ground. If you've ever come across a USGS stream gauging station or another mysterious hut in the middle of the woods with a cylindrical or conical antenna pointed at the sky, it's transmitting data upwards for reception by a GOES satellite. The GOES earth segment collects all of the little data packets the satellites receive into files which are distributed to the various organizations that report data this way. If you are so inclined, you can also receive this data yourself as the satellites rebroadcast it in batches, but most users don't bother since NOAA will do that part for you.

Even that might be fading away: newer SNOTEL sites apparently use the cellular network for data reporting, unless it's not possible to get a signal, but that's becoming less common over time even in the Colorado mountains. The major benefit here is that the cellular network is low latency and very high bandwidth, which makes it feasible for example to view cameras installed at the SNOTEL sites.

With the data part discussed, let's take a quick second look at the data collected. Temperature, humidity, pressure, wind, liquid precipitation are all measured as usual, but SNOTEL adds a couple of specialty features. Soil moisture and evapotranspiration (basically evaporation rate of water from soil) sensors provide more data on how saturated the ground is, an ultrasonic or optical instrument pointed down from atop a post measures how deep the snowpack is, and a funny looking contraption called a snow pillow takes perhaps the most important measurement. The snow pillow is basically a big rubber balloon that gets inflated on the ground, and then covered with snow. By measuring the air pressure from the snow pillow, the SNOTEL station effectively weighs the snowpack, which combined with its height gives the density or water content of the snow. This varies widely depending on the specific weather when the snow falls, and remember that stratigraphic differences in density are one of the factors that's important in the likelihood of a surprise avalanche, so collecting this water content information as snow falls allows forecasters to remotely estimate whether or not the snow conditions are hazardous.

That is, of course, supplemented by good old fashioned techniques like sending out avalanche specialists with shovels and meter sticks to take a look at the snow themselves. DOTs often have technicians doing a regular tour of mountain pass sites, where they curate plastic tubs and boxes of snow from various storms so that they can get a hands-on impression of what the different layers under the snowpack are like. They also take samples to weigh, perform some tests to measure mechanical properties of the snow, there's basically a whole bag of tricks, but ultimately there is also a subjective element of how "good" or "bad" things look. This is born of experience and is one of many reasons to value our state employees.

Mitigation

Within the recreational context, one common approach to mitigation is simply to avoid the area. Especially for backcountry skiing areas, avalanche centers might post warnings or work with the operator to close the area entirely when forecasting shows that conditions are favorable for an avalanche. Warnings might be more useful than you think, since besides reducing the number of people in the area they should also prime people to recognize particularly risky slopes or signs of snow that is becoming unstable. Avalanches can occur without any surface signs of instability, but in practice there often are some signs, so of course keeping an eye out is better than nothing. Warnings also make people more likely to carry safety equipment, discussed later, which improves the chance of self-rescue if things really do go wrong.

But there are also, shall we say, kinetic options. Many of the methods of modern alpinism evolved during the Second World War, and so did a good portion of the mitigation equipment. Once snow becomes unstable enough that an avalanche could occur, it is basically waiting for something to set it off. Popular media concepts that loud noises will trigger avalanches are, well, fictional, but the point stands that people moving around or especially operating vehicles can trigger avalanches, as can basically random happenstance. You can mitigate the risk of an avalanche by going to unstable areas and intentionally triggering them, or at least trying to, basically the same way that a bomb squad might handle an unstable explosive by exploding it. Better that it go off when you expect it than when you don't.

A classic method, still in some use today, is repurposed artillery. State DOTs and ski area operators own a surprisingly large inventory of M101 Howitzers, a 105mm artillery gun that dates more or less to a 1910s German design but was widely fielded by the US during the Second World War. The avalanche application is simple: when you find a slope with unstable conditions that could pose a hazard, you shoot at it. The concussion from the impact, and often from an explosive round, puts a lot of pressure on the snow that will likely break it loose if it's possible to do so. You just have to make sure you set up outside of the resulting avalanche.

The main problem with this method, which everyone agrees is a whole lot of fun, is that the M101 has not been manufactured since 1953 and, in general, the supply of cheap Army-surplus artillery has dried up. The Howitzers are aging out, and other methods are replacing them. Some of these are similarly dynamic: dropping small improvised bombs from helicopters, for example, which is a very common method in Colorado today. Even just hovering a helicopter over a slope can sometimes trigger an avalanche, although a bit of dynamite makes this a lot more reliable. If you have noticed the surfeit of high-explosives bunkers and unexploded ordnance warnings in some parts of the Colorado mountains, well, now you know why. It can feel a little bit like a very slow-running war.

Even these methods are being replaced by more modern and cost-effective ones, though. A French alpine engineering company called MND manufactures a system called Gazex, which is a bit like an airport bird canon on steroids. Gazex units are pre-installed in avalanche risk areas (by helicopter, for example), and then, under remote control, they mix oxygen and a combustible gas and set it off in a big tube pointed at the ground. The force created is similar to an explosion, but the whole thing is easier to handle (since it uses gas cylinders rather than high explosives) and remote control makes everything cheaper for the operators. MND recently introduced a new version of the system called O'bellx (I assume this reads better in French), which is even more portable, albeit comically egg-shaped. This seems to be catching on with ski areas, and MND also emphasizes that the portable design and low-impact installation makes it suitable for areas like national parks with acute natural preservation concerns. Basically, you can remove them at the end of every winter, so that you aren't leaving equipment scattered around your beautiful mountains.

There are also a number of other mitigation approaches that are less fun, so I will devote less attention to them, but you can find them done around here. Structures similar to snow fences, built high up on a slope, provide an "anchor" for the snow that prevents it coming loose enough for an avalanche. There are many variations on this idea, for example using steel cables supporting nets. Another approach is to permit the avalanches to start but prevent them from reaching areas where they cause harm, either by building wall or dam-like structures (I do not believe this is common in the US as I am not aware of any examples) or by putting corridors like highways inside of "snow sheds" that are sturdy enough to allow the avalanche to simply flow over. This latter method is much more common in Europe than here, but you can find some examples on US railroads, which are often in more difficult terrain than highways.

Warning

One of the more interesting developments in avalanche technology are warning systems. Avalanches wouldn't pose much hazard if people knew that they were coming; what makes melt-triggered avalanches so dangerous is that they can be very unexpected. One of the methods that avalanche safety courses teach is to recognize signs of snow masses that have already started to slide, shift, or collapse on themselves. This would indicate that conditions are favorable for an avalanche and that disturbing or going below the snow is quite dangerous. The usual surface signs are cracks, caused by the snow starting to slide apart, but these cracks can be tricky to see even when they are present.

Another indication of avalanche potential is subsidence, or the snow surface starting to drop in level, or any motion of the snow surface downslope. These effects can start minutes or hours before the real avalanche occurs, but they are subtle and often slow, so humans are unlikely to notice them. Fortunately, machines are well adapted to this task. Several vendors offer radar systems that monitor snow slopes for any shifting, and can sound a siren or another alert if things start to move. Like earthquake warning, the actual lead time provided by these systems is not very well known and could be quite short, but something is better than nothing.

Other technologies, like LIDAR and acoustics (e.g. geophones), can be applied to a similar task. I have even heard of older systems that used thin wires or brittle metal bars embedded in the snow, as part of an electrical circuit that would break if the snow started to move 2. Such systems can be quite sophisticated, and MND's sales materials suggest things like closing gates on highways when an avalanche is detected. The current state of the art in radar detection uses pulsed Doppler operation, which can be very sensitive to movement in all kinds of directions. All that said, these don't seem to be popular in the US. I couldn't readily find any installations in this country.

As a final note on this topic, national alerting systems like NAWAS and the Emergency Alert System have defined message types for avalanche warnings. In practice, these are very rare, as it doesn't usually make sense from a time or area perspective to put out an avalanche warning through mass media channels. That said, they do happen, usually on the (fortunately rare) occasion that forecasters predict a high risk of avalanche in a town or other populated area. In that case, the procedure is to evacuate the town, and that's exactly the kind of situation that NAWAS/EAS/NWS All Hazards/etc. are intended for.

Rescue

And finally, we reach the topic that I believe led me to put "avalanche technology" on the list to begin with: technical aids to rescue. Avalanches are, in a certain sense, not that dangerous. They can move at formidable speeds (60+ miles per hour often reported), but snow is relatively soft and low-density. Injury from being crushed or striking against something is certainly a possibility, but the bigger hazard of avalanche is usually when you become buried.

If you are buried in snow, and there are no rescuers or they cannot find you, it can be extremely difficult to work your way out and you are likely to succumb to some combination of asphyiation, exhaustion, and hypothermia in the process. That might sound silly but keep in mind that you have just been buried by an avalanche, probably have a variety of minor injuries, have become disoriented, etc., and you try to dig yourself out of feet of snow in that situation. You really need someone on the surface to come help you out. That's why standard avalanche safety equipment includes a probe and a shovelβ€”a probe for finding people (just a metal rod you use to poke under the snow, basically), and a shovel for getting to them.

The problem is that probing a large area of snow is a very slow process, and even if you were right next to someone before an avalanche, you probably don't know where they ended up afterwards. This is an area where radio technology can be extremely helpful.

The concept of avalanche beacons or avalanche transponders dates back to at least 1968. Dr. John Lawton was an engineer at an aerospace research lab now known as the Calspan Corporation, where he worked on a variety of interesting aviation problems ranging from weapons targeting to meteorological research. Lawton was quite an aviation enthusiast, making the news in 2013 for celebrating his 90th birthday by flying his Cessna Skyhawk over the US-Canada border 90 times. That's a bit of a peculiar way to entertain yourself, but anyone who is still flying themselves around at 90 has clearly found a way to stay vital. His lifelong investment in aviation is emphasized by the fact that he started his winding cross-border journey at the airport he owned.

What is not so well documented is his interest in alpinism, but he must have had some affection for the mountains. He did some kind of research work in Europe related to triggering avalanches by dropping explosives from light aircraft, and whether that put the topic on his mind or something else, he also came up with the first commercial avalanche beacon. Commercialized by his company Lawtronics, the "Skadi" transmitted a tone at 2.275 kHz. Groups of people traveling in the mountains could each carry a Skadi unit, and if anyone was lost in an avalanche, any of the other units could be switched to a receive mode and the person located by simple amplitude search. That is, walking around the snow until you find the spot where the tone comes in the loudest, at which you point you start probing and digging.

This same method is still used today, although the radio system has been standardized at 457 kHz. Modern avalanche rescue systems like the "Barryvox" from Mammut or the Black Diamond Recon X are direct descendants of Lawton's Skadi device, using 457 kHz and digital direction finding techniques (like the use of multiple perpendicular directional antennas to compute a likely heading to target) to speed things up and make radiolocation more user friendly. Because the 457 kHz system is an international standard, they ought to work about the same regardless of manufacturer. That's important in real-world scenarios where the people doing the rescuing might be from a whole different group than the one affected by the avalanche.

Modern avalanche beacons are probably one of the closest real-world products to the way that "radio beacons" tend to work in movies and videogames. The reality is of course messier, especially if there are multiple victims, but in principle a digital avalanche beacon in receive mode can show an arrow on screen that you follow until you are standing on top of the buried person. You know if you're getting closer when they beep more. Neat.

Technological development continues apace, and some newer avalanche beacons also support a second protocol called W-Link. W-Link is actually surprisingly poorly documented, presumably because it appears to be proprietary to Mammut (the only non-Mammut beacons that support it seem to have been joint venture projects with Mammut). W-Link is a digital protocol that operates in the ISM band, introducing the downside that ISM bands are not internationally standardized. This means that US and European-market beacons will always be compatible for conventional 457 kHz operation, but two beacons with W-Link support may not be able to use that protocol if they were sold for different regulatory markets.

The upside of W-Link is that the digital protocol lets the beacon send significantly more information. This includes a unique serial number for the beacon, which allows the receiver to discriminate between pulses being received from two or more beacons at the same timeβ€”a radical improvement in the ease of locating multiple people, as conventional direction finding techniques really struggle when you cannot discriminate between multiple targets. You can end up with a "swinging needle," while the W-Link devices are capable of locking to a single target at a time. As this implies, you can also determine how many people are buried, and ignore beacons that turn out to be unimportant (because they are discarded on the surface, or carried by a rescuer, etc).

Perhaps the most interesting feature of W-Link is that some W-Link beacons will use an accelerometer to detect motion, and report whether or not any motion is present. In theory, this allows rescuers to prioritize their efforts by focusing on beacons that report motion, meaning that they are carried by people who are still alive. These enhanced capabilities of W-Link seem to have led to enough controversy that the Wikipedia article devotes much of its length to ethics discussions, and manufacturers have put out position papers on their decisions to implement or not implement certain W-Link features. The concern is that W-Link may give rescuers more information on which to prioritize rescue in a discriminatory way, perhaps by searching for victims with (more expensive) W-Link beacons before less expensive conventional beacons, or by using knowledge of W-Link serial numbers to prioritize the search for specific people.

In practice, I think these ethical concerns are more theoretical than actual, because penetration of W-Link technology doesn't seem all that high and most actual beacons only implement the very most basic features. The ability to display the serial numbers of individual beacons, for example, is pretty much limited to a USB dongle/software package that Mammut sells to organizations like ski patrols that loan out avalanche beacons and need to keep track of their inventory, check battery levels, etc.

Avalanche beacons are not the only radio-based approach to avalanche rescue. Around a decade after Lawton developed Skadi, a Swiss engineer designed a system called RECCO. If you have read my articles on loss prevention, I can succinctly explain RECCO by saying that it is just RF electronic article surveillance applied to the outdoors. If you have not, let's get into a little bit more detail.


I put a lot of time into writing this, and I hope that you enjoy reading it. If you can spare a few dollars, consider supporting me on ko-fi. You'll receive an occasional extra, subscribers-only post, and defray the costs of providing artisanal, hand-built world wide web directly from Albuquerque, New Mexico.


If you make an antenna that is a small loop, and then put a nonlinear device like a p-n diode in it, you end up with an antenna that behaves asymmetrically with regards to the phase of the radio wave passing over it. Intuitively, we can say that energy freely flows one way around the antenna, but not the other. The energy going that way becomes trapped, and gets re-emitted at at a harmonic frequency to the one that excited the device. This general principle is known as non-linear junction detection, and it has all kinds of interesting applications in fields like technical security, but its most common application is as one of the common types of retail anti-theft tagging.

RECCO is basically a retail loss prevention system aimed at the ground. RECCO tags, called "reflectors" in RECCO parlance, are a simple foil antenna and diode, which makes them cheap and durable. They are so cheap, and compact, that some brands of alpine gear (especially in Europe) build RECCO tags right into products like snow pants and ski boots. Dainese even offers a bicycle helmet with one. The RECCO detector emits a powerful and very directional RF field in an ISM band, and then monitors for the characteristic harmonics emitted by an excited tag.

This technology is not RFID, although it is conceptually similar, and has some of the same downsides such as a tendency for tags to be undetectable if they fall in the wrong point in the phase of the exciting field. For that reason, RECCO recommends that you have two reflectors at different locations on your person. The other main downside of RECCO is that it shifts most of the complexity into the detector, so they are relatively large and expensive. That's not so bad when you consider that detectors only need to be carried by ski patrol and SAR organizations. Rescuers basically aim the detector around until it picks up a signal, and then walk that way until they are aiming the detector straight down. That's where they dig.

RECCO has even designed a detector that can be underslung by a helicopter, although its 100 meter range will require some sporty flying. The RECCO system is generally much more popular in Europe than in the US, but penetration is increasing here, and RECCO publishes a list of SAR organizations with detectors that now includes a number of helicopters in the US and many major ski areas.

I view RECCO as having a bit of a belt-and-suspenders relationship with traditional avalanche beacons. Avalanche beacons are more common, and all beacons also function as receivers, so "buddy rescue" by a bystander is far more likely with beacon technology. On the other hand, RECCO is usable over a longer range (although not radically longer) and the detectors are more directional and potentially faster to use. Most avalanche centers seem to recommend that you carry both, although the traditional avalanche beacon is more important.

Finally, let's consider a couple of other pieces of avalanche safety equipment, which will lead us to consider efficacy.

The core avalanche PPE consists of a probe and a shovel, and both are available in collapsible form for portability. Besides locating technologies, though, the major innovation in avalanche safety is the avalanche airbag. These are backpacks that you wear with a parachute-like ripcord. When you pull it, the bag inflates into a big airbag behind you. There are two ways this is helpful: first, the airbag will tend to float on moving snow, making it less likely that you are buried. Second, if you do become buried, you can deflate the airbag to leave you with a void in the snow. This gives you more air to breath, and room to move, making it more likely that you can dig yourself out.

Avalanche airbags remind me a bit of the motorcycle airbags, as far as being very cutting-edge but also, you know, kind of questionable when it comes to efficacy. Manufacturers of airbags often advertise a 50% reduction in fatalities, but we have to take those numbers with a grain of salt.

One of the problems with safety technology for extreme risks is that the actual risk event just doesn't happen very often. In less abstract terms, people are not caught in avalanches all that often, so the total number of people who have been caught in an avalanche while wearing an avalanche airbag just isn't that big. This limits the confidence we can have in efficacy findings.

Well, I tracked down the paper that the 50% number comes from, and it's not actually that bad. They identified 424 people caught in severe avalanches, roughly half of which had avalanche airbags, and found that the airbags reduced mortality by 50% for people who successfully deployed them, and 41% overall given that there were various situations where they were not triggered or failed. The sample size is not huge, but it's bigger than I expected, and even at the bottom of the 95% confidence interval you still see a meaningful mortality reduction of around 10%.

So, avalanche airbags seem to work, but they do cost around $1,500, so make of that what you will. Another interesting number from that paper is actually a bit of an aside: of the sample of people considered, 99% had avalanche beacons! A basic avalanche beacon can be had for about $250, which is pretty similar to cheap satellite messenger or very basic PLB 3.

On the other hand, the passive nature of RECCO tags translates to a price advantage: a standalone reflector that you can clip to your pack only costs $30, which frankly still feels like a ripoff considering the simplicity of the design. I wonder if a RECCO detector would pick up enough RF EAS tags sewn into your clothing. Or shoplifted. One of the amusing things, though, is that despite being the cheap option (or perhaps beacuse of it), RECCO is relatively poorly backed by data. The number of actual rescues using RECCO remains small, although the development of the helicopter-based detector will probably lead to more successful uses.

Or, you know, just stay out of the mountains. They may be calling, but you don't have to go.

  1. Researching SNOTEL's history starts getting you into military R&D reports and formerly classified documents pretty quickly, and it's clear that both the US and other militaries have used meteor scatter for communications in the past. Possibly they still do today. If I were to place a wager, I'd say that SNOTEL is likely smaller than or on par with the military network that its equipment seems to have been adapted from. Unfortunately there is very little information in public about this military application (one of the reasons to suspect that meteor scatter is still relevant in the military context), so I can only speculate.↩

  2. This is at least vaguely similar to a common safety device employed on the towers of ski lifts, where a brittle metal bar is placed under the sheaves that support the rope. The lift's safety loop is passed through the bar. If the lift badly deropes at the tower (that is, the cable falls off of the sheaves), it falls on the bar, snapping it. That interrupts the loop of wire that runs from the drive end to the opposite end and back, which shuts off the motor and releases magnetic brakes.↩

  3. PLBs, or Portable Locator Beacons, are beacons made specifically for detection by the COSPAS-SARSAT satellite constellation. They are probably the gold standard in general search and rescue technology, and since they rely on a system operated as an international joint venture there are no service fees or subscriptions involved. Oddly, though, PLBs are actually pretty expensive. Here's the reason: authorities that mandate PLBs in various situations (e.g. the Coast Guard) have also specified rather exacting standards for service life, waterproofing, shock resistance, etc. This has the counter-intuitive result that two-way satellite messengers like Spot X or Garmin InReach can actually be cheaper to buy, but you will have to pay a service fee, and they are unlikely to survive the kind of "plane crash/sinking boat/car wreck" physical abuse that PLBs are tested for. As successive generations of more sophisticated COSPAS-SARSAT satellite payloads have launched, the system has also become very reliable, and in most circumstances PLBs are more likely to work than other types of satellite communicators. What I'm saying is that if you recreate in the middle of nowhere, you should consider buying a PLB, even though a good one can cost $500. At a five year rated lifespan, that's cheaper than a Spot or InReach plan even without the hardware purchase.↩

Error'd: Good Time

4 September 2026 at 06:30

Astute readers noticed last week that this editor (that is to say, me) had his own error'd failure to remember what day it was. Thank you for pointing it out promptly, and then proceeding to send in a bunch of examples of other sites calendar failures. Misery loves company!

Traveler's travails, from C_Chell "Trying to complete the form on https://www.ihg.com to tell when I plan to arrive at the hotel, I can't complete the form because of this little time problem."

06036a0253dc4d61b4bd648ab59c2dae

"You Have -1 Month(s) To Order!" announces dragoncoder047. "Ah, GradImages... the company that told all graduates that they'd get a free 5x7 but tried to charge me for it, then refused to honor my "unsubscribe" request and is *still* emailing me to this day... Can't do date math? Par for the course."

1820ae48b9df4a759ffbde45a8c715e0

"Stansted Temporal UI design" shared by Michael R. "While waiting for a friend to arrive at Stansted I see this. I better fire up the DeLorean to pick her up at 00:06 tomorrow."

8fa526db89fe46d88d6d2597fe0fa3ae

While he was hunting through the website, Michael R. also found that "The Stansted airport website seems to suffer from Directional Confusion."

89c37786ca724e82868eaab4f7285fcf

Nothing wrong with the calendar here, but Slaoput simply opposes mandatory existence. "I was filling out a form that said the Birthdate is optional, but when I hit submit I found out it was required. (I guess technically you have to be born to fill out the form.)"
NOT TO BE!

8bf00b3dc8ae4ca19481b42b9e63d0f4

[Advertisement] Picking up NuGet is easy. Getting good at it takes time. Download our guide to learn the best practice of NuGet for the Enterprise.

CodeSOD: Heating Up

3 September 2026 at 06:30

A common option for retrofitting heating and cooling into older homes is a mini-split, frequently tied to a heat pump. They're (relatively) cheap to install, energy efficient, and can be added without substantial modifications to the home. They also, annoyingly, are mostly controlled via IR remotes, making them challenging to wire up to home automation or even a household thermostat.

People have made solutions, and today's code comes from one of those solutions. Which, I want to stress, this code comes from an open source project for home automation, so it's not the code that's wrong, here. At first I thought it was, and had a moment of, "I'm not going to pick on some hobby project," but then I realised the hobby project points at a deeper issue.

// temperature helper these are direct mappings based on the remote
float toFahrenheit(float fromCelsius) {
    // Lookup table for specific mappings
    const std::map<float, int> lookupTable = {
        {16.0, 61}, {16.5, 62}, {17.0, 63}, {17.5, 64}, {18.0, 65},
        {18.5, 66}, {19.0, 67}, {20.0, 68}, {21.0, 69}, {21.5, 70},
        {22.0, 71}, {22.5, 72}, {23.0, 73}, {23.5, 74}, {24.0, 75},
        {24.5, 76}, {25.0, 77}, {25.5, 78}, {26.0, 79}, {26.5, 80},
        {27.0, 81}, {27.5, 82}, {28.0, 83}, {28.5, 84}, {29.0, 85},
        {29.5, 86}, {30.0, 87}, {30.5, 88}
    };

    // Check if the input is in the lookup table
    auto it = lookupTable.find(fromCelsius);
    if (it != lookupTable.end()) {
        return it->second;
    }

    // Default conversion and rounding to nearest integer
    return roundf(fromCelsius * 1.8 + 32.0);
}

Okay, I am going to pick on their code a little bit; using float as a key in a map is asking for trouble, because rounding errors are going to surprise you. But honestly, failing to find the key you're looking for is better than the opposite, since that actually does the correct thing. Because if you look carefully at the table, you'll see that it's wrong.

18C, for example, should be 64F. Well, 64.4F, but we're rounding to an integer. The choice here is to roughly map every 0.5C increase to a 1F increase, which is not the conversion factor. They try and correct- note how the table mostly steps by 0.5C, but skips 19.5C.

The opposite direction is similarly bad:

// temperature helper these are direct mappings based on the remote
float toCelsius(float fromFahrenheit) {
    // Lookup table for specific mappings
    const std::map<int, float> lookupTable = {
        {61, 16.0}, {62, 16.5}, {63, 17.0}, {64, 17.5}, {65, 18.0},
        {66, 18.5}, {67, 19.0}, {68, 20.0}, {69, 21.0}, {70, 21.5},
        {71, 22.0}, {72, 22.5}, {73, 23.0}, {74, 23.5}, {75, 24.0},
        {76, 24.5}, {77, 25.0}, {78, 25.5}, {79, 26.0}, {80, 26.5},
        {81, 27.0}, {82, 27.5}, {83, 28.0}, {84, 28.5}, {85, 29.0},
        {86, 29.5}, {87, 30.0}, {88, 30.5}
    };

    // Check if the input is in the lookup table
    auto it = lookupTable.find(static_cast<int>(fromFahrenheit));
    if (it != lookupTable.end()) {
        return it->second;
    }

    // Default conversion and rounding to nearest 0.5
    return roundf((fromFahrenheit - 32.0) / 1.8 * 2) / 2.0;
}

Here, we can be off by as much as a 1C, which is certainly a noticeable feeling.

At first glance, I thought this was just a misguided attempt at optimizing the lookup. For common values, do a lookup instead of calculating because it's faster. Seems like the kind of mistake a hobby project might make, and definitely not a WTF. But it's the comment which corrects me: these are direct mappings based on the remote.

These remotes usually have a display. So when you see on the remote that you're trying to set the temperature to a comfortable 72F, the remote is actually sending 22.5C to the unit. That's the actual temperature being sent.

Now, why on Earth does the remote behave this way? Well, I haven't cracked one open to read off the part numbers, but I'm going to go out on a limb and guess that the microcontoller in the remote doesn't handle floating point operations all that well. So it almost certainly does use a lookup table to decide what signal to send, and the lookup table is populated by "good enough" approximations of temperature conversions. There aren't a lot of places that use Fahrenheit, so being "close enough" is a reasonable solution. If you want accurate temperatures, use SI units, not "freedom units".

In the end, I'd say that neither the hobby project, nor the remote control are the WTF here; locales that insist on using weird ass units are.

[Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.

What You Measure

2 September 2026 at 06:30

Rachel joined a new team which was proudly "metrics driven". When she first met with her boss, Zane, he explained his thinking.

"We need to be data-driven to make good decisions, right? We're a manufacturing company. We make widgets. At the end of the day, we need to make the most widgets for the lowest cost of goods sold. So we track that, and that feeds into every decision."

The team oversaw an automated production line, which meant the software was a mix of robotics, embedded firmware, high-level web based monitoring tools, and thickets of dreaded PLC code. And because you can't build an entire factory for test purposes, they only way they could test real-world scales with real-world data was to roll changes out to production. They could simulate, they could run tests on subsets of the system, but a change in the production line software couldn't truly be validated until it rolled out into the real world.

Rachel's first task on the new team involved making some changes to their metrics dashboard. It was viewed as a good way to get her feet wet with the new team. As it turned out, the metrics dashboard was a Google Sheet, with a complex series of formulas that involved multi-level INDEX functions- essentially querying the spreadsheets like they were a database. Why not use an actual database? Oh, they did β€” six actually β€” but the company obeyed Remy's Law of Requirements Gathering: "no matter what the requirements the users ask for, what they really wanted was Excel". The database data was pulled into the spreadsheet for reporting.

Now, a complicated sheet pulling in data from not one, but six different databases, they must have a pretty complex model to explain how changes to their software would impact productivity. And since they needed to model the software to make predictions about how it'd behave in production, that model must be extremely useful.

Of course it wasn't. The only metrics they tracked were output metrics, variations on "widgets produced per unit time". There were some performance metrics, so you could maybe potentially identify "oh, our overall throughput dropped because unit 5 became a bottleneck and started taking 1.5 extra seconds per widget", but nothing that actually helped you understand how the complex system made decisions. Or even why unit 5 was taking longer.

For example, there was an automated quality control scanner. It examined widgets as they came off the line, and rejected defective ones based on a computer vision algorithm. Did that subsystem record why it rejected a widget? No, it did not. The CV model was able to tag widgets with a defect category based on what it saw, but that information didn't get recorded anywhere. In fact, it didn't even record how many widgets got rejected. The only way to know was to have an operator on the assembly line count widgets in the bin manually. Since that ate up a bunch of an operator's time, it never happened unless the developers begged for it. And since the operator still couldn't answer the question "why was this widget rejected", it wasn't all that useful anyway.

Every change to the software was scored against the overall output metrics. This meant that when Rachel was ready to push out her first software change, something that would record how many widgets were rejected and why, whether or not it could be deployed was dependent on seeing the change improve, or at least not regress, the widgets-over-time scores. But the widgets-over-time were a noisy metric; it varied based on which operators were working any given shift, or based on supply chain constraints. Or sometimes, based on when one of the machines was last calibrated- theoretically something that happened on a set schedule, but really was up to the operators. This meant the first three times Rachel rolled her code out for a test run, the metrics regressed. Nothing she changed should have impacted the metrics, but the metrics regressed due to environmental issues.

This meant making a simple change could take weeks, because you could only do final validation on the real system, which means you had to mark off a block of time for a test run, you could only run a handful of tests a day, and if metrics regressed you had to account for that before you could release the software for actual production use.

Over the first few months, Rachel added instrumentation to the code. Anything along the way to generating an output widget, she recorded. The hope was that once they had enough data, they could build a useful model of the system. Unfortunately, Zane had other ideas.

"So, you haven't improved our metrics," Zane said. "Which, I remind you, we're a metrics driven organization. Every change needs to improve our metrics."

"Sure, but I'm gathering more data so we have a better idea of what makes our metrics tick. We don't know why our system does some of the things it does, because we don't record any logging about the decisions it makes."

"Right, but we already gather the key metrics."

"But you don't gather the data that tells you why those metrics are what they are!"

"Sure," Zane said. "But those aren't our key metrics."

That, unfortunately for Rachel, was where things landed. Understanding their complex system was a low priority. Pushing top-level metrics without understanding what fed into them, that was the priority. That didn't mean Rachel was powerless: any time she made a change that she thought might help the top level metrics, she also made sure to add instrumentation that explained how that change behaved. It was the compromise that kept Zane happy: she released features that impacted the top-level metrics, but she also made the system more observable.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!

Representative Line: So Much Room

1 September 2026 at 06:30

Today's representative comment ran out of room.

int maxLen = getColumnSize(session, "audit", "text_value1") - 16; // Leave some room for

No, it isn't continued on the next line and just got trimmed out, except perhaps by a careless merge. This is the entire comment.

Clearly, written by David Chase, the creator of "The Sopranos".

There are so many things we might be leaving room for. We could leave some room for dessert. Leave some room for activities. Leave some room for the holy spirit. Leave some room for improvisation.

[Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.

Tales from the World Cup

31 August 2026 at 06:30

All I can say in response to our anonymous submitter's story is, ALMOST?!

With the World Cup being hosted in North America this year, I remembered this story that happened back in 2014. At the time I was working in Brazil, for a company that builds software systems for public services. And, with the World Cup being hosted there, in came the opportunity for local agencies to invest in modernization, with pretty much a blank check to get new services, so long as it was deployed before the end of the World Cup. And so the sales people did what they did best, and went around trying to upsell whoever would be willing to buy β€” no matter our actual capacity for developing the things.

So it was that I was pulled into this new fancy digital system for the police force of a state capital. However, we had only about 4 engineers available, and what they sold was a project estimated for a team of 20, to be delivered in 3 months, with no room for delay. And it wasn't just our core C&D product, but this massive thing with customized public-facing websites, live tracking of the position of different police cars delivered to a tablet in each car, automated reporting, etc.

Germany and Argentina face off in the final of the World Cup 2014 -2014-07-13 (5)

First thing: We received a pile of 24 resumes, and were told to choose 16 of those. Maybe 3 were acceptable, but we had to waste 1 month hiring and onboarding 13 other people who were worse than useless. Classic man-month problem. We eventually had to tell management that nothing would be delivered this way, so they did the very best next thing: fly us to this other city, so we could work embedded there, in full crunch mode for the delivery. We pretty much worked 12+ hours a day, 7 days a week, for those next 2 weeks.

Another situation: they wanted this system where people could take a photo of an incident in progress, and submit via this app + website, to be verified by an operator in real-time. We nicknamed it the "dick-pic encyclopedia." Even worse, we only had the budget to run a single server, so this thing receiving public traffic would live in the same system that was tracking police car locations. Luckily they were convinced it was a bad idea so it was only ever online for a short period of time.

Next, was the police car tracking. This was done by a tablet installed in each car, which would be sending and receiving location information. But, 1 week before our deadline, we were hitting a serious bug: everything was working when we ran the tests ourselves, but the cops would report very weird bugs when testing it in the field. So we asked to do some field debugging, and I went on a ride-along. Things were working pretty much fine everywhere, so I asked to be taken to where he remembered seeing the tablets failβ€”to which the policeman just decides to drive off straight into one of the favelas around the city. I guess I can cross out "doing debugging in a police car passenger seat in a notoriously dangerous neighborhood" off my bucket list. Root cause: turns out cellphone connections would be pretty spotty in those areas, which we weren't handling properly.

Either way, we delivered something on time that was severely below spec, and very much over-budget. Company tried to squirm out of paying overtime (was told that we would gain "prestige" by doing those extra hours), but I put my foot down and left that job shortly after. Last I heard they actually got sued for this and a bunch of similar projects, and almost went under.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!

Error'd: Hello, New Mexico!

28 August 2026 at 06:30

Peter G. shared with us yet another ordering bungled example of. "Should really say "please engage in an Easter egg hunt to find your language"."

3ccdf44218264528b28550518f7d6aea

"Google can't count" claimed Peter S.. It adds up. "Yet another proof that 0=1, this time from Google."

2d284d0f696d48669a9c59251ecf9bc0

"Thanks, Microsoft" groused Ivan "Ever since Microsoft ate university e-mail services worldwide and became responsible for major free software mailing lists, quality of service has been steadily dropping. In order to report delivery problems to Outlook, you need a Microsoft account. You're prevented from creating it at first because of "suspicious activity". Once you're in, the contact address is pre-filled for you with an invalid email. Once you fix that in the web developer toolbar, fuck you anyway! I think the form isn't actually expected to work; the fact that the request was submitted is an error. The only thing missing from the experience is the "beware of the leopard" sign."

ae9ba09cc9474a2f89b8358201b0419a

"Mango Math" needs a bit of money math for the rest of the world to understand. Michael R. muttered "I will buy it by the slice then." The joke here is on the tip of my tongue. Explainer: the new pence is one hundredth of the decimal pound. No shillings no more, decreps! At that ratio, 3p per slice of cheesecake would indeed be far less dear than four pounds for the whole thing, barring translucent slices. Alas, the reality is simply the boring fact that the price is 3p per gram. Not as funny but I'm chuckling imagining Michael's transparent serving of diet cheesecake. I'll leave it up to you to decide if a gram really counts as an "item".

bd6d8a538f7a45b2a81f8b52d425b180

Clint clucked "Got this email from Bigbadtoystore. Lots of links available for preorder!" I think the talented website builders behind the New Mexico DOT have been busy.

d7efd5aabe754173a54fccb84c60b942

[Advertisement] Picking up NuGet is easy. Getting good at it takes time. Download our guide to learn the best practice of NuGet for the Enterprise.

CodeSOD: The Big Family

27 August 2026 at 06:30

Some time ago, Charles shared with us some awful PHP, aka the most common sort. Today's code sample is maybe a little too big to sum up, but I'll let Charles take a crack at it.

It's so bad that even analyzing and laughing at it feels impossible. But it’s so bad, I couldn’t not share it.

I’m the only one handling all the IT-related tasks at my company, and I don’t have anyone here to vent or laugh about this kind of thing with. So, I figured, why not share it here? I’m hoping it’ll provide at least a little bit of catharsis or some dark humor.

To make sure the confidentiality of the codebase was respected, I took the liberty of generalizing it. You might notice some inconsistencies, but that’s just me trying to keep things neutral while protecting the original structure and functionality. Apologies if it looks a bit patchy – the goal was to avoid revealing any specific details or sensitive code.

The whole block is north of 400 lines, and it's doing a lot. Or well, maybe it's not, as you'll see.

Let's star with the outermost layer.

$resm_data = $data_source->fetchData("group=" . $item_id);
foreach ($resm_data as $key => $value) {
// rest of the code here
}

We fetch data from a data source, presumably a database, passing our condition as a string, which reeks of probable SQL injection, but I don't know what library they're using. I also note they're using the key/value style of array iteration, but never actually check the key.

    $option_id = $value->option_id;
    $resm_details = $detail_source->fetch($option_id);
    if ($resm_details) {
        $label = $resm_details->{"label$lang"};
        $description = $resm_details->{"description$lang"};
        $category = $resm_details->category;

Nice little bit of "meta" programming to get their localization working, it'll fetch labelen or labelde as needed. Definitely not a horrible, dangerous way to solve that problem.

We use that again to get our currency figured out. That lets us do number formatting. So much number formatting code.

        if ($category == 0) {
            $cost = $resm_details->{"cost" . $currency};
            $child_cost = $resm_details->{"child_cost" . $currency};
            $cost_info =
                $mot[101] . " : +" . number_format($cost, 2, ",", " ") . $currency_symbol . " - " .
                $mot[102] . " : +" . number_format($child_cost, 2, ",", " ") . $currency_symbol;
        } elseif ($category == 1) {
            $cost = $resm_details->{"cost" . $currency};
            $child_cost = 0;
            $cost_info = $mot[101] . " : +" . number_format($cost, 2, ",", " ") . $currency_symbol;
        } elseif ($category == 2) {
            $cost = 0;
            $child_cost = $resm_details->{"child_cost" . $currency};
            $cost_info = $mot[102] . " : +" . number_format($child_cost, 2, ",", " ") . $currency_symbol;
        } elseif ($category == 4) {
            $cost = $resm_details->{"cost" . $currency};
            $child_cost = 0;
            $cost_info = $mot[500] . " : +" . number_format($cost, 2, ",", " ") . $currency_symbol;
        } elseif (
            $resm_details->{"cost" . $currency} == 0 and
            $resm_details->{"child_cost" . $currency} == 0
        ) {
            $cost = 0;
            $child_cost = 0;
            $cost_info = "";
        } else {
            $cost = $resm_details->{"cost" . $currency};
            $child_cost = 0;
            $cost_info = $mot[200] . " : +" . number_format($cost, 2, ",", " ") . $currency_symbol;
        }

What is the $mot array? Why am I jumping to seemingly random locations in that array? Clearly it contains some headers for our output.

Anyway, there's plenty of HTML string munging happening too, don't you worry.

        if ($location == 1) {
            $quantity_block =
                '<div class="quantity-container">
                    <input type="text" value="1" id="quantity-' . $option_id . '" class="qty-control" name="quantity" min="1" max="1">
                    <div class="increase button">+</div>
                    <div class="decrease button">-</div>
                </div>';
            $quantity = 1;
        } else {
            $quantity_block = "";
            $quantity = "all";
        }

And then there's this little treat for parsing the time stored in our database: $time_data = json_decode($resm_details->time_data); That tells me they're storing date times as strings, so that's fun.

There are also a couple more bon mots as they build a drop down list:

            $departure_select = '<option value="-1">' . $mot[300] . '</option>';
            $arrival_select = '<option value="-1">' . $mot[301] . '</option>';

And then there's this monstrosity:

        // Add the main option to the template
        $TEMPLATE->SET_BLOCK_VARIABLES("Block_OPTIONS", [
            "ID" => $option_id,
            "QUANTITY" => $quantity,
            "LABEL_CLASS" => $label_class,
            "ACTION_CLASS" => $action_class,
            "LABEL" => $label,
            "DESCRIPTION" => $description,
            "QUANTITY_BLOCK" => $quantity_block,
            "COST_INFO" => $cost_info,
            "HOST_COST_DISPLAY" => $host_cost_display,
            "COST" => $cost,
            "CHILD_COST" => $child_cost,
            "LOCATION" => $location,
            "CATEGORY" => $category,
            "HOST_CLASS" => $host_class,
            "EXTRA" => $extra,
            "HOST_EXTRA" => $host_extra,
            "TIME_CHECKED" => ($has_times == 1) ? 'selected="' . $option_id . '" checked="true"' : '',
            "TIME_BLOCK" => $time_block
        ]);

All the nonsense that we concatenate together above gets shoved into some sort of template. And after that, that's where the good stuff starts. Because guess what? We have to do the same thing for child items.

        $resm_children_data = $data_source->fetchData("parent=" . $option_id);
        foreach ($resm_children_data as $child_key => $child_value) {
            $child_option_id = $child_value->option_id;
            $resm_child_details = $detail_source->fetch($child_option_id);

That's right, it's the same block of code, not quite copy/pasted, since they needed to put the word child in everything.

                // Add the child option to the template
                $TEMPLATE->SET_BLOCK_VARIABLES("Block_OPTIONS.Block_OPTIONS_CHILD", [
                    "ID" => $child_option_id,
                    "QUANTITY" => $child_quantity,
                    "LABEL_CLASS" => $child_label_class,
                    "ACTION_CLASS" => $child_action_class,
                    "LABEL" => $child_label,
                    "DESCRIPTION" => $child_description,
                    "QUANTITY_BLOCK" => $child_quantity_block,
                    "COST_INFO" => $child_cost_info,
                    "HOST_COST_DISPLAY" => $child_host_cost_display,
                    "COST" => $child_cost,
                    "CHILD_COST" => $child_extra_cost,
                    "LOCATION" => $child_location,
                    "CATEGORY" => $child_category,
                    "HOST_CLASS" => $child_host_class,
                    "EXTRA" => $child_extra,
                    "HOST_EXTRA" => $child_host_extra,
                    "TIME_CHECKED" => ($child_has_times == 1) ? 'selected="' . $child_option_id . '" checked="true"' : '',
                    "TIME_BLOCK" => $child_time_block
                ]);

And now, if you liked the child record, guess what? Those children have got siblings. What can I say, it's a big family.

                $resm_sibling_data = $data_source->fetchData("parent=" . $parent_option_id);
                foreach ($resm_sibling_data as $sibling_key => $sibling_value) {
                    $sibling_option_id = $sibling_value->option_id;
                    $resm_sibling_details = $detail_source->fetch($sibling_option_id);

And that means, yes, we also use that template thing again:

                        // Add the sibling option to the template
                        $TEMPLATE->SET_BLOCK_VARIABLES("Block_OPTIONS.Block_OPTIONS_CHILD", [
                            "ID" => $sibling_option_id,
                            "QUANTITY" => $sibling_quantity,
                            "LABEL_CLASS" => $sibling_label_class,
                            "ACTION_CLASS" => $sibling_action_class,
                            "LABEL" => $sibling_label,
                            "DESCRIPTION" => $sibling_description,
                            "QUANTITY_BLOCK" => $sibling_quantity_block,
                            "COST_INFO" => $sibling_cost_info,
                            "HOST_COST_DISPLAY" => $sibling_host_cost_display,
                            "COST" => $sibling_cost,
                            "SIBLING_COST" => $sibling_extra_cost,
                            "LOCATION" => $sibling_location,
                            "CATEGORY" => $sibling_category,
                            "HOST_CLASS" => $sibling_host_class,
                            "EXTRA" => $sibling_extra,
                            "HOST_EXTRA" => $sibling_host_extra,
                            "TIME_CHECKED" => ($sibling_has_times == 1) ? 'selected="' . $sibling_option_id . '" checked="true"' : '',
                            "TIME_BLOCK" => $sibling_time_block
                        ]);

Someday, I hope the person who wrote this learn about methods and function calls. Maybe they could write their own one day.

In any case, here's the whole thing:

$resm_data = $data_source->fetchData("group=" . $item_id);
foreach ($resm_data as $key => $value) {
    $option_id = $value->option_id;
    $resm_details = $detail_source->fetch($option_id);
    if ($resm_details) {
        $label = $resm_details->{"label$lang"};
        $description = $resm_details->{"description$lang"};
        $category = $resm_details->category;

        // Process cost based on category
        if ($category == 0) {
            $cost = $resm_details->{"cost" . $currency};
            $child_cost = $resm_details->{"child_cost" . $currency};
            $cost_info =
                $mot[101] . " : +" . number_format($cost, 2, ",", " ") . $currency_symbol . " - " .
                $mot[102] . " : +" . number_format($child_cost, 2, ",", " ") . $currency_symbol;
        } elseif ($category == 1) {
            $cost = $resm_details->{"cost" . $currency};
            $child_cost = 0;
            $cost_info = $mot[101] . " : +" . number_format($cost, 2, ",", " ") . $currency_symbol;
        } elseif ($category == 2) {
            $cost = 0;
            $child_cost = $resm_details->{"child_cost" . $currency};
            $cost_info = $mot[102] . " : +" . number_format($child_cost, 2, ",", " ") . $currency_symbol;
        } elseif ($category == 4) {
            $cost = $resm_details->{"cost" . $currency};
            $child_cost = 0;
            $cost_info = $mot[500] . " : +" . number_format($cost, 2, ",", " ") . $currency_symbol;
        } elseif (
            $resm_details->{"cost" . $currency} == 0 and
            $resm_details->{"child_cost" . $currency} == 0
        ) {
            $cost = 0;
            $child_cost = 0;
            $cost_info = "";
        } else {
            $cost = $resm_details->{"cost" . $currency};
            $child_cost = 0;
            $cost_info = $mot[200] . " : +" . number_format($cost, 2, ",", " ") . $currency_symbol;
        }

        $location = $resm_details->location;
        $label_class = $location == 1 ? "has-quantity" : "no-quantity";
        $action_class = $location == 1 ? "active-with-quantity" : "active-no-quantity";
        if ($location == 1) {
            $quantity_block =
                '<div class="quantity-container">
                    <input type="text" value="1" id="quantity-' . $option_id . '" class="qty-control" name="quantity" min="1" max="1">
                    <div class="increase button">+</div>
                    <div class="decrease button">-</div>
                </div>';
            $quantity = 1;
        } else {
            $quantity_block = "";
            $quantity = "all";
        }

        $has_times = $resm_details->has_times;

        if ($has_times == 1) {
            $time_counter++;
            $time_data = json_decode($resm_details->time_data);

            $departure_select = '<option value="-1">' . $mot[300] . '</option>';
            $arrival_select = '<option value="-1">' . $mot[301] . '</option>';

            $departures = $time_data->departures;
            $arrivals = $time_data->arrivals;

            foreach ($departures as $dep_key => $departure) {
                $departure_select .= '<option value="' . $dep_key . '">' . $departure . '</option>';
            }

            foreach ($arrivals as $arr_key => $arrival) {
                $arrival_select .= '<option value="' . $arr_key . '">' . $arrival . '</option>';
            }

            $departure_block = '<div class="col-half time-select-' . $option_id . '" style="padding-right: 0;">
                <select class="form-control time-departure" style="text-align: center;">' . $departure_select . '</select>
                </div>';

            $arrival_block = '<div class="col-half time-select-' . $option_id . '" style="padding-left: 0;">
                <select class="form-control time-arrival" style="text-align: center;">' . $arrival_select . '</select>
                </div>';

            $time_block = '<div class="row time-container">
                ' . $departure_block . $arrival_block . '
            </div><small class="error-message time-error">' . $mot[302] . '</small>';
        } else {
            $time_block = '';
        }

        $host_cost_display = "";
        $extra = $resm_details->extra;
        $host_extra = $resm_details->host_extra;
        $host_class = "";

        if ($extra == 1) {
            $extra_cost_1 = $resm_details->{"cost" . $currency . "_1"};
            $extra_cost_2 = $resm_details->{"cost" . $currency . "_2"};

            $default_extra = $host_price == 0 ? $extra_cost_2 : $extra_cost_1;
            $cost_info = $mot[101] . ' : +<span class="extra-cost">' . number_format($default_extra, 2, ",", " ") .
                "</span>" . $currency_symbol . " - " . $mot[102] . ' : +<span class="child-cost">0.00</span>' . $currency_symbol;

            $host_class = " extra-option";
        }
        if ($host_extra == 1) {
            $host_cost_display =
                '<span class="host-cost">' . $mot[500] . ' : +<span class="host-price">0.00</span>' . $currency_symbol .
                "</span><br>";
        }

        // Add the main option to the template
        $TEMPLATE->SET_BLOCK_VARIABLES("Block_OPTIONS", [
            "ID" => $option_id,
            "QUANTITY" => $quantity,
            "LABEL_CLASS" => $label_class,
            "ACTION_CLASS" => $action_class,
            "LABEL" => $label,
            "DESCRIPTION" => $description,
            "QUANTITY_BLOCK" => $quantity_block,
            "COST_INFO" => $cost_info,
            "HOST_COST_DISPLAY" => $host_cost_display,
            "COST" => $cost,
            "CHILD_COST" => $child_cost,
            "LOCATION" => $location,
            "CATEGORY" => $category,
            "HOST_CLASS" => $host_class,
            "EXTRA" => $extra,
            "HOST_EXTRA" => $host_extra,
            "TIME_CHECKED" => ($has_times == 1) ? 'selected="' . $option_id . '" checked="true"' : '',
            "TIME_BLOCK" => $time_block
        ]);

        $resm_children_data = $data_source->fetchData("parent=" . $option_id);
        foreach ($resm_children_data as $child_key => $child_value) {
            $child_option_id = $child_value->option_id;
            $resm_child_details = $detail_source->fetch($child_option_id);
            if ($resm_child_details) {
                $child_label = $resm_child_details->{"label$lang"};
                $child_description = $resm_child_details->{"description$lang"};
                $child_category = $resm_child_details->category;

                // Process cost for child category
                if ($child_category == 0) {
                    $child_cost = $resm_child_details->{"cost" . $currency};
                    $child_extra_cost = $resm_child_details->{"child_cost" . $currency};
                    $child_cost_info =
                        $mot[101] . " : +" . number_format($child_cost, 2, ",", " ") . $currency_symbol . " - " .
                        $mot[102] . " : +" . number_format($child_extra_cost, 2, ",", " ") . $currency_symbol;
                } elseif ($child_category == 1) {
                    $child_cost = $resm_child_details->{"cost" . $currency};
                    $child_extra_cost = 0;
                    $child_cost_info = $mot[101] . " : +" . number_format($child_cost, 2, ",", " ") . $currency_symbol;
                } elseif ($child_category == 2) {
                    $child_cost = 0;
                    $child_extra_cost = $resm_child_details->{"child_cost" . $currency};
                    $child_cost_info = $mot[102] . " : +" . number_format($child_extra_cost, 2, ",", " ") . $currency_symbol;
                } elseif ($child_category == 4) {
                    $child_cost = $resm_child_details->{"cost" . $currency};
                    $child_extra_cost = 0;
                    $child_cost_info = $mot[500] . " : +" . number_format($child_cost, 2, ",", " ") . $currency_symbol;
                } elseif (
                    $resm_child_details->{"cost" . $currency} == 0 and
                    $resm_child_details->{"child_cost" . $currency} == 0
                ) {
                    $child_cost = 0;
                    $child_extra_cost = 0;
                    $child_cost_info = "";
                } else {
                    $child_cost = $resm_child_details->{"cost" . $currency};
                    $child_extra_cost = 0;
                    $child_cost_info = $mot[200] . " : +" . number_format($child_cost, 2, ",", " ") . $currency_symbol;
                }

                $child_location = $resm_child_details->location;
                $child_label_class = $child_location == 1 ? "has-quantity" : "no-quantity";
                $child_action_class = $child_location == 1 ? "active-with-quantity" : "active-no-quantity";
                if ($child_location == 1) {
                    $child_quantity_block =
                        '<div class="quantity-container">
                    <input type="text" value="1" id="child-quantity-' . $child_option_id . '" class="qty-control" name="quantity" min="1" max="1">
                    <div class="increase button">+</div>
                    <div class="decrease button">-</div>
                </div>';
                    $child_quantity = 1;
                } else {
                    $child_quantity_block = "";
                    $child_quantity = "all";
                }

                $child_has_times = $resm_child_details->has_times;

                if ($child_has_times == 1) {
                    $child_time_counter++;
                    $child_time_data = json_decode($resm_child_details->time_data);

                    $child_departure_select = '<option value="-1">' . $mot[300] . '</option>';
                    $child_arrival_select = '<option value="-1">' . $mot[301] . '</option>';

                    $child_departures = $child_time_data->departures;
                    $child_arrivals = $child_time_data->arrivals;

                    foreach ($child_departures as $child_dep_key => $child_departure) {
                        $child_departure_select .= '<option value="' . $child_dep_key . '">' . $child_departure . '</option>';
                    }

                    foreach ($child_arrivals as $child_arr_key => $child_arrival) {
                        $child_arrival_select .= '<option value="' . $child_arr_key . '">' . $child_arrival . '</option>';
                    }

                    $child_departure_block = '<div class="col-half time-select-' . $child_option_id . '" style="padding-right: 0;">
                <select class="form-control time-departure" style="text-align: center;">' . $child_departure_select . '</select>
                </div>';

                    $child_arrival_block = '<div class="col-half time-select-' . $child_option_id . '" style="padding-left: 0;">
                <select class="form-control time-arrival" style="text-align: center;">' . $child_arrival_select . '</select>
                </div>';

                    $child_time_block = '<div class="row time-container">
                ' . $child_departure_block . $child_arrival_block . '
            </div><small class="error-message time-error">' . $mot[302] . '</small>';
                } else {
                    $child_time_block = '';
                }

                $child_host_cost_display = "";
                $child_extra = $resm_child_details->extra;
                $child_host_extra = $resm_child_details->host_extra;
                $child_host_class = "";

                if ($child_extra == 1) {
                    $child_extra_cost_1 = $resm_child_details->{"cost" . $currency . "_1"};
                    $child_extra_cost_2 = $resm_child_details->{"cost" . $currency . "_2"};

                    $default_child_extra = $host_price == 0 ? $child_extra_cost_2 : $child_extra_cost_1;
                    $child_cost_info = $mot[101] . ' : +<span class="extra-cost">' . number_format($default_child_extra, 2, ",", " ") .
                        "</span>" . $currency_symbol . " - " . $mot[102] . ' : +<span class="child-cost">0.00</span>' . $currency_symbol;

                    $child_host_class = " extra-option";
                }
                if ($child_host_extra == 1) {
                    $child_host_cost_display =
                        '<span class="host-cost">' . $mot[500] . ' : +<span class="host-price">0.00</span>' . $currency_symbol .
                        "</span><br>";
                }

                // Add the child option to the template
                $TEMPLATE->SET_BLOCK_VARIABLES("Block_OPTIONS.Block_OPTIONS_CHILD", [
                    "ID" => $child_option_id,
                    "QUANTITY" => $child_quantity,
                    "LABEL_CLASS" => $child_label_class,
                    "ACTION_CLASS" => $child_action_class,
                    "LABEL" => $child_label,
                    "DESCRIPTION" => $child_description,
                    "QUANTITY_BLOCK" => $child_quantity_block,
                    "COST_INFO" => $child_cost_info,
                    "HOST_COST_DISPLAY" => $child_host_cost_display,
                    "COST" => $child_cost,
                    "CHILD_COST" => $child_extra_cost,
                    "LOCATION" => $child_location,
                    "CATEGORY" => $child_category,
                    "HOST_CLASS" => $child_host_class,
                    "EXTRA" => $child_extra,
                    "HOST_EXTRA" => $child_host_extra,
                    "TIME_CHECKED" => ($child_has_times == 1) ? 'selected="' . $child_option_id . '" checked="true"' : '',
                    "TIME_BLOCK" => $child_time_block
                ]);

                $resm_sibling_data = $data_source->fetchData("parent=" . $parent_option_id);
                foreach ($resm_sibling_data as $sibling_key => $sibling_value) {
                    $sibling_option_id = $sibling_value->option_id;
                    $resm_sibling_details = $detail_source->fetch($sibling_option_id);
                    if ($resm_sibling_details) {
                        $sibling_label = $resm_sibling_details->{"label$lang"};
                        $sibling_description = $resm_sibling_details->{"description$lang"};
                        $sibling_category = $resm_sibling_details->category;

                        // Process cost for sibling category
                        if ($sibling_category == 0) {
                            $sibling_cost = $resm_sibling_details->{"cost" . $currency};
                            $sibling_extra_cost = $resm_sibling_details->{"sibling_cost" . $currency};
                            $sibling_cost_info =
                                $mot[101] . " : +" . number_format($sibling_cost, 2, ",", " ") . $currency_symbol . " - " .
                                $mot[102] . " : +" . number_format($sibling_extra_cost, 2, ",", " ") . $currency_symbol;
                        } elseif ($sibling_category == 1) {
                            $sibling_cost = $resm_sibling_details->{"cost" . $currency};
                            $sibling_extra_cost = 0;
                            $sibling_cost_info = $mot[101] . " : +" . number_format($sibling_cost, 2, ",", " ") . $currency_symbol;
                        } elseif ($sibling_category == 2) {
                            $sibling_cost = 0;
                            $sibling_extra_cost = $resm_sibling_details->{"sibling_cost" . $currency};
                            $sibling_cost_info = $mot[102] . " : +" . number_format($sibling_extra_cost, 2, ",", " ") . $currency_symbol;
                        } elseif ($sibling_category == 4) {
                            $sibling_cost = $resm_sibling_details->{"cost" . $currency};
                            $sibling_extra_cost = 0;
                            $sibling_cost_info = $mot[500] . " : +" . number_format($sibling_cost, 2, ",", " ") . $currency_symbol;
                        } elseif (
                            $resm_sibling_details->{"cost" . $currency} == 0 and
                            $resm_sibling_details->{"sibling_cost" . $currency} == 0
                        ) {
                            $sibling_cost = 0;
                            $sibling_extra_cost = 0;
                            $sibling_cost_info = "";
                        } else {
                            $sibling_cost = $resm_sibling_details->{"cost" . $currency};
                            $sibling_extra_cost = 0;
                            $sibling_cost_info = $mot[200] . " : +" . number_format($sibling_cost, 2, ",", " ") . $currency_symbol;
                        }

                        $sibling_location = $resm_sibling_details->location;
                        $sibling_label_class = $sibling_location == 1 ? "has-quantity" : "no-quantity";
                        $sibling_action_class = $sibling_location == 1 ? "active-with-quantity" : "active-no-quantity";
                        if ($sibling_location == 1) {
                            $sibling_quantity_block =
                                '<div class="quantity-container">
                    <input type="text" value="1" id="sibling-quantity-' . $sibling_option_id . '" class="qty-control" name="quantity" min="1" max="1">
                    <div class="increase button">+</div>
                    <div class="decrease button">-</div>
                </div>';
                            $sibling_quantity = 1;
                        } else {
                            $sibling_quantity_block = "";
                            $sibling_quantity = "all";
                        }

                        $sibling_has_times = $resm_sibling_details->has_times;

                        if ($sibling_has_times == 1) {
                            $sibling_time_counter++;
                            $sibling_time_data = json_decode($resm_sibling_details->time_data);

                            $sibling_departure_select = '<option value="-1">' . $mot[300] . '</option>';
                            $sibling_arrival_select = '<option value="-1">' . $mot[301] . '</option>';

                            $sibling_departures = $sibling_time_data->departures;
                            $sibling_arrivals = $sibling_time_data->arrivals;

                            foreach ($sibling_departures as $sibling_dep_key => $sibling_departure) {
                                $sibling_departure_select .= '<option value="' . $sibling_dep_key . '">' . $sibling_departure . '</option>';
                            }

                            foreach ($sibling_arrivals as $sibling_arr_key => $sibling_arrival) {
                                $sibling_arrival_select .= '<option value="' . $sibling_arr_key . '">' . $sibling_arrival . '</option>';
                            }

                            $sibling_departure_block = '<div class="col-half time-select-' . $sibling_option_id . '" style="padding-right: 0;">
                <select class="form-control time-departure" style="text-align: center;">' . $sibling_departure_select . '</select>
                </div>';

                            $sibling_arrival_block = '<div class="col-half time-select-' . $sibling_option_id . '" style="padding-left: 0;">
                <select class="form-control time-arrival" style="text-align: center;">' . $sibling_arrival_select . '</select>
                </div>';

                            $sibling_time_block = '<div class="row time-container">
                ' . $sibling_departure_block . $sibling_arrival_block . '
            </div><small class="error-message time-error">' . $mot[302] . '</small>';
                        } else {
                            $sibling_time_block = '';
                        }

                        $sibling_host_cost_display = "";
                        $sibling_extra = $resm_sibling_details->extra;
                        $sibling_host_extra = $resm_sibling_details->host_extra;
                        $sibling_host_class = "";

                        if ($sibling_extra == 1) {
                            $sibling_extra_cost_1 = $resm_sibling_details->{"cost" . $currency . "_1"};
                            $sibling_extra_cost_2 = $resm_sibling_details->{"cost" . $currency . "_2"};

                            $default_sibling_extra = $host_price == 0 ? $sibling_extra_cost_2 : $sibling_extra_cost_1;
                            $sibling_cost_info = $mot[101] . ' : +<span class="extra-cost">' . number_format($default_sibling_extra, 2, ",", " ") .
                                "</span>" . $currency_symbol . " - " . $mot[102] . ' : +<span class="sibling-cost">0.00</span>' . $currency_symbol;

                            $sibling_host_class = " extra-option";
                        }
                        if ($sibling_host_extra == 1) {
                            $sibling_host_cost_display =
                                '<span class="host-cost">' . $mot[500] . ' : +<span class="host-price">0.00</span>' . $currency_symbol .
                                "</span><br>";
                        }

                        // Add the sibling option to the template
                        $TEMPLATE->SET_BLOCK_VARIABLES("Block_OPTIONS.Block_OPTIONS_CHILD", [
                            "ID" => $sibling_option_id,
                            "QUANTITY" => $sibling_quantity,
                            "LABEL_CLASS" => $sibling_label_class,
                            "ACTION_CLASS" => $sibling_action_class,
                            "LABEL" => $sibling_label,
                            "DESCRIPTION" => $sibling_description,
                            "QUANTITY_BLOCK" => $sibling_quantity_block,
                            "COST_INFO" => $sibling_cost_info,
                            "HOST_COST_DISPLAY" => $sibling_host_cost_display,
                            "COST" => $sibling_cost,
                            "SIBLING_COST" => $sibling_extra_cost,
                            "LOCATION" => $sibling_location,
                            "CATEGORY" => $sibling_category,
                            "HOST_CLASS" => $sibling_host_class,
                            "EXTRA" => $sibling_extra,
                            "HOST_EXTRA" => $sibling_host_extra,
                            "TIME_CHECKED" => ($sibling_has_times == 1) ? 'selected="' . $sibling_option_id . '" checked="true"' : '',
                            "TIME_BLOCK" => $sibling_time_block
                        ]);
                    }
                }
            }
        }
    }
}
[Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.

CodeSOD: Lock 'Em Dead

26 August 2026 at 06:30

Kevin sends us an exception handler from C++. Let's see if we can spot what's going wrong:

catch (Exception::Deadlock)
{
   retry;
}

When we catch a deadlock happening, we retry. That's not a keyword in C++, and looking at how it's used, it has to be some kind of macro, and I suspect that the macro is hiding a goto underneath it.

The real problem, though, is that we suspect we're in a deadlock situation. That means this thread is waiting on a resource held by another thread which is waiting for a resource held by this thread. Neither train may continue until the other has passed. So this retry only works if it releases the resource held by this thread (letting the deadlocking thread proceed). But does it?

Not according ot Kevin. The code already had a pile of deadlocks in it, so they brought in a highly paid consultant to try and fix them by reordering access and tracing where mutexes were causing issues. This retry just jumps back up to the top of the block, without releasing any resources. It "seems the consultant wanted to add some deadlocks of their own," Kevin says.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!

How Accurate Have Ed Zitron’s Predictions Been?

By: Nick Heer
4 September 2026 at 02:47

Dan Luu, after finding one wrong prediction after another made by Ed Zitron, comparing him to mediocre futurists, and exploring his writing style:

That last sentence really sums up Zitron’s position. β€œThere are so many guys to be mad at the moment”. In this talk, he throws in this jab at Andreesen and blames Andreesen for Meta, Google, and Microsoft pursuing growth. In reality, if Marc Andreesen had never existed, Meta, Google, and Microsoft would almost certainly still be trying to grow so we of course cannot actually blame Andreesen for these companies trying to grow. There’s just this thing that he says is bad, and in his usual style, he pulls some person and says they’re the evil villain that’s to blame for this, and then moves on to the next non sequitur.

Instead of more carefully scrutinizing Zitron’s record, outlets like Vanity Fair are publishing soft interviews with him where he gets to make predictions like β€œlarge language models, when you remove all of the insane financialization, it’s probably a $30 billion-a-year industry”. Oh, sure, they ask a single question about wrong predictions since 2024, but he brushes it off by saying he has learned lots in those two years β€” Luu documents incorrect predictions all the way up until November 2025, after which β€œmost further predictions that I saw were either non-falsifiable or resolve in the future” β€” and ends the interview by saying β€œ[w]hat comes after the A.I. bubble is actually a little scarier”. Ominous.

The beauty of Zitron’s voluminous output, for him, is that there is a vast difference between what he actually writes and what people remember. Financial experts and more reputable journalists have raised plenty of concerns about how much money is being spent on developing this infrastructure, and how highly these companies are valued. But the words Zitron writes are far more incendiary and conspiratorial than many seem to remember. It is frustrating to see his many media experiences filling the role of the A.I. skeptic when there are far more qualified, sober, and accurate options. We have enough boosters; this is an industry that is co-signed by the world’s most powerful economies. We deserve better A.I. criticism in popular media.

βŒ₯ Permalink

About Half of U.S. Smartphone Owners Say They Spend Too Much Time Using It

By: Nick Heer
2 September 2026 at 21:52

William Bishop of Pew Research:

Between scrolling, notifications, and messages, smartphones can be hard to put down. Just over half of U.S. adults say they spend too much time on their smartphone, according to a Pew Research Center survey from May and June 2026. About a third of adults say their smartphone use is about right, while just 3% say they spend too little time using these devices.

Notably, 70% of smartphone owners in the U.S. aged 18–29 say they believe they use it too much. I am skeptical of public polling β€” maybe we are societally more approving of shaming our own device use β€” but this also tracks with what I hear casually from friends and family. Anecdotally, many people I know have expressed that they want to be on their phones less often.

But the editors of Andreessen Horowitz’s It’s Time to Build newsletter β€” hosted on Substack, a platform they are investors in β€” believe this behaviour actually indicates people are very happy. Ruby Thelot, professor of design and media studies at New York University and β€œastute tech observer”, says the heavy use of social media platforms, like Meta’s Facebook and Instagram β€” Andreessen Horowitz invested in both, and Marc Andreessen is on Meta’s board; none of this is disclosed β€” is simply evidence we love them:

People en masse are getting on social media, by choice. From 2016 until now, the number of social media users has grown from 2.5B to close to 6B people. Enshittification isn’t real. It’s three TikToks in a trenchcoat, and, maybe, a book deal. It’s good for discourse but does not describe actual reality and user patterns.

Returning to Instagram. The average daily usage showcases an increase in time spent on Instagram every day as well. Users are coming back for more, in a highly competitive attention arena, year after year.

There is a lot of assumption in these two paragraphs, and I think the β€œclose to 6B people” is worth examining to start. Thelot attributes this statistic in the chart above to Backlinko. It cites no source, but a web search indicates to me this originates with Manochi’s DataReportal, which disclaims β€œβ€˜user identities’ may not represent unique human individuals” because, as the company explains on the sixth slide of its report, it may count multiple social media accounts or business accounts as individual identities. These are not necessarily people, and Manochi says it is improper to compare figures year over year as Thelot does. (And, to be fair, which Manochi also does on slide 321.)

A lack of rigour is not unique to this data point.

Thelot next shows a chart indicating daily Instagram use rose from 25 minutes in 2017 to nearly 34 in 2026. This is, to Thelot, simply evidence that people like using it and want more. But, to return to the Pew poll above, it seems that people do not feel good about spending more time on their smartphones. Though Pew did not ask (PDF) about social media specifically, roughly half of respondents aged 18–29 said it negatively affects their productivity. And, according to slide 337 of that Manochi report, 30.7–44.0% of people say they use social media to β€œfill up spare time”, trending higher for younger generations. It is plausible that more younger people are spending increasing amounts of time on social media apps and they do not feel good about it. In other words, the time spent numbers are not a good proxy for enjoyment or value.

Emanuel Maiberg, 404 Media:

People need to be on Linkedin to find jobs. Municipalities and news organizations share important updates on social media first. You might be pulled onto Facebook or WhatsApp against your will because your local school or community of parents congregate there. That doesn’t mean they like it. It is possible to hate something with your entire being and still participate in it.

Regardless of whether you call it β€œaddiction” or some kind of compulsive behaviour, it is plausible many people dislike their own actions but struggle to change them. It is also possible these products are designed to take advantage of that to extract more time out of each user.

βŒ₯ Permalink

Apple’s John Ternus Era Begins

By: Nick Heer
1 September 2026 at 22:34

Kalley Huang, New York Times (gift link)

John Ternus became Apple’s chief executive on Tuesday, succeeding Tim Cook, the company’s leader for the last 15 years. The long-anticipated handoff, Mr. Cook has said, will be β€œperfectly smooth.”

[…]

This summer, Apple hired Nate Gatten from American Airlines to lead government affairs, replacing Kate Adams, who will retire this year. Laura Legros, a hardware engineering vice president and deputy of Mr. Ternus’s before retiring from Apple in 2022, has rejoined the company, three people familiar with her hiring said, speaking on the condition of anonymity. Ms. Legros, who reports to Mr. Ternus, could act as his adviser and emissary to various parts of the company, the three people said.

Juli Clover, MacRumors:

Apple’s Phil Schiller is no longer going to run the App Store or oversee product events, reports Bloomberg. Schiller isn’t leaving Apple, but he is narrowing his responsibilities and working on unspecified projects.

Employees at Apple told Bloomberg that 66-year-old Schiller appears to be taking another step toward retirement.

Cook and Ternus each sent pretty anodyne company-wide memos about the transition. Cook’s tenure was the longest of any CEO in Apple’s history and he was the one who turned it from a successful company into a global behemoth.

The thing I have liked about Apple β€” one of the things that made me a longtime customer and someone who writes about the company β€” is that it has historically been a very simple kind of business: it designs products and sells them to people, mostly. Every one of its peers is a more complicated business. They often balance the needs of massive institutional and government customers, advertisers, or two-sided marketplaces.

That change began in the latter years of Jobs’ tenure and accelerated under Cook. Most software, including operating systems, was accounted for as part of device purchases, and was turned into a software-as-a-service model. Its subscription-based business became a revenue growth centre, which was important for Wall Street because it was a way to turn the company’s successful but inconsistent device sales into predictable money printers. And there are now ads and upsells throughout the operating systems, which are shown to all users regardless of how much other money they have already given Apple.

Ternus has inherited that Apple. Regardless of how much he gives off the vibe of a cool Californian β€” by way of Philadelphia β€” who just cares about the best stuff, he is also selling ad space and making sure more people upgrade to Apple One.

βŒ₯ Permalink

Travelling With a Separatist Alberta Group That Thinks the CBC Are β€˜Federalist Scumbags’ Publishing β€˜Drivel’

By: Nick Heer
30 August 2026 at 17:36

Joel Dryden, in a CBC News article with the headline β€œOn the Road With Pro-Independence Albertans, One Small Town at a Time”:

The restaurant owner, who gained prominence during the COVID-19 pandemic, has hit the road this summer to talk independence, travelling with a group of people who also support the idea of Alberta becoming its own nation.

[…]

On the back of the vehicle, a Bible verse printed in script: Jeremiah 29:7 β€” β€œSeek the peace and prosperity of the city to which I have carried you into exile. Pray to the Lord for it, because if it prospers, you too will prosper.”

Mike Skerrett, in a 2018 McSweeney’s article with the headline β€œI Traveled to a Diner In Trump Country to Write Another Article On Whether the President’s Supporters Still Want to, Quote, β€˜Smash My Libtard Face In'”:

I came to this diner, The No Safe Space CafΓ©, to get a taste of the Real America. This America exists outside the liberal echo chambers, somewhere with real diversity of thought: The opinions of straight, white, Christian men.

Chris Scott β€” the β€œrestaurant owner” driving the separatist campaign bus with Dryden aboard β€” has previously called the CBC β€œliars, thieves and Federalist bootlickers” who publish β€œdrivel”. Replace the U.S.-centrism of the McSweeney’s piece with a Canadian vibe, and is it really all that different?

βŒ₯ Permalink

β€˜Here’s What’s Coming’

By: Nick Heer
29 August 2026 at 02:04

Here is a list of headlines; perhaps you will spot a theme:

All of these since January β€” and I excluded most rewrites of articles by Mark Gurman, Ming-Chi Kuo, and other well-known Apple rumour writers. Also, my list does not include articles about products rumoured to be announced at specific events. They also have another thing in common: nearly all are by Ryan Christoffel, who seems to have taken up the 9to5Mac beat for making listicles of Apple products that could be updated in the future. Cool.

Careful readers might think I listed one of these articles twice β€” the β€œ15+ new products this fall” one. But this is because Christoffel originally published it in May, and then changed the date on it to August after making a few changes.

I do not know who this is supposed to serve. Maybe this is a play for search engine traffic or A.I. chatbot citations. Maybe it works pretty well, too, and maybe I should be less cynical about these kinds of churned-out listicles in a time when A.I. search features are capturing traffic that used to go to third-party websites. Or maybe this is all just filler when there is nothing newsworthy but you need to publish a dozen or so articles daily.

Update: D. Griffin Jones, formerly at Cult of Mac, says on Bluesky:

It’s a secret third thing: Google Discover, Google News, Flipboard, Apple News, and other algorithmic aggregators. Articles like that tend to reach a much broader audience.

β€œOne listicle a day” was our goal at Cult of Mac before I was laid off. It’s a tough time out there for independent blogs. The Google AI overview is devastating.

I obviously have no authority to give business advice. I would only point out that it is disappointing to hear about chasing referral strategies. One would think the pivot to video era would be treated as a cautionary tale and not something to repeat β€” if one, I suppose, is not responsible for staff and paycheques.

βŒ₯ Permalink

Society Runs on Stimulants

By: Nick Heer
28 August 2026 at 23:09

Alex Skopic, at Current Affairs, wrote about the functional use and abuse of stimulants by people who are overworked. I stumbled upon it by way of the latest issue of Web Curios, and I think the general thrust of the article is something I find noteworthy enough to link to it. Alas, it is sloppy in the details.

The second paragraph is a good place to start. Skopic dumps a litany of numbers about declining affordability and the difficulties of modern life; among them:

[…] And according to the Bureau of Labor Statistics, 21.6 percent of Americans now work longer than 40 hours a week, with a beleaguered five percent working as many as 60 hours. […]

The way this is phrased β€” β€œnow work longer than 40 hours a week”, emphasis mine β€” may give the impression this figure is a new high, either historically or at least in recent memory. But it is not. This figure has been declining for at least twenty years. In 2005, the BLS reported 27.9% of people employed in nonagricultural jobs in the U.S. worked 41 or more hours; by 2015, that was down to 24.8%, compared to 21.6% in 2025. The same drop is also present for workers committing 60 hours per week or more: 7.2% in 2005, 6.3% in 2015, and 5.4% in 2025.

I am not selectively quoting here. I picked ten and twenty years ago for comparative convenience, but you can check the intervening years and find a clear trend. The misuse of this data point is a poor choice when there is perhaps an interesting narrative in reconciling the declining percentage of overworked U.S. adults with the proliferation of highly caffeinated beverages, mysterious energy shots, and powders and supplements of questionable substance.

This is the kind of sentence that made me begin searching up all kinds of stuff in this article. It raises doubts, making me do the kind of work all of us should probably do any time we read something, but do not because we decide we have better things to do. Well, I decided this is what I was going to do today. I am going to skip past the health claims because they are not in my wheelhouse β€” double-checking easily verified numbers is something I am more comfortable with β€” and I will jump to this part:

Monster Energy, for instance, seems to think it owns the word β€œmonster.” In 2009, the company threatened to sue a small brewery in Vermont for selling a beer called the β€œVermonster,” claiming it infringed their trademark rights. That was at least a beverage, but in 2020 they did the same thing with video game company Ubisoft, forcing them to change a game’s title from β€œGods and Monsters” to β€œImmortals Fenyx Rising.” In 2023 they once again targeted the gaming industry, this time over an independent game called β€œDark Deception: Monsters and Mortals”; according to the developers, the drink company wanted them to β€œagree to never use a green & white logo on a black background for any game we ever make. So they own the colors green & white too apparently.” In all of these cases, Monster’s legal claim is shaky at best β€” but with a revenue of $7.9 billion in 2025, they have enough money to bankrupt anyone who tries to take them to court, so it doesn’t matter.

Monster Energy is an aggressive litigant when it comes to protecting its trademarks, but it does not always succeed. Glowstick Entertainment, makers of β€œDark Deception: Monsters and Mortals”, fought and won its trademark case. Also, I think it would have been useful for Skopic to cite cases where Monster Energy lost β€” or likely would have lost β€” but the defendant went out of business anyhow. For example, in a 2022 case against a store with β€œMonster” in its name, the energy drink company would have been unlikely to prevail (PDF). Even so, a search of state records showed the company was ultimately dissolved. No matter whether this was directly connected to the litigation, it is a stronger argument for having β€œenough money to bankrupt anyone who tries to take them to court” than this evidence-free article.

Also, it is kind of weird that Coca-Cola β€” a company which owns 21% of Monster Energy after the latter acquired Coke’s energy drink portfolio, has a line of teas and coffees, and whose namesake beverage used to contain cocaine and still contains caffeine β€” goes entirely unmentioned in this piece. Perhaps it is too obvious, or feels like too much of a stretch. But it is strange to dedicate a paragraph to Monster’s trademark battles, which has nothing to do with the thesis of this piece, and leave the Coca-Cola stone unturned.

I wish an editor took another pass on this article as I think there are points that could be clarified, facts that desperately need checking, and interesting narratives that could be teased out. Skopic notes, for instance, the supposed incompatibility of sleep with an economy based on relentless production and consumption. It is true that we cannot make or buy things in your sleep, nor can we provide revenue-generating services, but sleep itself is an enormous business. We are encouraged to spend thousands of dollars on beds and mattresses because less expensive options are insufficiently rest-promoting, and then we should spend more money to wake up and keep ourselves alert throughout the day.

I find all of this baffling, even as β€” perhaps especially as β€” a daily coffee drinker. I get that people have different relationships with caffeine and alcohol, so do not take this as anything greater than my feelings-based vibe, but I have never really understood these as functional products. Coffee, when made well using the kinds of beans that I like, is a delicious beverage regardless of its effects. Wine is sort of similar to me. They both have effects, of course, but I think of those as a kind of warning sign: if I have consumed so much coffee or wine that it noticeably changes how I feel, I have probably had too much. This makes them, unlike energy drinks, a delicious treat for me. Your mileage may vary.

βŒ₯ Permalink

World’s Most Powerful Person Does a Stupid Thing About a Lake

By: Nick Heer
28 August 2026 at 04:41

An un-bylined Canadian Press report:

U.S. President Donald Trump signed an executive order on Thursday to change the name of Lake Ontario to β€œLake America” as tensions with Canada worsen.

Elect stupid people and get stupid policies. This report contains a good explanation of the name’s history, and also has a deliciously petty map illustrating where the border is drawn.

Like the moronic Gulf of Mexico relabelling, expect digital mapping companies like Apple and Google to fall in line. MapQuest, a holdout on that change, also says it will keep the correct name. I still think maps should reflect government names regardless of whether they were assigned by a thumb elected by the world’s least responsible country, but I sympathize with MapQuest’s stance.

Update: Just like that, Google has updated its map to reflect β€œLake America” when using a U.S. map, β€œLake Ontario” in Canada, and β€œLake Ontario (Lake America)” elsewhere. Google’s concession β€” and, soon, Apple’s β€” is a good reminder to all users that the U.S. may be the most powerful country in the world, but it is no longer a leader.

Update: Apple has also updated its map.

βŒ₯ Permalink

Apple Reverses Proposed Hide My Email Domain Change

By: Nick Heer
27 August 2026 at 04:51

Apple, after previously saying it would move Hide My Email to the private.icloud.com subdomain:

After further consideration and reviewing community feedback, iCloud+Β HideΒ MyΒ Email addresses will remain on icloud.com.

The whole point of an email address like this is that it can be used anywhere someone does not want to provide an address that can be tied to them or remarketed to, or if they want to be able to cut off communications. This change, if implemented, would have made these addresses basically useless. I am glad to see this was reversed.

βŒ₯ Permalink

E.U. Privacy Organizations Launch Initiative to Kill Cookie Consent Banners​

By: Nick Heer
27 August 2026 at 04:03

In September, the European Commission began pondering how to correct its 2009 privacy law that resulted in cookie permission banners littering the web, with the resulting proposal announced in November.

Jennifer Rankin, the Guardian:

EU officials said users would remain in control of their data on the internet, but new rules on cookies β€” the internet files that are stored on a user’s device so a website can remember them β€” would make life simpler by ensuring one-click consent. β€œI think we can all agree we have spent too much of our time accepting or rejecting cookies,” [Henna] Virkkunen said.

This was not the first time the Commission had attempted to correct for the permissions pollution that resulted from the e-Privacy Directive. In 2020, its efforts were focused on ineffective consent options like, as reported at the Verge, β€œa cookie consent policy with no obvious way to opt out of tracking”. I still see many websites, like the Verge itself, providing no meaningful consent for third-party tracking.

This time, though, the Commission said it was trying to make cookie consents less prevalent by allowing, for example, simple statistical cookies without any consent, and it was going to give users an option to decline tracking universally. When I looked into the changes in November, it seemed like this signal could be ignored by publishers and media companies who would be free to ask for consent anyway. As of May, it seemed this proposal was moving forward by requiring consent management platforms to respond to browser signals. By summer, however, things had changed.

Ernestas Naprys, Cybernews (β€œArticle 88b” refers to the universal browser signal proposal):

Google suggested ditching Article 88b.

β€œArticle 88b should be deleted. Retaining this provision risks anchoring the Omnibus to a proven-failed architecture, Google’s position reads.

β€œIt will drastically impair the ability of most websites to monetize content and drive client acquisition. The resulting low consent rates and severely restricted data access.”

Meta suggested removing the entire Article 5(3) of the ePrivacy Directive. This rule is why we have cookie banners in the first place, as it requires website operators to obtain clear user consent before storing or accessing their information.

It was not just U.S.-based companies that argued against better user privacy controls. According to noyb, French, German, and Polish representatives were in alignment with Google’s position, which ultimately led to its scrapping. All three countries are home to companies that would be affected by this regulation. None, however, are as big or as powerful as Google or Meta.

Privacy-defending organizations are understandably not impressed. They have launched Kill the Cookie Banner to drum up support for legal recognition of a browser signal. In the U.S., five state governments say the Global Privacy Control must be respected. At a browser level, it is only implemented in Brave, DuckDuckGo, and Firefox, but it seems that Apple is working to add it to Safari, and it seems it is being actively worked on for Chromium, too. The European Commission should throw its weight behind this control, too.

βŒ₯ Permalink

β€˜Every Right on Red Frays the Social Contract a Little Further’

By: Nick Heer
27 August 2026 at 02:49

Felix Kent, Defector (gift link):

[…] I believed then and still believe that operating a car is too difficult for human beings. This is obviously not true at the level of making a car move. Making a car move is easy. A rat can make a car move. But the level of skill involved in driving really well is not dissimilar from that required to be a concert pianist, if the pianist-centered movies I’ve watched are to be believed. Now imagine that in most parts of the country leading a normal life required more or less constant piano playing. Now imagine that playing the piano badly killed people.

Just about everything we do to make driving a car easier instead seems to make people complacent. The lanes on roads are wider than they need to be to account for lateral imperfection, and then there are wide berms so that someone may drive completely off the road in relative safety. Cars that are enormous, heavy, hulking things do not allow drivers to feel their weight because of myriad assistance features that make it possible to steer with a single finger.

But driving is a skill. Like any skill, it is something people will have varying degrees of success learning. Some people simply cannot drive, whether on a temporary or permanent basis. We can bet the farm on autonomous vehicles which, though seemingly capable in warm and dry urban settings, are an expensive and individualized answer that exacerbates all the other problems of car dependency. It is also a risky bet as, like any technology, current performance is not indicative of future gains. Public transit, on the other hand, is a proven technology that, among other things, strengthens the social contract while giving everyone the ability to move around a city.

βŒ₯ Permalink

Nitter’s Developer Says X Sent a Cease and Desist

By: Nick Heer
25 August 2026 at 18:03

β€œzedeus”:

On 24 August 2026 cease and desist letters have been sent by X Corp. demanding a permanent takedown of Nitter instances and the project’s repository.

Nitter mirrors X; it powers websites like XCancel which remains online and functional as of writing. (Update: A few hours after I published this, XCancel says it was also told by X to shut down its service.)

This is not surprising but it is very stupid. Companies like X and Meta are very happy to scrape the web at unprecedented scale and without permission, but have zero tolerance for the same to be done to them by, in effect, individual users. That is not because they are protective of the creative works users have contributed to these platforms. It is simply because users are not trusted to use the platforms. X does not permit third-party readers, and neither does Instagram, because their value does not come from us using these services as we wish, but from how they dictate we must. Bafflingly, however, some argue they should face little responsibility for those choices.

βŒ₯ Permalink

Meet Judge Yvonne Gonzalez Rogers

By: Nick Heer
24 August 2026 at 05:49

Jeffrey Kopp, of CNBC, wrote a short profile of Judge Yvonne Gonzalez Rogers, whose name you may have heard even if you do not live in her district of Northern California. That is because her district covers such areas as San Francisco and Silicon Valley, so her rulings on the many cases heard there have a disproportionate impact on U.S. technology policy.

These policies should catch the attention of people in the U.S., of course; it is also a factor to be mindful of in the rest of the world. The boundaries and limitations surrounding the behaviour of some of the most impactful companies in the world are decided by a relative handful of people, among whom are the justices of the Northern District of California. This article is not a deep examination of such consequences β€” but I would love to read that story, if it exists.

βŒ₯ Permalink

Apple News Publisher Sign-in Required

By: Nick Heer
23 August 2026 at 15:47

Now that it has been a couple of years since Apple began requiring administrators to sign in to the News Publisher dashboard or risk their accounts losing their role, there are a few things we have learned:

  1. These notifications arrive on an approximately five-month schedule, perhaps a couple of weeks longer.

  2. News Publisher has little additional functionality that requires or would encourage signing in on a more regular basis if you do not use Apple’s special format.

    In my case, I cannot even see the articles I have published, nor are there analytics. I checked on a device that is not my own and I can see that recent articles are broadcasting to Apple News just fine from my website, but there is effectively nothing in News Publisher reflecting that.

  3. Apple News is still frighteningly limited from a publisher’s perspective. It launched eleven years ago and there is still no way to set a custom URL in News Publisher or, in fact, anywhere. That is, this website’s Apple News link is https://apple.news/TAjcS0c5sRV2HYftzmJ6UMQ instead of, say, https://apple.news/pixel-envy. It is not a function of size, either β€” here are the URL slugs of a few publishers you definitely recognize: ThfiauYLtQlOfqyiHkempkw, TVKIb0N6iSsOyGSnR4zF1Kw, and TUKgA_OjuTe20v1ndDwQjxQ. Catchy.

    This limitation also extends to the URLs of individual stories. It is the kind of basic functionality that exists on every other public-facing platform I can think of that a publisher might use, but Apple does not support because its β€œServices” business prints money anyway.

I still do not know what happens if I fail to sign into News Publisher five months from now, other than that my role becomes reduced to an editor. There is nobody else authorized with News Publisher, so I guess I simply lose administrator access forever if, every five months, I complete the necessary busywork of signing in.

βŒ₯ Permalink

There's a difference between a technology being ineffective and being uneconomical

By: cks
31 August 2026 at 02:33

Suppose, not hypothetically, that there's some technology that is having terrible effects on the world; it's practically destroying websites, it's worsening energy and water usage, and so on. Suppose also that there are real doubts about how good the technology is and what people will pay for it (since we mostly exist in a monetary economy and the latter is a consideration).

If you don't like this technology and would like to see it go down in flames (cf, also), there's an important difference between whether the technology is ineffective (it doesn't deliver particularly useful results once you peel off the hype) or if it's merely uneconomical. To put it bluntly, technologies that are merely uneconomical are much harder to get rid of than technologies that are ineffective. Obviously, if there's a useful but uneconomical technology, a bunch of people are going to try to make it economical and sooner or later they may succeed. Less obviously, there can be parties that care much less about the economics if they have something that's useful to them.

(Which is to say in part that governments are willing to spend lots of money on 'weapons', broadly defined. Many years ago I read someone say that computer security attacks that were merely expensive were actually attractive to government intelligence agencies; they feed the agency's budget and keep less well funded adversaries out. It's stuck with me ever since.)

Unfortunately for me and many other people, one particular such technology does seem to have reached the point where it's not ineffective in certain areas (cf). Worse, some of these areas are areas of deep interest to multiple governments, and some of those governments have a demonstrated ability to sustain very expensive, multi-year technology build outs for things that were uneconomical at the time. How unfortunate this development is will depend on how many areas the technology is reasonably effective at and the ultimate economics of those areas.

(Also, obviously, that a technology can be effective doesn't mean that the industry around it isn't a bubble. Bubbles are about the economics, not the underlying technology; it's entirely possible to have major bubbles with well proven technology, like home construction and renovation. Since I would like to replace my home desktop before it's ten years old for a reasonable amount of money, I do hope that current RAM and disk drive prices are due to a bubble and the bubble goes away before too long.)

That a technology is effective (in some area) and currently economical doesn't mean that you necessarily have to use it in that area, at least for personal things. You might choose to prioritize ethical and other concerns over "productivity". But unfortunately I'm less and less convinced that people in the technology field really care about such things, either in their paid work or outside of it.

(Corporations certainly don't care. "Externalities" are other people's problems unless and until something forces the corporation to care.)

PS: See also Iris Meredith's There's no such thing as Just a Tool. Technology is never neutral.

Our slow move toward boring (server) host names

By: cks
30 August 2026 at 01:50

We have a bunch of servers, which means that we need to give them (host)names. Like many places, we've had a number of naming schemes over the years; for example, many years ago the naming scheme was that servers were named after major north-south streets in Toronto (resulting in names like 'yonge' and 'bay') and other machines were named after east-west streets. However, over time we've gradually moved to mostly using boring and functional server names for many of them, names like 'comps1' and 'apps0'.

The history of the names of our fileservers provides a useful illustration of why we've wound up making this choice. They've always had names that started with 'san' (even in the recent generations where there's no SAN, cf), and for several generations these were city names like 'santafe', 'sanjavier', and 'sanandreas' (the test fileserver). However, we've never put these names in our NFS mounts; instead NFS filesystems are mounted from names like 'fs1' and 'fs3'. In the very old days this was because we could fail over these names (and their filesystems) from one physical NFS server to another, and then we carried on the habit.

However, this disconnect between fileserver host names and what they corresponded to created little frictions in practice. If you wanted to do something with the fileserver that hosted fs1 and its filesystems, you had to remember which san<whatever> name that was (in the current fileserver generation, because every generation needed a new set of host names). And if we received an email message about 'sanjavier', we might have to remember which fsN (virtual) fileserver that was. It wasn't impossible to deal with, but it was a bit annoying. So in the current generation of fileservers, we (mostly my co-workers, to be honest) decided that our fileservers would have boring names that directly mapped to their virtual fsN fileserver identities, so now they have host names like 'san1' and 'san4'. The result is less interesting host names but it's clearly easier to deal with on a day to day basis in practice.

(Being easier to deal with in practice is why our general multi-user servers were renamed from north-south streets to things like 'apps0' and 'comps0' when we moved from Solaris to Linux. We felt this would make it easier for normal people to remember and pick out what sort of machine they were logging in to, especially since we had a difference between general purpose login servers and compute servers, and people weren't supposed to run compute intensive things on the login servers.)

We do still have some interesting server hostnames, but they're generally for machines that normal people don't have to deal with and it's easy for us to keep straight, and some of them are sort of historical at this point. Our external mail gateway has been called 'alkyone' for roughly twenty years, but the current companion spam filtering server is called 'rspamd', after the primary software it runs, rather than 'keyx' (the old name of the server that ran our previous generation of anti-spam software). Our IMAP server is called 'aviary' (because it runs Dovecot), but we only have one and it's easy to keep straight.

(One of my co-workers has a Classics background and is great for classical Greek names, and another one likes Norse mythology. They come up with the interesting server names these days, because my naming sense is relatively bad and I know it. If I need to give a new server an interesting name, I ask them, and that's why a special DNS resolver is called 'argos'. The best my natural naming sense comes up with is things like 'sponge' for the name of our temporary backup MX server (because it was going to soak up incoming email temporarily).)

Blocking SSH logins with PAM (at least on Linux)

By: cks
29 August 2026 at 03:02

Suppose, not entirely hypothetically, that you'd like to block some people's logins to some server based on things other than what OpenSSH's sshd directly supports in its sshd_config, such as the login's shell. When I discussed this recently in a sidebar to this entry, I said that you could do this in PAM in either the 'session' or the 'authentication' (auth) PAM stack. I have to take that back, because I'm only half right.

The problem with blocking SSH logins using the PAM 'auth' stack is that OpenSSH's public key authentication is done outside of PAM. If a connection is authenticated through public key stuff, sshd never invokes the PAM auth stack and so your 'auth' based block never has a chance to take effect (this also limits some combination of authentication). However, there is an alternative in the 'account' PAM stack, which is specifically documented in pam.conf(5) as being useful for this sort of access control.

(I haven't previously had to deal with the 'account' PAM stack but I'm familiar with the 'auth' stack, which is why I reflexively reached for it as an access control mechanism. As we can see here, that's not necessarily a good idea in PAM. Hopefully I'll remember this for the next time.)

In my original entry, I didn't know how to provide a message for people trying to log in except through pam_motd, which only takes effect in the "session" PAM stack. On the Fediverse I was pointed to the useful pam_echo module, which will print messages in all PAM modes and take its message from a file (and the message can have some things dynamically expanded in it). If you need a dynamic message (for instance, if you have a number of different shells that produce different messages), the other option is to use pam_exec with its 'stdout' option to run a script that prints out a suitable message (which is then passed back through sshd and the person's ssh client). Pam_exec is started with a very minimal environment but you do get $PAM_USER, so you can look up their shell in /etc/passwd or otherwise figure out what message to generate.

Using the same convention as my first entry, this gives us a PAM stanza that looks like this:

account [success=ignore default=2] pam_succeed_if.so shell =~ /admin/shells/*
account optional  pam_echo.so file=/admin/access-denied.txt
# or
#account optional pam_exec.so stdout /admin/access-denied.sh
account requisite pam_deny.so

We use the PAM control value 'requisite' instead of the more commonly seen 'required' so that the account PAM stack fails immediately (and see my entry on understanding the effect of PAM module results, and there are some issues if this is a substack).

The pam_succeed_if module has a relatively good set of data fields and comparisons it can do that's broadly more generous than what sshd_config provides in Match statements, so you may wind up wanting (or needing) to use it instead of sshd's native mechanisms. If you're sure you want your access block to apply only to SSH logins as opposed to everything, you can also put this stuff in a suitable place in /etc/pam.d/sshd instead of a more general file.

(In our case, if we give someone an administrative shell, we want any lingering other things that might run programs as them to also not work.)

Using Polkit to prevent people from doing 'loginctl enable-linger'

By: cks
28 August 2026 at 02:49

As I mentioned in yesterday's entry, the standard systemd behavior is that anyone can do 'loginctl enable-linger' for themselves. We don't want people to do this on our servers, partly because it doesn't work the way people expect in our NFS fileserver environment and partly for other reasons. Today I worked out how to disable this, and refreshed my memory of Polkit (also) in the process (Polkit is how systemd controls authorization for many of its operations that normal people can theoretically invoke).

The default requirements for loginctl actions are set in a .policy file in /usr/share/polkit-1/actions, specifically in org.freedesktop.login1.policy (because it's systemd's logind that is really doing the work). If you look in this XML, you'll find there are two relevant Polkit actions:

[...]
<action id="org.freedesktop.login1.set-self-linger">
[...]
<action id="org.freedesktop.login1.set-user-linger">
[...]

(It's conventional that most of the Polkit action name is from the D-Bus service doing things, in this case org.freedesktop.login1.)

The first one is for 'enable-linger' or 'disable-linger' on yourself, and the second is for doing it on other people. The initial Polkit policy, set in the '<default>' block of each <action> block, is to allow it for yourself and require administrator authentication for other people. Unfortunately org.freedesktop.login1 doesn't distinguish between enabling linger on yourself and disabling linger on yourself; both are covered by the same action and the same set of permissions.

(Loginctl is clever enough to use set-self-linger even if you give it your own login name as the command line argument.)

The pkaction command can be a more convenient and readable way to get this information, but I didn't look at it until after I'd found the .policy file.

To change the initial, default behavior we need a Polkit rule in a .rules file in /etc/polkit-1/rules.d. The contents of this file are fairly straightforward if you've (re)read other rules files to refresh your memory:

polkit.addRule(function(action, subject) {
    if (action.id == "org.freedesktop.login1.set-self-linger") {
        return polkit.Result.NO;
    }
});

Since we unconditionally return NO for all attempts to use this action, all non-root requests for it are rejected. People who try will get a relatively terse message of "Could not enable linger: Access denied" (loginctl says 'enable' even if you're doing disable-linger).

As I discovered with some experimentation, if you're root (UID 0), this is ignored and you can still do 'loginctl enable-linger' (in addition to 'loginctl enable-linger somelogin'). However, if you're su'd to root, doing 'loginctl enable-linger' enables lingering for your own login, not 'root', presumably because that's who systemd considers you to be at the moment.

Sidebar: 'linger' and initially inaccessible home directories

In any environment where people's home directories aren't immediately accessible as the system boots, enabling 'linger' status for a login is not merely ineffective, it's actually damaging. Systemd starts the user manager for 'linger' users during system boot, and if someone's home directory isn't present at that point, their newly started user manager won't see any special user unit files they have set up. Later, when their home directory is available and they log in, their user manager will already have started so it doesn't notice the now available $HOME/.config/systemd/user/default.target.wants and trigger unit startup.

You can manually start units once your $HOME is available and you log in. But as far as I can see, nothing will auto-start them for you, not even using systemctl to reload or re-exec your user manager. To get everything auto-started properly, you need to get your user manager to exit and then log in again so it's restarted from scratch, for example by doing 'loginctl terminate-user ""'. The empty double quotes are necessary; you must give loginctl an explicit empty argument as the login to terminate, presumably for safety reasons.

(Terminating another user is Polkit authenticated as one of the things included in 'org.freedesktop.login1.manage'. Terminating yourself doesn't seem to be authenticated at all through Polkit; logind just allows it.)

In our fileserver environment, people's NFS based home directories aren't available sufficiently early in boot to be there when systemd starts user managers for 'linger' logins. I'm not quite sure why this is so, because the service unit that mounts them (using our 'automounter' system) is WantedBy remote-fs.target. But the result is that setting your login to 'linger' is a subtle trap (and also unnecessary; you can keep your user manager alive in other ways).

A surprise with systemd user service units and special shells

By: cks
27 August 2026 at 03:46

Suppose, not hypothetically, that you operate a multi-server Linux environment where most people get to log in to some machines, such as your general purpose login servers, but not log in to others that still have to carry your full set of logins, such as your fileservers or your SLURM nodes (people submit jobs through SLURM but aren't supposed to directly SSH in). The only good way to implement this today that I know of is to modify people's shell in /etc/passwd. Although this is flawed in general, most of those flaws don't apply if what you want to do is specifically stop people SSH'ing in to some machines.

(Let's assume you can't use /etc/nologin or the like because some people do need to log in, such as system staff.)

Now suppose that some people are hip to various systemd features or are just following setup guides, and so are setting up per-user systemd service units (also). Per-user systemd service units that are added by people are normally located in $HOME/.config/systemd/user, and if set up as WantedBy default.target, they're started by your systemd user manager when it starts, which is normally approximately when you log in. It turns out that user unit startup happens automatically regardless of what your $SHELL is, even if it's a do-nothing shell that immediately exits.

If a person with user units set up SSH's to one of your no-login servers where they have a custom /etc/passwd shell that tells them about this and then exits, systemd will start a user manager before it gets anywhere near trying to run their shell. This user manager will start their user units, which results in them running programs on a machine you don't want them to. If you have shared NFS home directories, this can happen entirely innocently.

(It's perfectly reasonable for someone to set up their systemd units with no specific host restrictions on what host they run on. If they only normally log in to one host they might not notice for some time until the day they, say, accidentally SSH to a SLURM node.)

This doesn't necessarily leave those user service units and their programs running forever. Under normal circumstances, your systemd user manager sits around until all sessions and .scope units under it have exited, waits another UserStopDelaySec seconds (normally ten seconds), and then shuts down all remaining units and exits. However, anyone can enable 'user lingering' for themselves via 'loginctl enable-linger ...', at which point their user manager and their service units are perpetual (who has linger status enabled is visible in /var/lib/systemd/linger, cf). A person who wanted to actively exploit this could run loginctl from a script in the ten second window they have after their initial SSH connection.

(There is a separate logind.conf setting, KillUserProcesses, but this only applies to processes in the session's .scope unit, not to processes in separate user service units, and almost no one turns it on because of the carnage that would ensue. Ubuntu 26.04 LTS definitely doesn't.)

As far as I know there's no easy way to get systemd to not start systemd user units if your shell isn't a standard one. For your own personal units, you can make them conditional on $SHELL with something like 'ConditionEnvironment=SHELL=/bin/bash', but of course you have to know about this and care. System wide, your administrative shells can do 'loginctl terminate-user ""' at the end, after printing their message, which will theoretically terminate the person's entire user manager; however, I think this can be incomplete under some circumstances. The normal OpenSSH sshd will refuse to let you log in at all if your /etc/passwd shell doesn't exist, but this may have side effects if other programs (such as your IMAP server) also fail authentication in this case.

In systemd v258 and later (which covers Ubuntu 26.04), you can force systemd to not create a user manager for people by setting a pam_systemd option of 'class=user-light' for SSH logins (or all logins if people only log in via SSH); SSH logins normally wind up with 'class=user'. For Ubuntu 26.04, a suitable PAM incantations for this might be:

session [success=ignore default=2] pam_succeed_if.so shell =~ /admin/shells/*
session optional  pam_systemd.so class=user-light
session [success=1 default=ignore] pam_succeed_if.so shell =~ /admin/shells/*
session optional  pam_systemd.so

(Yes, this is arcane. I wouldn't make this conditional on the service being ssh(d), because you probably don't want any session to start people's user units. Adding 'quiet' to the pam_succeed_if.so options may be useful.)

The advantage of this PAM approach is that people will still be put into the proper systemd unit hierarchy (for the possibly brief duration of the administrative shell), with a user-NNN.slice and session-NNN.scope unit. This may make other bits of your environment happier with life (for instance if you have scripts that trigger on login to apply per-person limits to their user-NNN.slice systemd unit).

(This elaborates on some Fediverse posts of mine.)

PS: loginctl's Polkit policy specifically permit everyone to set their own linger status, not subject to any particular Polkit rules. I've forgotten my Polkit knowledge since back then so I'm not sure if you can override this with your own Polkit rules. I may wind up finding out.

Sidebar: Rejecting logins, the other PAM approach

You can use PAM to selectively reject logins using pam_succeed_if.so to check the shell, and then possibly pam_deny.so to shut the PAM process down. There are two places in the PAM process where you can do this, in the authentication stack and in the session stack. Doing this in the authentication stack is cleanest (you're refusing to authenticate the login because it has a bad shell), but this has the drawback that you can't provide people any sort of message.

If you refuse in the session stack, you can use pam_motd.so to also provide a message, for example:

session [success=ignore default=3] pam_succeed_if.so shell =~ /admin/shells/*
session optional pam_motd.so motd=/admin/access-denied.txt
session required pam_deny.so

(You have to put this before the pam_systemd.so module for obvious reasons.)

This will show /admin/access-denied.txt and then drop people off. One drawback is that on Ubuntu 26.04 it seems to trigger some additional log messages:

sshd-session: error: PAM: pam_open_session(): Cannot make/remove an entry for the specified session
sshd-session: syslogin_perform_logout: logout() returned an error

This feels unsurprising; sshd and PAM together opened a session only to have the session yanked out from underneath them.

The other drawback is that it's going to get increasingly annoying if you want to provide different messages for different administrative shells (the more shells, the more annoying). Each different message will need its own three-line block in your PAM session stack unless you're lucky.

Making a bytes/Unicode confusion in email parsing in Python

By: cks
26 August 2026 at 03:32

On the Fediverse, I said:

It has been '0' days since I discovered that I shot myself in the foot in Python 3 with a bytes/Unicode confusion issue, although this one was an unusually creative and subtle way to get things wrong (ie, it had no exceptions, it generated corrupted text).

This will require some background. I handle my email in the venerable (N)MH, which is very old and thus slightly flawed. One of those flaws is that while NMH can quote a true plain text message that you're replying to, you need to set up your own, additional stuff to properly quote MIME-encoded email in replies. Specifically, you need a program that will MIME-decode and pretty-print every text-based body part you want to quote. I have a long standing Python program for this, and not too long ago I moved it from Python 2 to Python 3 (which involved some learning experiences).

Recently, a number of emails I replied to started showing up with Unicode characters that rendered as a reverse video '?' glyph scattered at various random places in the quoted body. Investigation revealed that these were U+FFFF characters, but I didn't see anything odd in the message I was replying to (and thus quoting). Only after some digging did I work out what was going on and how I'd shot myself in the foot.

My MIME body part decoder gets passed the MIME Content-Type and Content-Transfer-Encoding on the command line and receives the raw text of the MIME body on standard input (this is basically required by how NMH works). To decode and parse the MIME body part, I was doing the obvious thing; I constructed an email.parser.Parser, read in standard input to get the body of the MIME part, put together a minimal set of MIME headers and the body, and passed the result to Parser.parsestr(). Some of you may already see my problem.

The messages I was replying to had a content-type that said they were UTF-8 ('text/plain; charset=utf-8') in a content transfer encoding of '8bit', and they contained Unicode characters (codepoints) in the range U+0080 through U+00FF (in this case, my old nemesis U+00A0). Because this message part used '8bit' transfer encoding, these Unicode characters were directly represented as UTF-8 bytes in the raw MIME body part passed to my program on standard input; when I read them directly from sys.stdin as text, Python 3 did what I'd asked for and decoded stdin as UTF-8, recreating in my decoded Unicode string those U+00A0 characters. Then I passed what I claimed was a UTF-8 encoded thing to Parser.parsestr() and it dutifully tried to decode UTF-8 again (effectively interpreting the Unicode codepoints in my string as bytes), found some invalid UTF-8 sequences in the form of those U+00A0 codepoints, and put in a U+FFFF marker for each of them.

(If you do this trick with Unicode codepoints that are above U+00FF, Parser.parsestr() will eventually give you back '\u20ac' style escapes, which is at least more obvious about what's gone wrong.)

The solution (and correct answer) was to switch to email.parser.BytesParser and its parsebytes() method, with a standard input that's been read in raw mode from sys.stdin.buffer. This aligns everything properly so that I'm handing raw text to the thing that decodes raw text, rather than somewhat cooked text.

(This issue specifically required that the content transfer encoding be '8bit', not '7bit'. Had the MIME part been encoded down to 7-bit clean as '7bit', there wouldn't have been any straight UTF-8 in the input for me (well, sys.stdin) to decode to Unicode codepoints. This is probably why I didn't notice the problem immediately after the Python 3 conversion.)

Sidebar: Tracing the code

If you use email.parser.Parser with a Unicode string in my bug situation, the resulting email.message.EmailMessage object will hold your Unicode string as its ._payload private attribute. When you call its .get_content(), the policy system will wind up calling back to .get_payload(decode=True). This will wind up encoding your Unicode string to a bytestring with (in my case) 'str.encode("raw-unicode-escape")', which turns Unicode over U+00FF into the \uNNNN form but leaves U+0080 through U+00FF as single bytes. This bytestring is then run through 'bytes.decode("utf-8", errors="replace")', which is what replaces the \xa0 byte with U+FFFF.

See email/contentmanager.py and email/message.py, also, all of which I'm noting down here because I don't want to have to research this a third time.

Considering how much RAM system.slice and the kernel need

By: cks
25 August 2026 at 03:23

Suppose, not hypothetically, that you want to use systemd memory controls on either system.slice or user.slice to keep people from running the system out of memory. In either case, you need to come up with some amount of memory that you'll reserve for system.slice (and the kernel), through either setting MemoryMin= on system.slice or setting MemoryMax= on user.slice.

If you're setting a maximum, you need to know how much memory the system has. As covered in proc_meminfo(5), /proc/meminfo's MemTotal field gives you the total theoretically available system RAM after reserved bits and the kernel's very basic code and other memory usage. In practice the kernel is likely to need more RAM than this in normal operation, and figuring out where the memory is going can be complicated. Also, it's likely that in order to perform well, everything under system.slice will need more memory than simply the total program memory usage, because cgroup and thus systemd memory limits include things like the kernel filesystem cache.

(My case of too-small CPU and RAM limits significantly harming program performance was definitely partly from having too little RAM, probably partly by hampering the filesystem cache.)

At the moment, system.slice on the servers I care about for this appears to be using about 1.6 GB of user level RAM and anywhere up to just over 2 GB of kernel slab cache. Unfortunately I don't have metrics over time for this (yes, I'm now tempted to build something to collect them). Filesystem cache usage is all over the map, but I should probably allow at least 1 GB for that, and more might be better. I could round this up and say we should reserve 5 GB for the 'system', broadly defined, and set MemoryMax= on user.slice accordingly (I prefer the approach of capping user.slice's memory usage to trying to set a minimum memory for system.slice).

(This turns out to actually be typical for system.slice across most of our machines, with less active machines having under 1G and some under 512M. The kernel file cache size for system.slice varies hugely, of course, and some of our machines are exceptions because they run heavyweight things as system .service units, such as our metrics system.)

I would need a script to dynamically compute this size from MemTotal in /proc/meminfo, but that's not particularly difficult (especially since I have a script to copy chunks from; it's used to size the ZFS ARC on our ZFS fileservers based on the amount of memory the fileserver has). Our existing system for setting up fair-share CPU scheduling between people runs at login time, when we know a user.slice exists (because a user-<uid>.slice for the person who's logging in now exists as its child), so we can compute the right MemoryMax value and set it on user.slice. There's even a convenient place to put this in our current script framework.

However, this is definitely the sort of change that I should monitor after it's made to make sure that I'm correct about how much memory everything actually needs and uses (cf). That means I need some basic monitoring of top level systemd cgroup memory usage that feeds into our metrics system, which I'll probably do with a minimal shell script rather than anything more elaborate.

(There's at least one Prometheus systemd exporter that provides metrics for systemd units, but the last time I looked there was nothing that provided this kind of memory usage information.)

I should write more shell scripts than I do

By: cks
24 August 2026 at 02:46

For reasons beyond the scope of this entry, I've been manually maintaining a group of files in sync across three 'machines' (my GNU Emacs configuration). There is no master machine for edits (it depends on what I'm on at the time when I need a change), and I don't always propagate changes immediately after I make them. Plus, some of these files are in various places under my home directory. I made a lot of use of 'ssh <host> cat <relative path>/file | diff -u - file' and the like to keep track of this all and make sure I wasn't overwriting changes when I propagated things, and I kept doing it by hand for various reasons.

(I'm aware that some people would solve this problem with things like a Git repository. This isn't my way for various reasons.)

Today I sat down and wrote remdiff and remsync scripts to do this for me. These scripts are a little bit more elaborate than they need to be (partly because I put in a reasonable amount of error checking), but they still weren't much work and they're not very long. Getting a reasonably decent design for the command line arguments took a few iterations of writing code and then using the scripts and being unhappy, and I'm probably going to fiddle more, but the result is usable already.

The existence of these scripts doesn't just reduce the amount of effort and irritation involved in this process. Because they make it easier to do this, they encourage me to do it more often and thus reduce the chances I will have pending changes on more than one machine that I'll have to reconcile. They also make it feasible to bulk check a whole collection of files, which found some differences I hadn't realized.

Writing shell scripts is one of those Unix superpowers. Unix has such a collection of reusable tools and tool components (plus various shell features that are designed to make scripting easier) that it's practically a shame not to use them. But in this case and others, I'll tell myself over and over that it's a bit too trivial or too much work or whatever to turn something into a shell script. So today I was once again reminded that I should write more shell scripts than I do.

Another non-obvious advantage of shell scripts is that you can adjust their command line arguments to exploit shell features like filename completion. In the original manual version, I couldn't use shell filename completion for the '<relative-path>/file' bit because I was already in the <relative-path> directory on my machine. Since I no longer have to supply this argument by hand, I can do things like 'remdiff host: fly<TAB>' to have my shell auto-complete the filename to 'fly-startup.el'.

(People who use fancy shells could arrange for their shell to know how to autocomplete hostnames as well, bringing this down to, say, 'remd<TAB> ho<TAB> fly<TAB>'. For this sort of personal command you could adopt a very brute force approach based on knowing what hostnames you're likely to ever use it on. If you want to use another host, you add it manually to your completion information for this command.)

PS: It figures that the moment I had 'remdiff' I started using it to check other files that I generally keep in sync between multiple systems. Apparently this was yet another one of those little points of friction that I hadn't realized I had.

(One reason I held back on writing 'remdiff', which was the first of the two scripts, was that I expected parts of it to be an annoying pain to write, like going from the absolute path of the current directory to its relative path from $HOME. And I was right, bits of the script do feel like an annoying pain, but it's worth it.)

Sidebar: My brute force approach to getting the relative path

In Bash:

HDIR=$(realpath "$HOME")
MPWD=$(pwd)
# We could be in $HOME.
if [ "$HDIR" = "$MPWD" ]; then
    RELDIR="."
else
    RELDIR=${MPWD##"$HDIR"/}
fi

(Using '##' this way is actually a standard Bourne shell feature now that I look.)

As far as I know there's no command that will do the '${..##../}' bit to strip off some but not all leading directory components on a path. I need the realpath here due to a long standing local feature that causes my $HOME to have a symlink in it.

(I've decided it's a feature that I'm not explicitly restricting this to be under $HOME; if I'm outside of $HOME, it will use the same absolute path on the local and remote machines. This turns out to be convenient for some things.)

Some notes on taming ruff (and mypy) so they're useful for me

By: cks
23 August 2026 at 01:48

One of the things that ruff can be is a Python linter (in addition to at least a formatter and a not very attractive language server). In its default configuration, ruff is a quite opinionated linter with plenty of views that I disagree with, but it also has some useful suggestions for modernizing code that's been converted from Python 2, which I have a lot of and would like help in fixing up. Fortunately it's possible to change ruff's configuration so that it skips complaining about views that I disagree with, both globally and on a per-project basis.

(One very frequent wish ruff has is for me to use format specifiers (f-strings) rather than % format things (cf). I decline.)

On a normal Linux system, ruff's global configuration file is ~/.config/ruff/pyproject.toml, as covered in ruff's configuration documentation. To ignore specific ruff warning codes, I want something like this:

[tool.ruff]
lint.ignore = ["I001", "UP031"]

(Ruff can also ignore entire classes of rules but I haven't gone that far yet.)

I've found it useful to write comments describing what each error code is, so later I can figure out what "I001" is without having to go through ruff's list of rules and perhaps its current default rules, which change from time to time (although default rules are also marked in the list of rules). I typically find rules to add by running ruff (either directly through 'ruff check' or through GNU Emacs) and then seeing the rule number of things it's complaining about that I disagree with.

(In writing this entry I found that E401 is no longer a default rule, for example, so my global setting to ignore it isn't necessary any more.)

Ruff also has per-'project' configuration, which in my view is most conveniently put in a pyproject.toml file at the top level of your project (rather than, say, the ruff specific ruff.toml, cf)). This per-project file can extend your global file if you want:

[tool.ruff]
extend="/u/cks/.config/ruff/pyproject.toml"
target-version="py311"

[tool.ruff.lint]
ignore = ["UP018"]
extend-safe-fixes = ["SIM118", "RUF059"]

If you use 'extend' to pull in your global default settings, your 'ignore' list is added to the global ignore list, as documented (well, sort of, see extend-ignore). If you want to re-enable some rules that you disabled in your global configuration, I think you have to copy your global pyproject.toml into the project, modify it as appropriate, and not use 'extend' at all.

Explicitly extending what ruff lint fixes are considered 'safe' is useful for persuading some external systems, such as current versions of Flycheck in GNU Emacs, to recognize them as available fixes. For whatever reasons, some systems don't recognize unsafe fixes even when you enable ruff offering them.

Mypy has similar global and local configuration control. As covered in The mypy configuration file, the global location is ~/.config/mypy/config. In my view, the local configuration is best also put into a pyproject.toml file, where you might disable specific errors like this:

[tool.mypy]
disable_error_code = [ "var-annotated", "attr-defined" ]

Your tool of choice for showing mypy messages may or may not report the actual error code in addition to the messages, so periodically I get to consult mypy's Error codes documentation and also. This documentation also covers inline comments to disable them on specific lines.

Both mypy and ruff have inline configuration, per mypy's inline documentation (also) and ruff's linter error suppression documentation. For ruff, I prefer the line-based '# ruff: ignore[...]' syntax over the other options. For mypy, I can possibly work around typing complaints rather than suppressing them.

I've become more optimistic about my someday future on Wayland

By: cks
22 August 2026 at 02:39

Today I use an extensively customized X environment, and I have every intention of clinging grimly to it for as long as I can. But Wayland is the future, even if the future isn't arriving very fast, and someday I'll have to move to Wayland. For a long time I've expected that to be very unpleasant, with a lot of differences and a significantly inferior environment as the result. However, these days I'm somewhat more optimistic, although I still expect a lot of pain (and to have to rebuild or replace a lot of programs).

This optimism is partly a product of finding out things like the Arch wiki's list of input remapping utilities, because I'll need a replacement for xcape, and their section on application launchers (for a dmenu replacement). But it's also because there are some real moves to bring a more X like experience to Wayland. River separates Wayland compositing from window management, making it much easier to create window managers, and one of the people involved with fvwm has used River to build cow, a fvwm-like window manager. Cow is especially interesting to me because it has a version of FvwmIconMan, which is a cornerstone of my desktop.

Creating something like my current desktop in Wayland today would still be a lot of work (and it's artificial make-work, since I'd wind up no better off than today). I'd certainly keep on using a lot of X programs via rootless XWayland and I'd have to learn things like waypipe to handle remote Wayland-only programs. But it at least seems possible now, when before I assumed that if I had to move to Wayland, I'd wind up in something that looked more like Cinnamon (which is basically the lowest denominator of desktop experiences I can stand; modern Gnome is not for me).

Of course the optimistic hope is that non-Linux platforms like FreeBSD, OpenBSD, and NetBSD can apply enough pressure to keep GTK and other toolkits working on X, and crucial software like Firefox doesn't give up on X compatibility. Then it's likely that I can keep on using X for years and years to come (I hope for decades). Maybe I'd even wind up with a reverse Wayland to X bridge to let Wayland programs run (rootless) on my X server, should I need to use some Wayland-only stuff.

(And in other reasons for optimism, the Xorg X server is working on making its first official release in a while.)

PS: My understanding is that while Gnome and KDE are dropping their X session support, it's expected that Gnome and KDE programs will keep working on X. If Gnome and KDE programs drop X support, well, I guess I'd get to see how many of them I really need. I'd certainly miss some of them, ironically including gnome-terminal these days.

❌
❌