manbytesgnu_site

Source files for manbytesgnu.org
git clone git://holbrook.no/manbytesgnu_site.git
Info | Log | Files | Refs

commit 5d67bddafc8aa785d52c4f9b1d00837ea19e0997
parent c93b03258d1daa7f2d7155e1156c6323080df02a
Author: lash <dev@holbrook.no>
Date:   Fri, 27 Feb 2026 21:26:17 -0600

Switch to nodom chronicle

Diffstat:
Acontent/20250201_nostr_python.rst | 233+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acontent/20250222_manual_venv.rst | 18++++++++++++++++++
Acontent/20250309_androidx86_sd.rst | 202+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acontent/20251117_casanono_ldaplogin.rst | 45+++++++++++++++++++++++++++++++++++++++++++++
Acontent/20260227_intro.rst | 13+++++++++++++
Acontent/code/hello-nostr-with-python/msg.py | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Acontent/code/hello-nostr-with-python/sub.py | 37+++++++++++++++++++++++++++++++++++++
Acontent/images/android_start.png | 0
Acontent/images/android_usb_apk.png | 0
Acontent/images/android_usb_format.png | 0
Acontent/images/gossip_feed.png | 0
Acontent/images/gossip_relay_global.png | 0
Acontent/images/gossip_relay_init.png | 0
Mcontent/pages/about.rst | 17+++++++++++++----
Acontent/pages/about_ng.rst | 19+++++++++++++++++++
Mlash/static/css/style.css | 20++++++++++++++++----
Mlash/templates/base.html | 6++++++
Mlash/templates/index.html | 13+++++++++----
Mpelicanconf.py | 5+++--
19 files changed, 664 insertions(+), 14 deletions(-)

diff --git a/content/20250201_nostr_python.rst b/content/20250201_nostr_python.rst @@ -0,0 +1,233 @@ +Hello nostr +########### + +:date: 2025-03-20 19:29:35 +:category: Coding +:author: Louis Holbrook +:tags: python,nostr,bitcoin,websockets,rust +:slug: hello-nostr-with-python +:summary: Assembling and publishing a simple text message to a nostr relay. +:lang: en +:status: published + + +Why nostr?! +=========== + +Because it is simple. + +And by that I mean the base layer is simple. There is no black magic that needs to happen to get admitted, synced and discovered. + +At its core, it goes something like this: + +1. Set up a server. +2. Start putting content there. +3. Point people to your server to find your content. +4. They know it's you because you signed your content with a key. + +Of course, apart from 4, that's not really that different of a description from any communications infrastructure anyone would use. + +I understand that many think 4 is a big deal. But let's face it; most people couldn't care less about their digital sovereignity. The vast majority of crypto holders defer to others to keep their key safe, one way or another. It is a big deal because you *can*, but not because people *do*. + +However, for those who *do* care, who *want* to take responsiblity, nostr offers some things I haven't seen in any other project of its kind so far: + +1. The `core protocol <nostrcore_>`_ is a couple of pages, elegant and simple, and expressed in clear language. +2. I can easily execute that same protocol with the most basic of tools. +3. There exists server software that is easy to build, easy to set up and that does not require much resources to run. +4. `"It does not rely on P2P techniques, and therefore it works." <https://github.com/nostr-protocol/nostr?tab=readme-ov-file#nostr---notes-and-other-stuff-transmitted-by-relays>`_ + +In other words. To do something with nostr does not involve connecting to some infernal machine that abstracts all its complexity. It is *in itself not complex*. + +It is simple. + +I hope this post works as a demonstration of that. + +At the time of writing I am using: + +* ``linux 6.12.10`` +* ``python 3.13.1`` +* ``rust 1.81 (2dbb1af80 2024-08-20)`` + +All rust applications below are built simply with ``cargo build --release``. + + +The message +=========== + +Please meet the script that will generate our scratch nostr message. All it took to write it was to read through the first `"Nostr Improvement Proposal" (NIP-01) <nostrcore_>`_. [1]_ + +.. include:: code/hello-nostr-with-python/msg.py + :code: python + :name: msg.py + +To run the script, you will need the coincurve_ python module. As always, a virtual environment is recommended as usual, while you are playing around: + +.. code-block:: bash + + $ python -m venv .venv + $ source .venv/bin/activate + # let's install bech32, too, we will need it later + $ pip install coincurve bech32 + $ python msg.py + +Once run, the script will produce the signed message ready to be sent to the relay. + +It will resemble the data below, but with a different value for the ``created_at``, which in turn results in different values for ``id`` and ``sig``. + + +.. code-block:: json + + ["EVENT", {"id": "63b43ae8d74b5df17659a4663f256c6829994970ca6b08a5d068e0c01a460461", "pubkey": "cc9519ba6fb1cb0cca53743dc90c2418440cf637f8b891ce2f0e2dc5c5b3cf01", "created_at": 1738407317, "kind": 1, "tags": [], "content": "hello nostr ¶", "sig": "c2ccf3524908c54a53e5fc1405b56b514dcf1d80baa7c135514fce3dcafb6646920c560f612e3769d2682b318f44f880c7147cb810ca88ffea0496cda087c583"}] + + +The runnning +============ + +To share this with the world we need some service to read and write from. + +Starting out I am using the nostr-rs-relay_ (``v0.9.0``, ``git:ff65ec2acd781150a585a78e1c60b0cdb104698e``). It gives you enough debug output to follow what's going on, and it's easy to build [2]_ and configure: + +.. code-block:: ini + + # nostr-rs-relay configuration toml file + # limited to the fields that should be changed + + [info] + relay_url = "ws://localhost" + name = "my_first_nostr_node" + description = "act local, think local" + # same pubkey as the script above + pubkey = cc9519ba6fb1cb0cca53743dc90c2418440cf637f8b891ce2f0e2dc5c5b3cf01 + + [network] + bind = 127.0.0.1 + port = 8081 + +In the repository, make a new directory ``db`` and run it. The process starts a websocket server which is used to send and receive events. One of the log lines in the terminal should show you where it is listening. + +.. code-block:: bash + + $ RUST_LOG=debug ./target/release/nostr-rs-relay -c config.toml -d db + [...] + 2025-02-01T11:05:55.223024Z INFO nostr_rs_relay::server: listening on: 127.0.0.1:8081 + + +The sending +=========== + +We use websocat_ (``v1.14.0``, ``git:83c4375ac9a0475b7a8f3b75e49290f4486a4914``) to provide the terminal connection to the node. + +Add a bit of fanciness by piping the output of our message generator python script to the command: + +.. code-block:: bash + + $  python msg.py | tee /dev/stderr | websocat -v ws://localhost:8080 + [INFO websocat::lints] Auto-inserting the line mode + [INFO websocat::stdio_threaded_peer] get_stdio_peer (threaded) + [INFO websocat::ws_client_peer] get_ws_client_peer + ["EVENT", {"id": "bac1d459b39ac0ba91951491e382b8b5648b149b509ea8585a369d0a84101447", "pubkey": "cc9519ba6fb1cb0cca53743dc90c2418440cf637f8b891ce2f0e2dc5c5b3cf01", "created_at": 1738408544, "kind": 1, "tags": [], "content": "hello nostr ¶", "sig": "322470a022f21fe4eae6b3652f4cca59a71d2e865791880117aba7bdb069bfed8780eea7874e0d475db71beef4c498b5a559bb4dd3ef87ecce68f157a7b64213"}] + [INFO websocat::net_peer] Failure during connecting TCP: Connection refused (os error 111) + [INFO websocat::net_peer] Connected to TCP 127.0.0.1:8081 + [INFO websocat::ws_client_peer] Connected to ws + ["OK","bac1d459b39ac0ba91951491e382b8b5648b149b509ea8585a369d0a84101447",true,""] + [INFO websocat::sessionserve] Forward finished + [INFO websocat::ws_peer] incoming None + [INFO websocat::sessionserve] Reverse finished + [INFO websocat::sessionserve] Both directions finished + +As we see, the response from the node (the line with ``"OK"`` as first value) indicates a success, and cites the ``id`` matching the ``id`` in the message json contents we emitted to ``stderr``. + +The message has now been published. + + +The viewing +=========== + +Of course we can use a similar trick to get the message back out. More on that a bit later. + +But for now, let's use a proper nostr client to view the published message. + +My introduction to nostr has been through gossip_ (``v0.13.0``, ``git:90712385f6f79b60c01ae588464be4c960e76836``), a rust-based graphical client with plenty of levers to pull, aswell as an easy way of running clients with different states and keys simultaneously. + +The only annoying thing about gossip I have found so far is that it will not accept localhost as a value when adding relays to use. So we will use our local hostname instead. Make sure you have an entry in your ``/etc/hosts`` and that it resolves: + +.. code-block:: bash + + $ cat /etc/hosts + [...] + 127.0.0.1 localhost myhost + + $ getent ahostsv4 myhost + 127.0.0.1 STREAM localhost + [...] + +Once built, create an empty data directory, and run gossip on it: + +.. code-block:: bash + + $ mkdir foo + $ RUST_LOG=debug GOSSIP_DIR=foo ./target/release/gossip + +You will go through a setup wizard, prompting you to create a new account, and then ask you for relays to use and people to follow. + +On the relay page, remove the one suggested and add instead ``ws://myhost:8081`` to the *Outbox* and *InBox* categories. + +.. image:: {static}/images/gossip_relay_init.png + +On the follow page, follow the public key of the message we already have published: ``cc9519ba6fb1cb0cca53743dc90c2418440cf637f8b891ce2f0e2dc5c5b3cf01``. [3]_ + +Completing this step will take you to the main application page. Here, navigate to ``Relays -> My relays``, which should list the one relay added using the wizard. Expand the entry, activate the *Global Feed* switch. + +.. image:: {static}/images/gossip_relay_global.png + +Navigate back to the *Global* menu item. In a short while, the post made with the script should appear [4]_. + +.. image:: {static}/images/gossip_feed.png + +With any luck, it should appear under the **Following** menu item, too. + +Getting hooked +============== + +Let us now read a response from the gossip client directly in the websocket connection. + +While in gossip, click the reply button on the post, write a message and send it. + +Then, choose the menu item *Settings* and copy the value in the *Public Key* entry (starts with ``npub...``) + +Back in the terminal, using a new script, we will now generate a subscription request for text message events signed with this public key. + +.. include:: code/hello-nostr-with-python/sub.py + :code: python + +For the public key ``npub1p2vekljvajp4d3kqdwvha54d6ywh2j360xpl32ug70r8y009h86q7g3tr4`` we would do: + +.. code-block:: bash + + $ python sub.py npub1p2vekljvajp4d3kqdwvha54d6ywh2j360xpl32ug70r8y009h86q7g3tr4 | tee /dev/stderr | websocat ws://alto:8081 + ["REQ", "2c626a2ee46a1751b271447264b1eb524b4ed0f834d35a94d3d74d21ed003fde", {"authors": ["0a999b7e4cec8356c6c06b997ed2add11d754a3a7983f8ab88f3c6723de5b9f4"], "kinds": [1]}] + ["EVENT","873a3ce558e0690c4d831bea2c74b24f40407ef2bc42e251792c0bf80e94eb28",{"id":"b1474751a1799c4785aa8272fba5f20034abe3312d38335ae5553c95045e03b3","pubkey":"0a999b7e4cec8356c6c06b997ed2add11d754a3a7983f8ab88f3c6723de5b9f4","created_at":1738415174,"kind":1,"tags":[["p","cc9519ba6fb1cb0cca53743dc90c2418440cf637f8b891ce2f0e2dc5c5b3cf01"],["e","55befa55c97b59fb4f206455246c6d14f0711c429502dc814036fd271a544ea6","ws://alto:8081/","root","cc9519ba6fb1cb0cca53743dc90c2418440cf637f8b891ce2f0e2dc5c5b3cf01"]],"content":"Hello back🙏🚀","sig":"1288e42970e746e6908350e434db27e408b40ff0290e7d54e32740a5dd6ceeca74cea410694eb5f1347099372fb2fdf8d58369c565fe2cc57a64be49c05dfa75"}] + ["EOSE","873a3ce558e0690c4d831bea2c74b24f40407ef2bc42e251792c0bf80e94eb28"] + + +And of course, while this connection is kept open, any other messages posted by the same key will appear in the terminal. + +.. _nostrcore: https://github.com/nostr-protocol/nips/blob/master/01.md + +.. _coincurve: https://github.com/ofek/coincurve + +.. _gossip: https://github.com/mikedilger/gossip + +.. _websocat: https://github.com/vi/websocat + +.. _nostr-rs-relay: https://github.com/scsibug/nostr-rs-relay + +.. + + .. [1] To be fair, in reality NIP-10 also, which is referenced by NIP-01, although the meat of it is already spelled out in the latter. + + .. [2] Simply ``cargo build --release``. If you are using the `rustup <https://rustup.rs/>`_ toolchain manager, you can set up provisions with ``rustup default 1.81`` first. + + .. [3] The public key will be encoded to the nostr `npub` encoding scheme, a `bech32` encoded address. It's really just semantics, and represents the same value. Have a look at `python_nostr <https://github.com/jeffthibault/python-nostr/blob/main/nostr/key.py#L30>`_. + + .. [4] The synchronization logic in gossip still seems to be slightly lazy, so you may have to move back and forth between menu items to trigger redraws that will list the message. diff --git a/content/20250222_manual_venv.rst b/content/20250222_manual_venv.rst @@ -0,0 +1,18 @@ +Local python repository +####################### + +:date: 2025-02-22 22:19:25 +:category: Offlining +:author: Louis Holbrook +:tags: python +:slug: manual-venv +:summary: Making a python virtual environment from scratch +:lang: en +:status: draft + + +.. + build python and install to destdir + build virtualenv with destdir paths for pythonpath, path + install virtualenv and supporting packages with PIP_TARGET + use PIP_TARGET as pythonpath to instantiate virtualenv diff --git a/content/20250309_androidx86_sd.rst b/content/20250309_androidx86_sd.rst @@ -0,0 +1,202 @@ +android-x86 on qemu +################### + +:date: 2025-03-13 04:18:05 +:category: Offlining +:author: Louis Holbrook +:tags: android,qemu,nbd +:slug: androidx86-sd-nbd-qemu +:summary: Methods to share files from a host to an android-x86 running on qemu +:lang: en +:status: published + + +I was so thrilled to find `this post`_ detailing how to create an Android app without the Android Studio or other IDE. + +To my absolute astonishment, the example still works to this day. + +But so the question remained: How to get my app onto the phone? + + +Untethering from the phone +========================== + +Short of the IDE emulator, putting it on a smartphone via `adb` was the only real option I knew about. There are probably other ways. I never did much with smartphones, anyway, so I wouldn't know. In fact I absolutely can't stand smartphones, and I never use them unless I have to. + +That's why, in the past, I've tried some emulators to replace the need for having a smartphone at all. + +Unfortunately, that doesn't work for most critical things you are *forced* to use a smartphone for, like banking authentication for online purchases, that mostly can't be done with SMS 2FA anymore. + +But for less picky apps, it can work. It's clunky, but it gets you off the spy device. + +And of course, that means we can use it for development. + + +The Android PC +============== + +Now `android-x86`_ is probably not the very best benchmark for what runs on a phone. It is, after all, a open source fork of Android designed to run as a PC OS. [1]_ + +But when using `qemu virtualization`_, having an x86 setup means one doesn't have to mess around with bootloaders and Device Tree Specifications and stuff to get everything running. + +As a first line test environment, it should work well enough. + +Getting android-x86 to run on qemu [2]_ isn't hard at all. You simply create a disk image, fire it up with that disk and the distribution ISO [3]_ as a "cdrom" and + +.. code-block:: bash + + # create disk image + $ qemu-img create -f raw ax86.img 10G + + # start the installer with the new disk image + $ qemu-system-x86_64 --enable-kvm -m 2G -cdrom /path/to/iso -hda ax86.img + +Once running, use whatever GUI client qemu is running (mine is VNC), and set up the OS, as follows: + + 1. Choose the "Installation ..." option in the boot menu. + 2. Create a single partition (do _not_ use GPT). + 3. Format with ext4. + 4. *Yes*, install GRUB. + 5. Make the partition read/write. + 6. Run the OS + 7. Go through the initial setup on the phone. + +When done [4]_, mine looked like this: + +.. image:: {static}/images/android_start.png + +(I chose the "taskbar" option for navigation) + + +App transport +============= + +Now, let's look at three ways to get your apk file onto the emulated instance. + + +The internet +------------ + +Smart phones are in reality mostly just remote controls to stuff happening on the internet. I guess that's why interacting with one from your computer always seems so unintuitive. + +Therefore, the most obvious way is to just upload the apk somewhere on the internet [5]_, and then punch in the URL to download it. + +Yes, you heard right. You are *typing* the URL. + +With your mouse on software keyboard. + +With this running on qemu you don't have a camera readily available. So no QR codes for you. Fun? On a phone: Not very. + + +Port-forwarded adb +------------------ + +Using the `hostfwd` option, qemu lets us expose guest ports to the host. That is very practical for us, as we can use `adb` with TCP, and send the application that way. + +Close down the emulator [4]_, and run it again with an edited qemu command: + + +.. code-block:: bash + + # qemu but this time with host forwarding + $ qemu-system-x86_64 --enable-kvm -m 2G -hda ax86.img -net user,hostfwd=tcp::5555-:5555 -net nic,model=rtl8139 + +Once the android instance has finished booting, we can use adb to push our app to it [6]_: + +.. code-block:: bash + + # connect to the remote administration tcp socket. + $ adb connect localhost:5555 + + # push the app to the phone through the tcp socket, replacing any existing app. + $ adb -s localhost:5555 install -r /path/to/apk + +You should be getting a message saying the installation was successful. After that, the app should be visible in the menu. + + +A phony usb stick +----------------- + +The last method to demonstrate is the usb solution. + +At the time of writing, androidx86 only supports `EHCI USB`_. + +Once you're powered off your instance again, adding the USB device can look like this [7]_: + +.. code-block:: bash + + # Make the usb image + $ qemu-img create -f raw usb.img 2G + + # In order for androidx86 to make sense of the disk, it also has to be partitioned. + # Just create a single partition on it (I used the Linux FS partition type, not sure if it matters). + $ fdisk usb.img + [...] + + # Start the emulator again with the USB drive. + $ qemu-system-x86_64 --enable-kvm -m 2G -hda ax86.img -drive if=none,id=usbstick,format=raw,file=img/usb.img -usb -device usb-ehci,id=ehci -device usb-tablet,bus=usb-bus.0 -device usb-storage,bus=ehci.0,drive=usbstick + +Once powered up, a notification will appear complaining that the USB disk needs a fix. + +.. image:: {static}/images/android_usb_format.png + +Format it for "removable drive" and power down again. + +Now that you have a formatted USB image [8]_, you can mount it as a loop device, and copy the apk file to it: + +.. code-block:: bash + + # The loop device must mount at the offset of the first partition. + # Use fdisk -l usb.img to see sector size and start sector of the partition. + # Remember your offset will be (sectors * sector size) + # On my setup the partition started on sector 2048, and the sector size was 512 bytes. 2048 * 512 = 1048576 + $ sudo mount -o loop,offset=1048576 usb.img /mnt + + # Copy the file + $ cp /path/to/apk /mnt/ + + # Unmount the drive + $ sudo umount /mnt + +Now, after you start up again, the USB device should appear in the file explorer. + +And in the USB folder, you should see the app. + +.. image:: {static}/images/android_usb_apk.png + +.. _`this post`: https://www.hanshq.net/command-line-android.html +.. _`EHCI USB`: https://g33k.holbrook.no/dc76e77870930617dc0b2c8195aff6073f652d593f7b90a47dd0f22d3329c5e1 +.. _`qemu virtualization`: https://www.qemu.org/ +.. _`android-x86`: https://www.android-x86.org/ + +.. + + .. [1] After writing all this, I noticed that the last version of android-x86 was released in 2022. So perhaps it has since been abandoned, who knows. Anyway, it is based on LineageOS, which means most of what is written here still should apply, e.g. when running `qemu-system-aarch64` with that. The USB stuff might look a bit different, though. Perhaps something for a future post. + +.. + + .. [2] I used QEMU version 9.2.0. + +.. + + .. [3] I used `androidx86 version 9.0-r2 <https://www.fosshub.com/Android-x86.html#>`_. + +.. + + .. [4] As the phone setup process is painfully slow and tedious, this can be a good time to make a copy of your disk image. That way, you can always make a new copy of that one to start with just as fresh a setup as you have now. You will want to stop the emulator first: press Alt+F1 to enter console, and issue the "poweroff" command. + +.. + + .. [5] If you set up a TAP device and use that as the network interface with qemu, you can also reach a webserver on your host. Have a look at `this article <https://blog.stefan-koch.name/2020/10/25/qemu-public-ip-vm-with-tap>`_ if you are feeling adventurous enough to try. + +.. + + .. [6] If you issue `adb devices` you may notice an additional device `emulator:5554` listed. This is why you need to specify the device to install to, otherwise you get an error. On my setup, this emulator device is offline and does not work. + +.. + + .. [7] Lifted from https://qemu-project.gitlab.io/qemu/system/devices/usb.html#ehci-controller-support + +.. + + .. [8] It is probably a good idea to make a copy of this one too, so you have a fresh one available when you need it. diff --git a/content/20251117_casanono_ldaplogin.rst b/content/20251117_casanono_ldaplogin.rst @@ -0,0 +1,45 @@ +Making an LDAP home +################### + +:date: 2025-11-17 10:27:58 +:category: Lab +:author: Louis Holbrook +:tags: ldap,linux,pam,nfs,autofs +:slug: ldap-homes +:summary: Preparing for centralized login +:series: Casa Nodominio +:seriesprefix: casanodo +:seriespart: 1 +:lang: en +:status: draft + +.. code-block:: bash + + apt-get install nslcd libpam-ldapd autofs autofs-ldap + + +* create ssl certificate +* update ssl certificate: + - copy to /usr/local/share/ca-certificates + - run /sbin/update-ca-certificates +* configure nslcd with ssl + - add bind + - set base for user, group + + +nslcd.conf:: + + foo + +/etc/auto.homes:: + + bar + +/etc/auto.master.d/home.autofs:: + + baz + + +/etc/pam.d/:: + + xyzzy diff --git a/content/20260227_intro.rst b/content/20260227_intro.rst @@ -0,0 +1,13 @@ +What is the way? +################ + +:date: 2026-02-27 20:56:24 +:category: Chronicle +:author: Louis Holbrook +:tags: planning,proxmox +:slug: early-sketches +:summary: Some early sketches of the road ahead. +:lang: en +:status: published + +TODO diff --git a/content/code/hello-nostr-with-python/msg.py b/content/code/hello-nostr-with-python/msg.py @@ -0,0 +1,50 @@ +# msg.py +import coincurve +import json +import time +import hashlib +import string + + +# generate secret for private key +h = hashlib.sha256() +h.update(b"foo") +k = h.digest() + +# create private and public keys +pk = coincurve.PrivateKey(k) +pubk = pk.public_key_xonly.format().hex() + +# create the unsigned message data with the current timestamp +t = int(time.time()) +msg = [ + 0, + pubk, + t, + 1, + [], + u"hello nostr \u00b6", + ] +v = json.dumps(msg, ensure_ascii=False, separators=(',',':')) + +# calculate the signature over the digest of the unsigned message +h = hashlib.sha256() +h.update(v.encode("utf-8")) +z = h.digest() +sig = pk.sign_schnorr(z) + +# assemble and output the message to be published, adding the digest and the signature +payload = { + "id": z.hex(), + "pubkey": msg[1], + "created_at": msg[2], + "kind": msg[3], + "tags": msg[4], + "content": msg[5], + "sig": sig.hex(), + } +cmd = [ + "EVENT", + payload, + ] +print(json.dumps(cmd, ensure_ascii=False)) diff --git a/content/code/hello-nostr-with-python/sub.py b/content/code/hello-nostr-with-python/sub.py @@ -0,0 +1,37 @@ +# sub.py +import sys +import bech32 +import coincurve +import json +import hashlib +import string +import os + +# parse public key from input +if len(sys.argv) == 1: + raise ValueError("need single argument") +r = bech32.bech32_decode(sys.argv[1]) +if r[0] == None: + raise ValueError("Invalid npub key {}".format(sys.argv[1])) + +# Can't use bech32.decode() directly because there is a data length check there that will always fail with nostr public keys. +# I guess the module was written with 160 bit bitcoin addresses in mind. +# So we manually have to convert the bits as in the function. +pubk_bytes = bytes(r[1]) +pubk_v = bech32.convertbits(pubk_bytes, 5, 8)[:-1] +pubk_bytes = bytes(pubk_v) +pubk_object = coincurve.PublicKeyXOnly(pubk_bytes) +pubk = pubk_object.format().hex() + +sub = os.urandom(32).hex() +cmd = [ + "REQ", + sub, + { + "authors": [pubk], + "kinds": [1], + } + ] +print(json.dumps(cmd)) + + diff --git a/content/images/android_start.png b/content/images/android_start.png Binary files differ. diff --git a/content/images/android_usb_apk.png b/content/images/android_usb_apk.png Binary files differ. diff --git a/content/images/android_usb_format.png b/content/images/android_usb_format.png Binary files differ. diff --git a/content/images/gossip_feed.png b/content/images/gossip_feed.png Binary files differ. diff --git a/content/images/gossip_relay_global.png b/content/images/gossip_relay_global.png Binary files differ. diff --git a/content/images/gossip_relay_init.png b/content/images/gossip_relay_init.png Binary files differ. diff --git a/content/pages/about.rst b/content/pages/about.rst @@ -1,12 +1,13 @@ Identities ########## +:slug: identities :title: Identities :author: Louis Holbrook -My own. My precious -=================== +Owned +===== PGP - personal `59A844A484AC11253D3A3E9DCDCBD24DD1D0E001`_ @@ -17,19 +18,27 @@ PGP - code signing PGP - `Delta Chat`_ `E19386B26EB1F4CC9B14B4E3D62A8A779612E773`_ -TOX_ - `70459C0568A64737F127CA1505FA0485FBB69831C7BD6AC269E369285C7F2E0282283B2AFCD0` +nostr_ + `nprofile1qqsz9mzww72uwakp0awajg59y8px3syua03p4sppyanlqeycur40c9gpr4mhxue69uhkummnw3ezucnfw33k76twv4ezuum0vd5kzmp0qyv8wumn8ghj7mn0wd68ytngdakxyun0da4jumn09uq3xamnwvaz7tm0venxx6rpd9hzuur4vghsz9mhwden5te0wfjkccte9ehx7uewwdhkx6tpdshsz9nhwden5te0wfjkccte9ehx7um5wghxuet59uq3wamnwvaz7tmjv4kxz7fwwpexjmtpdshxuet59uzkgnds`_ + +.. + (_npub1ytkyuau4camvzl6amy3g2gwzdrqfe6lzrtqzzfm87pjf3c82ls2s36j5ek) .. .. _Delta Chat: https://delta.chat/ + .. _nostr: https://nostr.com/ + .. _59A844A484AC11253D3A3E9DCDCBD24DD1D0E001: https://holbrook.no/keys/louis.asc .. _0826EDA1702D1E87C6E2875121D2E7BB88C2A746: https://holbrook.no/keys/louis_dev.asc .. _E19386B26EB1F4CC9B14B4E3D62A8A779612E773: https://holbrook.no/keys/louis_deltachat.asc + .. _nprofile1qqsz9mzww72uwakp0awajg59y8px3syua03p4sppyanlqeycur40c9gpr4mhxue69uhkummnw3ezucnfw33k76twv4ezuum0vd5kzmp0qyv8wumn8ghj7mn0wd68ytngdakxyun0da4jumn09uq3xamnwvaz7tm0venxx6rpd9hzuur4vghsz9mhwden5te0wfjkccte9ehx7uewwdhkx6tpdshsz9nhwden5te0wfjkccte9ehx7um5wghxuet59uq3wamnwvaz7tmjv4kxz7fwwpexjmtpdshxuet59uzkgnds : https://holbrook.no/.well-known/nostr.json + + .. _TOX: https://tox.chat/ diff --git a/content/pages/about_ng.rst b/content/pages/about_ng.rst @@ -0,0 +1,19 @@ +About this blog +############### + +:date: 2026-02-27 19:38:22 +:modified: 2026-02-27 19:38:22 +:slug: about +:title: About +:author: Louis Holbrook + + +This blog chronicles my work to establish a self-hosted and sovereign infrastructure for and around the `Casa Nodominio`_ community centre in the town of Las Tunas in eastern El Salvador. + +If you want more information or get involved, please contact me on nostr_. + +.. + + .. _Casa Nodominio: http://playalastunas.org + + .. _nostr: http://nostrudel.ninja/u/nprofile1qqsz9mzww72uwakp0awajg59y8px3syua03p4sppyanlqeycur40c9gpr4mhxue69uhkummnw3ezucnfw33k76twv4ezuum0vd5kzmp0qyv8wumn8ghj7mn0wd68ytngdakxyun0da4jumn09uq3xamnwvaz7tm0venxx6rpd9hzuur4vghsz9mhwden5te0wfjkccte9ehx7uewwdhkx6tpdshsz9nhwden5te0wfjkccte9ehx7um5wghxuet59uq3wamnwvaz7tmjv4kxz7fwwpexjmtpdshxuet59uzkgnds diff --git a/lash/static/css/style.css b/lash/static/css/style.css @@ -42,16 +42,21 @@ header div { h3 { color: #840020; - font-size: 1.5em; + font-size: 1.4em; +} + +h3.legacy a { + color: #888888; } + h2 { color: #840020; - font-size: 2.0em; + font-size: 1.7em; } h1 { - font-size: 3.0em; + font-size: 2.2em; text-transform: uppercase; } @@ -162,10 +167,12 @@ div.highlight, div.highlight pre, pre.code { background-color: #e5e0e0; - padding: 1em; + padding: 0.8em; + padding-bottom: 1.0em; margin-bottom: 0.3em; word-wrap: break-word; overflow: hidden; + font-size: 1.1em; } @@ -211,6 +218,7 @@ table.footnote, table.citation { border-left: 3px solid #ccc; margin-top: 2.0em; + font-size: 0.9em; } table.footnote td.label, @@ -256,3 +264,7 @@ div.entry-content > p:first-of-type::first-letter { font-weight: 900; color: #840020; } + +article.legacy li p { + color: #888888; +} diff --git a/lash/templates/base.html b/lash/templates/base.html @@ -1,3 +1,5 @@ +{% set cat_ignore = ['archiving', 'code', 'coding', 'hygiene', 'offlining' ] %} +{% set page_ignore = ['identities', 'shares' ] %} <!DOCTYPE html> <html lang="{% block html_lang %}{{ DEFAULT_LANG }}{% endblock html_lang %}"> <head> @@ -18,10 +20,14 @@ <nav id="menu"> <ul> {% for cat, null in categories %} + {% if not cat in cat_ignore %} <li{% if cat == category %} class="active"{% endif %}><a href="{{ SITEURL }}/{{ cat.url }}">{{ cat }}</a></li> + {% endif %} {% endfor %} {% for p in pages %} + {% if not p.slug in page_ignore %} <li{% if p == page %} class="active"{% endif %}><a href="{{ SITEURL }}/{{ p.url }}">{{ p.title }}</a></li> + {% endif %} {% endfor %} {% for title, link in MENUITEMS %} diff --git a/lash/templates/index.html b/lash/templates/index.html @@ -2,14 +2,19 @@ {% block content %} <section id="content"> {% block content_title %} -<h1 class="top-body-title">All articles</h1> +<h1 class="top-body-title">Chronicle</h1> {% endblock %} <ol id="post-list"> {% for article in articles_page.object_list %} - <li><article class="hentry"> - <header> <h3 class="entry-title"><a href="{{ SITEURL }}/{{ article.url }}" rel="bookmark" title="Permalink to {{ article.title|striptags }}">{% if article.series %} {{ article.series }}: {% endif %}{{ article.title }}</a></h3> </header> - <p>{{ article.summary }}</p> + {% set legacy = '' %} + {% if article.date < CUTOFF %} + {% set legacy = ' legacy' %} + {% endif %} + <article class="hentry{{ legacy }}"> + <li> + <header> <h3 class="entry-title{{ legacy }}"><a href="{{ SITEURL }}/{{ article.url }}" rel="bookmark" title="Permalink to {{ article.title|striptags }}">{% if article.series %} {{ article.series }}: {% endif %}{{ article.title }}</a></h3> </header> + <p class="entry-summary{{legacy}}">{{ article.summary }}</p> </article></li> {% endfor %} </ol><!-- /#posts-list --> diff --git a/pelicanconf.py b/pelicanconf.py @@ -1,13 +1,14 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # - +from datetime import datetime +CUTOFF = datetime.fromisoformat('2026-02-26T00:00:00Z') AUTHOR = 'Louis Holbrook' SITENAME = 'man bytes gnu' SITEURL = '' PATH = 'content' -TIMEZONE = 'Europe/Berlin' +TIMEZONE = 'Europe/Lisbon' DEFAULT_LANG = 'en'