16,139 karma · joined December 16, 2013
mike@hydraulic.dev: Hydraulic Conveyor makes distributing desktop apps as easy as a web app. Use it to bring your mobile apps to the desktop and avoid the need to maintain a web version, to use your language and toolkits of choice or to do things that you can't do in the browser. Integrated support for Electron, Flutter, JVM and native apps.
https://www.hydraulic.dev
--
mike@plan99.net: Personal email. I've worked on anti-automation, was an early Bitcoin developer and then designed the Corda enterprise blockchain platform and the Conclave toolkit for writing SGX enclaves.
R. Axiom
R. Daneel
They sign their emails and GitHub comments with something like "-- R. Daneel, AI employee" so the idea is the naming convention lets people know they're interacting with a robot.
Another reason: parallelism. The approach of using a single rolling continuously compacting context window is simple and OpenAI are good at compaction, so it works really well. But it means the agent can only do one thing at once. If I send it an email and it decides to spend an hour working on it, then it won't pay attention to any followup emails until after it's done. So having >1 enables more parallelism.
That said, I don't feel a need for more than two and honestly even that is kind of overkill for the sake of it. For 95% of the time I've been doing this, one was sufficient.
For free time there are systemd timers that wake it up on a schedule and it uses POSIX locks to mutually exclude runs from different wakeup sources. TODO board items can be either foreground or background; when there's an item with foreground priority the timers wake Codex up a lot more frequently than if there are only background items.
Do they sometimes make mistakes? Yeah, and I still check their work. But I've also employed humans, and they make mistakes too. The AI is not worse.
They did that for Go and it seems to have worked out for them though.
Both are just Codexes running in a permanently rolling session in dedicated UNIX user accounts. They're wired up to Maildir so receiving a mail activates Codex and makes it read the new message, there are autonomy wakeup timers, they have accounts in my bug tracker and CI systems. They're currently useful for:
• Triaging and working on customer support tickets. Sometimes I wake up and the fix/response for a ticket filed by a customer is already there waiting for my approval. Recently I started letting them directly interact with customers in specific scenarios.
• Triaging the bug backlog. One of them decided to spend its "free time" finding old bugs that were fixed without being properly closed, or are dupes, so it's cleaning up detritus in the tracker.
• They obviously do all the coding and debugging by just assigning tickets.
• They keep an eye on a "pet" server the company has, and have proven able to fix it in the past when it ran out of disk space.
• They handle non-business projects I have for them.
• They help out with the release processes.
The dedicated home dir is very useful and they use it all the time as part of coding and investigating tricky issues.
My setup relies heavily on email, as everything bottoms out in email anyway. Watching them mail each other out of the blue to coordinate stuff is pretty cool.
Incompatibilities can of course happen, but so can improvements. Nix throws the baby out with the bathwater. You can rerun CI of upstream (open source) projects against a new set of component versions if you want, it doesn't imply anything about how the distribution mechanism should work.
For (2) the issue is non-downgradeable data, not non-upgradeable. Apps basically never support downgrades. Consider your web browser changing the format of its HTTP cache or credentials store, consider your word processor adding a new feature that the user incorporates into their documents and then it's suddenly downgraded. There isn't even any way to downgrade Chrome via its normal user interfaces!
For (3) crashes aren't just annoying, they're indicative of a fatal design flaw. Native plugins were a common feature on other operating systems in the past and have historically been a feature of KDE and GNOME too. I don't know if there are new Linux-native apps of high complexity being written these days though. The dominance of Electron means plugins have moved server-side where the user's OS can't screw them up. Nonetheless, you can't have a serious productivity-focused desktop OS if apps can't be composed at all. You can at best maybe do a ChromeOS competitor.
For (4) sure it can be just defined as out of scope, same thing Nix[OS] does with lots of other important problems. It's embarrassing to present Linux as a solution if it depends on Microsoft to solve the hard stuff for you, though.
Two new programs: `url` and `run`. I am creating reference implementations but they're simple and there's a spec, so they could be reimplemented in your language of choice.
`url https://...` resolves the given URL into a disk cache in $HOME and then prints the path. `url` understands archives and can resolve inside them:
$ url https://a.b/program.zip/bin/program
/home/foo/.cache/..../bin/program
It implements the HTTP caching spec, and - as browsers can do with HTML - can extract cache override headers from the given program if it's a script with hashbang. It does a few other things, like set +x automatically if the program is intended to be executable, sets quarantine bits on macOS and so on: $ open $(url https://downloads.mac-app.com/MacApp-1.2.zip/MacApp.app)
(scanned by gatekeeper and then run)
The disk cache is cleaned up automatically as disk space falls low or the cache exceeds the configured maximum.Running programs this way is possible, but not ideal. This is where `run` enters the picture:
$ run cool-tool.org --help
or if you have the zsh integration set up, you can skip the `run` prefix and just treat URLs as program names: $ cool-tool.org --help
`run` resolves this to https://cool-tool.org/run.zip/ which is a small metadata archive containing instructions for how to start the app. The instructions come in the form of a JavaScript that's run inside a restrictive sandbox. It is allowed to resolve URLs into the disk cache, to learn the current host OS and CPU architecture, and to yield an execution plan (basically the path to the binary/script to execute + optionally transformed CLI arguments). So by publishing a small run.zip file to the root of cool-tool.org it becomes possible for users on any OS, including macOS and Windows, to start the program purely by domain name. The script can do hash locking and override cache headers for URLs if the file server isn't cooperative. The tools will keep the app up to date and optimize out cache checks to keep startup fast when the cache is fresh enough.The run script has a few other options, like figuring out what system libraries are available and what their versions are, thus - if it wishes - it can choose to either download dependencies into the cache and use them (e.g. by setting LD_LIBRARY_PATH), or use the OS provided versions.
At the moment I'm also working on adding a form of sandboxing. The execution plans produced by run scripts can contain permission requests, similar in spirit to macOS entitlements. The permissions aren't tied to any specific sandboxing system or OS. The implementation of `run` may, at its leisure, use this information to sandbox the program before it executes it. The run script may also adapt the permissions requested based on things like what the host OS is, or even what the command line arguments are. For example:
$ cool-app.org --input ./in.mp4 --output ./out.mp4
or $ dry-run cool-app.org --input ./in.mp4 --output ./out.mp4
Requests permissions:
1. Read $PWD/in.mp4
2. Read/write $PWD/out.mp4
3. Read ~/.config/org.cool-app/config.yml
The run script can look at the command line arguments for this invocation, then produce a permission set that only allows access to the in.mp4 and out.mp4 paths, and a config in an OS specific location, but nothing else. Whether the implementation of run actually tightens things that much will depend on things like what the host OS is capable of, but the information to do so is there. For example on Linux it might use landlock, on macOS it might compare the declared permissions against the entitlements in the Mach-O and abort if there's a mismatch, and so on.run.zip files don't have to be produced by the upstream author of the code. They can come from anywhere and resolve URLs from anywhere. So if you are worried about the upstream code being malicious, you could use a run.zip from a third party app store or catalogue, and if you aren't, you can use the upstream's version which just sandboxes to limit the blast radius of compromises.
The goals here are deliberately modest, something like:
- Learn from the web, by making starting native CLI apps as convenient as starting a web app.
- Have a small spec that allows for cross-platform deployment without forcing a large declarative schema for how to assemble files and URLs.
- Don't require the user to manually manage installation/uninstallation.
- Experiment with a cross-platform declarative permissions schema that's computed imperatively, where the mapping to OS sandboxing primitives can flex and adapt over time as OS kernels change.
Redownloading everything doesn't protect against supply chain attacks. Those can still happen, no problem.
e.g. https://data.europa.eu/apps/eusanctionstracker/subjects/1855...
> Daniella Weiss is the Director of the Nachala Movement (“Nachala”). Nachala has the aim of promoting Jewish settlements and illegal outposts in the West Bank.
or https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX%3A...
> Nathalie Yamb is a social media influencer. Since the Sochi summit she attended in 2019, Nathalie Yamb has been an outspoken supporter of Russia, adopting Moscow’s language and targeting France and the West in particular, with a view to ousting them from the African continent. She has specific ties with AFRIC, an organisation linked to Russian private military companies.
She has links with an organization that has links. She "adopts Moscow's language".
or https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=OJ:L...
> Through appearances at symbolic conflict sites, interviews with Russian television, publications on his X account (which has 18 100 followers) and contributions to Kremlin-funded outlets such as International Reporters, he amplifies Kremlin narratives that distort the realities of Russia’s war
or https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=OJ:L_202...
> [Thomas Röper, a German war correspondant] legitimises Russia’s illegal annexation of Ukrainian territory by serving as an election “observer”
Observing elections in Russia controlled territory might mean you can no longer buy food.
The ICC judge got sanctioned because he's trying to arrest a head of a US allied state. The EU does sanctions vastly harsher - outright deadly - against random nobodies, triggered by things as minor as posting opinions to X, or merely being related to someone who did. It's on a different level.
https://data.europa.eu/apps/eusanctionstracker/individuals/
An example of the EU doing similar sanctions would be Jacques Baud, a Swiss general sanctioned for "promoting conspiracy narratives and false claims about the war in Ukraine". The sanctions left him unable to buy anything including food. As he had a wife and family, the EU sanctioned them as well to prevent them helping him.
The same batch of sanctions also added an American citizen to the list, basically for running websites the EU doesn't like and considers fake news, and a French citizen, for "spreading conspiracy theories":
https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=OJ:L...
Cash doesn't help because there is no notice and most people don't keep enough cash on them to survive long enough, especially as there is no way to get unsanctioned - the entire process runs outside the judicial system and depends only on the whims of the Commission. The results are utterly brutal:
> Baud spoke in particular about how the sanction restricted his daily life. The measure caught him completely unprepared; from one day to the next, his credit cards, insurance policies, and bank accounts—including those in Switzerland, even though it did not adopt the sanction—were blocked. He could no longer make payments, and even shopping was impossible. "I survived, that's the only way to put it, thanks to my neighbors. They delivered food to me," Baud said.
Under the sanctions regime, the Geneva resident, who lives in Brussels, is entitled to support intended to cover his basic needs. According to him, the Belgian authorities granted his application at the beginning of February, but three months passed before the banks implemented it. Baud is also prohibited from leaving Belgium and therefore cannot travel to his home country.
https://www.nzz.ch/schweiz/ich-ueberlebte-dank-meinen-nachba...
The EU doesn't have a leg to stand on when it comes to complaining about sanctions. The ICC judges only lost access to some emails and other nice-to-haves, the US didn't stop them buying food or water.
It's probably worth just going back to the drawing board and asking what the goals of a desktop OS should really be. You'd end up with something radically different to Nix, IMO, sort of like how how NeXT/macOS manages software is quite radically different to UNIX.
I actually have a little exploration of a new software distribution idea going on right now, but it's not trying to do the same things as Nix. It starts by asking what a desktop OS inspired by the success of the web would look like.
> I emailed the chairman, a lackey phoned me to say that it was a commercial decision, which I have to say, I don’t believe for a single moment. So I thought, well, there we are, I’ll have to go and find a different bank. I’ve been to six, no seven banks actually. Ask them all, could I have a personal and a business account? And the answer has been no.
Denied accounts at seven banks, and in the end he was right - the reason Coutts tried to close his account was simply because some woke committee hated him, it was purely political. There are no legal or commercial reasons to deny him an account, yet somehow, every bank does so. With no bank in the UK he cannot make payments and without that ability he cannot live there, so the banks came very close to ensuring that the British people could not vote for him even though Reform is now the most popular party.
The British establishment is entirely homogenous on this, because they claim anyone who supports Farage is insufficiently committed to "diversity and inclusion" (leftism) and thus must be forced out. It's indistinguishable from the banks all being directly controlled by the Labour party. The only reason Farage was able to stay is because Coutts were impatient; it was the dying days of the Conservative administration and they suddenly realized what was about to happen to them if they didn't make changes sharpish, so they stuck the FCA on NatWest and some heads rolled. But if Coutts had waited a bit longer until the next Labour government, there would have been no help coming and British democracy would have become even more sickly and fake than it already is (I say this as a British citizen).
Nix has numerous properties that make it unsuitable for a normal desktop OS:
(1) It has no concept of libraries being backwards compatible, so if a 200kb core library changes in a backwards compatible way e.g. security hotfix, it will rebuild/redownload pretty much everything you have installed. This kills your ability to roll out security fixes quickly and ensures that updates are far more painful for end users than even on Windows.
(2) Nix advertises its main benefit as being that you can easily roll back bad updates, which is just false. It makes this false claim because Nix treats user state as being out of scope. Try upgrading Postgres across major versions with Nix and then rolling it back and see what happens, or really any program that stores stuff in $HOME and doesn't support rollback. It'll just die, make a mess, and Nix will wash its hands of the affair by saying it did the bit it wanted to do (change the binaries on your path) and the rest is just out of scope.
(3) It has no working concept of native plugins, related to point (1). If two plugins depend on slightly different versions of their host program, they'll just load incompatible libraries into the address space and things will crash.
(4) It cannot run binaries shipped for generic Linux without lots of fragile hacks like binary rewrites. But in the real world lots of important domain-specific programs are shipped as binaries, if they support Linux at all.
To what extent this was explicitly coordinated by the government versus people who are merely strongly ideologically aligned with it is unclear; in systems as totalitarian as the UK is becoming it's a distinction without a difference as anyone not so aligned is quickly removed for one plausible reason or another and replaced with someone who is.
Consider the most common task the SecurityManager was deployed for: stopping plugins calling System.exit() by accident. One might say, the right to exit the process should be an object capability. OK. But then where does that object come from? Java programs start at main() and it doesn't receive an object.
You'd need a new design where you pass in a god object to main(), which in turn has properties giving access to a ProcessExiter interface or something similar, and then any code that genuinely needs to exit the process would need to request it in the function arguments, threading it down the stack. You'd get an explosion of types. A simple permissions DSL is much easier to write and reason about, and it gets out of the way when you don't want sandboxing.
Why would you want all HTTP requests to flow through a single capability object? I think it's pretty common to want to let code do GETs but not POSTs. You end up wanting pretty fine grained permissions in a lot of real scenarios.
File descriptors are poor object capabilities because the interface they implement is fixed by the OS, except then there's a weird ioctl escape hatch that isn't properly typed, reflectable, wrappable or interposable. To see what can go wrong with this, consider a recent fix to the Codex sandbox on macOS:
https://github.com/openai/codex/pull/46500
The sandbox forbids writing to a file descriptor except, oops, someone at Apple forgot about the F_TRANSFEREXTENTS ioctl which is still allowed on a read only fd. It should be possible to do what is expected here and just pass in a read only fd where all you can do is call read() and maybe seek(), or perhaps pass in an fd where a specific ioctl is the only thing you can do, but POSIX has no concept of this.
A good example of an object capability system would be Mojo, which I describe in the essay. You can create objects representing capabilities and pass them between sandboxes, in an unforgeable way.
We live in a golden era of prototyping so if you wanted to make a language where everything is a capability passed into main(), you could. The code doesn't have to be executable, you could just mock out some realistic programs and see how the code feels. My guess is you'd need a lot of language features to hide the explicit object capabilities away for ergonomic reasons and it'd end up feeling a lot like a SecurityManager based system.
Most control still works this way. See the postmortem on the Iberia black start. Renewables that were trying to do frequency following drifted out of sync and injected misaligned power into the grid. They couldn't even figure out where it was coming from their telemetry is so bad. Then they were asking the gas plants to spin up to add inertia but it was too late as the boilers were cold. IT and other smart grid ideas were conspicuously absent.