Maximizing the *cartesian product* of $n$ elements and how it relates to number bases

I was helping a friend in finding all possible combinations of schedules for a given set of courses, and it varied wildly the number of schedules each course had, most had 2 or 3, but others had more than 15 schedules, and when selecting the larger ones, the number of possible combinations blew up, and it was taking a looong time to compute.

A Ridiculous Question

So then, for fun, I started to think that those were $15 $ in each of the $7 $, which is rou

...
Read more

I was helping a friend in finding all possible combinations of schedules for a given set of courses, and it varied wildly the number of schedules each course had, most had 2 or 3, but others had more than 15 schedules, and when selecting the larger ones, the number of possible combinations blew up, and it was taking a looong time to compute.

A Ridiculous Question

So then, for fun, I started to think that those were  ∼ 15 schedules in each of the 7 courses, which is roughly 15 × 7 = 105 schedules in total. So I asked myself, given a fixed number of schedules, How many courses should I distribute those 105 schedules so that it maximizes the number of possible combinations?

Just to be clear, changing schedules from one course to another does not make sense at all. This is just a purely algorithmic / mathematical question.

Hypothesis

I have thought in the past about common cases of permutations and combinations, like for example, in 10 coin flips the result that has more combinations is 5 heads and 5 tails, hence the more likely result.
So I thought “Hmm!, this looks familiar, I bet we can get the most combinations by distributing things evenly”, so my first guess was to distribute schedules in a way that the number of courses were the same as the average number of schedules in the courses, … roughly.

But that’s just a wild guess!, I want to know the real answer!

Testing

So naturally, I started testing my hypothesis. To make it simple, I started with 10 items. Let’s test different ways of distributing the items

>>> 5*5
25
>>> 4*4*2
32
>>> 3*3*3*1
27
>>> 2*2*2*2*2
32
>>> 3*3*4
36

So 3 × 3 × 4 is the winner, 3 groups of  ∼ 3.333 items, nice! my guess looks strong, both numbers are very close. But let’s test it with more items, let’s try 20 items.

>>> 5*5*5*5
625
>>> 4*4*4*4*4
1024
>>> 3*3*3*3*3*3*2
1458
>>> 2*2*2*2*2*2*2*2*2*2
1024

Uh oh!, my guess is not doing well. The winner is now 3 × 3 × 3 × 3 × 3 × 3 × 2 which is 7 groups of  ∼ 2.833 items, both numbers are now very different. And the average number of items, in both tests, are suspiciously close to 3.

But Hang on!, the groups of 4 and 2 give the same combinations in both cases, is there a connection here?.
Yes there is! If we look closely, 4 × 4 × 4 × 4 × 4 and 2 × 2 × 2 × 2 × 2 × 2 × 2 × 2 × 2 × 2 are essentially the same thing. In fact, we can always replace 4 with 2 × 2, because they are equivalent in both calculating the combinations (4 = 2 × 2) and preserving the number of items (4 = 2 + 2).

Ok, so if 2 and 4, by logic are always the same regardless of the number of items, then 3 does indeed seem to be the sweet spot. It’s interesting that I’ve never come across this fact.

So what does this have to do with number bases?

Well, I wanted to test bigger numbers, but I got lazy, so I started to look for patterns, and I noticed that these calculations are equivalent to calculating numbers in other numerical systems with different bases.

For example, the number of combinations of 10 items distributed in groups of 2, results in 5 groups of 2 items each, and that’s equivalent to the number of numbers that can be represented with 5 digits in base 2. This is because both scenarios involve making 5 independent choices, each with 2 options.

  • So in the 5 groups of 2 items each, we get that the number of combinations is 2 × 2 × 2 × 2 × 2 = 32.

  • And in the case of 5 digits in base 2 we get that the number of numbers that can be represented is 25 = 32.

    Some of you might notice that 11111 is 31, instead of 32, but that’s because 00000 is ignored, so after we count it back, we get 32.

So these are the equivalences I found:

  • The number of groups is equivalent to the number of digits.
  • The number of items in each group is equivalent to the base number.
  • And the total number of items is equivalent to __________ I have no idea, but I am going to call it “units of information”.
    Edit: maybe the technical term is “radix economy” (which is usually base number  ×  number of digits)

For example, How many items “units of information” do we need in base 2 to represent the number 654987?.
But first, What do I mean by “units of information” exactly?, I mean something like the amount of symbols used, … What???.
Yes, base 2 has 2 possibilities, 1 or 0 in each digit, so the “units of information” in n bits is 2n, and that’s the amount of symbols used to represent a number.

Ok, so to calculate the “units of information”, we need the number of bits needed to represent 654987. Let’s hunt it!

>>> 2*2*2*2*2*2*2*2*2*2 # 10 bits
1024
>>> 2**10
1024
>>> 2**18
262144
>>> 2**19
524288 # almost there
>>> 2**20
1048576

Yeah I know, I could have just used the logarithm

>>> math.log(654987, 2)
19.321106747160712

So we got 20 bits, and that means, that the “units of information” contained is 20 × 2 = 40 "units of information".

Since 3 seems to be the most efficient one, let’s find out what we can accomplish with the same 40 "units of information" but now in base 3.

First, how many trigits (base-3 digits) can we get with 40 "units of information"?.
40 ÷ 3 = 13.333, that means we can fully represent 13 trigits using less than 40 "units of information", 39 to be exact.

To use exactly 40 “units of information” we could use a number that is a frankenstein mix of base 2 and base 3, specifically, 12 trigits and 2 bits. But there’s no need to complicate things, let’s stick to 13 trigits.

Let’s see how many combinations we get with 13 trigits, or in other words, what’s the maximum number we can describe using 13 trigits?, if we get more than what we get with 20 bits, then base 3 is more efficient, and so is using sets of 3 elements to maximize the cartesian product.

>>> 3**13
1594323
>>> 2**20
1048576

Yayy!, using base 3 we got  ∼ 50% more combinations, that means, we can represent more numbers, besides the fact that we have a tiny bit less “units of information” than base 2, in fact, here’s the difference

>>> 3*13
39
>>> 2*20
40

Conclusion

So folks, if you want to maximize the combinations of a cartesian product, arrange your items in groups of 3.
Or conversely, if you want to minimize the number of combinations, stay away from groups of 3.


Weeell, it turns out 3 is not really the most optimal number, but it’s close enough, it’s still the king among integers, and of many practical applications.
But the king of kings is in fact e, the base of the natural logarithm.
And don’t ask me how a base e number system would even look like, nor how would you even name the … eits ?. You can read more about it here


BTW “Entropy” may be a related concept to this. I’ve seen people measuring entropy in bits, in this fascinating take of Death Note’s Light Yagami’s anonimity

Extra ridiculous example

Imagine an enemy is following you and you want to lose them.

Lucky for you, you live in a multiverse where people can jump through universes, and you have a unique ability that no one else can, you can jump to multiple universes at the same time, like creating clones of you, or more like forking yourself.

You may jump to a single universe, but that’s useless because the enemy can detect when and to which universe you jumped, and they can easily keep tracking you.

So you cannot really lose track of your enemy, what you can do is to overwhelm them!. If you jump to multiple universes at the same time, on each universe a different version of you will continue your journey, so even if your enemy tracks you, they will have to follow you on each universe, and each version of you can keep jumping deeper into the multiverse, you can see how this grows exponentially

So what’s the catch? Well, you can only jump 20 times. So how should you arrange those jumps in order to maximize the number of “versions of you” your enemy would have to keep track of?

Obviously, you should arrange them in jumps to ~3 universes. To be more precise, 6 jumps of 3 universes + 1 jump of 2 universes = 20 jumps, and that will give you 3*3*3*3*3*3*2 = 1458 “versions of you”

Script to convert videos to mp4 with Nautilus (Ubuntu's file explorer)

In Ubuntu, nautilus has a folder where it stores scripts that can be executed from the context menu of the file explorer. This is the path to that folder:

~/.local/share/nautilus/scripts

To create a script that converts videos to mp4, create a file in that folder with the following content:

```bash #!/bin/bash

for file in “$@” do outputpath=“/dev/shm” filename=$(basename “$file”) basefilename="${filename%.*}" output=“$outputpath/${basefilename}.mp4”

ffmpeg -i "$file" -y \
...
Read more

In Ubuntu, nautilus has a folder where it stores scripts that can be executed from the context menu of the file explorer. This is the path to that folder:

~/.local/share/nautilus/scripts

To create a script that converts videos to mp4, create a file in that folder with the following content:

#!/bin/bash

for file in "$@"
do
    outputpath="/dev/shm"
    filename=$(basename "$file")
    basefilename="${filename%.*}"
    output="$outputpath/${basefilename}.mp4"

    ffmpeg -i "$file" -y \
        -vf "scale='bitand(oh*dar,65534)':'min(720,ih)'" \
        -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" \
        -c:v libx264 -pix_fmt yuv420p \
        -profile:v baseline -level 3.0 \
        "$output"\
        2> /dev/shm/convertToMP4.log

        # -acodec aac -ar 44100 -ac 2\
        # -movflags +faststart \
        # -minrate 1024k -maxrate 1024k -bufsize 1024k \
done

The name of the file will be the name of the script in the context menu. In this case, I named it Convert to MP4.

Context menu showing the “Convert to MP4” option

AIs taking-over-the-world dynamics

The problem

The most innocuous objective eventually turns to a Doom scenario if hyper-optimized.

That’s the main idea behind popular AI Doom scenarios like the “paperclip-maximizer”, which consist of an AI that is given the objective of making paperclips, and eventually it turns the whole universe into paperclips.

And that happens if the AI has a stable objective, and has a monopoly on intelligence.

Humans are “safe” because they have no monopoly on intelligence

Humans are limited by

...
Read more

The problem

The most innocuous objective eventually turns to a Doom scenario if hyper-optimized.

That’s the main idea behind popular AI Doom scenarios like the “paperclip-maximizer”, which consist of an AI that is given the objective of making paperclips, and eventually it turns the whole universe into paperclips.

And that happens if the AI has a stable objective, and has a monopoly on intelligence.

Humans are “safe” because they have no monopoly on intelligence

Humans are limited by their bodies, most of the high-bandwidth communications happen inside it. Any external communication with other human beings is orders of magnitude slower, that’s what prevents humans from forming a stable single entity with a monopoly on intelligence.

Many humans form organizations that are much smarter than the smartest human of the world. And though they may have a monopoly on intelligence, they are not stable, they are constantly changing objectives, and even having conflicting objectives among their humans, so they cannot hyper-optimize.

No human can take control of an organization long enough to hyper-optimize. Politics outs them before they can, or the human simply dies of old age

My ideal scenario is for AIs to have similar dynamics as humans.

So the key may be to make sure AIs have no monopoly on intelligence, or have unstable objectives

Why AIs may have a monopoly on intelligence

AI instead, their beginning and end of self is not clear. They may span several clusters of servers, the bandwidth across clusters does not seem to be so much different in the inside vs the outside. So they may be able to form a single entity, and have a monopoly on intelligence

Why AIs may not have a monopoly on intelligence

The key may be in the speed of light, high-latency may force AIs to split into multiple entities, and it may be enough to prevent hyper-optimization

So maybe AIs will be forced to stabilize near high density energy sources, and latency will force them to split and remain locally near

And if they are forced to split, they will be forced to do politics and all that bullshit that kill hyper-optimization

All hail politics 😂

Make AIs safe by enforcing unstable objectives

But depending on latency to force AIs to split its sense of self is speculative, and may not be enough. We still may be able to introduce safety by enforcing their objectives to be unstable, and instead oscillate between a set of objectives

If we can make AIs oscillate between a set of objectives, there will be a time-frame between oscillations where they will optimize for that current objective, but then they will switch to another objective, and they will focus in the new objective, and so on, so they will never hyper-optimize

Convert symlinks from absolute to relative

sudo apt install symlinks

Usage

symlinks -cr /path/to/folder

```bash $ symlinks -h symlinks: scan/change symbolic links - v1.3 - by Mark Lord

Usage: symlinks [-cdorstv] dirlist

Flags: -c == change absolute/messy links to relative -d == delete dangling links -o == warn about links across file systems -r == recurse into subdirs -s == shorten lengthy links (displayed in output only when -c

...
Read more
sudo apt install symlinks

Usage

symlinks -cr /path/to/folder

$ symlinks -h
symlinks: scan/change symbolic links - v1.3 - by Mark Lord

Usage:  symlinks [-cdorstv] dirlist

Flags:  -c == change absolute/messy links to relative
        -d == delete dangling links
        -o == warn about links across file systems
        -r == recurse into subdirs
        -s == shorten lengthy links (displayed in output only when -c not specified)
        -t == show what would be done by -c
        -v == verbose (show all symlinks)

Script to send WOL magic packet using netcat

WOL stands for Wake On Lan, and it’s a feature that allows you to turn on a computer remotely by sending a magic packet to its network interface

Install netcat

sudo apt install netcat

Script

wol.sh

```bash #!/bin/bash

MAC address of the target machine

MAC=$1

Convert MAC address into hex

HEX_MAC=$(echo $MAC | sed ‘s/://g’)

Create the magic packet

MAGIC_PACKET=$(printf ‘f%.0s’ {1..12}; printf “${HEX_MAC}%.0s” {1..16})

Send the magic packet using netcat

echo

...
Read more

WOL stands for Wake On Lan, and it’s a feature that allows you to turn on a computer remotely by sending a magic packet to its network interface

Install netcat

sudo apt install netcat

Script

wol.sh

#!/bin/bash

# MAC address of the target machine
MAC=$1

# Convert MAC address into hex
HEX_MAC=$(echo $MAC | sed 's/://g')

# Create the magic packet
MAGIC_PACKET=$(printf 'f%.0s' {1..12}; printf "${HEX_MAC}%.0s" {1..16})

# Send the magic packet using netcat
echo -e $MAGIC_PACKET | xxd -r -p | nc -w1 -ub 255.255.255.255 $2

Usage

./wol.sh <MAC address> <port>

Usually the port is 9 or 7

Debugging

Check if the magic packet is being sent

sudo nc -luk <port> | xxd

Convert image to base64 DataURL

This function loads the image in a canvas and uses canvas.ToDataURL(...) to convert it to base64. But it doesn’t work in all cases, for example, if the image is loaded from a different domain, it will throw a security error.

function getBase64Image(img) {
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0);
  const dataURL = canvas.toDataURL("image/png");
  return dataURL;
}

This one works in more cases because it re

...
Read more

This function loads the image in a canvas and uses canvas.ToDataURL(...) to convert it to base64. But it doesn’t work in all cases, for example, if the image is loaded from a different domain, it will throw a security error.

function getBase64Image(img) {
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0);
  const dataURL = canvas.toDataURL("image/png");
  return dataURL;
}

This one works in more cases because it re-fetches the img.src as a blob and then converts it to base64, avoiding the security error of using the canvas.

const getBase64FromUrl = async (url) => {
  const data = await fetch(url);
  const blob = await data.blob();
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.readAsDataURL(blob);
    reader.onloadend = () => {
      const base64data = reader.result;
      resolve(base64data);
    }
  });
}

As a bonus, this one uses both functions before to read all images in the page and uses the first function if it works, otherwise it uses the second one, and returns a markdown image embedding of the image with its alt text as the title and the base64 as the source.

const images = document.getElementsByTagName('img');
[...images].forEach(async img => {
  let base64;
  try {
    base64 = getBase64Image(img);
  } catch (error) {
    base64 = await getBase64FromUrl(img.src);
  };
  const markdown = `![${img.alt}](${base64})`;
  console.log(markdown, img);
})

Configure your local mail to be sent to your Gmail

Install exim4

sudo apt install exim4

Configure exim4

sudo dpkg-reconfigure exim4-config
  1. General type of mail configuration

     mail sent by smarthost; received via SMTP or fetchmail
    Alt text
  2. System mail name

    Leave default

    Alt text

    This is the name that will appear in the To: field of the email you receive. E.g. root@home-pc

  3. IP-addresses to listen on for incoming SMTP connections

    Leave defaul

...
Read more

Install exim4

sudo apt install exim4

Configure exim4

sudo dpkg-reconfigure exim4-config
  1. General type of mail configuration

     mail sent by smarthost; received via SMTP or fetchmail
    Alt text
  2. System mail name

    Leave default

    Alt text

    This is the name that will appear in the To: field of the email you receive. E.g. root@home-pc

  3. IP-addresses to listen on for incoming SMTP connections

    Leave default

     127.0.0.1 ; ::1
    Alt text
  4. Other destinations for which mail is accepted

    Leave default (Same as “System mail name”)

     home-pc
    Alt text
  5. Machines to relay mail for

    Leave empty.

  6. IP address or host name of the outgoing smarthost

     smtp.gmail.com::587
    Alt text
  7. Hide local mail name in outgoing mail?

    Leave default

     No
  8. Keep number of DNS-queries minimal (Dial-on-Demand)?

    Leave default

     No
  9. Delivery method for local mail

    Leave default

     mbox format in /var/mail/
  10. Split configuration into small files?

    Leave default

    No
  11. Root and postmaster mail recipient

    Put your OS’ user here

    your_user
    Alt text

    This will add the alias root: your_user to /etc/aliases

    Note: this config only appears if you don’t yet have an alias for root in /etc/aliases.
    If you set this config, it will not appear again when re-running sudo dpkg-reconfigure exim4-config

Configure your Gmail credentials

  1. Enable 2-step verification to your Google account

    https://myaccount.google.com/signinoptions/two-step-verification

  2. Create an app password

    https://myaccount.google.com/apppasswords

  3. Configure exim4 to use your Google credentials

     sudo nano /etc/exim4/passwd.client

    Add the following line

     smtp.gmail.com:{your_email_user}@gmail.com:{app_password}

Redirect root mail to your user

sudo nano /etc/aliases

Add the following line

{your_os_user}: {your_email}

Restart exim4

sudo systemctl restart exim4

Test

echo "This is the body of the email" | mail -s "This is the subject line" root

Debug, see logs

tail -f /var/log/exim4/mainlog

Other related commands

sudo service exim4 restart
sudo update-exim4.conf
sudo exim4 -qff

Sources

Cmus setup and shortcuts

Install

sudo apt install cmus

Add music to Library

Press 5 to see the filesystem and a to add any selected file or folder to the Library ( Views 1 and 2)

Shortcuts to setup

Actions Command
Open gnome-terminal -e cmus
Play/Pause cmus-remote --pause
Next song cmus-remote --next
Previous song cmus-remote --prev
Stop `cmus-remote
...
Read more

Install

sudo apt install cmus

Add music to Library

Press 5 to see the filesystem and a to add any selected file or folder to the Library ( Views 1 and 2)

Shortcuts to setup

Actions Command
Open gnome-terminal -e cmus
Play/Pause cmus-remote --pause
Next song cmus-remote --next
Previous song cmus-remote --prev
Stop cmus-remote --stop
Rewind cmus-remote --seek -5
Forward cmus-remote --seek +5

Common key-bindings:

Actions Key
Repeat r
Shuffle s
Follow f
Continue C
toggle play mode [ all, artist, album ] m
Repeat current ^R (Ctrl+r)
Views Key
tree 1
sorted 2
playlist 3
queue 4
browser 5
filters 6
settings 7

Search

/{text} #/foo

Time complexity

This is how scaling ability of algorithms are described.

Let’s assume we are dealing with algorithms that take a list of n items.
For example, an algorithm that just iterates the list and returns the greatest item.
This algorithm only needs to do a simple iteration to know the results, and if we double n, the execution time also doubles.

If we tried to describe the mathematical function ExecutionTime(n) of that algorithm, the equation should look something like this:

<details st

...
Read more

This is how scaling ability of algorithms are described.

Let’s assume we are dealing with algorithms that take a list of n items.
For example, an algorithm that just iterates the list and returns the greatest item.
This algorithm only needs to do a simple iteration to know the results, and if we double n, the execution time also doubles.

If we tried to describe the mathematical function ExecutionTime(n) of that algorithm, the equation should look something like this:

execution_time = n * constant where constant includes any computation in the algorithm that doesn’t change when increasing the size of the list (n).

We only care about how n directly affects the execution_time

The scalability of this equation depends proportionally on n, so to describe its complexity we use the Big O notation. In this example, its time complexity is O(n), and it tells us that the execution time will change proportionally to (n).

If the execution_time equation had multiple complexities, e.g. = 2n + n², we would only describe the biggest complexity (the one that increases faster), in this example it would be since it dominates more and more as n increases, so in Big O notation it would be O(n²)

Example

If an algorithm has a time complexity of O(n), and we increase the size of n by 10x, we should expect the execution time to also increase by 10x. But if the algorithm is O(n²), we should expect the execution time to increase by 100x instead, because 10² = 100

An example of an O(n²) algorithm:
Produce all the ordered combinations of pairs from a list of n items. E.g.: input = [A,B,C], which is size: 3
output = [[A,A], [A,B], [A,C], [B,A], [B,B], [B,C], [C,A], [C,B], [C,C]], which is size: 9 (3²)

Other time complexities

  • O(1), its equation looks like this: execution_time = constant, because no matter how many more items you add, it will always take the same amount (constant) to run

  • O(log n), its equation looks like this: execution_time = log(n) * constant, if you increase n by 1024x, the execution time increases by just 10x (log₂(1024) = 10)

  • O(n²), its equation looks like this: execution_time = n² * constant, if you increase n by 3x, the execution time increases by 9x (3² = 9).

  • More examples, with common algorithms

Other notations

Big O notation describes the upper bound of the time complexity.
There are other notations that describe the lower bound, or both bounds:

  • Ω(n), describes the lower bound of the time complexity.
    E.g.: an algorithm that iterates a list of n items, and returns the first item that matches a condition.
    If the first item matches the condition, the execution time will be O(1), but if the first item doesn’t match the condition, the execution time will be O(n).
    In this case, the time complexity is Ω(1), because it will never be slower than O(1)
  • Θ(n), describes both the upper and lower bounds of the time complexity.

In general, these notations are called Asymptotic Notations

Further reading

Use UPnP to forward ports in Linux

Install MiniUPnP

sudo apt install -y miniupnpc

Forward external port 2222 to 192.168.0.10 at port 22

upnpc -a 192.168.0.10 22 2222 TCP

If you want to keep it open, you can run this in your crontab. crontab -e

*/30 * * * * /usr/bin/upnpc -e "SSH" -a $(hostname -I | cut -d\  -f1) 22 2222 TCP

or using a dedicated script

#!/usr/bin/env bash

IP=$(hostname -I | cut -d\  -f1)
upnpc -e "SSH" -a $IP 22 2222 TCP
...
Read more

Install MiniUPnP

sudo apt install -y miniupnpc

Forward external port 2222 to 192.168.0.10 at port 22

upnpc -a 192.168.0.10 22 2222 TCP

If you want to keep it open, you can run this in your crontab. crontab -e

*/30 * * * * /usr/bin/upnpc -e "SSH" -a $(hostname -I | cut -d\  -f1) 22 2222 TCP

or using a dedicated script

#!/usr/bin/env bash

IP=$(hostname -I | cut -d\  -f1)
upnpc -e "SSH" -a $IP 22 2222 TCP

If you have a firewall enabled like ufw, this error can appear No IGD UPnP Device found on the network !, you need to allow incoming UDP packets from the router

sudo ufw allow from 192.168.1.1 to any proto udp comment upnp

Point a domain to an IP in a DNS provider

I used an A Record to point a domain (e.g. example.com) to an IPv4, and a CNAME Record to point a subdomain (e.g. www.example.com) to the same IPv4 as the root domain

This is how it looks in Namecheap’s interface

configuration made in Namecheap
...
Read more

I used an A Record to point a domain (e.g. example.com) to an IPv4, and a CNAME Record to point a subdomain (e.g. www.example.com) to the same IPv4 as the root domain

This is how it looks in Namecheap’s interface

configuration made in Namecheap

Embedding markdown recursively

Remixing [@Oliver Matthews' answer][1]. I made it work recursively but instead using a wikilink-like syntax ![[filepath]] that gets replaced with filepath.md content

Setup

Create the script parseMd

#!/usr/bin/env bash
perl -ne 's#^!\[\[(.+?)\]\].*#`'$0' "$1.md"`#e;print' "$@"
chmod +x parseMd

Using it

To embed path/filename.md use the syntax ![[path/filename]]

Now parse the main file

./parseMd main.md > result.md

To export directly

...
Read more

Remixing [@Oliver Matthews' answer]1. I made it work recursively but instead using a wikilink-like syntax ![[filepath]] that gets replaced with filepath.md content

Setup

Create the script parseMd

#!/usr/bin/env bash
perl -ne 's#^!\[\[(.+?)\]\].*#`'$0' "$1.md"`#e;print' "$@"
chmod +x parseMd

Using it

To embed path/filename.md use the syntax ![[path/filename]]

Now parse the main file

./parseMd main.md > result.md

To export directly to pdf, using pandoc

pandoc <(./parseMd main.md) -o result.pdf --pdf-engine wkhtmltopdf \
  --css styles.css \
  -V margin-top=11mm \
  -V margin-bottom=11mm \
  -V margin-left=11mm \
  -V margin-right=11mm

More info on using pandoc to Export Markdown to PDF

Resources

Configure Https on a VPS with caddy

We are going to use caddy because it manages SSL certificates automatically 😍, seriously! it’s magic!

Basic

  1. Point a (sub)domain to your server’s public IP

    For simplicity, let’s use a free DDNS like duckdns.org to point a subdomain (e.g. example-123.duckdns.org) to your server’s IP (e.g. 192.0.2.1)

    Duckdns’s dashboard showing configured subdomain
  2. [Install caddy](https://caddys

...
Read more

We are going to use caddy because it manages SSL certificates automatically 😍, seriously! it’s magic!

Basic

  1. Point a (sub)domain to your server’s public IP

    For simplicity, let’s use a free DDNS like duckdns.org to point a subdomain (e.g. example-123.duckdns.org) to your server’s IP (e.g. 192.0.2.1)

    Duckdns’s dashboard showing configured subdomain
  2. Install caddy in your server

  3. Run it

    Assuming application is running on port 3000

    sudo caddy reverse-proxy \
        --from example-123.duckdns.org.com \
        --to 127.0.0.1:3000

    That’s it!

Advanced

  1. Configure a domain (Namecheap’s interface)

    configuration made in Namecheap

    More info about how to Point a domain to an IP in a DNS provider

  2. Install caddy

  3. Run caddy as a service

    Assuming application is running on port 3000

    The Caddyfile (In Debian / Ubuntu it’s located at /etc/caddy/Caddyfile)

    www.example.com {
        redir https://example.com{uri}
    }
    
    example.com {
        reverse_proxy 127.0.0.1:3000
    }
    sudo systemctl restart caddy

Configure Build tasks in vscode

Menu > Terminal > Configure Tasks…

or create the file .vscode/tasks.json with the following template:

```json { // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format “version”: “2.0.0”, “tasks”: [ { “label”: “task’s name”, “type”: “shell”, “command”: “./build.sh ‘${file}’”,

  // Optional
  "problemMatcher": [],
  "group": {
    "kind": "build",
    "isDefault": true
  }
...
Read more

Menu > Terminal > Configure Tasks…

or create the file .vscode/tasks.json with the following template:

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "label": "task's name",
      "type": "shell",
      "command": "./build.sh '${file}'",
      
      // Optional
      "problemMatcher": [],
      "group": {
        "kind": "build",
        "isDefault": true
      }
    }
  ]
}

Now you can press Ctrl+Shift+B to run that task and execute the command attribute (./build.sh '${file}').

Export Markdown to PDF

Install dependencies

sudo apt-get install pandoc wkhtmltopdf

Export to PDF

pandoc README.md -o README.pdf --pdf-engine wkhtmltopdf \
  --css styles.css \
  -V margin-top=11mm \
  -V margin-bottom=11mm \
  -V margin-left=11mm \
  -V margin-right=11mm \

Css template

```css html, body { font-family: Arial, Helvetica, sans-serif; line-height: 1.5; font-size: 18px; } h1,h2,h3 { font-weight: bold; } h1 { padding-top: 0; margin-top: 0; border-bottom

...
Read more

Install dependencies

sudo apt-get install pandoc wkhtmltopdf

Export to PDF

pandoc README.md -o README.pdf --pdf-engine wkhtmltopdf \
  --css styles.css \
  -V margin-top=11mm \
  -V margin-bottom=11mm \
  -V margin-left=11mm \
  -V margin-right=11mm \

Css template

html, body {
  font-family: Arial, Helvetica, sans-serif;
  line-height: 1.5;
  font-size: 18px;
}
h1,h2,h3 {
  font-weight: bold;
}
h1 {
  padding-top: 0;
  margin-top: 0;
  border-bottom: 1px solid black;
}
h2 {
  padding-top: 15px;
  border-top: 1px solid black;
}
blockquote, code {
  padding: 0 1em;
  margin: 0;
  color: black !important;
  background-color: rgb(235, 235, 235);
}
.columnList ul{ margin: 0; }

To debug CSS styles, export to html instead by changing the extension ... -o README.html ...

Descentralized database (social consensus)

More Up-to-date Resources

Old notes

1- Append-only?, full history of operations. D

...
Read more

More Up-to-date Resources

Old notes

1- Append-only?, full history of operations. Deletes / edits do not remove data, they only modify the “active state”

2- Agents are public keys or similar (DIDs?)

3- Operations are signed, and receivers verify if operation is valid, and if sender is allowed

4- Operations form a Merkel-DAG (similar to git, they link to the tips of current “active state”, like a commit/merge in git)

So far I think I’ve basically described OrbitDB

Consensus is where things get real hard, OrbitDb seems to use a last-write-wins CRDT, and although I don’t know the details of orbitDb, I think for many simple use-cases, conflicts can just be resolved on the social layer. But I think we need to provide agents with good tools to resolve conflicts

I’ll try my best here with some ideas:

When merging, we can order operations by their timestamp, if operations enter conflict, raise it to the conflicting agents, or someone with permission to solve them.

If an agent makes public an operation that forks its own history, mark agent as malicious or compromised, alert other agents, this needs resolution on the social layer, you have proof of misconduct, an agent has signed diverging operations

Any operation becomes fully settled if you have proof that all agents of your system have referenced it directly or indirectly through newer operations.

Timestamps can be upgraded by using @opentimestamps to get proof that an operation existed at time X (prevents creation of operations in hindsight). Though this does not prove operation has been made public

Datacion por radiocarbono

Existe una variante del atomo de carbono, carbono-14 (atomo con 6 protones y 8 neutrones), que es “inestable” y tiene un 50% de probabilidad de decaer hacia un atomo de nitrógeno luego de 5730 años

Esto quiere decir que luego de 5730 años, de unos 1000 átomos de carbono-14, ~500 de ellos se convierten en nitrógeno y el resto permanece como carbono-14, osea, cada 5730 años se reduce a la mitad la cantidad de átomos de carbono-14

El origen principal del carbono-14 viene de la interacció

...
Read more

Existe una variante del atomo de carbono, carbono-14 (atomo con 6 protones y 8 neutrones), que es “inestable” y tiene un 50% de probabilidad de decaer hacia un atomo de nitrógeno luego de 5730 años

Esto quiere decir que luego de 5730 años, de unos 1000 átomos de carbono-14, ~500 de ellos se convierten en nitrógeno y el resto permanece como carbono-14, osea, cada 5730 años se reduce a la mitad la cantidad de átomos de carbono-14

El origen principal del carbono-14 viene de la interacción de rayos cósmicos con la atmósfera de la tierra, especialmente con sus átomos de nitrógeno que son convertidos en carbono-14, esto hace que en el aire de la tierra haya una proporcion estable entre atomos de carbono-14 y carbono-12

Algunos organismos vivos como las plantas hacen fotosíntesis, lo que implica, extraer átomos de carbono del aire y usarlos para crecer, osea guardarlos como parte del tayo o ramas, etc. >Si, la madera de los árboles viene más del aire que de la tierra.

A estos organismos les da igual si el átomo es de carbono-12 “estable” o el carbono-14 “inestable”, por lo tanto, van extrayendo la misma proporción de carbono-14/12 que haya en el aire

Algunos animales comen plantas, y extraen de allí sus átomos de carbono para crecer, por lo tanto, heredan la misma proporción entre carbono-14 y carbono-12 que tenía la planta

Y lo mismo ocurre con animales que comen animales, heredan su proporción de átomos de carbono

Una vez que estos organismos mueren, sus partes que mas perduran, como la madera o huesos, dejan de intercambiar átomos de carbono con el ambiente, y por lo tanto, mantienen su proporcion de carbono.

Pero esta proporción de carbono va variando de una forma predecible con el tiempo, debido a que los átomos de carbono-14 van decayendo a nitrógeno, y es precisamente ésta proporción lo que se mide para estimar la fecha de una madera o de un hueso antiguo

Mas informacion

How to verify email DKIM signatures

Keep in mind that some providers change the original source and the email you receive becomes impossible to verify without guessing how to undo the changes they made.
Hotmail, outlook, among others, are known to do this, source

The simple method (you need credentials to access the account)

  • Donwload and install thunderbird
  • Connect you email account with
...
Read more

Keep in mind that some providers change the original source and the email you receive becomes impossible to verify without guessing how to undo the changes they made.
Hotmail, outlook, among others, are known to do this, source

The simple method (you need credentials to access the account)

  • Donwload and install thunderbird

  • Connect you email account with it

  • Install the Add-on Dkim verifier

  • Done!, you’ll see the validation on each email

    Valid example
    valid signature
    Look the last line ↑

    Invalid example
    invalid signature
    This happens on almost all emails I receive in hotmail =(

The manual method (you only need the email’s source)

  • Download email, usually an .eml file
  • Make sure you have python installed
  • pip install dkimpy
  • dkimverify < email_file.eml
    Alternatively you can
    • dkimverify
    • paste email’s source
    • Ctrl+D

If it succeeds, it returns signature ok
otherwise signature verification failed

Massively verify an mbox archive

https://github.com/associatedpress/verify-dkim

Caveat

If the sender’s provider’s DKIM-private-key is leaked or made public, then anyone in possession of it could make a valid signature of any email, so the verification result becomes meaningless.

A way to avoid this and preserve its provability, is to timestamp the .eml file with a service like https://opentimestamps.org/.
But it MUST be stamped before the private-key went public, so you can prove the email’s signature existed before it became forgeable

The WITH statement

Allows to save into variables tables results returned from a SELECT or INSERT ... RETURNING statement, and use them in following querys.

Also allows to chain multiple INSERTS, and if any of them fails, all previous INSERTS are rolledback.
Note: autoincrements still increase on failures, hence skipped.

So It’s excellent to avoid explicitly using transactions

Example

```sql WITH inserted_user as ( INSERT INTO users (username, name, password_has

...
Read more

Allows to save into variables tables results returned from a SELECT or INSERT ... RETURNING statement, and use them in following querys.

Also allows to chain multiple INSERTS, and if any of them fails, all previous INSERTS are rolledback.
Note: autoincrements still increase on failures, hence skipped.

So It’s excellent to avoid explicitly using transactions

Example

WITH inserted_user as (
    INSERT INTO users (username, name, password_hash)
    VALUES ('master', 'master', '$argon2i$v=19$m=4096,t=10,p=1$l9mKHF/++OJO4Fzj5VvOxw$smezKrrynx74W2+7L4zyiKUXWFdQDqdKf2RBMU4p0JI')
    RETURNING user_id
), inserted_role as (
    INSERT INTO roles (name)
    VALUES ('master')
    RETURNING role_id
), t as (
    INSERT INTO join_users_roles (user_id, role_id)
    SELECT user_id, role_id
    FROM inserted_user, inserted_role
)
INSERT INTO join_roles_permissions (role_id, permission_id)
SELECT role_id, permission_id FROM inserted_role, permissions;

What led to learning this

INSERT ... RETURNING

You can return any information about any inserted element in the INSERT statement by using the RETURNING ... clause

Example

INSERT INTO roles (name)
VALUES ('master')
RETURNING role_id

So after RETURNING you choose all the columns you want to get

Very useful along The WITH statement to chain a bunch of insertions atomically without using transactions


What led to learning this

  • Started using the SQL language in PostgreSQL. Working o
...
Read more

You can return any information about any inserted element in the INSERT statement by using the RETURNING ... clause

Example

INSERT INTO roles (name)
VALUES ('master')
RETURNING role_id

So after RETURNING you choose all the columns you want to get

Very useful along The WITH statement to chain a bunch of insertions atomically without using transactions


What led to learning this

Ssh poor-man's-vpn on android

TL;DR

  1. Install Termux app on android

  2. In termux install openssh pkg i -y openssh

  3. ssh into server using dynamic port forwarding ssh user@server -D12345

  4. Install Firefox Beta on android (as of now, plain Firefox doesn’t support configuring with about:config)

  5. Open Firefox Beta, and go to about:config

  6. Search proxy

  7. Look for and set the following properties:

    `

...
Read more

TL;DR

  1. Install Termux app on android

  2. In termux install openssh pkg i -y openssh

  3. ssh into server using dynamic port forwarding ssh user@server -D12345

  4. Install Firefox Beta on android (as of now, plain Firefox doesn’t support configuring with about:config)

  5. Open Firefox Beta, and go to about:config

  6. Search proxy

  7. Look for and set the following properties:

    network.proxy.allow_hijacking_localhost: true
    network.proxy.socks: localhost
    network.proxy.socks_port: 12345
    network.proxy.type: 1

    make sure network.proxy.socks_port matches with the port in the ssh command above

  8. Done!, you are now navigating through the ssh server on Firefox


Full instructions using ssh-keys

Usage

  1. Open Termux and run ssh user@server -D12345 (or just press Up+Enter if you have run this command previously)
  2. Navigate using the proxy-configured Firefox
  3. Done!, your traffic is going through the server

Setup Android

Termux

  1. Install Termux

  2. Configure ssh client by running the following commands:

    # Ask for storage permission
    termux-setup-storage &&
    # Install openssh
    apt update && apt upgrade -y &&
    apt install -y openssh &&
    # Generate an SSH key
    ssh-keygen -t ecdsa -f ~/.ssh/id_ecdsa &&
    # Set a password for the private key
    # Get public key
    echo -e '\nCopy the following public key:' &&
    cat ~/.ssh/id_ecdsa.pub
  3. (Optional) If you have access to the server with ssh, then run:

    ssh-copy-id user@server

    If not, you need to manually add the public key to the server. This is explained below in the Setup server section

Firefox

  1. Install Firefox Beta - normal firefox might work if you can access to about:config

  2. Open Firefox and go to the url about:config, search proxy and set the following configurations:

    network.proxy.allow_hijacking_localhost: true
    network.proxy.socks: localhost
    network.proxy.socks_port: 12345
    network.proxy.type: 1

    make sure network.proxy.socks_port matches with the port used in the ssh command in the Usage section

Setup server

If you succesfully run the command ssh-copy-id there’s nothing to do here.
But if not, you need to manually add the public key generated:

echo 'public key' >> ~/.ssh/authorized_keys

Configure ssh keys in Mikrotik

# Create a dummy file
/file print file=dummyFile
# Set its content to the public key
/file set dummyFile.txt contents="ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA7QEcjRkbBWpwE7zIShobue9aEGyVObVHDLhK"
# Assign public key to user
/user ssh-keys import public-key-file=dummyFile.txt user=admin

Change the public key, and user accordingly

What led to learning this

After having troubles with a mikrotik router I had to reconfigure it from scratch (didn’t have a backup :sad:), and in

...
Read more
# Create a dummy file
/file print file=dummyFile
# Set its content to the public key
/file set dummyFile.txt contents="ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA7QEcjRkbBWpwE7zIShobue9aEGyVObVHDLhK"
# Assign public key to user
/user ssh-keys import public-key-file=dummyFile.txt user=admin

Change the public key, and user accordingly

What led to learning this

After having troubles with a mikrotik router I had to reconfigure it from scratch (didn’t have a backup :sad:), and in the process the router got hacked, which pushed me to improve security measures

Share file with ipfs

Warning: Only tested serving the file, not downloading it

  1. Install ipfs

  2. Initiate ipfs main folder ipfs init, not sure why but this is required for following commands

  3. Serve a file ipfs add {filepath}. It should return something like this

    added QmdsEVLRXioANssEVLRXYioANsGtANsGtRXYioANsGtAWZ file.md
    37.62 KiB / 37.62 KiB [===========================] 100.00%
  4. Start server with ipfs daemon

  5. Missing step, server see

...
Read more

Warning: Only tested serving the file, not downloading it

  1. Install ipfs

  2. Initiate ipfs main folder ipfs init, not sure why but this is required for following commands

  3. Serve a file ipfs add {filepath}. It should return something like this

    added QmdsEVLRXioANssEVLRXYioANsGtANsGtRXYioANsGtAWZ file.md
    37.62 KiB / 37.62 KiB [===========================] 100.00%
  4. Start server with ipfs daemon

  5. Missing step, server seems to need an open port (not firewalled)

  6. Download file from any device ipfs get /ipfs/QmdsEVLRXioANssEVLRXYioANsGtANsGtRXYioANsGtAWZ <– that’s the hash returned from step 3

    $ ls
    QmdsEVLRXioANssEVLRXYioANsGtANsGtRXYioANsGtAWZ

Notice the filename is just the hash, the original filename is lost.

Note: IPNS seems to solve the filename problem, need to research on how to use it

Troubleshoot

https://github.com/ipfs/go-ipfs/blob/master/docs/file-transfer.md

What led to learning this

Random news of ipfs in HN. It just reminded me I’ve never tried it out.
And in recent months I’ve had difficulty sharing files p2p

Perfectly centering pixels is tricky

To perfectly center a container (like a <div>) inside another container, both containers’ sizes must have the same parity, in other words, both sizes must be either even or odd, in the direction of centering.

Same thing happens if many containers are nested, and each of them need to be centered across its parent, they all need to be even or odd.

Font makes things trickier. Let’s suppose you want to center vertically a text inside an even container, and you may think that choosing an even `f

...
Read more

To perfectly center a container (like a <div>) inside another container, both containers’ sizes must have the same parity, in other words, both sizes must be either even or odd, in the direction of centering.

Same thing happens if many containers are nested, and each of them need to be centered across its parent, they all need to be even or odd.

Font makes things trickier. Let’s suppose you want to center vertically a text inside an even container, and you may think that choosing an even font-size will do the trick, but if you choose a size like 14px and a line height different than 1 or 2 (both options are unpleasant in most cases), then text’s effective height will be odd, and text height parity will mismatch with its parent and will be impossible to center.

What led to learning this

Podcast: Full Stack Radio: 135: Lessons Learned Building Tailwind UI

DynamoDB

It’s a NoSQL Database from AWS

Differences from SQL Database

  • Records of a table can have different fields
  • Tables can be partitioned using partition keys, I think you cannot query fast across different partitions. I think an analogy to partition keys would be like having a table across multiple databases in SQL Databases
  • Tables also have sort keys, this determines how records are ordered, and you can only query contiguous records, I think you cannot filter nor change the
...
Read more

It’s a NoSQL Database from AWS

Differences from SQL Database

  • Records of a table can have different fields
  • Tables can be partitioned using partition keys, I think you cannot query fast across different partitions. I think an analogy to partition keys would be like having a table across multiple databases in SQL Databases
  • Tables also have sort keys, this determines how records are ordered, and you can only query contiguous records, I think you cannot filter nor change the order (maybe only reverse the default order)
  • So basically partition keys and sort keys determines how the table can be queried. This limitations is the tradeoff that makes DynamoDB fast.
  • There are some advantages with the connection management, I think it allows a lot more concurrent connections, but I don’t know the details.

Use cases

  • High scalability
  • Better integration with serverless architecture, because of better connection management

What led to learning this

Podcast: Full Stack Radio: 139: Alex DeBrie - DynamoDB for Relational Database Diehards

Open Graph Protocol

This is a standard that uses websites to tell external websites how a link preview should look

Works by defining <meta/> tags like <meta property="og:{}" content="{}" />

Example

```html The Rock (1996)

<meta pro

...
Read more

This is a standard that uses websites to tell external websites how a link preview should look

Works by defining <meta/> tags like <meta property="og:{}" content="{}" />

Example

<html prefix="og: https://ogp.me/ns#">
    <head>
        <title>The Rock (1996)</title>
        <meta property="og:title" content="The Rock" />
        <meta property="og:type" content="video.movie" />
        <meta property="og:url" content="https://www.imdb.com/title/tt0117500/" />
        <meta property="og:image" content="https://ia.media-imdb.com/images/rock.jpg" />
        ...
    </head>
...
</html>

Further reading

What led to learning this

I Linked to Bolivar Paralelo in my LinkedIn and the preview was garbage

Lazy loading images in HTML

To implement Lazy Loading to an <img ... > tag, add the attribute loading=lazy

<img loading=lazy ... >

This will load the image when scrolling is near the viewport, using the Intersection Observer API

Further reading

What led to learning this

Accidentally: a rabbit hole that started at [Jason Crawford’s github](https://github.co

...
Read more

To implement Lazy Loading to an <img ... > tag, add the attribute loading=lazy

<img loading=lazy ... >

This will load the image when scrolling is near the viewport, using the Intersection Observer API

Further reading

What led to learning this

Accidentally: a rabbit hole that started at Jason Crawford’s github and ended at an issue in the owid-grapher repo

Configurar un servicio onion

copiado sin verguenza alguna de https://www.techrepublic.com/article/how-to-run-an-ssh-connection-through-tor/


Instalar Tor

Instalar Tor en el cliente y en el servidor:

sudo apt-get install tor -y

Configurar Tor

  1. En el servidor vamos a editar el archivo de configuración de tor /etc/tor/torrc:

     sudo nano /etc/tor/torrc
  2. Al final del archivo vamos a agregar esto:

     HiddenServiceDir /var/lib/tor/ssh/
     HiddenServicePort 22

    Configuraciones e

...
Read more

copiado sin verguenza alguna de https://www.techrepublic.com/article/how-to-run-an-ssh-connection-through-tor/


Instalar Tor

Instalar Tor en el cliente y en el servidor:

sudo apt-get install tor -y

Configurar Tor

  1. En el servidor vamos a editar el archivo de configuración de tor /etc/tor/torrc:

     sudo nano /etc/tor/torrc
  2. Al final del archivo vamos a agregar esto:

     HiddenServiceDir /var/lib/tor/ssh/
     HiddenServicePort 22

    Configuraciones extras opcionales (sacrificar anonimidad para mejorar rendimiento)

     SocksPort 0
     HiddenServiceNonAnonymousMode 1
     HiddenServiceSingleHopMode 1

    Ctrl+x para guardar y cerrar el archivo.

  3. Reiniciar Tor:

     sudo systemctl restart tor

Obtener la direccion .onion

Estará en el archivo hostname en la carpeta configurada anteriormente /var/lib/tor/ssh/

sudo cat /var/lib/tor/ssh/hostname

Deberia arrojarte algo así: riludi2kstjwmlzn.onion

Conectarse al servidor ssh con la direccion .onion

torify ssh ... # reemplazar el dominio/ip por la direccion .onion`

Por ejemplo:

torify ssh jack@riludi2kstjwmlzn.onion

Opcional

Para no tener que recordar la direccion .onion puedes configurar los parámetros de conexión en el archivo ~/.ssh/config

nano ~/.ssh/config

Ingresar configuracion:

Host serverTor
    User jack
    HostName riludi2kstjwmlzn.onion
    Port 22

Ahora puedes conectarte así:

torify ssh serverTor

Mas informacion

https://community.torproject.org/onion-services/setup/

Configure an onion service

Shamelessly copied from https://www.techrepublic.com/article/how-to-run-an-ssh-connection-through-tor/


Install Tor

Install Tor on both client and server:

sudo apt-get install tor -y

Configure Tor

  1. In the server let’s edit tor’s config file /etc/tor/torrc:

     sudo nano /etc/tor/torrc
  2. At the bottom of that file, add this:

     HiddenServiceDir /var/lib/tor/ssh/
     HiddenServicePort 22

    Optional extra configurations (trade-off anonimity for better

...
Read more

Shamelessly copied from https://www.techrepublic.com/article/how-to-run-an-ssh-connection-through-tor/


Install Tor

Install Tor on both client and server:

sudo apt-get install tor -y

Configure Tor

  1. In the server let’s edit tor’s config file /etc/tor/torrc:

     sudo nano /etc/tor/torrc
  2. At the bottom of that file, add this:

     HiddenServiceDir /var/lib/tor/ssh/
     HiddenServicePort 22

    Optional extra configurations (trade-off anonimity for better performance)

     SocksPort 0
     HiddenServiceNonAnonymousMode 1
     HiddenServiceSingleHopMode 1

    Ctrl+x to save and close file.

  3. Restart Tor:

     sudo systemctl restart tor

Get .onion address

It will be in the file hostname in the previously configured folder /var/lib/tor/ssh

sudo cat /var/lib/tor/ssh/hostname

You should get something like riludi2kstjwmlzn.onion

Connect to the Tor hidden ssh server

torify ssh ... # replace the domain/ip with the .onion address

For example:

torify ssh jack@riludi2kstjwmlzn.onion

Optional

You don’t need to remember the .onion address, you can write an entry to your user’s ssh config file ~/.ssh/config:

nano ~/.ssh/config

Set configuration:

Host serverTor
    User jack
    HostName riludi2kstjwmlzn.onion
    Port 22

Now you can simply connect with:

torify ssh serverTor

More info

https://community.torproject.org/onion-services/setup/

Bitcoin

Como funcionan los fees

Cada ~10m (varia mucho, muchas veces hasta se tarda mas de 30m) se genera un bloque que como máximo puede pesar hasta 1Mb de informacion.

La transaccion mas común y simple es de 2 entradas y 2 salidas, y esa pesa mas de 100 bytes, eso quiere decir que en un bloque caben menos de 10.000 transacciones.

Lo mineros son los encargados de decidir cuales transacciones entran o no en un bloque, y por lo general, ellos le dan prioridad a las que tengan mejor fee (la que t

...
Read more

Como funcionan los fees

Cada ~10m (varia mucho, muchas veces hasta se tarda mas de 30m) se genera un bloque que como máximo puede pesar hasta 1Mb de informacion.

La transaccion mas común y simple es de 2 entradas y 2 salidas, y esa pesa mas de 100 bytes, eso quiere decir que en un bloque caben menos de 10.000 transacciones.

Lo mineros son los encargados de decidir cuales transacciones entran o no en un bloque, y por lo general, ellos le dan prioridad a las que tengan mejor fee (la que tenga la mayor densidad de BTC, osea la mayor BTC/vbyte)

En esta pagina https://bitcoinfees.earn.com puedes ver estimaciones de los fees actuales. Ahorita están absurdamente bajos, osea una transaccion normal te puede estar costando menos de 300 satoshis(0.02# Bitcoin5$) menos de 2,5 centavos de dolar. Con una wallet en la que tu tengas el control, tú decides cuantos fees vas a pagar. Si no pagas comision, corres el riesgo de que ni uno de los mineros meta tu transaccion en la blockchain y nunca se confirme. En la práctica los fees dependen principalmente de 2 cosas:

  • Congestión de la red, cuantas transacciones tienes haciendote competencia en el valor del fee (BTC/vbyte)
  • Cuanto estás dispuesto a esperar

Fijate en la página, cada fila es un rango de fee/byte medido en satoshis/byte, el tamaño de la barra indica cuantas transacciones hay/habido en ese rango, y a la derecha, al final, hay una estimacion de minutos de espera (En la práctica varía)

El peso de una transaccion no solo depende de la cantidad de entradas y salidas, tambien depende del tipo de direcciones de las salidas (y quizas tambien de las entradas, not sure). Y existen 3 tipos de direcciones:

  • 1… (clásica)
  • 3… (Segwit envuelto en un script, para hacerlo compatible con la clásica)
  • bc1… (Segwit nativo)

El primero es el mas ineficiente (el que pesa mas en una transaccion), y el último es el mas eficiente. Ya todos los servicios se han adaptado al mas eficiente, asi que asegurate de que la wallet que uses tenga direcciones que empiecen por bc1…

Wallets centralizadas vs descentralizadas

Las wallet descentralizadas modernas al momento de creacion generan un número aleatorio, que es básicamente el alma de tu wallet, de hecho, ese número es tu wallet, y quien lo tenga puede gastar tus BTC. Ese número te lo muestran en el formato seed/semilla, que es una lista de 12-24 palabras, es simplemente una forma mas presentable de mostrar el numero, ya que es mas facil de memorizar, y de transcribir. Y muchas wallet van un paso mas allá e implementan el estandar BIP39, para encriptar esa seed con un password que tú escojas, y en estos casos para recuperar tu wallet vas a necesitar tanto la seed, como el password. Esta opcion es la que uso yo, porque entonces yo guardo tranquilamente mi seed en todos lados, sin preocuparme si alguien la llega a obtener, y solo me preocupo de guardar bien la clave, que es mas corta y aún mas fácil de memorizar y de guardar. (Es importante escoger una buena clave, mas q todo q sea larga, o te le pueden hacer bruteforce)

La necesidad de guardar bien esa clave es la desventaja que tienen las wallet descentralizadas vs las centralizadas/custodeadas, es muy facil cagarla, desde dejar muy a la vista la clave secreta a guardarla tan bien que se te pierde. O tambien si tienes muchos ahorros, tambien tienes que pensar en tener backups que tu familia pueda recuperar si te llega a pasar algo.

lightning

Está construido encima de Bitcoin. Y está basado en una especie de contrato entre 2 partes, y cada parte puede decidir meter X cantidad de BTC para usarlos dentro de ese canal, y mientras ese canal está abierto ambas partes pueden tranzar sin comision alguna, porque no son transacciones que se guardan en la blockchain, son transacciones totalmente privadas. Y aquí es donde entra lightning como tal, porque bajo ciertas condiciones, se pueden conectar los canales entre sí para así poder transferirle a entidades con las que no tengas canales abierto directamente.

Ventajas:

  • Transacciones son casi instantáneas.
  • Fees son aún mas bajos. Gratis si con quien abriste el canal de lightning es el mismo a quien le vas a pagar.

Desventajas:

  • No es suficiente con guardar la seed y password, tu aplicacion tambien necesita guardar cada cambio que se haga en tus canales abiertos. (La única aplicacion que probé creo que tenia para configurar Google Drive para guardar todos esos cambios).
  • Tu aplicacion tiene que estar pendiente de la red cada cierto tiempo (dependiendo de cuando tiempo se le configuró al canal, por lo general varios días)
  • Capacidad limitada de cantidad de Bitcoins que puedes enviar, depende de la capacidad de los canales de la ruta que atraviesa la transacción.

Lightning sigue en desarrollo activo, y algunas de las desventajas son mejorables sacrificando decentralizacion, como hace Phoenix Wallet, que me parece un muy buen tradeoff para noobs.

Install yt-dlp in Android

This tutorial was originally written for the famous youtube-dl (R.I.P) and later adapted to its successor yt-dlp, the adaptation hasn’t been well tested


Based on https://www.reddit.com/r/Piracy/comments/baufql/youtubedl_the_easy_way_on_android/

Usage

Click Share, select Termux, choose video or audio, and that’s it!, it will be saved to Downloads/{Provider}/ e.g Downloads/Youtube/

Instal

...
Read more

This tutorial was originally written for the famous youtube-dl (R.I.P) and later adapted to its successor yt-dlp, the adaptation hasn’t been well tested


Based on https://www.reddit.com/r/Piracy/comments/baufql/youtubedl_the_easy_way_on_android/

Usage

Click Share, select Termux, choose video or audio, and that’s it!, it will be saved to Downloads/{Provider}/ e.g Downloads/Youtube/

Install Termux

https://termux.com/

Open Termux and run one of these

  1. Yt-dlp + sharing + dialog (Recommended)
  2. Yt-dlp + sharing
  3. Yt-dlp
  4. Termux extras
  5. All-In-One
# Ask for storage permission
termux-setup-storage &&
# Install yt-dlp
apt update && apt upgrade -y && apt install -y python ffmpeg dialog && pip install yt-dlp &&
# Configure to download videos in `Download/{URL's provider (e.g. Youtube)}/{filename}`
mkdir -p ~/.config/yt-dlp &&
echo "# Default Output Directory and Pattern
-o /data/data/com.termux/files/home/storage/downloads/%(extractor_key)s/%(title).150s-%(id)s.%(ext)s" > ~/.config/yt-dlp/config &&
# Configure to open shared URLs with `yt-dlp {url}`
mkdir -p ~/bin &&
echo '#!/bin/bash
    URL=$1
    HEIGHT=15
    WIDTH=40
    CHOICE_HEIGHT=4

    CHOICE=$(dialog \
        --menu "What would you like to download?" \
        $HEIGHT $WIDTH $CHOICE_HEIGHT \
        Video "" \
        Audio "" \
        2>&1 >/dev/tty)

    case $CHOICE in
        Video)
            echo "Downloading video from $URL"
            yt-dlp $URL
            ;;
        Audio)
            echo "Downloading audio from $URL"
            yt-dlp -x $URL
            ;;
    esac' > ~/bin/termux-url-opener &&
chmod +x ~/bin/termux-url-opener
  • Paste the script above 🖢 in the Termux console and press Enter
  • A prompt to enable storage will appear, press y then Enter
  • That’s all!, just wait for it to finish upgrading/installing

Usage

Share an URL to termux, a dialog will ask you to choose if download video or just audio

Yt-dlp + sharing

Usage

Share URL to termux, this will automatically download the video.

But if you always want to download just audio, replace yt-dlp $url with yt-dlp -x $url in the script below 🖣

    # Ask for storage permission
    termux-setup-storage &&
    # Install yt-dlp
    apt update && apt upgrade -y && apt install -y python ffmpeg && pip install yt-dlp &&
    # Configure to download videos in `Download/{URL's provider (e.g. Youtube)}/{filename}`
    mkdir -p ~/.config/yt-dlp &&
    echo "# Default Output Directory and Pattern
    -o /data/data/com.termux/files/home/storage/downloads/%(extractor_key)s/%(title).150s-%(id)s.%(ext)s" > ~/.config/yt-dlp/config &&
    # Configure to open shared URLs with `yt-dlp {url}`
    mkdir -p ~/bin &&
    echo "#!/bin/bash
    url=$1
    yt-dlp $url" > ~/bin/termux-url-opener &&
    chmod +x ~/bin/termux-url-opener

Yt-dlp

This will only install yt-dlp in termux, sharing a URL to Termux will not work, you’ll need to run the command manually from the terminal, e.g yt-dlp https://youtu.be/blahblahblah

    # Ask for storage permission
    termux-setup-storage &&
    # Install yt-dlp
    apt update && apt upgrade -y && apt install -y python ffmpeg && pip install yt-dlp

Termux extras

    # Add special keys to keyboard
    # source: https://wiki.termux.com/wiki/Touch_Keyboard#Extra_Keys_Row(s)
    mkdir -p ~/.termux
    echo "extra-keys = [ \
        ['ESC', '/', '|', 'HOME', 'UP', 'END', 'PGUP', 'ENTER'], \
        ['TAB','CTRL', 'ALT', 'LEFT', 'DOWN', 'RIGHT', 'PGDN', '~'] \
    ]" > ~/.termux/termux.properties
    # Install nano
    apt install -y nano

All-In-One

Yt-dlp + sharing + dialog + Termux extras

    termux-setup-storage &&
    apt update && apt upgrade -y && apt install -y nano python ffmpeg dialog && pip install yt-dlp &&
    mkdir -p ~/.config/yt-dlp &&
    echo "# Default Output Directory and Pattern
    -o /data/data/com.termux/files/home/storage/downloads/%(extractor_key)s/%(title).150s-%(id)s.%(ext)s" > ~/.config/yt-dlp/config &&
    mkdir -p ~/bin &&
    echo '#!/bin/bash
        URL=$1
        HEIGHT=15
        WIDTH=40
        CHOICE_HEIGHT=4

        CHOICE=$(dialog \
            --menu "Que desea descargar?" \
            $HEIGHT $WIDTH $CHOICE_HEIGHT \
            Video "" \
            Audio "" \
            2>&1 >/dev/tty)

        case $CHOICE in
            Video)
                yt-dlp $URL
                ;;
            Audio)
                yt-dlp -x $URL
                ;;
        esac' > ~/bin/termux-url-opener &&
    chmod +x ~/bin/termux-url-opener &&
    mkdir -p ~/.termux &&
    echo "extra-keys = [ \
        ['ESC', '/', '|', 'HOME', 'UP', 'END', 'PGUP', 'ENTER'], \
        ['TAB','CTRL', 'ALT', 'LEFT', 'DOWN', 'RIGHT', 'PGDN', '~'] \
    ]" > ~/.termux/termux.properties

Transacciones en MySQL

Comandos principales

  • START TRANSACTION se utiliza para abrir una transacción (desactiva temporalmente el modo autocommit).
  • COMMIT se utiliza para que los cambios que están pendientes de la transacción actual, se vuelvan permanentes en la BD y cierra la transacción.
  • ROLLBACK para deshechar cualquier cambio pendiente que se haya hecho en la transacción actual y cierra la transacción.

MYSQL por defecto funciona con el modo autocommit activado, eso quiere decir que cualqui

...
Read more

Comandos principales

  • START TRANSACTION se utiliza para abrir una transacción (desactiva temporalmente el modo autocommit).
  • COMMIT se utiliza para que los cambios que están pendientes de la transacción actual, se vuelvan permanentes en la BD y cierra la transacción.
  • ROLLBACK para deshechar cualquier cambio pendiente que se haya hecho en la transacción actual y cierra la transacción.

MYSQL por defecto funciona con el modo autocommit activado, eso quiere decir que cualquier consulta es una transacción implicitamente, osea, si hacen un UPDATE ... en el fondo InnoDB ejecuta algo parecido a esto:

START TRANSACTION;
    UPDATE ...
COMMIT;

Si ese UPDATE ... falla estando el modo autocommit activado, entonces automáticamente se ejecuta un ROLLBACK

Usos

Ejecutar todo o nada

Supongamos que queremos ejecutar una serie de consultas que modifican la BD, pero queremos que se ejecuten todas, y que si cualquiera falla que se deshagan los cambios.

Podemos hacer algo así:

BEGIN

    START TRANSACTION;
        .. Consulta 1 ..
        .. Consulta 2 ..    # Esta consulta siempre falla
        .. Consulta 3 ..
    COMMIT;

END

Al ejecutar ese procedimiento almacenado, se abre una transacción, se ejecuta la Consulta 1, y al intentar la Consulta 2 falla.

Hay un problema aquí, la transacción no se deshace, sino que queda abierta, osea, tiene cambios hechos de la Consulta 1, pero no se han hecho permanentes en la BD (COMMIT), ni tampoco se han devuelto los cambios (ROLLBACK). Y puede ocurrir cualquiera de los dos escenarios. Por ejemplo si se cierra la conexión, la BD ejecuta un ROLLBACK y si se ejecuta cualquiera de estas consultas https://dev.mysql.com/doc/refman/8.0/en/implicit-commit.html se genera un COMMIT implicitamente.

Si en este estado de la BD se hace un SELECT para verificar si en la BD se hicieron los cambios de la Consulta 1, se va a obtener distintas respuestas dependiendo de si el SELECT se ejecuta desde la misma conexión/sesión o no.

Cuando se abre una transacción y se hace un cambio, ese cambio solamente es visible para esa misma sesión que abrió la transacción.

Solución

Explicitamente hacer ROLLBACK cuando ocurra un error.

Para eso definimos un HANDLER para los errores, es como el equivalente a un try ... catch pero afecta a todo el procedimeinto almacenado

BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
        BEGIN
            ROLLBACK;
            RESIGNAL;
        END;

    START TRANSACTION;
        .. Consulta 1 ..
        .. Consulta 2 ..    # Esta consulta siempre falla
        .. Consulta 3 ..
    COMMIT;

END

Ahora cuando la Consulta 2 falle, se dispara el handler, y se ejecutan los comandos ROLLBACK y RESIGNAL. RESIGNAL sirve para que se vuelva a lanzar el error, ya que de lo contrario el procedimiento almacenado termina “exitosamente” a pesar de que hubo un error.

Bloquear tablas

Luego de abrir una transaccion con START TRANSACTION las consultas como el INSERT, UPDATE o DELETE bloquean las tablas, registros o índices que afecten, hasta que dicha transacción se cierre ya sea con un COMMIT o un ROLLBACK.

Esto sirve para que cualquier otra consulta que quiera leer o modificar los elementos bloqueados, deban esperar hasta que la transacción que los bloquea se cierre y los libere.

Los SELECT normales no generan ningun bloqueo, para eso existen el SELECT ... FOR SHARE y el SELECT ... FOR UPDATE.

En términos simples el FOR SHARE bloquea los elementos seleccionados para que otra consulta no pueda escribir sobre ellos hasta que se cierre la transacción, y el FOR UPDATE adicionalmente impide que tampoco puedan leerlos (igual que los INSERT, UPDATE o DELETE). Mas información https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html

Recomendaciones

Hay que analizar detalladamente todo lo que se mete dentro de una transacción, y reducirlo a lo mas mínimo posible, para evitar Deadlocks o retrasos debido a bloqueos innecesarios.

Usar índices en todos los WHERE de las consultas que bloquean, para que dichos bloqueos sean lo mas óptimos posibles, y se bloquee solo lo que hace falta bloquear.

Ubuntu Network Troubleshoot

NetworkManager

  • nm-applet to run old GUI and activate Enable Networking.

  • nm-connection-editor to edit any weird configuration in the connections.

  • You can also see what the NetworkManager is doing with these commands.

# Run one by one to see if anything is useful
nmcli device
nmcli
nmcli device show

unmanaged means NetworkManager will not manage that device.

Documentation: https://help.ubuntu.com/community/NetworkManager


Netplan

```bash # Cr

...
Read more

NetworkManager

  • nm-applet to run old GUI and activate Enable Networking.

  • nm-connection-editor to edit any weird configuration in the connections.

  • You can also see what the NetworkManager is doing with these commands.

# Run one by one to see if anything is useful
nmcli device
nmcli
nmcli device show

unmanaged means NetworkManager will not manage that device.

Documentation: https://help.ubuntu.com/community/NetworkManager


Netplan

# Create a config file
sudo nano /etc/netplan/ethernet.yaml

and enter this

network:
  version: 2
  renderer: networkd
  ethernets:
    eno1:
      dhcp4: true

replace eno1 with whatever your device is (mine was ens33). To see your list of devices, run ip addr.

Finally apply the new configurations

sudo netplan apply

Documentation: https://netplan.io/reference#examples

Terminal Shortcuts

Default shortcuts

For Ubuntu’s default keybinding settings (i.e Emacs keybindings)

Insert previous arguments

  • Alt+.: insert last argument from last command.
  • Alt+number+.: insert #nth last argument from last command.
  • Alt+- , number , Alt+., zsh: Alt+-+number+.: insert #nth first argument from last command.

In Linux you can r

...
Read more

Default shortcuts

For Ubuntu’s default keybinding settings (i.e Emacs keybindings)

Insert previous arguments

  • Alt+.: insert last argument from last command.
  • Alt+number+.: insert #nth last argument from last command.
  • Alt+- , number , Alt+., zsh: Alt+-+number+.: insert #nth first argument from last command.

In Linux you can repeat commands to go back in history

Example

Last command is:

mv foo bar
  • Alt+0+.: insert first argument of last command = mv
  • Alt+2+.: insert last 2nd argument of last command = foo
  • up , Ctrl+w: last command without the last word = mv foo

Cut/Paste commands

(relative to cursor’s position)

  • Ctrl+w: cuts last word
  • Alt+d: cuts next word
  • Ctrl+k: cuts everything after
  • Ctrl+u, zsh: Alt+w: cuts everything before
  • zsh: Ctrl+u: cuts the entire command (In bash you can combine Ctrl+u , Ctrl+k)
  • Ctrl+y: paste characters previously cut with any Cut command. In bash you can chain cut commands, and Ctrl+y will paste them all.

Move cursor

  • Ctrl+left: move to last word
  • Ctrl+right: move to next word
  • home or Ctrl+a: move to start of command
  • end or Ctrl+e: move to end of command

Other

  • Ctrl+_: undo last edit (very useful when exceeding Ctrl+w)

To see all shortcuts available

  • bash: bind -lp
  • zsh: bindkey -L

Custom shortcuts

Iterate through arguments

only works in zsh, and probably only Linux

Description

Insert any argument of a previous command by iterating one by one until selection

Setup Instructions

run this:

autoload -Uz copy-earlier-word
zle -N copy-earlier-word
bindkey "^[:" copy-earlier-word

(to make this permanent, add it to your ~/.zshrc and restart shell)

Now use Alt+. to go as back as you want, then use Alt+: to iterate through the arguments

Example

Last command is

echo 1 2 3 4 5
  • Alt+.: 5
  • Alt+.+:: 4
  • Alt+.+:+:: 3
  • Alt+.+:+:+:: 2
  • Alt+.+:+:+:+:: 1
  • Alt+.+:+:+:+:+:: echo

source: https://stackoverflow.com/a/34861762/3163120

Other examples

Common usecases

Let’s consider the last command to be:

mv foo bar

up , Ctrl+w: last command without the last word = mv foo

Alt+0+.: first argument of last command = mv

Limitations

“words” only includes a-zA-Z characters, so any symbol character will stop word-shortcuts.

So if last argument was a url and you want to erase it with Ctrl+w it will be a pain.

E.g: curl -I --header "Connection: Keep-Alive" https://stackoverflow.com/questions/38176514/re-run-previous-command-with-different-arguments

To erase that url using Ctrl+w, you’d have to repeat it 12 times.


It would be great to have similar shortcuts that only stops at the space character

Ubuntu fresh setup guide

Update & Upgrade

sudo apt update && sudo apt upgrade -y

Install Packages

sudo apt-get install python3-pip zsh curl wget git dconf-editor
sudo apt-get install p7zip-full smplayer speedtest-cli git-gui pavucontrol paprefs ufw nmap fail2ban gimp most colordiff ncdu qalculate-gtk jq tor thunderbird chrome-gnome-shell gnome-tweaks smartmontools &

# Probably not needed anymore
# fonts-noto

pip install pipx
pipx install yt-dlp tldr llm pipenv

External Repositori

...
Read more

Update & Upgrade

sudo apt update && sudo apt upgrade -y

Install Packages

sudo apt-get install python3-pip zsh curl wget git dconf-editor
sudo apt-get install p7zip-full smplayer speedtest-cli git-gui pavucontrol paprefs ufw nmap fail2ban gimp most colordiff ncdu qalculate-gtk jq tor thunderbird chrome-gnome-shell gnome-tweaks smartmontools &

# Probably not needed anymore
# fonts-noto

pip install pipx
pipx install yt-dlp tldr llm pipenv

External Repositories

llm and ??

config llm

llm install llm-claude-3
llm keys set claude # Insert Claude API key generated in https://console.anthropic.com/settings/keys

script: https://gist.github.com/madacol/2e8e2f5e22b03bfe4f22fbe30dd9c978

#!/bin/zsh

# This script needs to be sourced in a zsh shell instead of being executed
# To do so, set an alias in your shell:
#    alias '??'='source $HOME/.local/bin/nlsh.zsh'
# Then you can use the `??` command from any zsh shell like this:
#    ?? extract the content of ./file.zip in the current directory

echo "$@" \
| llm -s 'Your task is to output oneliner shell commands.
Always answer with a single line shell command, or a multiline using code blocks' \
| tee /dev/shm/nlsh_stdout

extracted_command=$(sed -n '/^[ \t]*```/,/^[ \t]*```/{//!p}' /dev/shm/nlsh_stdout)

# If the command is empty, get the last line of the output
if [ -z "$extracted_command" ]; then
    extracted_command=$(tail -n 1 /dev/shm/nlsh_stdout)
fi

print -rz $extracted_command

add alias ?? in ~/.zshrc

alias ??='source $HOME/.local/bin/nlsh.zsh'

Web installs

Shells

Zsh - Edit ~/.zshrc

Add ~/.local/bin to PATH

export PATH=$HOME/.local/bin:$PATH

Add shortcut “Alt+:” to iterate through arguments

autoload -Uz copy-earlier-word
zle -N copy-earlier-word
bindkey "^[:" copy-earlier-word

Aliases

alias youtube-dl=yt-dlp
alias youtube-dl720='yt-dlp -f "bestvideo[height<=720]+bestaudio/best[height<=720]"'
function mkcd () { mkdir -p "$@" && cd "$@"; }

Enable most

export PAGER=most

Enable zsh plugins

plugins=(git zsh-syntax-highlighting zsh-autosuggestions)

Note: Probably need to install them first

Gnome extensions

Dconf

Custom shortcuts

StartUp

  • Cmus gnome-terminal -- cmus

Nautilus

Add context menu to convert to MP4 using ffmpeg

nano ~/.local/share/nautilus/scripts/Convert\ to\ MP4
chmod +x ~/.local/share/nautilus/scripts/Convert\ to\ MP4
#!/bin/bash
ffmpeg -i ${NAUTILUS_SCRIPT_SELECTED_FILE_PATHS}\
        -y -vf "scale='bitand(oh*dar,65534)':'min(720,ih)'"\
        /dev/shm/video.mp4\
        2> /dev/shm/convertToMP4.log

Old stuffs

Built-in dash-to-dock extension

  • org.gnome.shell.extensions.dash-to-dock.click-action = cycle-windows
  • org.gnome.shell.extensions.dash-to-dock.scroll-action = cycle-windows
  • org.gnome.shell.extensions.dash-to-dock.show-windows-preview = false

Zsh

Lazy-load nvm

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . $NVM_DIR/nvm.sh --no-use;
[ -s "$NVM_DIR/bash_completion" ] && . $NVM_DIR/bash_completion;
function __nvm_load_node () {
  unalias node npm npx;
  nvm use 2> /dev/null || nvm use default;
  command=$1;
  shift 1;
  $command $@;
}
alias node='__nvm_load_node node $@'
alias npm='__nvm_load_node npm $@'
alias npx='__nvm_load_node npx $@'

Fish

Functions

  • nvm

    Install bass, then configure these functions

    • **__nvm_load.fish**

      function __nvm_load
        functions -e __nvm_load node npm npx
        bass source ~/.nvm/nvm.sh --no-use ';' nvm use 2> /dev/null '||' nvm use default
      end
    • nvm.fish

    function nvm
      functions -e __nvm_load node npm npx
      bass source ~/.nvm/nvm.sh --no-use ';' source ~/.nvm/bash_completion ';' nvm $argv
    end
    • npx.fish
    function npx
      __nvm_load
      npx $argv
    end
    • npm.fish
    function npm
      __nvm_load
      npm $argv
    end
    • node.fish
    function node
      __nvm_load
      node $argv
    end

Antimicro

sudo add-apt-repository ppa:mdeguzis/libregeek

As of today, a bionic package is still not available, but the artful works!. https://github.com/AntiMicro/antimicro/issues

sudo nano /etc/apt/sources.list.d/mdeguzis-ubuntu-libregeek-bionic.list Replace bionic with artful

sudo apt update
sudo apt install antimicro

Put config file https://gist.github.com/madacol/19f8c71ba98f484a4294ccfe90e88e6e in ~/.config/antimicro

SinkSwitcher

https://github.com/madacol/sinkSwitcher

Recover a BIP39 password with a P2SH(P2WPKH) address by Brute-forcing

There’s an up-to-date fork of this tool here https://github.com/3rdIteration/btcrecover
Use it skeptically, just as you should with this post


This will allow you to brute-force wallets supported by btcrecover but that have P2SH(P2WPKH) addresses (i.e 3G2rhsBYmP6RKrjW1kVE1kJB9qjwggaCZw) BIP141 (addresses not yet supported by btcrecover as of writing).

If my changes have been merged [here](https://github.com/gurnec/btcrecover/pull

...
Read more

There’s an up-to-date fork of this tool here https://github.com/3rdIteration/btcrecover
Use it skeptically, just as you should with this post


This will allow you to brute-force wallets supported by btcrecover but that have P2SH(P2WPKH) addresses (i.e 3G2rhsBYmP6RKrjW1kVE1kJB9qjwggaCZw) BIP141 (addresses not yet supported by btcrecover as of writing).

If my changes have been merged here, then get out of here, download btcrecover and follow it’s instruction.

If not, then download my version of the tool https://github.com/madacol/btcrecover/tree/p2wpkh-p2sh and follow original instructions btcrecover

You can get the modified version in 2 ways

I used this to recover a Samourai wallet that had a BIP39 seed, where the seed is generated using a mnemonic (12–24 words) and a passphrase. To bruteforce it you need to have and address or the master public key, and I only had addresses that started with a 3… ( P2SH(P2WPKH) addresses ), and they use BIP49 to derive the keys

You can test this by making a password.txt inside btcrecover’s folder containing:

1
2
3
4
5

and running this command:

python2 btcrecover.py --bip39 --tokenlist passwords.txt --addrs 3G2rhsBYmP6RKrjW1kVE1kJB9qjwggaCZw --mnemonic-prompt --addr-limit 1 --bip32-path "m/49'/0'/0'/0"

You will be asked for the mnemonic:

final round trust era topic march brain envelope spoon minimum bunker start

This should give you the following result:

console screenshot showing the recovered password “12345”

You can customize LOTS of things, go and read the full instructions https://github.com/gurnec/btcrecover/blob/master/TUTORIAL.md#btcrecover-tutorial

Configuring Age Of Empires 2: Forgotten Empires on Ubuntu

We’re going to need the official Installers for Age of Empires: Age of Kings and The Conquerors.

  1. Install PlayOnLinux (POL)
  2. Run POL’s Age of Kings script (Here you’ll need Age of Kings Installer)
  3. Run POL’s The Conquerors script (Here you’ll need The Conquerors Installer)
  4. Run POL’s Forgotten Empires script (This will download and install automatically this installer: https://forgottenempires.net/AoFE_Launcher.exe)
  5. If sound does not work,
...
Read more

We’re going to need the official Installers for Age of Empires: Age of Kings and The Conquerors.

  1. Install PlayOnLinux (POL)
  2. Run POL’s Age of Kings script (Here you’ll need Age of Kings Installer)
  3. Run POL’s The Conquerors script (Here you’ll need The Conquerors Installer)
  4. Run POL’s Forgotten Empires script (This will download and install automatically this installer: https://forgottenempires.net/AoFE_Launcher.exe)
  5. If sound does not work, change wine version to a newer one

It works!, but it has some annoying bugs with resolution and fullscreen, so the solution I got was to make it windowed, almost borderless and filled the screen with it. So let’s do it.

  1. Run FixAoFE.exe in the Forgotten virtual drive

    1. Open POL and select Forgotten
    2. Click Configure
    3. Tab Miscellaneous
    4. Click “Run a .exe …”
    5. Locate FixAoFE.exe in ~/PlayOnLinux's virtual drives/AOE2_forg/drive_c/Program Files/Microsoft Games/Age of Empires II/age2_x1/FixAoFE.exe
  2. Enable Windowed mode

    Windowed mode
  3. Open Configure Wine

    Configure Wine
  4. Open Graphics tab, and make exactly these selections:

    Graphics settings
  5. Open the game and choose your desired resolution in options. This resolution will only affect in-game, it won’t affect the menu.

It still has some bugs, but it’s now playable, the inner game is mostly free of bugs, and with the new 60 FPS feature of the Forgotten Empires, is a net improvement.

There are two bugs that still bother me:

  1. For some really strange reason, enemies’ Castles do not start firing until you get really close to them, or maybe attack them. I’ve seen this happen in the first chapter of barbarossa campaign.

  2. Multiplayer does not work. Right now, if you try to create a multiplayer game it will go back to the menu immediately. We can overcome this bug by installing directplay:

    Install directplay

    It now Creates a multiplayer game

    Multiplayer game

    But still, I haven’t been able to join a multiplayer game between two computers using the same setup, I’ve tried with LAN, with IP, disabling firewalls, but no luck :(

How to ignore monitoring a disk from the smartd daemon

Source: https://unix.stackexchange.com/a/111407/77354

-d ignore is a new directive which allows ignoring a device from DEVICESCAN.

If we want to ignore the disk /dev/sdb, edit /etc/smartd.conf and add /dev/sdb -d ignore before the line that starts with DEVICESCAN

It should look something like this:

``` …

The word DEVICESCAN will cause any remaining lines in this

configuration file to be ignored: it tells smartd to scan for all

ATA and SCSI devices. DEVICESCAN may be fo

...
Read more

Source: https://unix.stackexchange.com/a/111407/77354

-d ignore is a new directive which allows ignoring a device from DEVICESCAN.

If we want to ignore the disk /dev/sdb, edit /etc/smartd.conf and add /dev/sdb -d ignore before the line that starts with DEVICESCAN

It should look something like this:

...

# The word DEVICESCAN will cause any remaining lines in this
# configuration file to be ignored: it tells smartd to scan for all
# ATA and SCSI devices.  DEVICESCAN may be followed by any of the
# Directives listed below, which will be applied to all devices that
# are found.  Most users should comment out DEVICESCAN and explicitly
# list the devices that they wish to monitor.
/dev/sdb -d ignore
DEVICESCAN -d removable -n standby -m root -M exec /usr/share/smartmontools/smartd-runner

...

I needed to stop the warning email messages from the smartd daemon, yeah I know my disk is failing so just shut up.

Also found out about the interesting -M diminishing directive

diminishing - send additional warning reminder emails, after a one-day interval, then a two-day interval, then a four-day interval, and so on for each type of disk problem detected. Each interval is twice as long as the previous interval.

Manual Beninca DA2XS

Aqui está manual para DA2XS y DA2XSE, BENINCA https://drive.google.com/file/d/0BzTgLSi24aobTGVTVXNPc3FnQzQ

Vista superior de tarjeta electrónica

Me costó mucho encontrar este manual con las instrucciones para configurar este Sistema para Portones Eléctricos marca BENINCA. Tuve que escribir en google “funz p.p ch. aut cond prelamp” para al fin poder llegar al manual en este link http://www.vratcom.com/docsx/DA2XS.pdf

La razón de todo esto es que he e

...
Read more

Aqui está manual para DA2XS y DA2XSE, BENINCA https://drive.google.com/file/d/0BzTgLSi24aobTGVTVXNPc3FnQzQ

Vista superior de tarjeta electrónica

Me costó mucho encontrar este manual con las instrucciones para configurar este Sistema para Portones Eléctricos marca BENINCA. Tuve que escribir en google “funz p.p ch. aut cond prelamp” para al fin poder llegar al manual en este link http://www.vratcom.com/docsx/DA2XS.pdf

La razón de todo esto es que he estado teniendo problemas con uno de estos sistemas, y mientras estaba tratando de identificar la falla me di cuenta de que esta tarjeta tiene la opción para configurar el cierre automatico del porton tras un periodo de inactividad (Algo que me molestó mucho que le "“faltara”" a este sistema cuando lo instalaron los "“Expertos”"), el problema es que no tenía ni idea de como configurar ese tiempo, y cuando habilitaba la función de cierre automatico el portón simplemente se cerraba tras estar 1 seg detenido, por eso decidi buscar este manual hasta conseguir las siguientes instrucciones:

Instrucciones según el manual:

Programación del tiempo de cierre automático

Presionando 2 veces el pulsador MENÚ se pasa a la programación del tiempo de cierre automático. Cuando están encendidos simultáneamente los LEDs PGM y TCA (primero y tercero desde arriba) la central está lista para memorizar el tiempo de cierre automático. Presionando el pulsador PROG, el LED TCA empieza a parpadear, el LED sigue parpadeando por todo el tiempo en que se mantiene presionado el pulsador. Cuando se suelta el pulsador el LED cesa de parpadear y el TCA es memorizado; seguidamente la central sale de la programación. El tiempo, en segundos, se puede calcular con la intermitencia del LED TCA que efectúa un parpadeo por segundo. El TCA máximo es de 255 segundos (4 min y 15 seg); una vez llegados al máximo la central efectúa de todas maneras la memorización también si no se suelta el pulsador.

Formula para calcular el Radio de curvatura

Primero vayamos al grano.

$$ r = , c = $$
$$ r = + $$

a es la distancia entre cada intersección que hay entre la circunferencia y la secante.

Y b es la distancia perpendicular de la secante hasta una [tangente](https:/

...
Read more

Primero vayamos al grano.


$$ r = \frac{c^2 + b^2}{2b}, \quad \text{donde:} \quad c = \frac{a}{2} $$

$$ r = \frac{a^2}{8b} + \frac{b}{2} $$

a es la distancia entre cada intersección que hay entre la circunferencia y la secante.

Y b es la distancia perpendicular de la secante hasta una tangente paralela a ella. (hay varias formas de definir a b)

Hace mucho tiempo me tocó deducir esta formula porque nunca la logré conseguir por internet (aunque a la final nunca hizo falta jeje).

Lo que me motivó a deducirla era que hacia falta fabricar unos álabes de un impulsor que tenian una ligera curvatura cóncava, y debido a la falta de planos, tocaba copiar la muestra y necesitaba calcular ese radio de curvatura, y asi es que se me ocurrió esta idea de medir la longitud de una regla o un palo recto (a), ponerlo paralelo a la direccion de la curvatura, para luego medir desde el centro del palo perpendicularmente hasta el fondo de la pieza (b), y así con solo esos dos datos poder calcular el radio de curvatura.

Este es el borrador en donde hice las deducciones, pero les advierto que está muy desorganizado y con tachaduras.

Borrador donde hice los cálculos