10 interesting stories served every morning and every evening.

Internet Archive Switzerland: Expanding a Global Mission to Preserve Knowledge

blog.archive.org

Thirty years ago, Brewster Kahle founded the Internet Archive with an am­bi­tious goal: Universal Access to All Knowledge. Today, that mis­sion con­tin­ues to grow with an ex­cit­ing new chap­ter: the launch of the Internet Archive Switzerland, a non-profit foun­da­tion based in St. Gallen.

The Internet Archive Switzerland, on­line at https://​in­ter­netarchive.ch/, is a newly-formed Swiss non-profit foun­da­tion that will op­er­ate in­de­pen­dently within its na­tional con­text. Its ef­forts will ini­tially fo­cus on pre­serv­ing en­dan­gered archives from around the world and col­lect­ing the gen­er­a­tive AI wave that is cur­rently upon us all. With a UNESCO con­fer­ence planned for November 2026 in Paris, Internet Archive Switzerland is tak­ing a con­crete step to ex­plore how en­dan­gered archives can be pro­tected.

In par­al­lel, the Swiss foun­da­tion is work­ing in part­ner­ship with the School of Computer Science at the University of St. Gallen, on the Gen AI Archive pro­ject led by Prof. Dr. Damian Borth. Together, they aim to be­gin archiv­ing AI mod­els, which is an emerg­ing fron­tier for preser­va­tion.

The choice of St. Gallen is no co­in­ci­dence. With a thou­sand-year tra­di­tion of archiv­ing and schol­ar­ship, the city of­fers a fit­ting home for this next phase of dig­i­tal preser­va­tion. Its strong aca­d­e­mic en­vi­ron­ment—in­clud­ing col­lab­o­ra­tion with the University of St. Gallen—makes it an ideal place to es­tab­lish a 21st cen­tury mem­ory or­ga­ni­za­tion.

St. Gallen is a very suit­able place to take the preser­va­tion of our uni­ver­sal knowl­edge a step fur­ther. Stability and in­no­va­tion go hand in hand here and are em­bed­ded in a deep un­der­stand­ing of the im­por­tance of cul­tural her­itage,” said Roman Griesfelder, the ex­ec­u­tive di­rec­tor of Internet Archive Switzerland.

Internet Archive Switzerland joins a grow­ing group of mis­sion-aligned or­ga­ni­za­tions, along­side Internet Archive, Internet Archive Canada, and Internet Archive Europe. Together, these in­de­pen­dent li­braries strengthen a shared vi­sion: build­ing a dis­trib­uted, re­silient dig­i­tal li­brary for the world.

Contact Internet Archive SwitzerlandRoman Griesfelder, ex­ec­u­tive di­rec­to­rof­fice@in­ter­netarchive.ch

I’ve banned query strings — Chris Morgan

chrismorgan.info

🗓️ 2026 – 05-08 • Tagged /web, /opinions, /meta=only

I don’t like peo­ple adding track­ing stuff to URLs. Still less do I like peo­ple adding track­ing stuff to my URLs.

https://​chris­mor­gan.info/​no-query-strings?ref=ex­am­ple.com? Did I ask? If I wanted to know I’d look at the Referer header; and if it is­n’t there, it’s prob­a­bly for a good rea­son. You abuse your users by adding that to the link.

https://​chris­mor­gan.info/​no-query-strings?ut­m_­source=ex­am­ple&utm_&c.? Hey! That one’s even worse, UTM pa­ra­me­ters are for me to use, not you. Leave my URLs alone.

So I’ve de­cided to try a blan­ket ban for this site: no unau­tho­rised query strings.

At pre­sent I don’t use any query strings. If I ever start us­ing any query strings, I’ll al­low only known pa­ra­me­ters. (In past times I used ?t=… and ?h=… cache-bust­ing URLs for stylesheet URLs; and I de­cided I’m okay break­ing such re­quests; there should­n’t be any le­git­i­mate ones.)

Want to see what hap­pens if you add a query string? Go ahead, try it.

It’s my web­site: I can do what I want with it.

And you can do what you want with yours!

This is cur­rently im­ple­mented in my Caddyfile.

I returned to AWS - and was reminded HARD why I left.

fourlightyears.blogspot.com

I was one of the very first ad­vo­cates for AWS back when it was brand new - SQS, S3, EC2 SimpleDB - it was a lot smaller back then. In fact I or­gan­ised the very first AWS event in Melbourne when the AWS rep came from the US to evan­ge­lise.

Cloud com­put­ing was an ab­solutely mind blow­ing rev­o­lu­tion - sud­denly your startup could run its own com­puter sys­tems in min­utes with­out need to in­stall and run your own sys­tems in a data cen­ter. This was an ab­solute game changer, and I re­ally drank the AWS Kool Aid down to every last drop then I licked out the cup. I was all in on AWS in a big way.

I re­mained a mas­sive AWS fan­boi for 15 years or so - a real true be­liever - I was all in on AWS.

Relationships break down a lit­tle at a time - one or two things start to bother you, you still love it over all but sure, there’s some mi­nor down sides here and there. But hey! It’s still ab­solutely awe­some and you still love it, right? But you no­tice more and more things that ain’t right, that you don’t like, that’s bro­ken or bad. Until one day there’s a fi­nal thing that you no­tice and the scales have tipped and you sud­denly re­alise all at once - I don’t love this re­la­tion­ship any­more”.

Here’s some of the things that chipped away over time:

It re­ally an­noyed me that for the first 6 years of its ex­is­tence AWS did not build its own client li­braries, in­stead leav­ing the job to our won­der­ful com­mu­nity” to im­ple­ment client libs for lan­guages like Python, happy to let pro­gram­mers burn their week­ends and nights writ­ing soft­ware for free for the ben­e­fit of AWS.

It su­per an­noyed me that AWS did not move from Python2 to Python 3 for a ridicu­lous pe­riod of time.

DynamoDB - there’s not much soft­ware that I hate - but oh boy - DynamoDB what a hot pile of garbage. I tried it and ended up with a $75USD bill by the end of the day. And its not just the cost, it’s just the worst sys­tem I can imag­ine in every pos­si­ble way.

20 cents a gi­ga­byte egress - oh boy - holy schmoly do you have any idea how ex­pen­sive this is? And it’s gone down over time to the still ridicu­lously ex­pen­sive 9 cents per gi­ga­byte. This is fuck­ing in­sanely ex­pen­sive. If you use AWS and 9 cents per gi­ga­byte egress is not front of mind then look in the mir­ror to find the stooge - it’s you.

Crazy sneaky com­plex billing in which they hit you for data move­ment within their own sys­tems, dou­ble billing you and some­times triple billing you. Billing foot­guns and traps are every­where - you must be a deep ex­pert to avoid them.

IAM - the hideously com­plex auth and ac­cess rules sys­tem - this was in­vented by Lucifer sit­ting on his burn­ing throne in the ninth level of Hell as the worst pos­si­ble tor­ment for those who have been sent be­low for us­ing AWS.

Complexity in every­thing - once I no­ticed the com­plex­ity of IAM I could not un­see the com­plex­ity every­where in AWS. The weird­est thing is that AWS true be­liev­ers say you MUST use AWS be­cause its too com­plex to run your own com­puter sys­tems, Linux, hard­ware net­work­ing se­cu­rity etc”. These true be­liev­ers have blinded them­selves to the un­be­liev­able, mas­sive com­plex­ity of pretty much *everything* in AWS. AWS is ridicu­lously com­plex and you need to em­ploy a team of ex­pen­sive ex­perts to run the show.

AWS Lambda - yeah I re­ally bought the sell on this - its scal­able!!!!”, and I ig­nored the slow startup times, the MASSIVE de­vel­op­ment com­plex­ity.  There’s sim­ply no gen­uine ben­e­fit to AWS Lambda com­pared to run­ning your own web servers, and there’s many many down sides. When even­tu­ally I moved out of AWS the hard­est thing to undo was the AWS Lambda stuff. The ven­dor lock-in is real. If you’re us­ing AWS Lambda then you have to work to keep con­vinc­ing your­self this is bet­ter than your own web servers. Keep con­vinc­ing your­self that us­ing AWS Lambda is not a hor­ri­ble mis­take.

AWS stomped on open source pro­jects - de­spite the clear de­sire of pro­jects like Elasticsearch, Redis, and MongoDB not to be cloned and mon­e­tized, AWS pushed ahead with OpenSearch, Valkey, and DocumentDB any­way, cap­tur­ing the hosted-ser­vice money af­ter those com­mu­ni­ties and com­pa­nies had built the mar­kets; the re­sult was a wave of de­fen­sive li­censes like SSPL, Elastic License, RSAL, and other source-avail­able mod­els de­signed less to stop or­di­nary users than to stop AWS from strip­ping open-source in­fra­struc­ture for parts, own­ing the cus­tomer re­la­tion­ship.  AWS is a preda­tor.

And there’s many, many more rea­sons to hate AWS, but I don’t even like think­ing about AWS so I’m not go­ing to sour my morn­ing by think­ing of more rea­sons to hate AWS.

Relations break down slowly, un­til a sud­den re­al­i­sa­tion that its over - that’s ex­actly what hap­pened to my love for AWS. One day my switch flicked and I went from fan­boi to hater pretty much in­stantly. I moved every­thing out of AWS and shut down all my ac­counts ex­cept one. I left a lit­tle bit of stuff on AWS be­cause its gen­uinely the right so­lu­tion for me - I left my do­mains on Route53, left a few back­ups in S3 and con­tin­ued to use AWS Workmail (which they have just no­ti­fied me is now shut­ting down in 12 months).

And re­cently I went back to AWS. WHAT?!?!? WHY? You might ask. To get some re­search done. Do a few tests, get in and out.

I wanted to see how well Claude/Anthropic works on AWS Bedrock (it works the same for Claude Code but it’s slower, and is WAY, WAY more ex­pen­sive than hav­ing an Anthropic sub­scrip­tion).

I wanted to bench­mark some of my code on a mega fast ma­chine - the fastest ma­chine I have at home is a 20 core ma­chine with 32GB RAM and I wanted to see how fast my code would run on a ma­chine with 192 cores and 1TB of RAM.

So I logged in to my AWS ac­count and did the AWS Bedrock tests about a month ago - no prob­lems there. Finished the tests shut it all down - I’m not go­ing back to Claude on AWS Bedrock - great for pri­vacy if you need it but hoo boy, the cost.

More re­cently I logged in and fired up an EC2 spot in­stance of a 192 core ma­chine and had been test­ing for 3 hours or so when I got an email from AWS: “Suspected se­cu­rity breach of your ac­count”.

Somewhere in the depths of AWS some sort of se­cu­rity alarm had been trig­gered prob­a­bly by the fact that my mostly dor­mant ac­count sud­denly started do­ing stuff with an ex­pen­sive com­puter. And I un­der­stand why they do that - and its a good thing - AWS wants to pro­tect its users. I ap­plaud that.

BUT they sus­pended/​re­stricted my ac­count.

Now my AWS WorkMail - my main busi­ness ac­count - does not work - no-one can send emails any more.

I can­not cre­ate any sort of AWS re­source, I can­not do the test­ing I was try­ing to get done.

I replied to their sup­port no­ti­fi­ca­tion ask­ing why they had sus­pended my ac­count and telling them it has not been hacked there is no prob­lem and no billing anom­alies. No re­sponse.

Of course I do not pay for pre­mium sup­port, so I have to wait the 24 hours that they said it would take them to re­ply.  It’s 3 days and AWS sup­port has not replied.

So I posted on the AWS fo­rums beg­ging for some­one to re­spond - some­one said focus on do­ing what they in­structed in the email and then use the chat fa­cil­ity in­stead of web be­cause they ac­tu­ally an­swer the chat”. Fine. I did that - I did every­thing they asked - changed pass­words, killed ac­cess to­kens, checked bills etc then had an ex­tended chat with an AWS rep af­ter wait­ing half an hour for the chat to be picked up. And they guy at the end of it seemed sat­is­fied and said he would ask the in­ter­nal peo­ple who han­dle such things to han­dle it. That was 24 hours ago. I fol­lowed them up af­ter 8 hours ask­ing when my ac­count would be un­sus­pended, they said be pa­tient”.

So here I am four days af­ter they sus­pended my ac­count. I still want to do the test­ing on a big ma­chine. I am dread­ing hav­ing to request quota” to be al­lowed to do that. My busi­ness email sys­tem still does not work.

I am re­minded why I left AWS and how I need to fin­ish the job, get off AWS Workmail, move my do­mains from Route53 and never re­turn.

I’m ex­tremely glad I moved off AWS all those years ago and its sad that a re­turn visit should bring down the email sys­tem that I left on AWS, fool­ishly trust­ing. Fool me once and all that.

Maybe one day they will get around to un­sus­pend­ing my ac­count.

nytimes.com

www.nytimes.com

Please en­able JS and dis­able any ad blocker

ymawky

imtomt.github.io

build­ing a web server in aarch64 as­sem­bly to give my life (a lack of) mean­ing

ymawky is a small, sta­tic http web server writ­ten en­tirely in aarch64 as­sem­bly for ma­cos. it uses raw dar­win syscalls with no libc wrap­pers, serves sta­tic files, sup­ports GET, HEAD, PUT, OPTIONS, DELETE, byte ranges, di­rec­tory list­ing, cus­tom er­ror pages, and tries to be as hard­ened as pos­si­ble.

why? why not? the dream of the 80s is alive in ymawky. every­body has ng­inx. hav­ing apache makes you a square. so why not strip every sin­gle con­ve­nience layer that com­puter sci­ence has given us since 1957? i wanted to un­der­stand how a web server ac­tu­ally works, some­thing i know lit­tle about com­ing from a low-level/​sys­tems back­ground. the risks that come up, the prob­lems that need to be solved, the things you don’t think about when you’re writ­ing python or c.

this (probably) won’t re­place ng­inx, but it is do­ing some­thing in the most dif­fi­cult way pos­si­ble.

con­straints

i gave my­self some con­straints for this pro­ject:

aarch64 as­sem­bly only

ma­cos/​dar­win, not linux. only be­cause that’s the sys­tem i have right now. sorry lin­ux­heads :(

raw syscalls only: no libc wrap­pers

sta­tic files only

no pre­ex­ist­ing parsers

ab­solutely no ex­ter­nal li­braries

as­sem­bly, my beloved

as­sem­bly lan­guage is the layer be­tween ma­chine code and other lan­guages. c gets com­piled into as­sem­bly, which then gets as­sem­bled into an ex­e­cutable bi­nary. as­sem­bly is es­sen­tially hu­man-read­able mnemon­ics that di­rectly cor­re­spond to raw ex­e­cutable bytes: mov, add, ldr, str, cmp, among oth­ers. svc #0x80 is the hu­man-read­able equiv­a­lent to the bytes D4 00 10 01 you’ll find in the ex­e­cutable bi­nary.

you get al­most no ab­strac­tions. you move val­ues around be­tween cpu reg­is­ters and mem­ory, com­pare them, jump to dif­fer­ent por­tions of your code, and call the ker­nel for syscalls. it makes sim­ple things look com­pli­cated, but it also makes al­most every step the cpu takes vis­i­ble and un­der your con­trol. it does ex­actly what you tell it to, with­out warn­ings, and with­out any help. if it’s be­hav­ing in­cor­rectly, it’s be­cause you wrote it in­cor­rectly.

writ­ing a web server in as­sem­bly means there are no http li­braries. no au­to­matic cleanup. no string types: strings are just re­gions of mem­ory that hold in­di­vid­ual bytes se­quen­tially. a struct as it ex­ists in c does­n’t re­ally ex­ist as a lan­guage fea­ture. you have to know the ex­act off­set in bytes be­tween each field, and the to­tal size of the struct, or the cpu will hap­pily read the wrong mem­ory.

raw syscalls

ymawky does­n’t use any libc wrap­pers, it just uses raw calls to the ker­nel. take, for ex­am­ple, this snip­pet of code that opens a file:

mov x16, #5 ; SYS_open syscall num­ber adrp x0, file­name@PAGE add x0, x0, file­name@PA­GE­OFF mov x1, #0x0 ; O_RDONLY is just 0x0000 svc #0x80 b.cs open_­failed

in dar­win, the syscall num­ber goes in the x16 reg­is­ter (in aarch64 linux, it goes in x8). syscall num­ber 5 is open(), which takes a cou­ple ar­gu­ments: file­name and mode. you put each ar­gu­ment in the reg­is­ters by hand, then call the ker­nel with svc #0x80.

if open() fails, the carry flag is set. we check that with b.cs open_­failed, which means if the carry flag is set, branch to open_­failed”. then we have to write open_­failed to do what­ever cleanup and re­sponse han­dling is needed.

this hap­pens a lot. as­sem­bly does­n’t have exceptions” or objects”, it just sets a cpu flag that you have to check and deal with.

gen­eral overview

at its most ba­sic, a web server re­ceives a re­quest, processes it, re­turns a sta­tus code, and maybe a file. a lot goes into that receives a re­quest” bit:

set up sock­ets with socket(AF_INET, SOCK_STREAM, 0)

con­fig­ure the socket with set­sock­opt(serverfd, SOL_SOCKET, SO_REUSEADDR, &buf, sizeof(int))

bind a file de­scrip­tor to an ad­dress with bind(sockfd, &addr, 16)

lis­ten to the socket for new con­nec­tions with lis­ten(sockfd, 5)

ac­cept a con­nec­tion with ac­cept(sockfd, NULL, NULL)

ymawky is a fork-on-re­quest server. that means for each new in­bound con­nec­tion, it calls the fork() syscall. this has some ad­van­tages:

mem­ory is not shared be­tween re­quest han­dlers

it’s eas­ier to un­der­stand

it’s eas­ier to write

but it also has some pretty sig­nif­i­cant dis­ad­van­tages:

bloat

each process has its own mem­ory space

it fun­da­men­tally han­dles fewer con­cur­rent con­nec­tions than mod­els like ng­inx’s event-dri­ven async non-block­ing model

with more con­cur­rent con­nec­tions, the ker­nel spends more time switch­ing be­tween processes than ac­tu­ally be­ing in the process

did i men­tion the bloat? and mem­ory con­sump­tion?

bind­ing to sock­ets and lis­ten­ing is the easy part. the real soul-crush­ing task is pro­cess­ing re­quests. a lot goes into this:

de­ter­min­ing re­quest type: GET, HEAD, OPTIONS, PUT, or DELETE

ex­tract­ing the re­quested path

nor­mal­iz­ing the path, like de­cod­ing %20 into a space

per­form­ing safety checks on the path

pars­ing header fields the client sent over

get­ting in­for­ma­tion about the re­quested file

fig­ur­ing out whether it is a di­rec­tory or a reg­u­lar file

writ­ing up­load bod­ies to tem­po­rary files for PUT

build­ing re­sponse head­ers

writ­ing the re­sponse, which is some­how not straight­for­ward

clos­ing any open files

han­dling er­rors with­out crash­ing the server

pars­ing http by hand

i hate string pars­ing. es­pe­cially in as­sem­bly. un­for­tu­nately, an http re­quest is just a string ask­ing a server to do some­thing, and the server has to un­der­stand it.

let’s walk through an ex­am­ple http re­quest:

GET /index.html HTTP/1.0\r\n Range: bytes=1 – 5\r\n\r\n

that first line tells us a lot. it’s a GET re­quest, which means the client would like us to send over in­dex.html. HTTP/1.0 tells the server what ver­sion of http the client is us­ing. the \r\n se­quence, car­riage re­turn plus line­feed, tells the server that’s the end of this line, please process the next one”. the \r\n\r\n at the end tells the server that’s the end of the header. if we never re­ceive \r\n\r\n, we have to bail with 400 Bad Request.

then there is Range: bytes=3 – 5, which means from this file, only give me bytes 3 through 5, ig­nore the rest.” if a file is 500gb large, but you only re­quest bytes 3 through 5, you only re­ceive 3 bytes back. yay! un­for­tu­nately for me, i have to process that header. boo!

first, ymawky de­ter­mines the re­quest type by com­par­ing the first few bytes against every method it sup­ports, then it ex­tracts the path. we scan along the header one byte at a time un­til we find a / or *. but we can’t as­sume every / is the re­quested path. if some­body sends:

GET HTTP/1.0\r\n \r\n

there is a / in HTTP/1.0. once we hit a /, we check that the pre­vi­ous byte was a space. if it was­n’t, we re­ply with 400 Bad Request.

once we find the path, we need some­where to store it. on most sys­tems, PATH_MAX is 4096 bytes, so ymawky has a 4096 byte file­name buffer plus one byte for the null ter­mi­na­tor:

.bss file­name_buffer: .skip 4097 .align 3

copy­ing the file­name is just a loop, but the loop has to con­stantly check both sides: don’t read past the header, and don’t write past the file­name buffer. if the client re­quests GET /aa…[5000 A]…a HTTP/1.0, they should get 414 URI Too Long rather than over­writ­ing 5KB of ar­bi­trary mem­ory.

in python, this is some­thing like:

text.split(“GET /“)[1].split(” )[0]

in as­sem­bly, it’s ~200 lines long, in­clud­ing en­sur­ing HTTP le­gal­ity. is­n’t as­sem­bly the best?

then the path has to be per­cent-de­coded. if the parser sees %, it has to read the next two bytes, ver­ify that they are valid hex char­ac­ters (0 – 9, a-f, A-F), con­vert them into the byte they rep­re­sent, and con­tinue.

GET re­quests can have a Range: header, and PUT re­quests re­quire Content-Length:. un­like the re­quested URL, these can ap­pear at any line in the header. we have to it­er­ate through the header char­ac­ter by char­ac­ter. if we find a \r, we need to check if the next char­ac­ter is \n. if it’s not, it’s a mal­formed header, and we have to send a 400 Bad Request. like­wise, if we find a \n with­out a pre­ced­ing \r, it’s also mal­formed. once we find \r\n, that marks the end of the cur­rent line, and the be­gin­ning of the next. we check if this new line starts with a space, and send a 400 Bad Request if it does (header fields can­not start with white­space). then we check for Range: (or Content-Length:, de­pend­ing on the method), us­ing a lit­tle string com­par­i­son func­tion:

streqn: ldrb w3, [x0] ldrb w4, [x1] cmp w3, w4 b.ne Lstreqn_no_match

cbz w3, Lstreqn_match ;; both equal and both NULL = end of string = match

;; if we’ve reached the end, it’s a match yeah? subs x2, x2, #1 b.eq Lstreqn_match

add x0, x0, #1 add x1, x1, #1 b streqn

Lstreqn_match: mov x0, #1 ret

Lstreqn_no_match: mov x0, #0 ret

this takes two string point­ers, x0 and x1, a max length in x2, and checks if each char­ac­ter is the same.

let’s see what a Range: header can look like:

Range: bytes=10- Range: bytes=-10 Range: bytes=5 – 10

both sides of the range are op­tional, but at least one of them is re­quired. since 10” is a string and not a lit­eral 10, each side has to be con­verted from ascii dig­its into an in­te­ger. we have to write an atoi-style func­tion, be­ing care­ful to check for an in­te­ger over­flow:

;; x0 -> pointer to string atoi: mov x1, #0 mov x3, #10 mov x4, #0 1: ; if the num­ber is >=19 dig­its long, it could over­flow the 64-bit reg­is­ters cmp x4, #19 b.hs Latoi_error

ldrb w2, [x0] cbz w2, 2f

cmp w2, #‘0’ b.lo Latoi_error cmp w2, #‘9’ b.hi Latoi_error

; re­sult = (result * 10) + cur­rent digit mul x1, x1, x3 sub w2, w2, #‘0’ add x1, x1, x2 add x0, x0, #1 add x4, x4, #1

b 1b 2: cmn xzr, xzr ; clear carry to sig­nal suc­cess mov x0, x1 ret

Latoi_error: cmp xzr, xzr ; set carry to sig­nal fail­ure mov x0, #0 ret

in python, that would be int(string). is­n’t as­sem­bly mag­i­cal?

put

PUT is in­ter­est­ing. it’s idem­po­tent, mean­ing the end re­sult on the server is the same re­gard­less of how many times you send the same re­quest. PUT /file.txt will cre­ate file.txt, or com­pletely over­write it if it al­ready ex­ists. putting 1234 to file.txt twice in a row re­sults in one file that con­tains 1234, not 12341234.

this makes PUT hon­estly pretty dan­ger­ous to have open glob­ally, but hey, who cares?

there are a few things to con­sider when han­dling PUT:

what if the process crashes in the mid­dle of han­dling the re­quest?

what if the client says the Content-Length is 2kb, but only sends 100 bytes?

what if the client says the Content-Length is huge, like 50gb?

that last one is easy to fix. con­fig­ure a max­i­mum file size. in con­fig.S, MAX_BODY_SIZE is 1gb by de­fault. if Content-Length is larger than that, ymawky re­fuses the re­quest with 413 Content Too Large. easy peasy.

the first two have the same ba­sic fix. if we blindly opened file.txt and started writ­ing into it, the file could be left half-writ­ten if some­thing goes wrong. so in­stead, ymawky writes to a tem­po­rary file:

.ymawky_tmp_<pid>

to get the pid, we use get­pid() (syscall #20), then a cus­tom itoa() to con­vert the num­ber to a string (while check­ing for buffer over­flow, of course). then, the re­quested con­tent from the client gets writ­ten to the temp file. if every­thing goes smoothly, the temp file is re­named in place, and file.txt now ex­ists on the server. if the client dis­con­nects un­ex­pect­edly, times out, or sends a mal­formed body, the temp file is un­link()’d (syscall #10/syscall #472 for un­linkat()). ex­ist­ing files are only over­writ­ten af­ter a com­plete re­quest was sent over suc­cess­fully.

di­rec­tory list­ing and more string pars­ing yay

have you ever no­ticed some­times you visit a di­rec­tory on a web­site, and it lists all the files with links you can click on? it seems like pretty ba­sic func­tion­al­ity, and it’s not too com­pli­cated. but like every­thing in as­sem­bly, you have to do every­thing by hand.

if you GET /somedir/, we check if di­rec­tory list­ing is en­abled (ALLOW_DIR_LISTING in con­fig.S). if it’s not, we send a 403 Forbidden and call it a day.

GitHub - imtomt/ymawky: MacOS Web Server written entirely in ARM64 assembly

github.com

This is ymawky (yuh maw kee), a web server writ­ten en­tirely in ARM64 as­sem­bly. ymawky is a syscall-only, no libc, fork-per-con­nec­tion web server writ­ten by hand. While it is de­vel­oped for MacOS, I’ve tried to make it as portable as pos­si­ble — how­ever, it’s likely you will still need to make some (hopefully mi­nor) Significant tweaks to get this to run on Linux/other Unix sys­tems. See Implementation Notes for more de­tails.

Building

Requires Xcode Command Line Tools. Install with xcode-se­lect –install. ymawky only runs on ap­ple sil­i­con (arm64).

Run make to build.

Ensure there is a www/ di­rec­tory next to the ymawky ex­e­cutable. That’s the doc­u­ment root where ymawky searches for files. GET with an empty file­name (GET /) will search for www/​in­dex.html, so you might want to make sure there’s an in­dex.html as well.

ymawky will try to serve sta­tic er­ror pages when a clien­t’s re­quest re­sults in er­ror, eg 404. The pages it searches for in err/(​code).html, so en­sure err/ ex­ists alongisde ymawky and www/. See Configuration to mod­ify the de­fault file and doc­root.

Running

./ymawky to start run­ning the web server on 127.0.0.1:8080.

./ymawky [port] to start run­ning the web server on 127.0.0.1:[port]

./ymawky [literally-any-character-other-than-0 – 9] to start run­ning the web server on 127.0.0.1:8080 in de­bug mode. Debug mode dis­ables fork­ing, and makes ymawky only han­dle one re­quest. (I needed to do this be­cause lldb was­n’t let­ting me de­bug the chil­dren, ugh.)

Unfortunately, while cus­tom ports are sup­ported, cus­tom ad­dresses are not. as of right now, ymawky can only run on 127.0.0.1. This is solely be­cause I haven’t im­ple­mented it — but if you’d like to con­sider this a safety fea­ture, then I guess it could be in­ten­tional.

To see ymawky in ac­tion, start run­ning ymawky with ./ymawky [port]. Then open your web browser of choice (or use curl), and visit 127.0.0.1:8080/ or 127.0.0.1:8080/pretty/index.html. Bask in the warmth of as­sem­bly.

What can it do?

ymawky is a sta­tic-file web server. It does­n’t sup­port server-side code to gen­er­ate con­tent on-the-fly, or more ad­vanced URL pars­ing, such as /search?query=term. That’s not to say it’s non-func­tional, though.

Supported HTTP meth­ods:

GET PUT DELETE OPTIONS HEAD

GET

PUT

DELETE

OPTIONS

HEAD

Basic pro­tec­tion from slowloris-like Denial of Service at­tacks

Decodes % hex en­cod­ing, eg, %20 de­codes to a space in file­names, and %61 de­codes to a

Smart path tra­ver­sal de­tec­tion and pre­ven­tion. Blocks .. from tra­vers­ing paths, while not dis­al­low­ing mul­ti­ple pe­ri­ods when they’re part of a file:

GET /../../../etc/passwd -> 403 Forbidden GET /ohwell…txt -> 200 OK GET /../src/ymawky.S -> 403 Forbidden GET /hehe..txt -> 200 OK

GET /../../../etc/passwd -> 403 Forbidden

GET /ohwell…txt -> 200 OK

GET /../src/ymawky.S -> 403 Forbidden

GET /hehe..txt -> 200 OK

Automatically prepends www/ to re­quested files. GET /index.html will re­trieve www/​in­dex.html

Empty GET / re­quests de­fault to GET www/​in­dex.html

PUT re­quests sup­port up­loads of up to 1GiB, though this can be con­fig­ured for larger files

PUT is atomic due to writ­ing to a tem­po­rary file then re­nam­ing, al­low­ing con­cur­rent PUT re­quests with­out leav­ing par­tially-writ­ten files

Content-Length: pars­ing and ver­i­fi­ca­tion in PUT re­quests

MIME type de­tec­tion, giv­ing Content-Type in the re­sponse header with the cor­re­spond­ing MIME type

Accepts Range: bytes= ranges in GET re­quests, sup­port­ing full ranges bytes=X-N, suf­fix ranges bytes=-N, and open-ended ranges bytes=X-. Video scrub­bing is well sup­ported

Basic HTTP ver­sion pars­ing. Requests need to spec­ify HTTP/1.1 or HTTP/1.0, and if re­quest­ing HTTP/1.1, a Host: field needs to be pre­sent in the header. Currently, ymawky does­n’t do any­thing with Host, but per RFC 9112 Section 3.2, the Header must be sent

Serves cus­tom HTML pages for er­ror codes, such as 404, or 500. Look in the err/ di­rec­tory for an ex­am­ple

If the re­quested re­source is a di­rec­tory, list all files and sub­dirs in the di­rec­tory. Note that this ex­cludes www/ (or what­ever your doc­root is): GET / will al­ways search for in­dex.html if no file is given.

Safety”

This is a web server writ­ten en­tirely by-hand in ARM64 as­sem­bly as a fun pro­ject. It’s prob­a­bly got a lot of vul­ner­a­bil­i­ties I’m un­aware of. However, I did do my best to make it safer. Here are some safety pre­cau­tions ymawky takes.

Rejects paths >= PATH_MAX (4096 bytes)

Reject any paths that in­clude path tra­ver­sal — /../..

Reject any re­quests that do not con­tain a path within 16 bytes

Confined to www/. Any path re­quested gets www/ prepended to it

Rejects any path con­tain­ing sym­links, with O_NOFOLLOW_ANY

PUT writes to a tem­po­rary file, www/.​ymawky_tmp_<pid>. Upon suc­cess­fully re­ceiv­ing the whole file, this tem­po­rary file is then re­named to the re­quested file­name. This pre­vents par­tial or cor­rupted PUT re­quests from over­writ­ing ex­ist­ing files.

Reject any re­quests whose path starts with www/.​ymawky_tmp_. This pre­vents some­one from GETing a tem­po­rary file, and pre­vents some­one from send­ing PUT /.ymawky_tmp_4533 or some­thing.

Must re­ceive data within 10 sec­onds. If it’s slower, the con­nec­tion will close. If the en­tire header is not re­ceived within 10 sec­onds to­tal, the con­nec­tion will be closed. This is to pre­vent slowloris-like at­tacks.

HTTP Status Codes

ymawky cur­rently sup­ports and can re­ply with the fol­low­ing sta­tus codes:

200 OK

201 Created

204 No Content

206 Partial Content

400 Bad Request

403 Forbidden

404 Not Found

408 Request Timeout

409 Conflict

411 Length Required

413 Content Too Large

414 URI Too Long

416 Range Not Satisfiable

418 I’m a teapot

431 Request Header Fields Too Large

500 Internal Server Error

501 Not Implemented

503 Service Unavailable

505 HTTP Version Not Supported

507 Insufficient Storage

Custom HTML pages will be served along­side the er­ror codes (400+). These HTML files are lo­cated in err/(​code).html. You can use build_er­r_­pages.sh to cre­ate a page for each code, with dif­fer­ent text at your leisure. Edit the source code of build_er­r_­pages.sh to mod­ify the text per-page, and mod­ify err/​tem­plate.html to mod­ify the base tem­plate. In err/​tem­plate.html:

{{CODE}} - HTTP Code: eg, 404

{{TITLE}} - Title text: eg, Not Found”

{{MSG}} - Custom mes­sage: eg, the rats ate this page”

MIME Types

MIME types are de­tected by an­a­lyz­ing the file ex­ten­sion. The fol­low­ing MIME types are rec­og­nized.

Web-related files:

.html -> text/​html; charset=utf-8

.htm -> text/​html; charset=utf-8

.css -> text/​css; charset=utf-8

.csv -> text/​csv; charset=utf-8

.xml -> text/​xml; charset=utf-8

.js -> text/​javascript; charset=utf-8

.json -> ap­pli­ca­tion/​json

.wasm -> ap­pli­ca­tion/​wasm

.mjs -> text/​javascript; charset=utf-8

.map -> ap­pli­ca­tion/​json

Image files:

.png -> im­age/​png

.jpg -> im­age/​jpeg

.jpeg -> im­age/​jpeg

.gif -> im­age/​gif

.svg -> im­age/​svg+xml

.ico -> im­age/​x-icon

.webp -> im­age/​webp

.avif -> im­age/​avif

.bmp -> im­age/​bmp

.tiff -> im­age/​tiff

.apng -> im­age/​apng

bits from the release team

lists.debian.org

To: de­bian-de­vel-an­nounce@lists.de­bian.org

Subject: bits from the re­lease team

From: Paul Gevers <el­brus@de­bian.org>

Date: Sun, 10 May 2026 04:47:30 +0000

Message-id: <[🔎] E1wLw4g-000n88 – 0p@respighi.debian.org>

Mail-followup-to: de­bian-de­vel@lists.de­bian.org

––-BEGIN PGP SIGNED MESSAGE––- Hash: SHA512

Hi,

We’re about half-way the forky re­lease cy­cle and we’d like to up­date you on a small step in code, but a gi­ant leap in com­mit­ment.

Reproducibility ===============

Aided by the ef­forts of the Reproducible Builds pro­ject [1], we’ve de­cided it’s time to say that Debian must ship re­pro­ducible pack­ages. Since yes­ter­day, we have en­abled our mi­gra­tion soft­ware to block mi­gra­tion of new pack­ages that can’t be re­pro­duced [2] or ex­ist­ing pack­ages (in test­ing) that regress in re­pro­ducibil­ity.

Testing bin­N­MUs ===============

Earlier this year, func­tion­al­ity was added to the mi­gra­tion soft­ware to run au­top­kgtests for bin­N­MUs, just like we do for source-full up­loads. While this is prob­a­bly not very rel­e­vant for the work of most main­tain­ers, it is an­other step in qual­ity as­sur­ance.

loong64 =======

Two weeks ago, a new ar­chi­tec­ture was added to the archive: loong64 [3]. Because we only al­low bi­na­ries built on the buildds to mi­grate and be­cause of multi-arch re­quire­ments, we had to re­build quite a few pack­ages on all ar­chi­tec­tures. Because of the new bin­NMU func­tion­al­ity men­tioned above, this means that the CI queue is cur­rently rather big. Please ex­er­cise a bit of pa­tience.

Post-upload fol­low-up =====================

It is the re­spon­si­bil­ity of the up­loader of a source pack­age to en­sure that it mi­grates. That means that if your pack­age is blocked by au­top­kgtest re­gres­sions in re­verse (test) de­pen­den­cies, which need up­dat­ing, we ex­pect you to file the ap­pro­pri­ate bugs (severity RC).

Greetings from Hamburg, on be­half of the Release Team Paul

[1] https://​re­pro­ducible-builds.org/ [2] on https://​re­pro­duce.de­bian.net/ [3] https://​wiki.de­bian.org/​Ports/​loong64

––-BEGIN PGP SIGNATURE––-

iQIzBAE­BC­gAd­FiEEG5OT­VaxM­N­cr­fvX­OcBq­Mo4Ax­qzh­U­FAmoAD­NU­ACgkQBq­Mo4Axq zhXwr­BAAg­Wf2yAFIc1hxGH­GRb+KF­faBZP9x3U­ufEd­ClJo3n0VSVYm­s16/​WEps­GMj u0­FISml/​Dzf4hJjxwrHgKvK8t­dE­Vt­d­c­meN0LuyxXFas1W­pi9B/​0eG­n­rEO/​wz­goN5 3xZ8Hdnm8fyVPgUReXWm0aGz8FsAYFTmB6LxQYD6CiPj4pb0R30PLZmFd11UOMYf CuIi3DM89hiqV3pFsI6JZPaGCDp+VpvwIynQHwVvUZEBLsQKrBhNMCVlObFcv7R4 gqTi9FlXC0+eaxL­RAm­fc­FaH0deA4L2ivR9jZnFmZW2qY­CR­ZOe1o6tHRw­B03Sk0Do l/​oJ6IZmx­GYJwdzJB­SVOD­m­DuM­fk­fXghSS­ca1ZE­DrkpxKR­YARAAf­ClL­L1UE­hqG//​Y 9p6pEPY3RlaDmhdKUQcqmK92Jc0XPI1feDfJTb9QvppI/kmXwDJTLopz+gnjQECK ub0j­GRdi­ki­coedAaFdY­D­WMgQD2PFW6ed5k­lKA­R3SaugFC­cvJyy4afel0bU8QSphi QKt0amymW7bPRz1yJlmwxa+f6xrHEvRDfNY7mTxRgluIt9yX61PwXEy6YAGNjcz/ UShCV23OqcKGHa2KjybVB/3VOB09BC8L5ppir6ylhT/uPLGBxMDFUkBL4GcAjt5l oI2u3G0­jEB­Mo6fTV8cWG­WN­vh8N­M­R00hU­luBzG0k­I9zvoD­pO7iIc= =7DZT ––-END PGP SIGNATURE––-

de­bian-de­vel-an­nounce@lists.de­bian.org

Paul Gevers (on-list)

Paul Gevers (off-list)

Prev by Date: Bits from past DPL

Previous by thread: Bits from past DPL

Index(es):

Date Thread

Date

Thread

Emerich Juettner: The One Dollar Counterfeiter

www.amusingplanet.com

In fic­tion as well as in real life, coun­ter­feit­ers have al­ways been por­trayed as mas­ter forg­ers and artists who re­pro­duced ban­knotes with as­ton­ish­ing pre­ci­sion. They were of­ten shown as vast crim­i­nal en­ter­prises and gang­sters who desta­bi­lized economies with fake cur­rency. Then there was Emerich Juettner, a frail old im­mi­grant liv­ing alone in a shabby New York apart­ment, qui­etly print­ing one-dol­lar bills on a cheap hand press. He was, by al­most every con­ven­tional stan­dard, ter­ri­ble at coun­ter­feit­ing.

And yet he suc­ceeded for nearly a decade. By the time the United States Secret Service fi­nally caught Juettner in 1948, he had be­come kind of folk hero. The press adored him and the pub­lic sym­pa­thized with him. A Hollywood film would soon im­mor­tal­ize him un­der the nick­name Mister 880,” a ref­er­ence to his Secret Service case num­ber.

Emerich Juettner (also known as Edward Mueller) hardly looked like the sort of per­son who would trou­ble fed­eral agents. Born in Austria-Hungary in 1876, he em­i­grated to the United States and spent most of his life drift­ing through mod­est oc­cu­pa­tions. Initially he worked as a pic­ture frame gilder be­fore mar­ry­ing Florence LeMein in 1902 at the age of 26. After the birth of his son and daugh­ter, Juettner be­gan work­ing as a main­te­nance man and build­ing su­per­in­ten­dent in New York’s Upper East Side. His job al­lowed him and his fam­ily to live rent free in the base­ment of the build­ing where he worked.

In 1937, Juettner’s wife died and the sixty-year-old sud­denly found him­self liv­ing alone in New York City. He then be­came a junk col­lec­tor.

Juettner bought a used, two-wheel push­cart and spent long days am­bling about the streets of New York pick­ing up the dis­carded goods of city dwellers and sell­ing off the oc­ca­sional find to a whole­sale dealer. But Juettner’s earn­ings were spo­radic and he was barely putting food in his mouth. This forced him to look into an­other way of mak­ing a liv­ing.

In his youth, Juettner had learned metal en­grav­ing. He had also dab­bled in pho­tog­ra­phy. Combining these two skills, in November 1938, he be­gan to make coun­ter­feit one-dol­lar bills. He snapped pic­tures of a $1 bill, trans­ferred the im­ages to a pair of zinc plates, and then metic­u­lously filled in small de­tails of the bill by hand.

Juettner’s fake notes were laugh­ably crude. He pro­duced them us­ing in­ex­pen­sive ma­te­ri­als and prim­i­tive tech­niques in his apart­ment kitchen. The pa­per was wrong. The ink was poor. The en­grav­ing lacked de­tail. The bills of­ten ap­peared slightly blurred or un­even. Some notes even had spelling er­rors.

Scenes from the movie Mis­ter 880 dra­ma­tiz­ing Juet­tner’s coun­ter­feit op­er­a­tion.

They were not the kind of coun­ter­feit cur­rency that could fool bankers or cashiers un­der care­ful in­spec­tion. But Juettner un­der­stood that al­most no­body ex­am­ines a one-dol­lar bill closely.

Large-denomination coun­ter­feits at­tract scrutiny be­cause peo­ple ex­pect fraud where sub­stan­tial sums are in­volved. A sus­pi­cious twenty-dol­lar bill may be held to the light, checked for tex­ture, or com­pared against a real note. But a worn one-dol­lar bill pass­ing quickly be­tween cus­tomers in a busy shop? Few peo­ple cared enough to look care­fully.

Juettner ex­ploited that in­dif­fer­ence bril­liantly. He also op­er­ated on an ex­tremely small scale. Instead of flood­ing the mar­ket, he re­leased only a hand­ful of fake bills at a time. The notes cir­cu­lated qui­etly through din­ers, bars, street ven­dors, and small stores, dis­ap­pear­ing into the im­mense ocean of American cur­rency.

The Secret Service be­came aware that poor-qual­ity coun­ter­feit dol­lar bills were ap­pear­ing in New York, but the case puz­zled in­ves­ti­ga­tors. Professional coun­ter­feit­ers usu­ally aimed for large prof­its. These bills, by con­trast, were am­a­teur­ish and lim­ited in num­ber. Whoever was mak­ing them seemed con­tent merely to sur­vive.

The United States Secret Service, which in­ves­ti­gates coun­ter­feit­ing, opened a file on the mys­te­ri­ous forger. The in­ves­ti­ga­tion was as­signed case num­ber 880. Over the years, agents tried in vain to lo­cate the cul­prit. They handed out some 200,000 warn­ing plac­ards at 10,000 stores. They tracked down dozens of folks who’d spent the bills and in­ter­viewed them.

But Juettner re­mained elu­sive. He was care­ful not to draw at­ten­tion. He never at­tempted large trans­ac­tions. He worked alone, and he printed only tiny amounts.

10 years went by and the search for Mister 880 turned into the largest and most ex­pen­sive coun­ter­feit in­ves­ti­ga­tion in Secret Service his­tory.

Then in January of 1948, some school­boys play­ing around in a va­cant lot in the Upper West Side un­cov­ered a cou­ple of zinc en­grav­ing plates buried in the snow. They also found 30 funny-look­ing dol­lar bills.”

A week later, one boy’s fa­ther caught him play­ing poker with a strange bill and turned it into the po­lice, who handed it over to the Secret Service. They soon tracked down the lot where the boys found the plates and learned that a few weeks ear­lier, there had been a fire in a bor­der­ing apart­ment. The fire­fight­ers had en­tered to find the place full to the brim with junk, and had thrown them out the win­dow into the al­ley to make room. The Secret Service had found their mys­tery man, Mister 880.

Emerich Juettner is in­ter­ro­gated by au­thor­i­ties af­ter his ar­rest.

Emerich Juettner was ar­rested. When ques­tioned about his crimes, he non­cha­lantly ad­mit­ted to them. Juettner said that he had been mak­ing them for 9 to 10 years, and that he never gave more than one bill to any one per­son, so no­body ever lost more than $1.”

Under or­di­nary cir­cum­stances, a fed­eral coun­ter­feit­ing ar­rest would have gen­er­ated lit­tle sym­pa­thy. But the story of Emerich Juettner struck the pub­lic imag­i­na­tion im­me­di­ately. Here was an old man sur­viv­ing in poverty by print­ing crude one-dol­lar bills one at a time. He was not vi­o­lent, greedy, or glam­orous.

At trial, Juettner ad­mit­ted his ac­tiv­i­ties openly. The judge sen­tenced him to only a year and a day in prison, and he was paroled af­ter 4 months. He was also made to pay a fine of $1. It has been agreed that Juettner’s com­plete lack of greed was the ra­tio­nale be­hind the light sen­tence.

After his re­lease, Juettner briefly achieved celebrity sta­tus. His no­to­ri­ety be­came so wide­spread that Hollywood adapted the story into the 1950 film Mister 880, di­rected by Edmund Goulding. Eventually, Juettner made more money from the re­lease of Mister 880 than he had made by coun­ter­feit­ing.

Juettner re­turned to a life of nor­malcy, and lived out the rest of his days in the sub­urbs of Long Island, where he died in 1955, at the age of 79.

References: # The 70-year-old re­tiree who be­came America’s worst coun­ter­feiter. Hustle # Finding Mr. 880’: The case of the $1 coun­ter­feit. NY Daily News

Access Denied

www.casio.com

France Moves to Break Encrypted Messaging

reclaimthenet.org

France’s in­tel­li­gence del­e­ga­tion in par­lia­ment has for­mally backed break­ing the en­cryp­tion that pro­tects WhatsApp, Signal, and Telegram con­ver­sa­tions, rec­om­mend­ing that mag­is­trates and in­tel­li­gence agents be granted what law­mak­ers de­scribe as tar­geted ac­cess to mes­sages that plat­forms cur­rently can­not read even them­selves.

The del­e­ga­tion, an eight-mem­ber body com­posed of four deputies and four sen­a­tors, pub­lished its con­clu­sions on Monday af­ter months of work on a ques­tion that keeps re­turn­ing to the French Parliament. The in­abil­ity to ac­cess the con­tent of en­crypted com­mu­ni­ca­tions con­sti­tutes a ma­jor ob­sta­cle for the work of the jus­tice sys­tem and in­tel­li­gence ser­vices,” the del­e­ga­tion wrote, fram­ing end-to-end en­cryp­tion as a prob­lem to be solved rather than a pro­tec­tion to be pre­served.

The tech­nol­ogy end-to-end en­cryp­tion uses is pre­cisely the thing the del­e­ga­tion wants weak­ened. Decryption keys live on user de­vices, not on com­pany servers, which means the plat­forms hold­ing your mes­sages gen­uinely can­not read them. That’s the de­sign and the point. Strip that prop­erty away and the pro­tec­tion col­lapses be­cause a sys­tem that lets in­ves­ti­ga­tors read mes­sages on de­mand is also a sys­tem that can be abused, leaked, sub­poe­naed, or hacked.

French po­lice and in­tel­li­gence ser­vices have spent years com­plain­ing about this tech. They can still in­ter­cept old-fash­ioned phone calls and SMS mes­sages with a judge’s war­rant but en­crypted plat­forms route around that ca­pa­bil­ity en­tirely.

Reclaim Your Digital Freedom.

Get un­fil­tered cov­er­age of sur­veil­lance, cen­sor­ship, and the tech­nol­ogy threat­en­ing your civil lib­er­ties.

The del­e­ga­tion ac­knowl­edged that in­ves­ti­ga­tors al­ready have a workaround called RDI, or collection of dig­i­tal data” which can in­volve com­pro­mis­ing a tar­get’s de­vice and har­vest­ing its con­tents whole­sale through what of­fi­cials call remote in­ter­cep­tion,” re­mote cap­tures. That tech­nique gives se­cu­rity ser­vices ac­cess to every­thing on a phone, far more than just the mes­sages they’re chas­ing. The del­e­ga­tion called it in­ad­e­quate any­way.

Senator Cédric Perrin, who chairs the for­eign af­fairs com­mit­tee and sits on the in­tel­li­gence del­e­ga­tion, has been push­ing this fight for over a year. During de­bate on a nar­co­trafic bill, he se­cured an amend­ment that would have forced mes­sag­ing plat­forms to implement the nec­es­sary tech­ni­cal mea­sures in or­der to al­low in­tel­li­gence ser­vices to ac­cess the in­tel­li­gi­ble con­tent of com­mu­ni­ca­tions and data pass­ing through them.”

Refusal to com­ply car­ried fines of up to 2 per­cent of world­wide an­nual rev­enue. The Senate passed it. The National Assembly killed it, with the Macronist deputies, the left, and even the Rassemblement National vot­ing it down.

Perrin’s fram­ing then was that noth­ing fun­da­men­tal was chang­ing. I don’t see how there would be any dif­fer­ence be­tween what is done to­day with SMS and emails and what would be done to­mor­row with WhatsApp, Signal, and Telegram,” he said at the time, with back­ing from then-In­te­rior Minister Bruno Retailleau and Justice Minister Gérald Darmanin.

The ar­gu­ment treats en­crypted mes­sag­ing as just an­other com­mu­ni­ca­tion chan­nel that should sit within reach of the state, ig­nor­ing that the en­tire rea­son these plat­forms ex­ist is to oc­cupy a dif­fer­ent cat­e­gory.

Aurélien Lopez-Liguori, the RN deputy who op­posed the amend­ment, made the tech­ni­cal ob­jec­tion bluntly. This is a to­tal mis­un­der­stand­ing of what en­cryp­tion means. The de­cryp­tion keys are at the level of users’ de­vices. The key is­n’t cen­tral­ized some­where within the plat­form. You would then have to set up back­doors for all com­mu­ni­ca­tions, which would go far be­yond the scope of fight­ing drug traf­fick­ing. The first hacker to come along would have ac­cess to our com­mu­ni­ca­tions,” he warned.

Translated into en­gi­neer­ing terms, his point was the one cryp­tog­ra­phers have been mak­ing for thirty years. There is no such thing as a back­door only the good guys can use.

Perrin now of­fers a dif­fer­ent fram­ing. Article 8 ter, which I had adopted, was not at all aimed at ob­tain­ing en­cryp­tion keys but at in­tro­duc­ing a ghost par­tic­i­pant into a con­ver­sa­tion be­fore en­cryp­tion,” he says.

The ghost par­tic­i­pant” ap­proach, some­times called a ghost user pro­posal, was floated by GCHQ in 2018 and re­jected by every ma­jor pri­vacy or­ga­ni­za­tion, civil lib­er­ties group, and se­cu­rity re­searcher who looked at it. The idea is that the plat­form silently adds a third re­cip­i­ent, an in­vis­i­ble in­tel­li­gence agent, to a sup­pos­edly two-per­son con­ver­sa­tion. Users never see them.

The en­cryp­tion tech­ni­cally still works, ex­cept that one of the par­ties is the state.

Perrin also re­jected the sug­ges­tion that civil lib­er­ties were at stake. Protecting pub­lic free­doms is our con­cern as par­lia­men­tar­i­ans. That was ad­dressed through var­i­ous ad­min­is­tra­tive and ju­di­cial checks. The work car­ried out within the del­e­ga­tion was about get­ting a clearer tech­ni­cal pic­ture of what is or is­n’t pos­si­ble. It’s cu­ri­ous that the RN, which con­stantly em­pha­sizes its de­sire to pro­tect the French peo­ple, does­n’t want to give in­tel­li­gence ser­vices the tools to do so,” he said.

The del­e­ga­tion’s re­port con­cluded that tar­geted ac­cess is not tech­ni­cally out of reach and noted that an ex­pert group con­vened by the European Commission is work­ing on a tech­no­log­i­cal roadmap to iden­tify how such ac­cess could be built. This means the tech­ni­cal prob­lem cryp­tog­ra­phers have been de­scrib­ing as un­solv­able is be­ing treated as a pro­ject man­age­ment ex­er­cise.

Mass sur­veil­lance, of course, is­n’t what the del­e­ga­tion is propos­ing. The fear is­n’t that a French in­ves­ti­ga­tor will read every WhatsApp mes­sage. The fear is that in­fra­struc­ture built for tar­geted ac­cess has a way of grow­ing, that an au­then­ti­ca­tion mech­a­nism built for ter­ror­ism cases ends up serv­ing or­ga­nized crime cases, then drug cases, then im­mi­gra­tion cases, then po­lit­i­cal sur­veil­lance cases, and that a French ghost-user sys­tem gets de­manded by less de­mo­c­ra­tic gov­ern­ments next.

Not every­one in the Senate’s right-and-cen­ter ma­jor­ity agrees with the del­e­ga­tion’s di­rec­tion. Senator Olivier Cadic, of the Centrist Union, se­cured an amend­ment to a sep­a­rate bill on crit­i­cal in­fra­struc­ture re­silience and cy­ber­se­cu­rity that would do the op­po­site, writ­ing en­cryp­tion pro­tec­tion into French law and pro­hibit­ing any oblig­a­tion on mes­sag­ing ser­vices to in­stall back­doors. The Senate adopted it in March 2025.

The in­tel­li­gence del­e­ga­tion’s re­port at­tacks that text di­rectly, claim­ing This new Article 16 bis would weaken the le­gal frame­work for in­tel­li­gence and in­ves­tiga­tive tech­niques and would hin­der their im­ple­men­ta­tion.” Cadic’s rea­son­ing was the one the del­e­ga­tion seems de­ter­mined to ig­nore. I am ob­vi­ously in fa­vor of track­ing down crim­i­nals, but not with tools that could bring us down. We must not cre­ate our own vul­ner­a­bil­i­ties,” he said. His bill was ex­am­ined in com­mit­tee at the National Assembly in September and has been stalled since.

Earlier this year, then-Prime Minister Sébastien Lecornu com­mis­sioned deputy Florent Boudié, who chairs the National Assembly’s law com­mit­tee, to ex­am­ine possible changes to ex­ist­ing le­gal frame­works” for giv­ing in­ves­ti­ga­tors ac­cess to en­crypted com­mu­ni­ca­tions. The leg­isla­tive ve­hi­cle for any new at­tempt re­mains un­de­cided, with par­lia­men­tar­i­ans wait­ing to see what Boudié pro­duces but re­port­edly open to a fresh propo­si­tion de loi if it’s needed.

What’s un­der­way in France is­n’t re­ally a de­bate about whether in­tel­li­gence ser­vices should have tools to in­ves­ti­gate se­ri­ous crime. They al­ready do. They have the RDI au­thor­ity to com­pro­mise in­di­vid­ual de­vices, the sur­veil­lance al­go­rith­mique they ex­panded last year, satel­lite in­ter­cep­tion pow­ers, tra­di­tional wire­taps, meta­data ac­cess, and the co­op­er­a­tion of every French tele­com op­er­a­tor.

The new fight is about whether the one cat­e­gory of com­mu­ni­ca­tion that cur­rently re­sists state in­ter­cep­tion, se­cured by math­e­mat­ics rather than by promise, should be re­shaped so that re­sis­tance dis­ap­pears. The del­e­ga­tion has an­swered yes. The cryp­tog­ra­phy has­n’t changed. The po­lit­i­cal will to ig­nore what the cryp­tog­ra­phy says has.

To add this web app to your iOS home screen tap the share button and select "Add to the Home Screen".

10HN is also available as an iOS App

If you visit 10HN only rarely, check out the the best articles from the past week.

Visit pancik.com for more.