What is AI?
Definition
By Artificial Intelligence (AI), I specifically mean systems powered by Large Language Models (LLMs).
How we should understand AI
There are many views on this. Some say it is just a statistical model and fundamentally just a computer program, though a very complicated one. Others say it has intelligence comparable to a human, and perhaps even has a consciousness.
I think both these extremes get it wrong. On the one hand, you cannot reduce today’s LLM-powered AI systems to just a statistical model and a regular computer program and believe that helps you understand it. On the other hand, I see no reason to believe that today’s AI systems have human-like intelligence, let alone consciousness.
MySuite
As I have written before, you can build efficient and easy to host web apps with Go and SQLite.
I have now developed a suite of personal productivity apps with Go, SQLite and TypeScript+Preact for the web UI.
And a simple app to save links with Go, SQLite and htmx:
I call the whole suite MySuite, which also contains a password manager MyPass made in Rust.
Each of those five has its own native Android app (made in Kotlin).
From XTerm to Alacritty
I have written before how to support tabs and scrollback in XTerm with tmux.
Now it seems like Wayland is replacing X11 as the default window system in Linux desktops. And XTerm, as the name implies, only supports X11. So I wanted to replace XTerm with another terminal with native Wayland support.
After trying a few options, I decided to go with Alacritty. As a bonus, you also get clickable hyperlinks (OSC 8).
Here is how I configured it to work with tmux:
Remote SQLite backup
I wrote earlier about how to backup a small SQLite database.
That solution works well for small databases, but can become impractical for larger databases. In particular if the machine with the database has not enough disk space for another copy of it, and you want to backup to another machine.
One option is to use sqlite3_rsync, but I could not get that to work, it kept running out of memory. So I found another way, dump the database to text and gzip it. Combined with ssh, you can make a backup to a remote machine without storing anything extra on disk on the database machine. Run this on the machine where you want to have the backup:
Ubuntu in Ubuntu
I want an easy way to run one or more ephermal Linux VMs in Ubuntu desktop, including the possibility to mount host directories into them in an efficient fashion. Those VMs should have the same CPU architecture and hardware features as the host, no need to emulate some foreign CPU or special hardware, and no GUI in the VMs. The intended use case is sandboxing and isolation for security and reproducability reason. To be used as a cheaper alternative to a remote cloud instance. Or as a more secure alternative to Docker/Podman containers, with possibility to install Docker or Podman in the VM, and run containers there without affecting the host or other VMs (it’s not feasible to nest containers in a secure manner without a proper VM).
How to run a user service with systemd
Most Linux systems uses systemd as init and service manager nowadays.
You can use systemd to start your services, so that they are automatically restarted if they crash, and automatically started when the system boots. You can even do this as a regular user, without root access (then the service will run with permissions of your user), and have it automatically started when the system boots without you having to login.
- Create the
~/.config/systemd/userdirectory if necessary:
mkdir -p ~/.config/systemd/user- Create a service unit configuration file
for your service in that directory
~/.config/systemd/user/myservice.service:
[Unit]
Description=MyService
[Service]
Type=exec
LoadCredential=my-password:%h/myservice.pw
ExecStart=%h/.local/bin/myservice -password-file ${CREDENTIALS_DIRECTORY}/my-password
Restart=on-failure
NoNewPrivileges=true
AppArmorProfile=myservice
[Install]
WantedBy=default.target
- Load the service definition:
systemctl --user daemon-reload- Enable the service:
systemctl --user enable myservice.service- Start the service
systemctl --user start myservice.service- Enable starting the service on boot without login (you might need to run this as root):
sudo loginctl enable-lingerThis will provide the service with credentials from file ~/myservice.pw, and run the service with AppArmor
profile myservice (which you must create and load first).
How to Publish a Gradle Project on Maven Central
You can publish a Java or Kotlin library built with Gradle on Maven Central like this.
First setup your Gradle build to produce a valid bundle for Maven Central.
-
Include the Maven Publish Plugin in your build.
-
Setup building of Javadoc and Source archives using Dokka.
-
Set the required POM metadata in Gradle like this.
See here for a complete working example.
Now you can run gradle publish to create the Maven bundle in the mavenPublish directory (which you probably want to include in .gitignore).
Linux sandboxing with bubblewrap
AppArmor is a good way to sandbox programs on a Linux system, but it has some limitations. In particular, it requires you to define a static profile for each program, and changing profiles requires root access. This can be impractical for ad-hoc usages, and in particular if you want to give the program access to a particular directory (such as the current directory). These gaps can be filled with bubblewrap.
Here is how you can use it on Ubuntu 24.04.
Sandbox IntelliJ IDEA with AppArmor
If you have a Linux system with AppArmor, you can use it to sandbox IntelliJ IDEA.
Do not install IntelliJ with snap, download the .tar.gz archive instead and unpack it in /opt/JetBrains/.
Then add this file to /etc/apparmor.d.
#include <tunables/global>
profile idea /opt/JetBrains/idea*/bin/* {
#include <abstractions/base>
#include <abstractions/consoles>
#include <abstractions/nameservice>
#include <abstractions/ssl_certs>
#include <abstractions/gnome>
#include <abstractions/xdg-open>
#include <abstractions/dbus-session>
/etc/** r,
/dev/** r,
/dev/ptmx rw,
@{PROC}/ r,
@{PROC}/** r,
/sys/** r,
/bin/* rixm,
/usr/bin/{[^s]*,?[^n]*,??[^a]*,???[^p]*} rixm,
/usr/local/bin/** rixm,
/usr/include/** r,
/usr/lib/ r,
/usr/lib/** rixm,
/usr/lib64/ r,
/usr/lib64/** rixm,
/usr/libexec/** rixm,
/usr/sbin/* rixm,
/usr/share/** r,
/{,snap/core/[0-9]*/,snap/snapd/[0-9]*/}usr/bin/snap mrCx -> snap_browsers,
owner @{PROC}/@{pid}/coredump_filter rw,
owner /tmp/ r,
owner /tmp/** rwlkix,
owner /var/tmp/ r,
owner /var/tmp/** rwlkix,
owner /run/user/@{uid}/at-spi/* rw,
owner /run/user/@{uid}/bus rw,
owner /run/user/@{uid}/dconf/user rw,
owner /run/user/@{uid}/gdm/Xauthority r,
owner /run/user/@{uid}/keyring/ssh rw,
/opt/JetBrains/** rixm,
/ r,
/home/ r,
owner @{HOME}/ r,
owner @{HOME}/.bashrc r,
owner @{HOME}/.bash_aliases r,
owner @{HOME}/.cache/JNA/*/ r,
owner @{HOME}/.cache/JetBrains/ r,
owner @{HOME}/.cache/JetBrains/** rwklix,
owner @{HOME}/.cache/ibus/* rw,
owner @{HOME}/.config/JetBrains/ r,
owner @{HOME}/.config/JetBrains/** rwkl,
owner @{HOME}/.config/dconf/user r,
owner @{HOME}/.config/ibus/bus/ r,
owner @{HOME}/.config/ibus/bus/* r,
owner @{HOME}/.gitconfig r,
owner @{HOME}/.gitignore r,
owner @{HOME}/.gradle/ r,
owner @{HOME}/.gradle/** rwklixm,
owner @{HOME}/.inputrc r,
owner @{HOME}/.ivy2/** rwkl,
owner @{HOME}/.java/** rwkl,
owner @{HOME}/.junie/ r,
owner @{HOME}/.junie/** rwkl,
owner @{HOME}/.local/bin/* rix,
owner @{HOME}/.local/share/JetBrains/ r,
owner @{HOME}/.local/share/JetBrains/** rwklix,
owner @{HOME}/.local/share/kotlin/** rwklix,
owner @{HOME}/.m2/ r,
owner @{HOME}/.m2/** rwkl,
owner @{HOME}/.profile r,
owner @{HOME}/.ssh/ r,
owner @{HOME}/.ssh/config r,
owner @{HOME}/.ssh/known_hosts* r,
owner @{HOME}/.ssh/*.pub r,
owner @{HOME}/src/ r,
owner @{HOME}/src/** rwklix,
signal (send,receive) set=(int,term,kill) peer=idea,
ptrace peer=idea,
profile snap_browsers {
#include <abstractions/base>
#include <abstractions/dbus-session-strict>
/etc/passwd r,
/etc/nsswitch.conf r,
/etc/fstab r,
/etc/local/fstab r,
# noisy
deny owner /run/user/[0-9]*/gdm/Xauthority r, # not needed on Ubuntu
/{,snap/core/[0-9]*/,snap/snapd/[0-9]*/}usr/bin/snap mrix, # re-exec
/{,snap/core/[0-9]*/,snap/snapd/[0-9]*/}usr/lib/snapd/info r,
/{,snap/core/[0-9]*/,snap/snapd/[0-9]*/}usr/lib/snapd/snapd r,
/{,snap/core/[0-9]*/,snap/snapd/[0-9]*/}usr/lib/snapd/snap-seccomp rPix,
/{,snap/core/[0-9]*/,snap/snapd/[0-9]*/}usr/lib/snapd/snap-confine Pix,
/var/lib/snapd/system-key r,
/run/snapd.socket rw,
/usr/bin/systemctl ix,
@{PROC}/version r,
@{PROC}/cmdline r,
@{PROC}/sys/net/core/somaxconn r,
@{PROC}/sys/kernel/seccomp/actions_avail r,
@{PROC}/sys/kernel/random/uuid r,
owner @{PROC}/@{pid}/cgroup r,
owner @{PROC}/@{pid}/mountinfo r,
owner @{HOME}/.snap/auth.json r, # if exists, required
dbus send bus="session" path="/org/freedesktop/systemd1" interface="org.freedesktop.systemd1.Manager" member="StartTransientUnit" peer=(name="org.freedesktop.systemd1"),
dbus receive bus="session" path="/org/freedesktop/systemd1" interface="org.freedesktop.systemd1.Manager" member="JobRemoved",
/sys/fs/cgroup/cgroup.controllers r,
/sys/kernel/security/apparmor/features/ r,
/sys/kernel/security/apparmor/features/** r,
# allow launching official browser snaps.
/snap/{brave,chromium,firefox,opera}/[0-9]*/meta/{snap.yaml,hooks/} r,
/var/lib/snapd/sequence/{brave,chromium,firefox,opera}.json r,
/var/lib/snapd/inhibit/{brave,chromium,firefox,opera}.lock rk,
}
}
Then run sudo service apparmor reload to activate it (it will be loaded automatically on boot). This is tested on Ubuntu desktop 24.04.
Secure your Go builds with AppArmor
If you have a Linux system with AppArmor, you can use it to secure your Go builds.
First install Go in /usr/local/go as in the the instructions.
Then add this file to /etc/apparmor.d, replace ${HOME} with your home directory.
#include <tunables/global>
profile go /usr/local/go/bin/go {
#include <abstractions/base>
#include <abstractions/consoles>
/tmp/ r,
/tmp/** rwkix,
@{PROC}/** r,
/sys/** r,
/dev/** r,
/etc/** r,
/usr/** r,
/bin/** ix,
/usr/bin/** ix,
/usr/libexec/** ix,
/usr/lib/** ix,
/usr/local/go/** rix,
owner @{HOME}/.config/go/** rw,
owner @{HOME}/.cache/go-build/** rwkix,
owner @{HOME}/.cache/JetBrains/IntelliJIdea*/tmp/GoLand/** rwkix,
owner @{HOME}/.cache/JetBrains/IntelliJIdea*/coverage/** rwk,
owner @{HOME}/.local/share/JetBrains/IntelliJIdea*/go-plugin/** rix,
owner @{HOME}/.config/dlv/** r,
owner @{HOME}/.gitconfig r,
owner @{HOME}/.gitignore r,
owner @{HOME}/go/** rwkl,
owner @{HOME}/go/bin/* rwklix,
owner @{HOME}/src/** rwk,
/etc/resolv.conf r,
@{PROC}/sys/net/** r,
/run/systemd/resolve/** r,
network,
signal (receive) set=(int,term,kill),
ptrace,
}
profile go-fmt /usr/local/go/bin/gofmt {
#include <abstractions/base>
#include <abstractions/consoles>
/tmp/** rwkix,
/usr/local/go/** rix,
owner @{HOME}/.config/go/** rw,
owner @{HOME}/.cache/JetBrains/IntelliJIdea*/tmp/GoLand/** rwk,
owner @{HOME}/go/** rwk,
owner @{HOME}/src/** rwk,
signal (receive) set=(int,term,kill),
}
profile go-bin ${HOME}/{go/bin/*,.cache/go-build/**,.cache/JetBrains/IntelliJIdea*/tmp/GoLand/*} {
#include <abstractions/base>
#include <abstractions/consoles>
/etc/** r,
/bin/** ix,
/usr/bin/** ix,
/tmp/ r,
/tmp/** rwk,
/usr/local/go/** rix,
owner @{HOME}/.config/go/** r,
owner @{HOME}/.cache/go-build/** rix,
owner @{HOME}/.cache/JetBrains/IntelliJIdea*/tmp/GoLand/** rix,
owner @{HOME}/go/** r,
owner @{HOME}/go/bin/* rix,
owner @{HOME}/src/** r,
/etc/resolv.conf r,
@{PROC}/sys/net/** r,
/run/systemd/resolve/** r,
network,
signal (receive) set=(int,term,kill),
ptrace (tracedby, readby) peer=go,
}
Then run sudo service apparmor reload to activate it (it will be loaded automatically on boot). This is tested un Ubuntu desktop 24.04.
It will also enable use of the Go plugin to IntelliJ IDEA.