OS Revisit: Ghostty and Alexandrie

Published: Jan 22, 2026 by Isaac Johnson

I tried out Mitchel’s Ghostty last February and wasn’t impressed then, but it was very new. I see more and more folks using it so I figured it might be time to revisit.

Additionally, I mentioned Alexandrie a few weeks back in a blog post. While one feature of it is being a Markdown editor, there looked to be a lot more going on and I wanted to really test it.

Ghostty

We can follow the instructions to install with a one line command

/bin/bash -c “$(curl -fsSL https://raw.githubusercontent.com/mkasberg/ghostty-ubuntu/HEAD/install.sh)”

I couldn’t get it to work as a non-root user, but as root it installed

builder@LuiGi:~/Workspaces$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/mkasberg/ghostty-ubuntu/HEAD/install.sh)"

Installing/Updating Ghostty...
Downloading ghostty_1.2.3-0.ppa1_amd64_25.10.deb...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 15.8M  100 15.8M    0     0  2826k      0  0:00:05  0:00:05 --:--:-- 3399k
Installing ghostty_1.2.3-0.ppa1_amd64_25.10.deb...
[sudo: authenticate] Password: 
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Note, selecting 'ghostty' instead of './ghostty_1.2.3-0.ppa1_amd64_25.10.deb'
Solving dependencies... Done
The following additional packages will be installed:
  libgtk4-layer-shell0
The following NEW packages will be installed:
  ghostty libgtk4-layer-shell0
0 upgraded, 2 newly installed, 0 to remove and 18 not upgraded.
Need to get 17.4 kB/16.6 MB of archives.
After this operation, 66.6 kB of additional disk space will be used.
Get:1 /home/builder/Workspaces/ghostty_1.2.3-0.ppa1_amd64_25.10.deb ghostty amd64 1.2.3-0~ppa1 [16.6 MB]
Get:2 http://us.archive.ubuntu.com/ubuntu questing/universe amd64 libgtk4-layer-shell0 amd64 1.0.4-2 [17.4 kB]
Fetched 17.4 kB in 0s (45.6 kB/s)                    
Selecting previously unselected package libgtk4-layer-shell0:amd64.
(Reading database ... 341368 files and directories currently installed.)
Preparing to unpack .../libgtk4-layer-shell0_1.0.4-2_amd64.deb ...
Unpacking libgtk4-layer-shell0:amd64 (1.0.4-2) ...
Selecting previously unselected package ghostty.
Preparing to unpack .../ghostty_1.2.3-0.ppa1_amd64_25.10.deb ...
Adding 'diversion of /usr/share/terminfo/g/ghostty to /usr/share/terminfo/g/ghostty.distrib by ghostty'
Unpacking ghostty (1.2.3-0~ppa1) ...
Setting up libgtk4-layer-shell0:amd64 (1.0.4-2) ...
Setting up ghostty (1.2.3-0~ppa1) ...
update-alternatives: using /usr/bin/ghostty to provide /usr/bin/x-terminal-emulator (x-terminal-emulator) in auto mode
Processing triggers for desktop-file-utils (0.28-1) ...
Processing triggers for hicolor-icon-theme (0.18-2) ...
Processing triggers for gnome-menus (3.36.0-3ubuntu2) ...
Processing triggers for libc-bin (2.42-0ubuntu3) ...
Processing triggers for man-db (2.13.1-1) ...
N: Download is performed unsandboxed as root as file '/home/builder/Workspaces/ghostty_1.2.3-0.ppa1_amd64_25.10.deb' couldn't be accessed by user '_apt'. - pkgAcquire::Run (13: Permission denied)
builder@LuiGi:~/Workspaces$ sudo su - 
root@LuiGi:~# /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/mkasberg/ghostty-ubuntu/HEAD/install.sh)"

Installing/Updating Ghostty...
Downloading ghostty_1.2.3-0.ppa1_amd64_25.10.deb...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 15.8M  100 15.8M    0     0  2645k      0  0:00:06  0:00:06 --:--:-- 2965k
Installing ghostty_1.2.3-0.ppa1_amd64_25.10.deb...
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Note, selecting 'ghostty' instead of './ghostty_1.2.3-0.ppa1_amd64_25.10.deb'
ghostty is already the newest version (1.2.3-0~ppa1).
Solving dependencies... Done
0 upgraded, 0 newly installed, 0 to remove and 18 not upgraded.

Launching with ghostty we see a nice font improvement over the standard Ubuntu terminal

/content/images/2026/01/ghostty-01.png

<Old Man> So this last time I tried getting glasses they offered me “progressives”. No, these aren’t Bernie/AOC endorsed glasses, they are the hip term for ‘bifocals’. They suck. OMG, i want to vomit wearing them. the things move in different speeds at the bottom half giving you a “drunk goggles” view. So i refuse to wear them. So now I wear cheaters (what old people call reading glasses) when in bed and otherwise i just hold the phone/laptop out. You don’t really lose your near sight focus till your 40s so if you are younger, enjoy that sh** while you have it. I wish I would have known I needed to periodically use my close up vision or i would lose it when i was in my 20s and 30s. In any case, yes, old man keeps yammering.. in any case, I like this font way better for when I’m using the higher resolution 4k laptop screen… </Old Man>

Since I want to try it a bit now that my main driver is Linux, I pinned it to the dock

/content/images/2026/01/ghostty-02.png

I can use ctrl + for increasing font size and ctrl - for lowering, which seems familiar

/content/images/2026/01/ghostty-03.png

We can use the command ghostty +list-keybinds --default to see key bindings that are set by default (all of them can be changed as well)

/content/images/2026/01/ghostty-04.png

Because Ghostty is a terminal, not a shell, it used BASH and showed me my history which was nice. My biggest gripe with MacOS and WSL nowadays is it keeps switching to zsh and I have to change it back

/content/images/2026/01/ghostty-05.png

The super (windows key) + ctrl + bracket for moving to preview and next tabs are fine, but the default of ctrl + alt + arrow keys to move around split panes comes into conflict with Gnome in Ubuntu which uses ctrl + alt + arrow right/left for switching workspace windows.

Having the split windows means I don’t need tmux to accomplish having several panes going in the same window. For instance, I can have watch free -m and top going to watch memory and cpu then run a command in the third pane

/content/images/2026/01/ghostty-06.png

While the close commands like close window tend to just end the whole Ghostty session, you can easily close terminal panes by just using exit to end that SSH session and the pane goes away

In the actual menu navigation of Ghostty, we can review, run and search for commands so those that like to use their mouse/trackpad can always go that way

/content/images/2026/01/ghostty-07.png

Next, we’ll look again at Alexandrie, but I did want to point out that I used the split view in Ghostty to make it easier to update docker compose with images I had just built and pushed

/content/images/2026/01/alex2-19.png

Alexandrie

When I did that previous blog post on Alexandrie, I just tried it with localhost and docker.

Let’s do TLS and try a proper ingressed version.

I’ll first bring Alexandrie to an alternate docker host

builder@bosgamerz9:~$ git clone https://github.com/Smaug6739/Alexandrie.git
Cloning into 'Alexandrie'...
remote: Enumerating objects: 21479, done.
remote: Counting objects: 100% (557/557), done.
remote: Compressing objects: 100% (216/216), done.
remote: Total 21479 (delta 477), reused 351 (delta 341), pack-reused 20922 (from 4)
Receiving objects: 100% (21479/21479), 67.63 MiB | 30.23 MiB/s, done.
Resolving deltas: 100% (14411/14411), done.
builder@bosgamerz9:~$ cd Alexandrie/
builder@bosgamerz9:~/Alexandrie$ cp .env.example .env
builder@bosgamerz9:~/Alexandrie$ 

I’m going to need at least three A names to work with my Nginx ingress as I cannot really do port routing with my setup (with TLS that is). I could do HTTP with NodePorts and forwarding and take DNS out of the picture.

builder@LuiGi:~/Workspaces$ az account set --subscription "Pay-As-You-Go" && az network dns record-set a add-record -g idjdnsrg -z tpk.pw -a 174.53.161.33 -n  alex
{
  "ARecords": [
    {
      "ipv4Address": "174.53.161.33"
    }
  ],
  "TTL": 3600,
  "etag": "6b6b33fa-a53d-4544-bfa5-9eef20a1778d",
  "fqdn": "alex.tpk.pw.",
  "id": "/subscriptions/d955c0ba-13dc-44cf-a29a-8fed74cbb22d/resourceGroups/idjdnsrg/providers/Microsoft.Network/dnszones/tpk.pw/A/alex",
  "name": "alex",
  "provisioningState": "Succeeded",
  "resourceGroup": "idjdnsrg",
  "targetResource": {},
  "trafficManagementProfile": {},
  "type": "Microsoft.Network/dnszones/A"
}
builder@LuiGi:~/Workspaces$ az account set --subscription "Pay-As-You-Go" && az network dns record-set a add-record -g idjdnsrg -z tpk.pw -a 174.53.161.33 -n  alex-cdn
{
  "ARecords": [
    {
      "ipv4Address": "174.53.161.33"
    }
  ],
  "TTL": 3600,
  "etag": "14e30bc3-eff6-4bdd-96a2-e4aee1702d8b",
  "fqdn": "alex-cdn.tpk.pw.",
  "id": "/subscriptions/d955c0ba-13dc-44cf-a29a-8fed74cbb22d/resourceGroups/idjdnsrg/providers/Microsoft.Network/dnszones/tpk.pw/A/alex-cdn",
  "name": "alex-cdn",
  "provisioningState": "Succeeded",
  "resourceGroup": "idjdnsrg",
  "targetResource": {},
  "trafficManagementProfile": {},
  "type": "Microsoft.Network/dnszones/A"
}
builder@LuiGi:~/Workspaces$ az account set --subscription "Pay-As-You-Go" && az network dns record-set a add-record -g idjdnsrg -z tpk.pw -a 174.53.161.33 -n  alex-api
{
  "ARecords": [
    {
      "ipv4Address": "174.53.161.33"
    }
  ],
  "TTL": 3600,
  "etag": "99b46428-6826-4192-968c-fda15e3ae8f7",
  "fqdn": "alex-api.tpk.pw.",
  "id": "/subscriptions/d955c0ba-13dc-44cf-a29a-8fed74cbb22d/resourceGroups/idjdnsrg/providers/Microsoft.Network/dnszones/tpk.pw/A/alex-api",
  "name": "alex-api",
  "provisioningState": "Succeeded",
  "resourceGroup": "idjdnsrg",
  "targetResource": {},
  "trafficManagementProfile": {},
  "type": "Microsoft.Network/dnszones/A"
}

With these three DNS entries, I can then create a functional manifest to use them:

spoiler alert: for those following along, I’ll later realize i missed the proxy body size for CDN uploads and that the right port for CDN is 9005 not 9000

---
apiVersion: v1
kind: Endpoints
metadata:
  name: alex-external-ip
subsets:
- addresses:
  - ip: 192.168.1.142
  ports:
  - name: alexint
    port: 8200
    protocol: TCP
---
apiVersion: v1
kind: Endpoints
metadata:
  name: alex-api-external-ip
subsets:
- addresses:
  - ip: 192.168.1.142
  ports:
  - name: alex-apiint
    port: 8201
    protocol: TCP
---
apiVersion: v1
kind: Endpoints
metadata:
  name: alex-cdn-external-ip
subsets:
- addresses:
  - ip: 192.168.1.142
  ports:
  - name: alex-cdnint
    port: 9000
    protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  name: alex-external-ip
spec:
  clusterIP: None
  clusterIPs:
  - None
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  - IPv6
  ipFamilyPolicy: RequireDualStack
  ports:
  - name: alex
    port: 80
    protocol: TCP
    targetPort: 8200
  sessionAffinity: None
  type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
  name: alex-api-external-ip
spec:
  clusterIP: None
  clusterIPs:
  - None
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  - IPv6
  ipFamilyPolicy: RequireDualStack
  ports:
  - name: alex-api
    port: 80
    protocol: TCP
    targetPort: 8201
  sessionAffinity: None
  type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
  name: alex-cdn-external-ip
spec:
  clusterIP: None
  clusterIPs:
  - None
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  - IPv6
  ipFamilyPolicy: RequireDualStack
  ports:
  - name: alex-cdn
    port: 80
    protocol: TCP
    targetPort: 9000
  sessionAffinity: None
  type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: azuredns-tpkpw
    ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.org/websocket-services: alex-external-ip
  generation: 1
  name: alexingress
spec:
  ingressClassName: nginx
  rules:
  - host: alex.tpk.pw
    http:
      paths:
      - backend:
          service:
            name: alex-external-ip
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - alex.tpk.pw
    secretName: alex-tls
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: azuredns-tpkpw
    ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.org/websocket-services: alex-api-external-ip
  generation: 1
  name: alex-apiingress
spec:
  ingressClassName: nginx
  rules:
  - host: alex-api.tpk.pw
    http:
      paths:
      - backend:
          service:
            name: alex-api-external-ip
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - alex-api.tpk.pw
    secretName: alex-api-tls
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: azuredns-tpkpw
    ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.org/websocket-services: alex-cdn-external-ip
  generation: 1
  name: alex-cdningress
spec:
  ingressClassName: nginx
  rules:
  - host: alex-cdn.tpk.pw
    http:
      paths:
      - backend:
          service:
            name: alex-cdn-external-ip
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - alex-cdn.tpk.pw
    secretName: alex-cdn-tls

Then apply it

$ kubectl apply -f ./ingress-alex.yaml 
endpoints/alex-external-ip created
endpoints/alex-api-external-ip created
endpoints/alex-cdn-external-ip created
service/alex-external-ip created
service/alex-api-external-ip created
service/alex-cdn-external-ip created
ingress.networking.k8s.io/alexingress created
ingress.networking.k8s.io/alex-apiingress created
ingress.networking.k8s.io/alex-cdningress created

In the .env file, I can now use those URLs

builder@bosgamerz9:~/Alexandrie$ cat .env | head -n21 | tail -n3
FRONTEND_URL=https://alex.tpk.pw
API_URL=https://alex-api.tpk.pw
CDN_URL=https://alex-cdn.tpk.pw

Then I can docker compose up to bring up the service

builder@bosgamerz9:~/Alexandrie$ docker compose up -d
[+] Running 45/45
 ✔ backend Pulled                                                                                                                                                                                        14.3s 
   ✔ fd4aa3667332 Pull complete                                                                                                                                                                           8.4s 
   ✔ bfb59b82a9b6 Pull complete                                                                                                                                                                           8.6s 
   ✔ 017886f7e176 Pull complete                                                                                                                                                                          10.7s 
   ✔ 62de241dac5f Pull complete                                                                                                                                                                          10.7s 
   ✔ 2780920e5dbf Pull complete                                                                                                                                                                          10.7s 
   ✔ 7c12895b777b Pull complete                                                                                                                                                                          10.7s 
   ✔ 3214acf345c0 Pull complete                                                                                                                                                                          10.7s 
   ✔ 52630fc75a18 Pull complete                                                                                                                                                                          10.7s 
   ✔ dd64bf2dd177 Pull complete                                                                                                                                                                          10.7s 
   ✔ 4aa0ea1413d3 Pull complete                                                                                                                                                                          10.7s 
   ✔ dcaa5a89b0cc Pull complete                                                                                                                                                                          10.8s 
   ✔ 069d1e267530 Pull complete                                                                                                                                                                          10.8s 
   ✔ 6191b4b5532e Pull complete                                                                                                                                                                          10.8s 
   ✔ e92289148968 Pull complete                                                                                                                                                                          11.6s 
   ✔ 7db81a621940 Pull complete                                                                                                                                                                          11.6s 
   ✔ 8a3362ac00f3 Pull complete                                                                                                                                                                          11.6s 
 ✔ mysql Pulled                                                                                                                                                                                          13.7s 
   ✔ 16506d4b4233 Pull complete                                                                                                                                                                           5.5s 
   ✔ 3387bdf9bfcc Pull complete                                                                                                                                                                           5.5s 
   ✔ 5a35458f48a1 Pull complete                                                                                                                                                                           5.5s 
   ✔ 1bed572afc9f Pull complete                                                                                                                                                                           5.6s 
   ✔ f3e7871685d1 Pull complete                                                                                                                                                                           5.6s 
   ✔ 9e9c9ba70723 Pull complete                                                                                                                                                                           5.6s 
   ✔ be113d11b355 Pull complete                                                                                                                                                                           6.1s 
   ✔ 05bdba050124 Pull complete                                                                                                                                                                           6.1s 
   ✔ 54a2bfb30cdf Pull complete                                                                                                                                                                          12.1s 
   ✔ 05ed66656b21 Pull complete                                                                                                                                                                          12.1s 
   ✔ 1c0ceff8a81b Pull complete                                                                                                                                                                          12.1s 
 ✔ rustfs Pulled                                                                                                                                                                                         16.3s 
   ✔ 7d773df7a2be Pull complete                                                                                                                                                                           8.7s 
   ✔ 39838f5db0cf Pull complete                                                                                                                                                                           9.0s 
   ✔ 67b398cfa20b Pull complete                                                                                                                                                                          11.3s 
   ✔ ffd60cccecd6 Pull complete                                                                                                                                                                          11.3s 
   ✔ 4f4fb700ef54 Pull complete                                                                                                                                                                          11.4s 
   ✔ 7746ba9768f2 Pull complete                                                                                                                                                                          11.4s 
 ✔ frontend Pulled                                                                                                                                                                                       11.2s 
   ✔ 1074353eec0d Pull complete                                                                                                                                                                           5.2s 
   ✔ d53378de7b14 Pull complete                                                                                                                                                                           8.2s 
   ✔ 1e51518bad62 Pull complete                                                                                                                                                                           8.3s 
   ✔ df42fde70614 Pull complete                                                                                                                                                                           8.3s 
   ✔ 2fee64f62ee0 Pull complete                                                                                                                                                                           8.3s 
   ✔ 4e30f5080197 Pull complete                                                                                                                                                                           8.7s 
   ✔ a4ee522d4181 Pull complete                                                                                                                                                                           8.7s 
   ✔ f806d52c7133 Pull complete                                                                                                                                                                           8.7s 
[+] Running 8/8
 ✔ Network alexandrie_alexandrie-network  Created                                                                                                                                                         0.1s 
 ✔ Volume "alexandrie_mysql_data"         Created                                                                                                                                                         0.0s 
 ✔ Volume "alexandrie_rustfs_data"        Created                                                                                                                                                         0.0s 
 ✔ Volume "alexandrie_rustfs_logs"        Created                                                                                                                                                         0.0s 
 ✔ Container alexandrie-rustfs            Healthy                                                                                                                                                         5.8s 
 ✔ Container alexandrie-mysql             Healthy                                                                                                                                                        10.8s 
 ✔ Container alexandrie-backend           Started                                                                                                                                                        10.9s 
 ✔ Container alexandrie-frontend          Started   

After the certs were clear, I could check the main site

/content/images/2026/01/alex2-01.png

I can then create an account

/content/images/2026/01/alex2-02.png

After account create, I was redirected to login. After login I get the main dashboard

/content/images/2026/01/alex2-03.png

I can then create a new workspace

/content/images/2026/01/alex2-04.png

I can then write a do a sample article, which by default is private. Flipping a toggle will make it public

/content/images/2026/01/alex2-05.png

Okay, it worked, but that URL looks pretty garbage

/content/images/2026/01/alex2-06.png

I can set some fields

/content/images/2026/01/alex2-07.png

But they didn’t really affect the page other than to render the markdown properly

/content/images/2026/01/alex2-08.png

In the editor there are some nice WYSIWYG controls for doing things like creating tables

/content/images/2026/01/alex2-09.png

Those just insert the proper markdown or HTML. I can still use Preview to see a rendered version

/content/images/2026/01/alex2-10.png

Interestingly, if I move from Public to Private, it doesn’t have a redirect page or a 404, rather shows a gray page (like a paywall might show)

/content/images/2026/01/alex2-11.png

One thing that threw me of a bit was the “Thumbnail”. A URL to an image didn’t work and it took searching the code a bit to figure out that it wants the contents of an SVG file. I use an Adobe icon as a small example and it looked like it worked:

/content/images/2026/01/alex2-12.png

If you put in SVG for the “Icon” or Emoji it shows in the nav on the left (here i used the AA logo)

/content/images/2026/01/alex2-13.png

And how it looks on the rendered page:

/content/images/2026/01/alex2-14.png

The print view cleans a lot of navigation up which works well for published documents

/content/images/2026/01/alex2-15.png

CDN

I wanted to do all this to see what the “CDN” would provide.

I tried to upload a small 1.5Mb video, but just saw a quickly disappearing error

/content/images/2026/01/alex2-16.png

“Failed to upload 005_Clowns_Defeated_WalkAway.mp4: [POST] “https://alex-api.tpk.pw/api/resources”: Failed to fetch"

The Console Log in the browser shows

https://alex-api.tpk.pw/api/resources": <no response> Failed to fetch
    at async bl (DQlLeSm7.js:4:148284)
    at async Te (DQlLeSm7.js:4:149130)
    at async Proxy.post (CUjCRRl6.js:1:120)
    at async ae (IzeUTYAo.js:3:225)Caused by: TypeError: Failed to fetch
    at DQlLeSm7.js:4:69016
    at i.o [as raw] (DQlLeSm7.js:4:67774)
    at bl (DQlLeSm7.js:4:148297)
    at Te (DQlLeSm7.js:4:149136)
    at Proxy.post (CUjCRRl6.js:1:126)
    at Proxy.x (DQlLeSm7.js:4:83514)
    at ae (IzeUTYAo.js:3:233)
    at Ms (DQlLeSm7.js:2:21315)
    at dt (DQlLeSm7.js:2:21385)
    at W0 (DQlLeSm7.js:4:15398)

/content/images/2026/01/alex2-17.png

Editing

Another quick test, besides video which didn’t really work was Mermaid

Both mermaid and mermaidjs tags failed to render

/content/images/2026/01/alex2-18.png

Trying to get videos to work, I ended up attempting to change the HTML escape blocks

builder@LuiGi:~/Workspaces/Alexandrie/backend$ git diff
diff --git a/backend/utils/escape-html.go b/backend/utils/escape-html.go
index c2b20329..c07675d5 100644
--- a/backend/utils/escape-html.go
+++ b/backend/utils/escape-html.go
@@ -37,7 +37,7 @@ func InitBluemonday() {
        policy.AllowElements("input", "textarea", "button", "label", "select", "option", "fieldset", "legend")

        // Multimedia
-       policy.AllowElements("audio", "video")
+       policy.AllowElements("audio", "video", "source")

        // Custom elements for frontend rendering
        policy.AllowElements("tag")
@@ -79,6 +79,10 @@ func InitBluemonday() {
        // Links
        policy.AllowAttrs("href", "rel").OnElements("a")

+       // Multimedia attributes
+       policy.AllowAttrs("src", "poster", "controls", "loop", "autoplay", "muted", "preload", "playsinline").OnElements("audio", "video")
+       policy.AllowAttrs("src").OnElements("source")
+
        // Form attributes
        policy.AllowAttrs("checked").OnElements("input")

builder@LuiGi:~/Workspaces/Alexandrie/backend$

I then used the new image

/content/images/2026/01/alex2-19.png

then bounced the Alexandrie service

/content/images/2026/01/alex2-20.png

However, this just created an infinite loop of login resets.

The good news is that setting it back to the former image restored everything.

On video

The question becomes do we need video? I often question whether adding embedded video brings value or just unnecessary noise.

Would just a link to a video suffice for those that want a recording? Clearly printing a page with video wouldn’t work.

I wrote a an article on David in MSP using Gemini on a long prompt then tweaking with a few details and perhaps different language.

I find AI generated content kind of cheap, so I hesitate in saying “I wrote that”, i just shaped it with prompts, albeit a longer one:

the biblical story of David is about an unexpected hero who trusted in God when the rest of his community had given up hope and the leaders had no power to stop the enclosing army. It would seem that the unstoppable ICE activity going on (search Minneapolis ICE death for examples) is similar to the activity of the Philistines. I want to create a break down of the story from 1 Samuel and compare it to current events. The takeaway should focus on “Seeking our David” and putting our faith in God. Store the output in a file David.md

But regardless of that content, we can see how the chapter or section markers bring clarity and it is quite readable

/content/images/2026/01/alex2-21.png

Kanban

While it doesn’t affect the published content, we can view our articles in a Kanban view which could make it easy to think about backlog vs In Progress vs Done content

/content/images/2026/01/alex2-22.png

Backups

We can create backups from the settings section

/content/images/2026/01/alex2-23.png

Summary

We reviewed Ghostty which had been a while since we explored. I found it far more useful in native Linux than I had in WSL last Feb. I plan to keep using it as a daily driver.

We also revisited Alexandrie exposing it as three separate DNS entries. While I got a bit stuck on the DNS and RustFS setup, as well as file uploads, in the end we sorted it out.

The only outstanding issue was the inability to embed videos. I’m okay with some limitations - like a lack of a hero image or less UI tweaking. I do think people can get really hung up on fiddling with layout and UI. Those looking for a straight-forward document suite might find Alexandrie a really good option.

alexandrie rustfs cdn ghostty docker kubernetes opensource

Have something to add? Feedback? You can use the feedback form

Isaac Johnson

Isaac Johnson

Cloud Solutions Architect

Isaac is a CSA and DevOps engineer who focuses on cloud migrations and devops processes. He also is a dad to three wonderful daughters (hence the references to Princess King sprinkled throughout the blog).

Theme built by C.S. Rhymes