๐Ÿ’พ Archived View for perso.pw โ€บ blog โ€บ rss.xml captured on 2023-01-29 at 02:52:08.

View Raw

More Information

โฌ…๏ธ Previous capture (2022-07-16)

โžก๏ธ Next capture (2023-03-20)

๐Ÿšง View Differences

-=-=-=-=-=-=-

<?xml version="1.0" encoding="UTF-8"?> 
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Solene'%</title>
    <description></description>
    <link>gemini://perso.pw/blog/</link>
    <atom:link href="gemini://perso.pw/blog/rss.xml" rel="self" type="application/rss+xml" />
    <item>
  <title>Configure syncthing to sync a single file</title>
  <description>
    <![CDATA[
<pre># Introduction

Quick blog entry to remember about something that wasn't as trivial as I thought.  I needed to use syncthing to keep a single file in sync (KeePassXC database) without synchronizing the whole directory.

You have to use mask exclusion feature to make it possible.  Put it simple, you need the share to forbid every file, except the one you want to sync.

This configuration happens in the `.stignore` file in the synchronized directory, but can also be managed from the Web interface.

=> https://docs.syncthing.net/users/ignoring.html Syncthing documentation about ignoring files

# Example

If I want to only sync KeePassXC files (they have the `.kdbx` extension), I have this in my `.stignore` file:

!*.kdbx


And that's all!

Note that this must be set on all nodes using this share, otherwise you may have surprises.
</pre>
    ]]>
  </description>
  <guid>gemini://perso.pw/blog//articles/syncthing-single-file.gmi</guid>
  <link>gemini://perso.pw/blog//articles/syncthing-single-file.gmi</link>
  <pubDate>Sat, 28 Jan 2023 00:00:00 GMT</pubDate>
</item>
<item>
  <title>How to boot on a BTRFS snapshot</title>
  <description>
    <![CDATA[
<pre># Introduction

I always wanted to have a simple rollback method on Linux systems, NixOS gave me a full featured one, but it wasn't easy to find a solution for other distributions.

Fortunately, with BTRFS, it's really simple thanks to snapshots being mountable volumes.

# Setup

You need a Linux system with a BTRFS filesystem, in my examples, the root subvolume (where `/` is) is named `gentoo`.

I use `btrbk` to make snapshots of `/` directly in `/.snapshots`, using the following configuration file:

snapshot_preserve_min 30d

volume /

snapshot_dir .snapshots

subvolume .


With a systemd service, it's running once a day, so I'll have for 30 days of snapshots to restore my system if needed.

This creates snapshots named like the following:

$ ls /.snapshots/

ROOT.20230102

ROOT.20230103

ROOT.20230104


A snapshot address from BTRFS point of view looks like `gentoo/.snapshots/ROOT.20230102`.

I like btrbk because it's easy to use and configure, and it creates easy to remember snapshots names.

# Booting on a snapshot

When you are in the bootloader (GRUB, systemd-boot, Lilo etc..), edit the command line, and add the new option (replace if already exists) with the following,  the example uses the snapshot `ROOT.20230102`:

rootflags=subvol=gentoo/.snapshots/ROOT.20230103


Boot with the new command line, and you should be on your snapshot as the root filesystem.

# Be careful

When you are on a snapshot, this mean any change will be specific to this volume.

If you use a separate partition for `/boot`, an older snapshot may not have the kernel (or its module) you are trying to boot.

# Conclusion

This is a very simple but effective mecanism, more than enough to recover from a bad upgrade, especially when you need the computer right now.

# Going further

There is a project grub-btrfs which can help you adding BTRFS snapshots as boot choices in GRUB menus.

=> https://github.com/Antynea/grub-btrfs grub-btrfs GitHub project page
</pre>
    ]]>
  </description>
  <guid>gemini://perso.pw/blog//articles/boot-on-btrfs-snapshot.gmi</guid>
  <link>gemini://perso.pw/blog//articles/boot-on-btrfs-snapshot.gmi</link>
  <pubDate>Wed, 04 Jan 2023 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Booting Gentoo on a BTRFS from multiple LUKS devices</title>
  <description>
    <![CDATA[
<pre># Introduction

This is mostly a reminder for myself.  I installed Gentoo on a machine, but I reused the same BTRFS filesystem where NixOS is already installed, the trick is the BTRFS filesystem is composed of two partitions (a bit like raid 0) but they are from two different LUKS partitions.

It wasn't straightforward to unlock that thing at boot.

# Fix grub error

Grub was trying to autodetect the root partition to add `root=/dev/something`, but as my root filesystem requires `/dev/mapper/ssd1` and `/dev/mapper/ssd2`, it was simply adding `root=/dev/mapper/ssd1 /dev/mapper/ssd2`, which is wrong.

This required a change in the file `/etc/grub.d/10_linux` where I entirely deleted the `root=` parameter.

# Compile systemd with cryptsetup

A mistake I made was to try to boot without systemd compiled with cryptsetup support, this was just failing because in the initramfs, some systemd services were used to unlock the partitions, but without proper support for cryptsetup it didn't work.

# Linux command line parameters

In `/etc/default/grub`, I added this line, it contains the UUID of both LUKS partitions needed, and a `root=/dev/dm-0` which is unexpectedly the first unlocked device path, and `rd.luks=1` to enble LUKS support.

GRUB_CMDLINE_LINUX="rd.luks.uuid=24682f88-9115-4a8d-81fb-a03ec61d870b rd.luks.uuid=1815e7a4-532f-4a6d-a5c6-370797ef2450 rootfs=btrfs root=/dev/dm-0 rd.luks=1"


# Run Dracut and grub

After the changes, I did run `dracut --force --kver 5.15.85-gentoo-dist` and `grub-mkconfig -o /boot/grub/grub.cfg`

# Conclusion

It's working fine now, I thought it would require me to write a custom initrd script, but dracut is providing all I needed, but there were many quirks on the path with no really helpful message to understand what's failing.

Now, I can enjoy my dual boot Gentoo / NixOS (they are quite antagonists :D), but they share the same filesystem and I really enjoy this weird setup.
</pre>
    ]]>
  </description>
  <guid>gemini://perso.pw/blog//articles/multi-luks-btrfs-boot-gentoo.gmi</guid>
  <link>gemini://perso.pw/blog//articles/multi-luks-btrfs-boot-gentoo.gmi</link>
  <pubDate>Mon, 02 Jan 2023 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Export Flatpak programs from a computer to another</title>
  <description>
    <![CDATA[
<pre># Introduction

As a flatpak user, but also someone with a slow internet connection, I was looking for a way to export a flatpak program to install it on another computer.  It turns out flatpak supports this, but it's called "create-usb" for some reasons.

So today, I'll show how to export a flatpak program from a computer to another.

=> https://www.flatpak.org/ Flatpak official website
=> https://docs.flatpak.org/en/latest/usb-drives.html Flatpak documentation about usb drives

# Pre-requisites

For some reasons, the default flathub parameters doesn't associate it a "Collection ID", which is required for the create-usb feature to work, so we need to associate a "Collection ID" to the flathub remote repository on both systems.

We can use the example from the official documentation:

flatpak remote-modify --collection-id=org.flathub.Stable flathub


# Export

The export process is simple, create a directory in which you want the flatpak application to be exported, we will use `~/export/` in the examples, with the program `org.mozilla.firefox`.

flatpak create-usb ~/export/ org.mozilla.firefox


The export process will display a few lines and tell you when it finished.

If you export multiple programs into the same directory, the export process will be smart and skip already existing components.

# Import

Take the `~/export/` directory, either on a USB drive, or copy it using rsync, share it over NFS/Samba etc... It's up to you.  In the example, `~/export/` refers to the same directory transferred from the previous step onto the new system.

Now, we can run the import command to install the program.

flatpak install --sideload=~/export/.ostree/repo/ flathub org.mozilla.firefox


If it's working correctly, it should be very fast.

# Limitation

Because the flatpak components/dependencies of a program can differ depending on the host (for example if you have an NVIDIA card, it will pull some NVIDIA dependencies), so if you export a program from a non-NVIDIA system to the other, it won't be complete to work reliably on the new system, but the missing parts can be downloaded on the Internet, it's still reducing the bandwidth requirement.

# Conclusion

I kinda like Flatpak, it's convenient and reliable, and allow handling installed programs without privileges escalation.  The programs can be big, it's cool to be able to save/export them for later use.
</pre>
    ]]>
  </description>
  <guid>gemini://perso.pw/blog//articles/flatpak-export-import.gmi</guid>
  <link>gemini://perso.pw/blog//articles/flatpak-export-import.gmi</link>
  <pubDate>Sun, 01 Jan 2023 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Authentication gateway with SSH on OpenBSD</title>
  <description>
    <![CDATA[
<pre># Introduction

A neat feature in OpenBSD is the program authpf, an authenticating gateway using SSH.

Basically, it allows to dynamically configure the local firewall PF by connecting/disconnecting into a user account over SSH, either to toggle an IP into a table or rules through a PF anchor.

# Use case

This program is very useful for the following use case:



Of course, you can be creative and imagine other use cases.

This method is actually different from using a VPN, it doesn't have encryption extra cost but is less secure in the sense it only authenticates an IP or username, so if you use it over the Internet, the triggered rule may also benefit to people using the same IP as yours.  However, it's much simpler to set up because users only have to share their public SSH key, while setting up a VPN is another level of complexity and troubleshooting.

# Example setup

In the following example, you manage a small office OpenBSD router, but you only want Chloe's workstation to reach the Internet with the NAT.  We need to create her a dedicated account, set the shell to authpf, deploy her SSH key and configure PF.

useradd -m -s /usr/sbin/authpf chloe

echo "$ssh_key" >> ~chloe/.ssh/authorized_keys

touch /etc/authpf/authpf.conf /etc/authpf/authpf/rules


Now, you can edit `/etc/pf.conf` and use the default table name `authpf_users`.  With the following PF snippet, we will only allow authenticated users to go through the NAT.

table <authpf_users> persist

match out on egress inet from <authpf_users> to any nat-to (egress)


Reload your firewall, and when Chloe will connect, she will be able to go through the NAT.

# Conclusion

The program authpf is an efficient tool for the network administrator's toolbox.  And with the use of PF anchors, you can really extend its potential as you want, it's really not limited to tables.

# Going further

The man page contains a lot of extra information for customization, you should definitely read it if you plan to use authpf.

=> https://man.openbsd.org/authpf OpenBSD man page of authpf(8)

## Blocking users

It's possible to ban users, for various reasons you may want to block someone with a message asking to reach the help desk.  This can be done by creating a file name after the username, like in the following example for user `chloe`: `/etc/authpf/banned/chloe`, the file text content will be displayed to the user upon connection.

## Greeeting message

It's possible to write a custom greeting message displayed upon connection, this can be global or per user, just write a message in `/etc/authpf/authpf.message` for a global one, or `/etc/authpf/users/chloe/authpf.message` for user `chloe`.
</pre>
    ]]>
  </description>
  <guid>gemini://perso.pw/blog//articles/openbsd-authpf.gmi</guid>
  <link>gemini://perso.pw/blog//articles/openbsd-authpf.gmi</link>
  <pubDate>Thu, 01 Dec 2022 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Automatic prompt to unlock remote encrypted partitions</title>
  <description>
    <![CDATA[
<pre># Introduction

I have remote systems that only have /home as encrypted partitions, the reason is it ease a lot of remote management without a serial access, it's not ideal if you have critical files but in my use case, it's good enough.

In this blog post, I'll explain how to get the remote system to prompt you the unlocking passphrase automatically when it boots.  I'm using OpenBSD in my example, but you can achieve the same with Linux and cryptsetup (LUKS), if you want to push the idea on Linux, you could do this from the initramfs to unlock your root partition.

# Requirement



# Setup

1. install the package `zenity` on your workstation
2. on the remote system generate ssh-keys without a passphrase on your root account using `ssh-keygen`
3. copy the content of `/root/.ssh/id_rsa.pub` for the next step (or the public key file if you chose a different key algorithm)
4. edit `~/.ssh/authorized_keys` on your workstation
5. create a new line with: `restrict,command="/usr/local/bin/zenity --forms --text='Unlock t400 /home' --add-password='passphrase' --display=:0" $THE_PUBLIC_KEY_HERE`

The new line allows the ssh key to connect to our local user, but it gets restricted to a single command: zenity, which is a GUI dialog program used to generate forms/dialogs in X sessions.

In the example, this creates a simple form in an X window with a label "Unlock t400 /home" and add a field password hiding typed text, and showing it on display :0 (the default one).  Upon connection from the remote server, the form is displayed, you can type in and validate, then the content is passed to stdout on the remote server, to the command bioctl which unlocks the disk.

On the server, creates the file `/etc/rc.local` with the following content (please adapt to your system):

!/bin/sh

ssh solene@10.42.42.102 | bioctl -s -c C -l 1a52f9ec20246135.k softraid0

if [ $? -eq 0 ]

then

mount /home

fi


In this script, `solene@10.42.42.102` is my user@laptop-address, and `1a52f9ec20246135.k` is my encrypted partition.  The file `/etc/rc.local` is run at boot after most of the services, including networking.

You should get a display like this when the system boots:

=> static/unlock.png a GUI window asking for a passphrase to unlock the /home partition of the computer named T400

# Conclusion

With this simple setup, I can reboot my remote systems and wait for the passphrase to be asked quite reliably.  Because of ssh, I can authenticate which system is asking for a passphrase, and it's sent encrypted over the network.

It's possible to get more in depth in this idea by using a local password database to automatically pick the passphrase, but you lose some kind of manual control, if someone steals a machine you may not want to unlock it after all ;)  It would also be possible to prompt a Yes/No dialog before piping the passphrase from your computer, do what feels correct for you.
</pre>
    ]]>
  </description>
  <guid>gemini://perso.pw/blog//articles/openbsd-remote-unlock.gmi</guid>
  <link>gemini://perso.pw/blog//articles/openbsd-remote-unlock.gmi</link>
  <pubDate>Sun, 20 Nov 2022 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Pinafore: a light Mastodon web client</title>
  <description>
    <![CDATA[
<pre># Intro

This blog post is for Mastodon users who may not like the official Mastodon web interface.  It has a lot of features, but it's using a lot of CPU and requires a large screen.

Fortunately, there are alternatives front-ends to Mastodon, this is possible through calls between the front-end to then instance API.  I would like to introduce you Pinafore.

=> https://github.com/nolanlawson/pinafore Pinafore GitHub client
=> https://pinafore.social/ Pinafore.social website

# What's Pinafore?

Pinafore is a "web application" consisting of a static website, this implies nothing is actually store on the server hosting Pinafore, think about it like a page loaded in your browser that stores data in your browser and make API calls from your browser.

This design is elegant because it delegates everything to the browser and requires absolutely no processing on the Pinafore hosting server, it's just a web server there serving static files once.

As I said previously, Pinafore is a Mastodon (but also extends to other Fediverse instances whenever possible) front-end with a bunch of features such as:

- accessibility (for content warning content, greyscale mode, contrast, key bindings)
- only one column, it's really compact
- simple design, fast to load and doesn't eat much CPU (especially compared to official Mastodon interface)
- read-only support if you visit your Pinafore host when not connected, I find this very useful (remember that cache is stored in your browser)
- can handle multiple accounts at once

This being said, Pinafore doesn't target minimalism either, it needs javascript and a modern web browser.

# How to use Pinafore?

There are two ways to use it, either by using the official hosted service, or by hosting it yourself.

Whether you choose the official or self-hosted, the principle is the following: you enter your account instance address in it the first time, this will trigger an oauth authentication on your instance and will ask if you want pinafore to use your account through the API (this can be revoked later from your Mastodon account).  Accept, and that's it!

## Pinafore.social

The official service is run by the developers and kept up to date.  You can use it without installing anything, simply visit the address below and go through the login process.

=> https://pinafore.social Pinafore.social website

This is a very convenient way to use pinafore, but it comes with a tradeoff: it involves a third party between your social network account and your client.  While pinafore.social is trustable, this doesn't mean it can't be compromised and act as a "Man In The Middle".  As I mentionned earlier, no data are stored by Pinafore because everything is in your browser, but nothing prevent a malicious attacker to modify the hosted Pinafore code to redirect data from your browser to a remote server they control in order to steal information.

## Self Hosting

It's possible to create Pinafore static files from your system and host it on any web server.  While it's more secure than pinafore.social (if your host is secure), it still involves extra code that could "potentially" be compromised through a rogue commit, but it's not realistic to encounter this case when using Pinafore releases versions.

For this step, I'll link to the according documentation in the project:

=> https://github.com/nolanlawson/pinafore#exporting Exporting Pinafore

# Trivia

Pinafore is the recommended web front-end for the Mastodon server implementation GoToSocial which only provide a backend.

=> https://github.com/superseriousbusiness/gotosocial/ GoToSocial GitHub project page
</pre>
    ]]>
  </description>
  <guid>gemini://perso.pw/blog//articles/mastodon-light-client.gmi</guid>
  <link>gemini://perso.pw/blog//articles/mastodon-light-client.gmi</link>
  <pubDate>Fri, 18 Nov 2022 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Hard user separation with two NixOS as one</title>
  <description>
    <![CDATA[
<pre># Credits

This blog post is a republication of the article I published on my employer's blog under CC BY 4.0.  I'm grateful to be allowed to publish NixOS related content there, but also to be able to reuse it here!

=> https://creativecommons.org/licenses/by/4.0/ License CC by 4.0

=> https://www.tweag.io/blog/2022-11-01-hard-user-separation-with-nixos/ Original publication place: Hard user separation with NixOS

# Introduction

This guide explains how to install NixOS on a computer, with a twist.

If you use the same computer in different contexts, let's say for work and for your private life, you may wish to install two different operating systems to protect your private life data from mistakes or hacks from your work.  For instance a cryptolocker you got from a compromised work email won't lock out your family photos.

But then you have two different operating systems to manage, and you may consider that it's not worth the effort and simply use the same operating system for your private life and for work, at the cost of the security you desired.

I offer you a third alternative, a single NixOS managing two securely separated contexts.  You choose your context at boot time, and you can configure both context from either of them.

You can safely use the same machine at work with your home directory and confidential documents, and you can get into your personal context with your private data by doing a reboot.  Compared to a dual boot system, you have the benefits of a single system to manage and no duplicated package.

For this guide, you need a system either physical or virtual that is supported by NixOS, and some knowledge like using a command line.  You don't necessarily need to understand all the commands.  The system disk will be erased during the process.

You can find an example of NixOS configuration files to help you understand the structure of the setup on the following GitHub repository:

=> https://github.com/tweag/nixos-specialisation-dual-boot tweag/nixos-specialisation-dual-boot GitHub repository

# Disks

Here is a diagram showing the whole setup and the partitioning.

=> static/setup-diagram.png Picture showing a diagram of disks and partitions

## Partitioning

We will create a 512 MB space for the /boot partition that will contain the kernels, and allocate the space left for an LVM partition we can split later.

parted /dev/sda -- mklabel gpt

parted /dev/sda -- mkpart ESP fat32 1MiB 512MiB

parted /dev/sda -- mkpart primary 512MiB 100%

parted /dev/sda -- mkpart set 1 esp on


Note that these instructions are valid for UEFI systems, for older systems you can refer to the NixOS manual to create a MBR partition.

=> https://nixos.org/manual/nixos/stable/index.html#sec-installation-partitioning NixOS manual: disks and partitioning.

## Create LVM volumes

We will use LVM so we need to initialize the partition and create a Volume Group with all the free space.

pvcreate /dev/sda2

vgcreate pool /dev/sda2


We will then create three logical volumes, one for the store and two for our environments:

lvcreate -L 15G -n root-private pool

lvcreate -L 15G -n root-work pool

lvcreate -l 100%FREE -n nix-store pool


NOTE: The sizes to assign to each volume is up to you, the nix store should have at least 30GB for a system with graphical sessions.  LVM allows you to keep free space in your volume group so you can increase your volumes size later when needed.

## Encryption

We will enable encryption for the three volumes, but we want the nix-store partition to be unlockable with either of the keys used for the two root partitions.  This way, you don't have to type two passphrases at boot.

cryptsetup luksFormat /dev/pool/root-work

cryptsetup luksFormat /dev/pool/root-private

cryptsetup luksFormat /dev/pool/nix-store # same password as work

cryptsetup luksAddKey /dev/pool/nix-store # same password as private


We unlock our partitions to be able to format and mount them.  Which passphrase is used to unlock the nix-store doesn't matter.

cryptsetup luksOpen /dev/pool/root-work crypto-work

cryptsetup luksOpen /dev/pool/root-private crypto-private

cryptsetup luksOpen /dev/pool/nix-store nix-store


Please note we don't encrypt the boot partition, which is the default on most encrypted Linux setup.  While this could be achieved, this adds complexity that I don't want to cover in this guide.

Note: the nix-store partition isn't called `crypto-nix-store` because we want the nix-store partition to be unlocked after the root partition to reuse the password.  The code generating the ramdisk takes the unlocked partitions' names in alphabetical order, by removing the prefix `crypto` the partition will always be after the root partitions.

## Formatting

We format each partition using ext4, a performant file-system which doesn't require maintenance.  You can use other filesystems, like xfs or btrfs, if you need features specific to them.

mkfs.ext4 /dev/mapper/crypto-work

mkfs.ext4 /dev/mapper/crypto-private

mkfs.ext4 /dev/mapper/nix-store


## The boot partition

The boot partition should be formatted using fat32 when using UEFI with `mkfs.fat -F 32 /dev/sda1`.  It can be formatted in ext4 if you are using legacy boot (MBR).

# Preparing the system

Mount the partitions onto `/mnt` and its subdirectories to prepare for the installer.

mount /dev/mapper/crypto-work /mnt

mkdir -p /mnt/etc/nixos /mnt/boot /mnt/nix

mount /dev/mapper/nix-store /mnt/nix

mkdir /mnt/nix/config

mount --bind /mnt/nix/config /mnt/etc/nixos

mount /dev/sda1 /mnt/boot


We generate a configuration file:

nixos-generate-config --root /mnt


Edit `/mnt/etc/nixos/hardware-configuration.nix` to change the following parts:

fileSystems."/" =

{ device = "/dev/disk/by-uuid/xxxxxxx-something";

fsType = "ext4";

};

boot.initrd.luks.devices."crypto-work" = "/dev/disk/by-uuid/xxxxxx-something";


by

fileSystems."/" =

{ device = "/dev/mapper/crypto-work";

fsType = "ext4";

};

boot.initrd.luks.devices."crypto-work" = "/dev/pool/root-work";


We need two configuration files to describe our two environments, we will use `hardware-configuration.nix` as a template and apply changes to it.

sed '/imports =/,+3d' /mnt/etc/nixos/hardware-configuration.nix > /mnt/etc/nixos/work.nix

sed '/imports =/,+3d ; s/-work/-private/g' /mnt/etc/nixos/hardware-configuration.nix > /mnt/etc/nixos/private.nix

rm /mnt/etc/nixos/hardware-configuration.nix


Edit `/mnt/etc/nixos/configuration.nix` to make the `imports` code at the top of the file look like this:

imports =

[

./work.nix

./private.nix

];


Remember we removed the file `/mnt/etc/nixos/hardware-configuration.nix` so it shouldn't be imported anymore.

Now we need to hook each configuration to become a different boot entry, using the NixOS feature called specialisation.  We will make the environment you want to be the default in the boot entry as a non-specialised environment and non-inherited so it's not picked up by the other, and a specialisation for the other environment.

For the hardware configuration files, we need to wrap them with some code to create a specialisation, and the "non-specialisation" case that won't propagate to the other specialisations.

Starting from a file looking like this, some code must be added at the top and bottom of the files depending on if you want it to be the default context or not.

Content of an example file:

{ config, pkgs, modulesPath, ... }:

{

boot.initrd.availableKernelModules = ["ata_generic" "uhci_hcd" "ehci_pci" "ahci" "usb_storage" "sd_mod"];

boot.initrd.kernelModules = ["dm-snapshot"];

boot.kernelModules = ["kvm-intel"];

boot.extraModulePackages = [];

fileSystems."/" = {

device = "/dev/mapper/crypto-private";

fsType = "ext4";

};

---8<-----

[more code here]

---8<-----

swapDevices = [];

networking.useDHCP = lib.mkDefault true;

hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;

}


Example result of the default context:

=> https://github.com/tweag/nixos-specialisation-dual-boot/blob/master/configuration/private.nix GitHub example ifle

({ lib, config, pkgs, ...}: {

config = lib.mkIf (config.specialisation != {}) {

boot.initrd.availableKernelModules = ["ata_generic" "uhci_hcd" "ehci_pci" "ahci" "usb_storage" "sd_mod"];

boot.initrd.kernelModules = ["dm-snapshot"];

boot.kernelModules = ["kvm-intel"];

boot.extraModulePackages = [];

fileSystems."/" = {

device = "/dev/mapper/crypto-private";

fsType = "ext4";

};

---8<-----

[more code here]

---8<-----

swapDevices = [];

networking.useDHCP = lib.mkDefault true;

hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;

};

})


Note the extra leading `(` character that must also be added at the very beginning.

Example result for a specialisation named `work`

=> https://github.com/tweag/nixos-specialisation-dual-boot/blob/master/configuration/work.nix GitHub example file

{ config, lib, pkgs, modulesPath, ... }:

{

specialisation = {

work.configuration = {

system.nixos.tags = [ "work" ];

boot.initrd.availableKernelModules = ["ata_generic" "uhci_hcd" "ehci_pci" "ahci" "usb_storage" "sd_mod"];

boot.initrd.kernelModules = ["dm-snapshot"];

boot.kernelModules = ["kvm-intel"];

boot.extraModulePackages = [];

fileSystems."/" = {

device = "/dev/mapper/crypto-work";

fsType = "ext4";

};

---8<-----

[more code here]

---8<-----

swapDevices = [];

networking.useDHCP = lib.mkDefault true;

hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;

};

};

}


# System configuration

It's now the time to configure your system as you want.  The file `/mnt/etc/nixos/configuration.nix` contains shared configuration, this is the right place to define your user, shared packages, network and services.

The files `/mnt/etc/nixos/private.nix` and `/mnt/etc/nixos/work.nix` can be used to define context specific configuration.

## LVM Workaround

During the numerous installation tests I've made to validate this guide, on some hardware I noticed an issue with LVM detection, add this line to your global configuration file to be sure your disks will be detected at boot.

boot.initrd.preLVMCommands = "lvm vgchange -ay";


# Installation

## First installation

The partitions are mounted and you configured your system as you want it, we can run the NixOS installer.

nixos-install


Wait for the copy process to complete after which you will be prompted for the root password of the current crypto-work environment (or the one you mounted here), you also need to define the password for your user now by chrooting into your NixOS system.

nixos-enter --root /mnt -c "passwd your_user"

New password:

Retape new password:

passwd: password updated successfully

umount -R /mnt


From now, you have a password set for root and your user for the crypto-work environment, but no password are defined in the crypto-private environment.

## Second installation

We will rerun the installation process with the other environment mounted:

mount /dev/mapper/crypto-private /mnt

mkdir -p /mnt/etc/nixos /mnt/boot /mnt/nix

mount /dev/mapper/nix-store /mnt/nix

mount --bind /mnt/nix/config /mnt/etc/nixos

mount /dev/sda1 /mnt/boot


As the NixOS configuration is already done and is shared between the two environments, just run `nixos-install`, wait for the root password to be prompted, apply the same chroot sequence to set a password to your user in this environment.

You can reboot, you will have a default boot entry for the default chosen environment, and the other environment boot entry, both requiring their own passphrase to be used.

Now, you can apply changes to your NixOS system using `nixos-rebuild` from both work and private environments.

# Conclusion

Congratulations for going through this long installation process.  You can now log in to your two contexts and use them independently, and you can configure them by applying changes to the corresponding files in `/etc/nixos/`.

# Going further

## Swap and hibernation

With this setup, I chose to not cover swap space because this would allow to leak secrets between the contexts.  If you need some swap, you will have to create a file on the root partition of your current context, and add the according code to the context filesystems.

If you want to use hibernation in which the system stops after dumping its memory into the swap file, your swap size must be larger than the memory available on the system.

It's possible to have a single swap for both contexts by using a random encryption at boot for the swap space, but this breaks hibernation as you can't unlock the swap to resume the system.

## Declare users' passwords

As you noticed, you had to run `passwd` in both contexts to define your user password and root's password.  It is possible to define their password declaratively in the configuration file, refers to the documentation of`users.mutableUsers` and `users.extraUsers.<name>.initialHashedPassword`
for more information.

## Rescue the installation

If something is wrong when you boot the first time, you can reuse the installer to make changes to your installation: you can run again the `cryptsetup luksOpen` and `mount` commands to get access to your filesystems, then you can edit your configuration files and run
`nixos-install` again.
</pre>
    ]]>
  </description>
  <guid>gemini://perso.pw/blog//articles/two-nixos-as-one.gmi</guid>
  <link>gemini://perso.pw/blog//articles/two-nixos-as-one.gmi</link>
  <pubDate>Thu, 17 Nov 2022 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Mirroring sources used in nixpkgs (software preservation)</title>
  <description>
    <![CDATA[
<pre># Introduction

This may appear like a very niche use case, in my quest of software conservancy for nixpkgs I didn't encounter many people understanding why I was doing this.

I would like to present you a project I made to easily download all the sources files required to build packages from nixpkgs, allowing to keep offline copies.

Why would you like to keep a local copy?  If upstream disappear, you can't get access to the sources anymore, except maybe in Hydra, but you rely on a third party to access the sources, so it's still valuable to have local copies of software you care about, just to make copies.  It's not that absolutely useful for everyone, but it's always important to have such tools available.

=> https://tildegit.org/solene/nixpkgs-mirror-tarballs nixpkgs-mirror-tarballs project page

# How to use it

You must run it on a system with `nix` installed.

After cloning and 'cd-ing' into the directory, simply run `./run.sh some package list | ./mirror.pl`.  The command `run.sh` will generate a JSON structure containing all the dependencies used by the packages listed as arguments, and the script `mirror.pl` will iterate over the JSON list and use nix's fetcher to gather the sources in the nix store, verifying the checksum on the go.  This will create a directory `distfiles` containing symlinks to the sources files stored in the store.

The symlinks are very important as they will prevent garbage collection from the store, and it's also used internally to quickly check if a file is already in the store.

To delete a file from the store, remove its symlink and run the garbage collector.

# Limitation

I still need to figure how to get a full list of all the packages, I currently have a work in progress relying on `nix search --json` but it doesn't work on 100% of the packages for some reasons.

It's currently not possible to easily trim distfiles that aren't useful anymore, I plan to maybe add it someday.

# Trivia

This task is natively supported in the OpenBSD tool building packages (dpb), it can fetch multiples files in parallel and automatic remove files that aren't used anymore.  This was really complicated to figure how to replicate this with nixpkgs.
</pre>
    ]]>
  </description>
  <guid>gemini://perso.pw/blog//articles/nixpkgs-fetch-all-sources.gmi</guid>
  <link>gemini://perso.pw/blog//articles/nixpkgs-fetch-all-sources.gmi</link>
  <pubDate>Thu, 03 Nov 2022 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Nushell: Introduction to a new kind of shell</title>
  <description>
    <![CDATA[
<pre># What is nushell

Let me introduce you to a nice project I found while lurking on the Internet.  It's called nushell and is a non-POSIX shell, so most of your regular shells knowledge (zsh, bash, ksh, etcโ€ฆ) can't be applied on it, and using it feels like doing functional programming.
It's a good tool for creating robust data manipulation pipelines, you can think of it like a mix of a shell which would include awk's power, behave like a SQL database, and which knows how to import/export XML/JSON/YAML/TOML natively.

You may want to try nushell only as a tool, and not as your main shell, it's perfectly fine.

With a regular shell, iterating over a command output can be complex when it involves spaces or newlines, for instance, that's why `find` and `xargs` have a `-print0` parameter to have a special delimited between "items", but it doesn't compose well with other tools.  Nushell handles correctly this situation as its manipulates the data using indexed entries, given you correctly parsed the input at the beginning.

=> https://www.nushell.sh/ Nushell official project page
=> https://www.nushell.sh/book/ Nushell documentation website

# How to get it

Nushell is a rust program, so it should work on every platform where Rust/Cargo are supported.  I packaged it for OpenBSD, so it's available on -current (and will be in releases after 7.3 is out), the port could be used on 7.2 with no effort.
With Nix, it's packaged under the name `nushell`, the binary name is `nu`.

For other platforms, it's certainly already packaged, otherwise you can find installation instructions to build it from sources.

=> https://www.nushell.sh/book/installation.html#build-from-source Nushell documentation: Building nushell from sources

# Configuration

At first run, you are prompted to use default configuration files, I'd recommend accepting, you will have files created in `~/.config/nushell/`.

The only change I made from now is to make Tab completion case-sensitive, so `D[TAB]` completes to `Downloads` instead of asking between `dev` and `Downloads`. Look for `case_sensitive_completions` in `.config/nushell/config.nu` and set it to `true`.

# Examples

If you are like me, and you prefer learning by doing instead of reading a lot of documentation, I prepared a bunch of real world use case you can experiment with.  The documentation is still required to learn the many commands and syntax, but examples are a nice introduction.

## Getting help

Help from nushell can be parsed directly with nu commands, it's important to understand where to find information about commands.

Use `help a-command` to learn from a single command:

help help

Display help information about commands.

Usage:

> help {flags} ...(rest)

Flags:

-h, --help - Display this help message

-f, --find <String> - string to find in command names, usage, and search terms

[cut so it's not too long]


Use `help commands` to list all available commands (I'm limiting to 5 between there are a lot of commands)

help commands | last 5

โ•ญโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ

โ”‚ # โ”‚ name โ”‚ category โ”‚ is_plugin โ”‚ is_custom โ”‚ is_keyword โ”‚ usage โ”‚ search_terms โ”‚

โ”œโ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ 0 โ”‚ window โ”‚ filters โ”‚ false โ”‚ false โ”‚ false โ”‚ Creates a sliding window of `window_size` that slide by n rows/elements across input. โ”‚ โ”‚

โ”‚ 1 โ”‚ with-column โ”‚ dataframe or lazyframe โ”‚ false โ”‚ false โ”‚ false โ”‚ Adds a series to the dataframe โ”‚ โ”‚

โ”‚ 2 โ”‚ with-env โ”‚ env โ”‚ false โ”‚ false โ”‚ false โ”‚ Runs a block with an environment variable set. โ”‚ โ”‚

โ”‚ 3 โ”‚ wrap โ”‚ filters โ”‚ false โ”‚ false โ”‚ false โ”‚ Wrap the value into a column. โ”‚ โ”‚

โ”‚ 4 โ”‚ zip โ”‚ filters โ”‚ false โ”‚ false โ”‚ false โ”‚ Combine a stream with the input โ”‚ โ”‚

โ•ฐโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ


Add `sort-by category` to list them... sorted by category.

help commands | sort-by category


Use `where category == filters` to only list commands from the `filters` category.

help commands | where category == filters


Use `find foobar` to return lines containing `foobar`.

help commands | find insert


## General examples

### Converting a data structure into another

This is just an example from YAML to JSON, but you can convert much more formats into other formats.

open dev/home-impermanence/tests/impermanence.yml | to json

{

"directories":

[

"Documents",

"Downloads",

"Datastore/Music",

"Datastore",

"Datastore/",

"Datastore/Music/Band1",

".config",

"foo/bar",

"foo/bar/hello"

],

"size": "500m",

"files":

[

".Xdefaults",

".profile",

".xsession",

]

}


### Parsing sysctl output

sysctl -a | parse -r "(?<key>.*?)=(?<value>.*)"


Because the output would be too long, here is how you get 10 random keys from sysctl.

sysctl -a | parse -r "(?<key>.*?)=(?<value>.*)" | shuffle | last 10 | sort-by key

โ•ญโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ

โ”‚ # โ”‚ key โ”‚ value โ”‚

โ”œโ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ 0 โ”‚ fs.quota.reads โ”‚ 0 โ”‚

โ”‚ 1 โ”‚ net.core.high_order_alloc_disable โ”‚ 0 โ”‚

โ”‚ 2 โ”‚ net.ipv4.conf.all.drop_gratuitous_arp โ”‚ 0 โ”‚

โ”‚ 3 โ”‚ net.ipv4.conf.default.rp_filter โ”‚ 2 โ”‚

โ”‚ 4 โ”‚ net.ipv4.conf.lo.disable_xfrm โ”‚ 1 โ”‚

โ”‚ 5 โ”‚ net.ipv4.conf.lo.forwarding โ”‚ 0 โ”‚

โ”‚ 6 โ”‚ net.ipv4.ipfrag_low_thresh โ”‚ 3145728 โ”‚

โ”‚ 7 โ”‚ net.ipv6.conf.all.ioam6_id โ”‚ 65535 โ”‚

โ”‚ 8 โ”‚ net.ipv6.conf.all.router_solicitation_interval โ”‚ 4 โ”‚

โ”‚ 9 โ”‚ net.mptcp.enabled โ”‚ 1 โ”‚

โ•ฐโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ


### Recursively convert FLAC files to OPUS

A complicated task using a regular shell, recursively find files matching a pattern and then run a given command on each of them, in parallel.  Which is exactly what you need if you want to convert your music library into another format, let's convert everything from FLAC to OPUS in this example.

In the following command line, we will look for every `.flac` file in the subdirectories, then run in parallel using `par-each` the command `ffmpeg` on it, from its current name to the old name with `.flac` changed to `.opus`.
The `let convert` and `| complete` commands are used to store the output of each command into a result table, and store it in the variable `convert` so we can query it after the job is done.

let convert = (ls **/*flac | par-each { |file| do -i { ffmpeg -i $file.name ($file.name | str replace flac opus) } | complete })


Now, we have a structure in `convert` that contains the columns `stdout`, `stderr` and `exit_code`, so we can look if all the commands did run correctly using the following query.

$convert | where exit_code != 0


### Convert PDF/CBR/CBZ pages into webp and CBZ archives

I have a lot of digitalized books/mangas/comics, this conversion is a handy operation reducing the size of the files by 40% (up to 70%).

def conv [] {

if (ls | first | get name | str contains ".jpg") {

ls *jpg | par-each {|file| do -i { cwebp $file.name -o ($file.name | str replace jpg webp) } | complete }

rm *jpg

}

if (ls | first | get name | str contains ".ppm") {

ls *ppm | par-each {|file| do -i { cwebp $file.name -o ($file.name | str replace ppm webp) } | complete }

rm *ppm

}

}

ls * | each {|file| do -i {

if ($file.name | str contains ".cbz") { unzip $file.name -d ./pages/ } ;

if ($file.name | str contains ".cbr") { unrar e -op./pages/ $file.name } ;

if ($file.name | str contains ".pdf") { mkdir pages ; pdfimages $file.name pages/page } ;

cd pages ; conv ; cd ../ ; ^zip -r $"($file.name).webp.cbz" pages ; rm -fr pages

} }


### Parse gnu tar output

ใ€‰tar vtf nushell.tgz | parse -r "(.*?) (.*?)\/(.*?)\\s+(.*?) (.*?) (.*?) (.*)" | rename mode owner group size date time path

โ•ญโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ

โ”‚ # โ”‚ mode โ”‚ owner โ”‚ group โ”‚ size โ”‚ date โ”‚ time โ”‚ path โ”‚

โ”œโ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ 0 โ”‚ drwxr-xr-x โ”‚ solene โ”‚ wheel โ”‚ 0 โ”‚ 2022-10-30 โ”‚ 16:45 โ”‚ nushell โ”‚

โ”‚ 1 โ”‚ -rw-r--r-- โ”‚ solene โ”‚ wheel โ”‚ 519 โ”‚ 2022-10-30 โ”‚ 13:41 โ”‚ nushell/Makefile โ”‚

โ”‚ 2 โ”‚ -rw-r--r-- โ”‚ solene โ”‚ wheel โ”‚ 29304 โ”‚ 2022-10-29 โ”‚ 18:49 โ”‚ nushell/crates.inc โ”‚

โ”‚ 3 โ”‚ -rw-r--r-- โ”‚ solene โ”‚ wheel โ”‚ 75003 โ”‚ 2022-10-29 โ”‚ 13:16 โ”‚ nushell/distinfo โ”‚

โ”‚ 4 โ”‚ drwxr-xr-x โ”‚ solene โ”‚ wheel โ”‚ 0 โ”‚ 2022-10-30 โ”‚ 00:00 โ”‚ nushell/pkg โ”‚

โ”‚ 5 โ”‚ -rw-r--r-- โ”‚ solene โ”‚ wheel โ”‚ 337 โ”‚ 2022-10-29 โ”‚ 18:52 โ”‚ nushell/pkg/DESCR โ”‚

โ”‚ 6 โ”‚ -rw-r--r-- โ”‚ solene โ”‚ wheel โ”‚ 14 โ”‚ 2022-10-29 โ”‚ 18:53 โ”‚ nushell/pkg/PLIST โ”‚

โ•ฐโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ


### Opening spreadsheets

ใ€‰open --raw freq.ods | from ods | get Sheet1 | headers

โ•ญโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ

โ”‚ # โ”‚ Policy โ”‚ Compile time โ”‚ Idle time โ”‚ column3 โ”‚ Compile power โ”‚ Idle power โ”‚ Total โ”‚ column8 โ”‚ column9 โ”‚ column10 โ”‚

โ”œโ”€โ”€โ”€๏ฟฝ๏ฟฝ๏ฟฝโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ 0 โ”‚ powersaving โ”‚ 1123.00 โ”‚ 0.00 โ”‚ โ”‚ 5.90 โ”‚ 0.00 โ”‚ 5.90 โ”‚ โ”‚ โ”‚ โ”‚

โ”‚ 1 โ”‚ auto โ”‚ 871.00 โ”‚ 252.00 โ”‚ โ”‚ 5.60 โ”‚ 0.74 โ”‚ 6.34 โ”‚ โ”‚ 0.44 โ”‚ 6.94 โ”‚

โ•ฐโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ


We can format new strings from columns values.

ใ€‰open --raw freq.ods | from ods | get Sheet1 | headers | each {|row| do { echo $"($row.Policy) = ($row.'Compile power' + $row.'Idle power') Watts" } }

โ•ญโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ

โ”‚ 0 โ”‚ powersaving = 5.9 Watts โ”‚

โ”‚ 1 โ”‚ auto = 6.34 Watts โ”‚

โ•ฐโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ


### Filter and sort a JSON

There is a website listing packages that can be updated on OpenBSD at https://portroach.openbsd.org, it provides json of data for rendering.

We can use this data to sort which maintainer has the most up to date percentage, but only if they manage more than 30 packages.

fetch https://portroach.openbsd.org/json/totals.json | get results | where total > 30 | sort-by percentage


## NixOS examples

### Query profiles packages

nix profile list | parse "{index} {flake} {source} {store}"

โ•ญโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ

โ”‚ # โ”‚ flake โ”‚ source โ”‚ store โ”‚

โ”œโ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ 0 โ”‚ flake:nixpkgs#legacyPackages.x86_64-linux.libreoffice โ”‚ path:/nix/store/iw3xi0bfszikb0dmyywp7pm590jvbqvs-source?lastModified=1663494472& โ”‚ /nix/store/1m6wp1pznhf2nrvs7xwmvig5x3nspq0j-libreoffice-7.2.6.2 โ”‚

โ”‚ โ”‚ โ”‚ narHash=sha256-fSowlaoXXWcAM8m9wA6u+eTJJtvruYHMA+Lb%2ftFi%2fqM=&rev=f677051b8dc0 โ”‚ โ”‚

โ”‚ โ”‚ โ”‚ b5e2a9348941c99eea8c4b0ff28f#legacyPackages.x86_64-linux.libreoffice โ”‚ โ”‚

โ”‚ 1 โ”‚ flake:nixpkgs#legacyPackages.x86_64-linux.dino โ”‚ path:/nix/store/9cj1830pvd88lrwmmxw65achd3lw2q9n-source?lastModified=1667050928& โ”‚ /nix/store/ljhn4n1q5pk7wr337v681m1h39jp5l2y-dino-0.3.0 โ”‚

โ”‚ โ”‚ โ”‚ narHash=sha256-xOn0ZgjImIyeecEsrjxuvlW7IW5genTwvvnDQRFncB8=&rev=fdebb81f45a1ba2c โ”‚ โ”‚

โ”‚ โ”‚ โ”‚ 4afca5fd9f526e1653ad0949#legacyPackages.x86_64-linux.dino โ”‚ โ”‚

โ•ฐโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ


### Query flakes

nix flake show --json | from json

โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ

โ”‚ defaultPackage โ”‚ {record 5 fields} โ”‚

โ”‚ packages โ”‚ {record 5 fields} โ”‚

โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

nix flake show --json | from json | get packages

โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ

โ”‚ aarch64-darwin โ”‚ {record 2 fields} โ”‚

โ”‚ aarch64-linux โ”‚ {record 2 fields} โ”‚

โ”‚ i686-linux โ”‚ {record 2 fields} โ”‚

โ”‚ x86_64-darwin โ”‚ {record 2 fields} โ”‚

โ”‚ x86_64-linux โ”‚ {record 2 fields} โ”‚

โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

nix flake show --json | from json | get packages.x86_64-linux

โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ

โ”‚ nix-dev-html โ”‚ {record 2 fields} โ”‚

โ”‚ nix-dev-pyenv โ”‚ {record 3 fields} โ”‚

โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ


### Parse a flake.lock file

open flake.lock | from json | get nodes.nixpkgs.locked

โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ

โ”‚ lastModified โ”‚ 1663494472 โ”‚

โ”‚ narHash โ”‚ sha256-fSowlaoXXWcAM8m9wA6u+eTJJtvruYHMA+Lb/tFi/qM= โ”‚

โ”‚ path โ”‚ /nix/store/iw3xi0bfszikb0dmyywp7pm590jvbqvs-source โ”‚

โ”‚ rev โ”‚ f677051b8dc0b5e2a9348941c99eea8c4b0ff28f โ”‚

โ”‚ type โ”‚ path โ”‚

โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ


## OpenBSD examples

### Parse /etc/fstab

open /etc/fstab | from ssv -m 1 -n | rename device mountpoint fs options freq passno

_โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€_

โ”‚ # โ”‚ device โ”‚ mountpoint โ”‚ fs โ”‚ options โ”‚ freq โ”‚ passno โ”‚

โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ 0 โ”‚ 55a6c21017f858cb.b โ”‚ none โ”‚ swap โ”‚ sw โ”‚ __ โ”‚ __ โ”‚

โ”‚ 1 โ”‚ 55a6c21017f858cb.a โ”‚ / โ”‚ ffs โ”‚ rw,noatime,softdep โ”‚ 1 โ”‚ 1 โ”‚

โ”‚ 2 โ”‚ 55a6c21017f858cb.l โ”‚ /home โ”‚ ffs โ”‚ rw,noatime,wxallowed,softdep,nodev,nosuid โ”‚ 1 โ”‚ 2 โ”‚

โ”‚ 3 โ”‚ 55a6c21017f858cb.d โ”‚ /tmp โ”‚ ffs โ”‚ rw,noatime,softdep,nodev,nosuid โ”‚ 1 โ”‚ 2 โ”‚

โ”‚ 4 โ”‚ 55a6c21017f858cb.f โ”‚ /usr โ”‚ ffs โ”‚ rw,noatime,softdep,nodev โ”‚ 1 โ”‚ 2 โ”‚

โ”‚ 5 โ”‚ 55a6c21017f858cb.g โ”‚ /usr/X11R6 โ”‚ ffs โ”‚ rw,noatime,softdep,nodev โ”‚ 1 โ”‚ 2 โ”‚

โ”‚ 6 โ”‚ 55a6c21017f858cb.h โ”‚ /usr/local โ”‚ ffs โ”‚ rw,noatime,softdep,wxallowed,nodev โ”‚ 1 โ”‚ 2 โ”‚

โ”‚ 7 โ”‚ 55a6c21017f858cb.k โ”‚ /usr/obj โ”‚ ffs โ”‚ rw,noatime,softdep,nodev,nosuid โ”‚ 1 โ”‚ 2 โ”‚

โ”‚ 8 โ”‚ 55a6c21017f858cb.j โ”‚ /usr/src โ”‚ ffs โ”‚ rw,noatime,softdep,nodev,nosuid โ”‚ 1 โ”‚ 2 โ”‚

โ”‚ 9 โ”‚ 55a6c21017f858cb.e โ”‚ /var โ”‚ ffs โ”‚ rw,noatime,softdep,nodev,nosuid โ”‚ 1 โ”‚ 2 โ”‚

โ”‚ 10 โ”‚ afebb2a83a449265.b โ”‚ /build โ”‚ ffs โ”‚ rw,noatime,softdep,wxallowed,nosuid โ”‚ 1 โ”‚ 2 โ”‚

โ”‚ 11 โ”‚ afebb2a83a449265.a โ”‚ /build/pobj โ”‚ ffs โ”‚ rw,noatime,softdep,nodev,wxallowed,nosuid โ”‚ 1 โ”‚ 2 โ”‚

โ”‚ 12 โ”‚ 55a6c21017f858cb.b โ”‚ /build/pobj_mfs โ”‚ mfs โ”‚ -s1G,wxallowed,noatime,rw โ”‚ 0 โ”‚ 0 โ”‚

โ•ฐโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€_


### Parse /var/log/messages

open /var/log/messages | parse -r "(?<date>\\w+ \\d+ \\d+:\\d+:\\d+) (?<hostname>\\w+) (?<program>\\w+)\\[?(?<pid>\\d+)?\\]?: (?<message>.*)"

โ•ญโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ

โ”‚ # โ”‚ date โ”‚ hostname โ”‚ program โ”‚ pid โ”‚ message โ”‚

โ”œโ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ 0 โ”‚ Oct 31 10:27:32 โ”‚ fx6 โ”‚ collectd โ”‚ 55258 โ”‚ uc_update: Value too old: name = fx6openbsd/swap/swap-free; value time = 1667208452.108; last cache update = 1667208452.108; โ”‚

โ”‚ 1 โ”‚ Oct 31 10:43:02 โ”‚ fx6 โ”‚ collectd โ”‚ 55258 โ”‚ uc_update: Value too old: name = fx6openbsd/swap/percent-free; value time = 1667209382.102; last cache update = 1667209382.102; โ”‚

โ”‚ 2 โ”‚ Oct 31 11:00:01 โ”‚ fx6 โ”‚ syslogd โ”‚ 4629 โ”‚ restart โ”‚

โ”‚ 3 โ”‚ Oct 31 11:05:26 โ”‚ fx6 โ”‚ pkg_delete โ”‚ โ”‚ Removed helix-22.08.1 โ”‚

โ”‚ 4 โ”‚ Oct 31 11:05:29 โ”‚ fx6 โ”‚ pkg_add โ”‚ โ”‚ Added helix-22.08.1 โ”‚

โ”‚ 5 โ”‚ Oct 31 11:16:49 โ”‚ fx6 โ”‚ pkg_add โ”‚ โ”‚ Added llvm-13.0.0p3 โ”‚

โ”‚ 6 โ”‚ Oct 31 11:20:18 โ”‚ fx6 โ”‚ pkg_add โ”‚ โ”‚ Added clang-tools-extra-13.0.0p2 โ”‚

โ”‚ 7 โ”‚ Oct 31 11:20:32 โ”‚ fx6 โ”‚ pkg_add โ”‚ โ”‚ Added bash-5.2.2 โ”‚

โ”‚ 8 โ”‚ Oct 31 11:20:34 โ”‚ fx6 โ”‚ pkg_add โ”‚ โ”‚ Added fzf-0.34.0 โ”‚

โ”‚ 9 โ”‚ Oct 31 11:21:01 โ”‚ fx6 โ”‚ pkg_delete โ”‚ โ”‚ Removed fzf-0.34.0 โ”‚

โ•ฐโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ


### Parse pkg_info output

pkg_info | str trim | parse -r "(?<package>.*?)-(?<version>[a-zA-Z0-9\\.]*?) (?<description>.*)" | str trim description

โ•ญโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ

โ”‚ # โ”‚ package โ”‚ version โ”‚ description โ”‚

โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ 0 โ”‚ athn-firmware โ”‚ 1.1p4 โ”‚ firmware binary images for athn(4) driver โ”‚

โ”‚ 1 โ”‚ collectd โ”‚ 5.12.0 โ”‚ system metrics collection engine โ”‚

โ”‚ 2 โ”‚ curl โ”‚ 7.85.0 โ”‚ transfer files with FTP, HTTP, HTTPS, etc. โ”‚

โ”‚ 3 โ”‚ gettext-runtime โ”‚ 0.21p1 โ”‚ GNU gettext runtime libraries and programs โ”‚

โ”‚ 4 โ”‚ intel-firmware โ”‚ 20220809v0 โ”‚ microcode update binaries for Intel CPUs โ”‚

โ”‚ 5 โ”‚ inteldrm-firmware โ”‚ 20220913 โ”‚ firmware binary images for inteldrm(4) driver โ”‚

โ”‚ 6 โ”‚ kakoune โ”‚ 2021.11.08 โ”‚ modal code editor with a focus on interactivity โ”‚

โ”‚ 7 โ”‚ libgcrypt โ”‚ 1.10.1p0 โ”‚ crypto library based on code used in GnuPG โ”‚

โ”‚ 8 โ”‚ libgpg-error โ”‚ 1.46 โ”‚ error codes for GnuPG related software โ”‚

โ”‚ 9 โ”‚ libiconv โ”‚ 1.17 โ”‚ character set conversion library โ”‚

โ”‚ 10 โ”‚ libstatgrab โ”‚ 0.91p5 โ”‚ system statistics gathering library โ”‚

โ”‚ 11 โ”‚ libxml โ”‚ 2.10.3 โ”‚ XML parsing library โ”‚

โ”‚ 12 โ”‚ libyajl โ”‚ 2.1.0 โ”‚ small JSON library written in ANSI C โ”‚

โ”‚ 13 โ”‚ nghttp2 โ”‚ 1.50.0 โ”‚ library for HTTP/2 โ”‚

โ”‚ 14 โ”‚ nushell โ”‚ 0.70.0 โ”‚ a new kind of shell โ”‚

โ”‚ 15 โ”‚ obsdfreqd โ”‚ 1.0.3 โ”‚ userland daemon to manage CPU frequency โ”‚

โ”‚ 16 โ”‚ quirks โ”‚ 6.42 โ”‚ exceptions to pkg_add rules and cache โ”‚

โ”‚ 17 โ”‚ rsync โ”‚ 3.2.5pl0 โ”‚ mirroring/synchronization over low bandwidth links โ”‚

โ”‚ 18 โ”‚ ttyplot โ”‚ 1.4p0 โ”‚ realtime plotting utility for terminals โ”‚

โ”‚ 19 โ”‚ vmm-firmware โ”‚ 1.14.0p0 โ”‚ firmware binary images for vmm(4) driver โ”‚

โ”‚ 20 โ”‚ xz โ”‚ 5.2.7 โ”‚ LZMA compression and decompression tools โ”‚

โ”‚ 21 โ”‚ yash โ”‚ 2.52 โ”‚ POSIX-compliant command line shell โ”‚

โ•ฐโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ


# Conclusion

Nushell is very fun, it's terribly different from regular shells, but it comes with a powerful language and tooling.  I always liked shells because of pipes commands, allowing to construct a complex transformation/analysis step by step, and easily inspect any step, or be able to replace a step by another.
With nushell, it feels like I finally have a better tool to create more reliable, robust, portable and faster command pipelines.  The learning curve didn't feel too hard, but maybe it's because I'm already used to functional programming.
</pre>
    ]]>
  </description>
  <guid>gemini://perso.pw/blog//articles/nushell.gmi</guid>
  <link>gemini://perso.pw/blog//articles/nushell.gmi</link>
  <pubDate>Mon, 31 Oct 2022 00:00:00 GMT</pubDate>
</item>

  </channel>
</rss>