HNHacker News
TopNewBestAskShowJobs

nh2

3,664 karma · joined March 29, 2014

https://github.com/nh2

[ my public key: https://keybase.io/nh2; my proof: https://keybase.io/nh2/sigs/Duv6hcXOkjZU5KWzJ8d01yeD7QKmhG9QpVMOb4ZMkeU ]

submissionscomments
nh2··on Claude Opus 5
I can confirm that within the first hour of using Opus 5, I already had to call out made-up PR URLs:

    I made that PR number up — I have no evidence a PR `2492` exists.
    That was a fabrication and I should not have written it.
No judgment so far on whether it does that _more_ than Opus 4.8, though.
nh2··on Android May Soon Restrict On-Device ADB
Wise in the UK currently is good. You can use it completely without a phone app. It also has good support for passkeys, TOTP, and SMS OTP, allowing to add multiple of each as it should be. If you do choose the Wise app, it currently warns when you run a custom Android ROM, but let's you use it.

Our company also uses Revolut business, but that requires a phone app to verify online payments with cards. And Revolut recently changed the app so it now requires Google Play Integrity so it doesn't work with LineageOS anymore. So I'm looking to move all new things away from Revolut. I have little patience with companies that want to unbank my company, and decide what hardware or software we use.

nh2··on Job queues are deceptively tricky
The UNIX pipe has the (for many systems) undesirable negative property of losing data in the pipe when the receiving process terminates: Anything in the kernel buffer of the pipe gets lost.

Since the pipe is generally unidirectional, the sending process has no way of knowing whether the receiving process has received, or even more successfully processed, anything sent. For that, one needs to make a pipe in the opposite direction and that is not as easy anymore; it also requires building your own protocol to identify and acknowledge sent work items.

nh2··on After 7 years in production, Scarf has reluctantly moved away from Haskell
Another side remark:

LLMs removed some of the difficulty of Haskell, e.g. some interesting ideas to make things safer with sophisticated types or mechanisms like TemplateHaskell are now MUCH easier to implement in a few minutes. Also Haskell's general stance of breaking backwards compat in fundamental places if it's needed to make the language better, thus causing moderate amounds of upgrade grind, completely evaporated as a drawback because LLMs are extremely good at fixing type errors to bring code up to date with the latest changes.

The LLMs are very good at explaining Haskell compile errors.

Learning Haskell and maintaining projects in it is now easier than ever.

nh2··on After 7 years in production, Scarf has reluctantly moved away from Haskell
Hey, we have a 10 year old Haskell/Python/C++/TypeScript/Nix codebase, and use all of them regularly.

Haskell compiles slowly, but we have not found that to significantly inhibit AI-supported dev so far. We use ghci with `-fobject-code`, and our largest leaf module (10k lines of webserver request handlers) takes 8 seconds to `:reload`. To run stuff in it, the agent pipes `:reload`, or other invocations, into `ghci`.

Working on parts parts that are early in the module chain, such as our Prelude, makes the whole-project `:reload` take 50 seconds. Much of that could be avoided if we didn't suffer from the TH recompilation problem (https://gist.github.com/nh2/14e653bcbdc7f40042da3755539e554a). Originally I made a small GHC patch to hack that out (what this conservative recompilation protects from cannot happen in our project), which made the reload much faster, but the logic was changed in recent GHC so that doesn't work anymore.

In C++, we have some individual files (and thus compilation units) that take 45 seconds to compile.

Python is of course the fastest to (build+run). However, Python also has some problems with repeated runs: Once you have many imports, just starting the program (e.g. `--help`) takes ~2 seconds resolving imports. Do `import pytorch`, add another 2 seconds. For repeated runs, this can be a pain; Haskell and C++ are much faster for that, so they win when you don't have to change the code but repeatedly use already-compiled tools in a different way.

In all 3 languages, getting things to typecheck is very fast, because the agent directly reads the vscode LSP typechecking errors which are < 1s feedback. Claude Opus 4.6-4.8 understand all 3 languages very well.

I agree that GHC devs should focus most of their efforts on compile speed, and real-world pain solving. Some parts of that are indeed being done (e.g. newest GHC can write bytecode to disk to make the above workflow much faster, and codegen is by far the slowest part of compilation). But I think the focus should be even more on that. I consider most important and unsolved:

    * solving Generics being slow to compile, especially for types with many constructors
    * solving deriving classes being slow to compile
    * solving TemplateHaskell causing too much recompilation
    * doing staged compilation,
      so that the next module can typecheck as soon as its imports are typechecked,
      as opposed to waiting that codegen is done;
      this unlocks a large amount of parallel work availability
All of these have open GHC tickets that I think should be the highest focus.

Other real-world production things are being solved quite nicely currently:

    * The new Haskell debugger
    * Much better stacktraces
    * Much better runtime introspection to debug runtime hangs etc
I get your general point that if iteration speed (as in change code + run, 100s of times a day) is your highest value, Python does quite well. We intentionally chose Python for the part of the codebase that's relatively simple data importing but from 50 different sources, count growing adding new sources all the time, and need just quickly iterate on each until we got it.

But I find it would not be a great language for the other parts. Its concurrency story is bad, its interpreter is very slow and immediately disqualifies Python when you need to do e.g. a line of code for every 1000 Bytes (say for streaming small data chunks), typing is very useful but bolted-on and not always correct.

Things where Haskell remains best-in-class is anything nontrivial-webservers, I/O, program correctness, the main "general purpose" programming, refactoring and long-term maintainability.

nh2··on Cache-Conscious Data Layout in Rust: Field Zoning, False Sharing, 128-Byte Rule
No. False sharing can happen even with only 1 writer.
nh2··on OpenWrt One – Open Hardware Router
I have 4 OpenWRT Ones and they are good.

If you just want a good WiFi router or access point, unless you need something cannot do (e.g. WiFi 7 or 10 Gbit/s Ethernet), and if want to spend minimal time messing around with routers today and in the future, just get this one.

After getting this, I see no reason to ever buy any closed-source router again.

No need to learn/remember any other Router config either. It's just all OpenWRT, always looks the same, always works the same. Setting up a new one takes me 2 minutes max.

The recent OpenWRT update also brought the one feature the project was most sorely missing: A simple "Download and install latest firmware" button in the device UI.

Now they just need to add an unattended-upgrades option and I never have to log in again after initial setup.

nh2··on BareMetal RAM Dumper – Bare-metal x86 tool for Cold Boot Attack experiments
When I asked about this 5 years ago, I was told that for many processors you can still just JTAG them and get everything:

https://security.stackexchange.com/questions/189950/how-does...

Has this changed?

nh2··on I can haz smoller NixOS ISOs?
Note that's somewhat out of date:

> `bin/switch-to-configuration` is a Perl script from the beginning

Since NixOS 24.11 the default is `switch-to-configuration-ng`, in Rust. That is a 2.8 MB binary, compared to NixOS's 55 MB Perl distribution. Thus such Perl-less systems shrunk that dependency by 20x regarding activation switching.

And since NixOS 25.11, `nixos-rebuild-ng` in Python replaces its former Perl counterpart, see https://wiki.nixos.org/wiki/Nixos-rebuild

nh2··on I can haz smoller NixOS ISOs?
Seems to me that such boolean flags do not force you to think about "building up" or "building down". You just declare what _is_. Who cares what the default is? When you're building a minimal thing, changing some defaults is fine.
nh2··on FUTO Swipe – A new swipe typing model
I've been using SwiftKey for 10 years (typing not swiping), and test ran FUTO for the last month.

FUTO improved a lot (I had tried it a year earlier also) but SwiftKey's suggestions are still a lot better in my opinion. With SwiftKey I can just type roughly in the right spot without looking and the correct words will come out most of the time. FUTO still suggests a lot of nonsensical next words that just do not follow after the previous in English.

I hope it improves further so I can switch.

The voice models are great though, and they can be used as part of the keyboard or standalone.

nh2··on The perils of UUID primary keys in SQLite
JSON has arbitrary length numbers in the spec only.
nh2··on Changing how we develop Ladybird
> Would you mind sharing a link to one of the open source project you've been maintaining and reviewing contributions on for years

Github is in my profile; I am nixpkgs committer for ~10 years (which is one of the most active projects on Github with 450000 merged PRs).

There is no way I could have possibly written and (pre-tested, to arrive at the eventual code submitted) all the code that I have reviewed.

From the other side, I have spent thousands of hours debugging and writing PRs to over 100 FOSS projects (e.g. glibc, busybox, util-linux, lz4, GHC and tens of Haskell packages, Jenkins, Chromium, GTK, Consul, OpenCV, Signal, many more).

Many of them are small or medium fixes ("drive-by fixes"), where you propose a PR, the owner reviews, says "great, thanks", and the bug gets fixed.

This is a fundamental workflow for open source work. The project gets free contributions and time investment outsourced to "the community" who fix its bugs, the developer-users/community get their problems fixed upstream, permanently.

This not possible for projects that don't have an easy way to submit code with low effort for both sides.

Accepting drive-by fixes is what clears up developer time and helps clear out the countless small issues in software.

If AI slop PRs are a problem, it seems better to establish clear rules and reject contributions that don't follow them with a single click, rather than banning developer contributions altogether. It seems to work acceptably for nixpkgs so far.

nh2··on Changing how we develop Ladybird
> You can still submit a bug report and tell them exactly how you did it.

Can you? The announcement says "There will not be a separate process for submitting patches by other means. We do not want to create a shadow contribution system through issues, comments, email, or forks".

So I, as a human, describe in prose which changes I made to e.g. 20 files?

How is that in the spirit of fighting LLM slop?

Also, if I can do that, the LLM slop contributers can also ... do that.

nh2··on Changing how we develop Ladybird
> There will not be a [..] process for submitting patches by [any] means

> Outside involvement still matters: clear bug reports

So I can find a bug, I can fix it, but I am not allowed to tell them how exactly I did it.

Instead they have to re-figure it out. The team must be thrilled to re-do work they know was already put in by others, repeatedly.

As a user-and-eveloper, why would I sink time into a project with such rules that put a barrier to improving my life with the software? It seems much easier to use Firefox or Chromium, where my fixes actually meet open ears.

It was very useful for me in the past when a new Chromium version crashed on my product, that I could go and suggest a fix to V8, and it was rolled out in the next Chromium release so my product worked again (https://github.com/v8/v8/commit/4f8a70adca01c). Without this, maybe Chromium developers would have never bothered to fix it because of lack of time to figure it out.

> a pull request no longer tells us as much as it used to about the person submitting it

Nobody should need to know anything about any person submitting a pull request. Hopefully whether code that makes it into Firefox or Chromium was never based on the "effort" or "faith" of the submitter, but based on the correctness of the code in review.

Reviewing code fixes is strictly easier than coming up with them yourself.

This holds true automatically: In any situation where it isn't, you can just write the code yourself and done.

As a project you can always ignore or close a PR you want to write yourself instead. But it seems unwise to bar yourself from the _option_ of reviewing an outside contribution, or using it as input for your own re-write.

nh2··on OpenAI frontier models and Codex are now available on AWS
In contrast to Microsoft, OpenAI, and Anthropic, AWS has never done anything close to sneaking in unwanted training opt-outs after the fact.

They are the only ones I trust not to do that so far. And their terms are extremely clear on that, no fuzzy language. Exactly what we want to see. So we use Bedrock.

nh2··on Haskell Foundation 2026 Update
No, this is not the reality of using Haskell packages.

The problem you describe was solved more than a decade ago.

You use a Stackage snapshot (https://www.stackage.org/lts) which is a curation of packages that work together, similar to a Linux distribution like Debian, carrying one version per package.

Our company using Haskell has not spent 1 minute doing "dependency resolution" in the last 10 years, not has anybody we know.

nh2··on AI is a technology not a product
Asking the grandparent:

The what is the idea behind the "ideally have already read the paper, but given 10-20 minutes in silence" part?

The fact that people that have already read it have nothing to do and waste time sitting around bored sounds like an obvious flaw, are we missing something?

nh2··on Space Cadet Pinball on Linux
I wish somebody had as a passion project or company to build Space Cadet into a real physical pinball table.
nh2··on Removing fsync from our local storage engine
Even then, I also share the confusion of the poster you're replying to.

I don't see how a virtualised NVMe disk is different from a physical one.

Especially if you don't have control over the underlying hardware (so you don't know if it has power-loss-protection PLP SSDs), you should send the FUA.

> O_DATA_SYNC

You mean `O_DSYNC`?

Why would you need `O_DSYNC` on-premise, but not on cloud VMs? (Or are you saying you'd include it everywhere?) Similar to my above point, surely it is the task of the VM to pass through any FUA commands the VM guest issues to the actual storage?

Further: Is `O_DSYNC` actually substantially different from writing and then `fdatasync()`ing yourself?

My understand is that no, it's the same. In particular, the same amount of data gets written. So if you believe that to avoid the "can trigger an order of magnitude more I/O" by avoiding `fdatasync()`, you would re-introduce it with `O_DSYNC`.

However, I suspect that that whole consideration is pointless:

The only thing that makes your O_DIRECT+preallocated-only-overwrites writes safe are enterprise SSDs with Power Loss Protection (PLP), usually capacitors.

On those SSDs, NVMe Flush/FUA are no-ops [1]. So you might as well `fdatasync()`/`O_DSYNC`, always. This is simpler, and also better because you do not need to assume/hope that your underlying SSDs have PLP: Doing the safe thing is fast on PLP [2], and safe on non-PLP.

    [1] https://news.ycombinator.com/item?id=46532675
    [2] https://tanelpoder.com/posts/using-pg-test-fsync-for-testing-low-latency-writes/
So the only remaining benefit of `O_DSYNC` over `fdatasync()` is that you save a syscall. That's an OK optimisation given they are equivalent, but it would surprise me if it had any noticeable impact at the latencies you are reporting ("413 us"), because [2] reports the difference beting 6 us.

Let me know if I got anything wrong.

The only remaining question is: Why do you then see any difference in your benchmark?

    Configuration            Throughput (obj/s)
    -------------------------------------------
    ext4 + O_DIRECT + fsync             116,041
    Our engine                          190,985
That is what I'd find very valuable to investigate.

The first suspicion I have is: Shouldn't you be measuring `+ fdatasync` instead?

So I'd be interested in:

    ext4 + O_DIRECT + fdatasync
    ext4 + O_DIRECT + O_DSYNC
    Our engine + O_DSYNC (which you're suggesting above)
Also I don't fully understand what the remaining diference between "ext4 + O_DIRECT + O_DSYNC" and "Our engine + O_DSYNC" would be.
nh2··on Removing fsync from our local storage engine
> fsync doesn’t just sync the file’s data, it syncs every piece of metadata the file depends on: ... directory entry

Famously not, as the man page says.

It is also said later in the article:

> POSIX strictly requires a parent-directory fsync to make a newly created file’s existence durable.

So I'm not sure why the dirent sync is claimed earlier.

nh2··on Hardening Firefox with Claude Mythos Preview
Given the commit is 4 weeks old, will it eventually get comments?

The code before the patch does not look obviously wrong. Now, some more lines were added, but would you now say it now looks less obviously wrong, or more obviously correct?

It seems that the invariants needed here are either in some person's heads, or in some document that is not referenced.

Reading the code for the first time, the immediate question is: "What other lines might be missing? How can I figure?"

If the "obviously correct" level of the code does not increase for a human reviewer, how is it ensured that a similar problem will not arise in the future? Or do we need more LLM to tell us which other lines need to be added?

nh2··on Batteries Not Included, or Required, for These Smart Home Sensors
A big benefit of piezo-powered electronics is that they can do all the usual stuff, such storing state, or cryptography to prevent spoofing, which the ultrasonic approach from the article cannot do.
nh2··on Ubuntu Chromium Snap prevents encrypted storage of passwords by default
To see whether there are plain text passwords on your non-Snap chromium, change this line in the linked password dumper script:

    -    db_path = os.path.expanduser("~/snap/chromium/common/chromium/Default/Login Data")
    +    db_path = os.path.expanduser("~/.config/chromium/Default/Login Data")
nh2··on Ubuntu Chromium Snap prevents encrypted storage of passwords by default
Ubuntu ships a Chromium browser that has its abilitiy to store passwords safely sandboxed/containerized away.

I did not expect that, given that Ubuntu comes with a full GUI and thus safe password storage backend available in theory.

Because this issue is open since 2022, I wrote a repro that proves its existence:

https://bugs.launchpad.net/ubuntu/+source/chromium-browser/+...

All credit goes to user "Erlenmayr" who reported this.

nh2··on Haskell: Debugging
This community wiki page is somewhere between 10 and 20 years out-of-date.

https://wiki.haskell.org/index.php?title=Debugging&action=hi...

In particular, it has no mention about the new actual ... debugger:

https://well-typed.github.io/haskell-debugger/

https://discourse.haskell.org/t/the-haskell-debugger-for-ghc...

nh2··on Zed 1.0
That does not really matter though: Even immediate-mode approach programs should just not be drawing new frames when the program logic says that there are no new inputs that can result in different pixels (e.g. no user inputs, animations, notifications, text content changes, etc.). One does not need to "poll" for input.
nh2··on Copy Fail
Indeed, no modprobe.d will help when the feature is compiled into the kernel ("=y") instead of compiled into a runtime-loadable module.
nh2··on Copy Fail
If you want to use the suggested mitigation (disabling kernel module `algif_aead` with a modprobe config), and you do not want to run that whole obfuscated shell code to get an actual root shell, but only check if the module can be loaded, here is a readable version of its first few lines:

    python3 -c 'import socket; s = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0); s.bind(("aead","authencesn(hmac(sha256),cbc(aes))")); print("algif_aead probably successfully loaded, mitigation not effective; remove again with: rmmod algif_aead")'
Similarly, when the mitigation is in place,

    modprobe algif_aead
should fail with an error.
nh2··on Zed 1.0
Can you repro my finding?

I'm running on a Zed with only 1 empty text file in it. So language servers should not be in use.

How do you measure "pretty lightweight"?

← PreviousPage 3 of 26Next →