├── src
├── recipes
│ ├── schneebaelle.jpg
│ └── schneebaelle.md
├── _img
│ └── macos-dhcp-hostname-override.png
├── quotes.md
├── software-development
│ ├── releasing.md
│ ├── learning.md
│ ├── testing.md
│ ├── documentation.md
│ ├── plone.md
│ ├── estimating.md
│ └── tech-debt.md
├── termux.md
├── nomad-life.md
├── music.md
├── ios.md
├── privacy.md
├── artificial-intelligence.md
├── hardware-hacking.md
├── great-bugs.md
├── productivity.md
├── git.md
├── python.md
├── intellij.md
├── package-tracking.md
├── linux.md
├── vim.md
├── http.md
├── keyboard.md
├── text-files.md
├── databases.md
├── books
│ └── 7-habits.md
├── community.md
├── chrome-os.md
├── finance.md
├── business.md
├── reinstall-guide.md
├── usb.md
├── shell-scripting.md
├── macos.md
├── unix.md
├── golang.md
├── wsl.md
├── php.md
├── openpgp.md
└── event-notes
│ └── 2018
│ └── international-php-conference.md
├── README.md
└── LICENSE.txt
/src/recipes/schneebaelle.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scy/knowledge/HEAD/src/recipes/schneebaelle.jpg
--------------------------------------------------------------------------------
/src/_img/macos-dhcp-hostname-override.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scy/knowledge/HEAD/src/_img/macos-dhcp-hostname-override.png
--------------------------------------------------------------------------------
/src/quotes.md:
--------------------------------------------------------------------------------
1 | # Quotes
2 |
3 | ## Focus
4 |
5 | > The essence of strategy is choosing what not to do.
6 | > — Michael Porter
7 |
--------------------------------------------------------------------------------
/src/software-development/releasing.md:
--------------------------------------------------------------------------------
1 | # Releasing Software
2 |
3 | * Why (and how) you should probably [keep a changelog](http://keepachangelog.com/en/1.0.0/).
4 |
--------------------------------------------------------------------------------
/src/termux.md:
--------------------------------------------------------------------------------
1 | # Termux
2 |
3 | _An awesome Unix environment for Android._
4 |
5 | ## Bring Termux to the front using a shell command
6 |
7 | ```sh
8 | am start -n com.termux/.app.TermuxActivity
9 | ```
10 |
11 | This doesn't seem to work if the screen is locked though.
12 |
--------------------------------------------------------------------------------
/src/nomad-life.md:
--------------------------------------------------------------------------------
1 | # (Digital) Nomad Life
2 |
3 | … is what I want to achieve.
4 |
5 | ## Where to go and where to stay
6 |
7 | * [Nomad List](https://nomadlist.com/) is a beautiful website that allows you to filter possible destinations based on living conditions, internet speed, safety etc. It's limited to cities, though.
8 |
--------------------------------------------------------------------------------
/src/music.md:
--------------------------------------------------------------------------------
1 | # Making Music
2 |
3 | ## Programmable
4 |
5 | * Csound
6 | * Pure Data
7 | * SuperCollider
8 | * I've tried [building SuperCollider on Termux/Android](https://scsynth.org/t/my-attempt-to-build-supercollider-on-termux-android/1297) and didn't quite manage to do it. Just in case anyone is interested in continuing from where I left off.
9 |
--------------------------------------------------------------------------------
/src/software-development/learning.md:
--------------------------------------------------------------------------------
1 | # Learning to Code
2 |
3 | Resources that are helpful for beginners, but also improve the skills of experts.
4 |
5 | ## Challenges/Assignments
6 |
7 | [Advent of Code](http://adventofcode.com/) is a yearly set of challenges aimed at a variety of skill levels.
8 | You get an assignment and randomized input data, develop a solution in a language that you choose and submit a result value.
9 |
--------------------------------------------------------------------------------
/src/ios.md:
--------------------------------------------------------------------------------
1 | # Apple iOS
2 |
3 | ## Folder Structure of an iTunes Backup
4 |
5 | The iPhone Wiki has a great [technical documentation about the iTunes Backup folder structure](https://www.theiphonewiki.com/wiki/ITunes_Backup).
6 | iTunes basically takes all the files on the Unix filesystem, hashes their path (prefixed by a “domain name”) using SHA1, and uses that as the file name.
7 | There’s also the `Manifest.mbdb` file containing an index.
8 |
--------------------------------------------------------------------------------
/src/privacy.md:
--------------------------------------------------------------------------------
1 | # Privacy and Anonymity
2 |
3 | Technical background, tutorials and stuff.
4 |
5 | ## Howtos
6 |
7 | Think of The Intercept what you want, but they’ve got a nice article by Micah Lee about [how to create an account for a website anonymously](https://theintercept.com/2017/02/20/how-to-run-a-rogue-government-twitter-account-with-an-anonymous-email-address-and-a-burner-phone/), even when they require you to provide a phone number.
8 |
--------------------------------------------------------------------------------
/src/artificial-intelligence.md:
--------------------------------------------------------------------------------
1 | # Artificial Intelligence (AI)
2 |
3 | Or Machine Learning, if you want to.
4 |
5 | ## Overview
6 |
7 | Yonatan Zunger wrote a long, but interesting article on Medium about [the basics of AI and why it forces us to ask (and answer!) some tough questions](https://medium.com/@yonatanzunger/asking-the-right-questions-about-ai-7ed2d9820c48).
8 | It can also serve pretty well as a general introduction into the topic.
9 |
--------------------------------------------------------------------------------
/src/hardware-hacking.md:
--------------------------------------------------------------------------------
1 | # Hardware Hacking
2 |
3 | Stuff that blinks, makes noise or breaks when you sit on it.
4 |
5 | ## Links
6 |
7 | * It's in German and it's sometimes hard to read, but [Raidys Bastlerecke](https://www.promobil.de/forum/threads/33585-Raidys-Bastlerecke) is a currently 104 pages long thread in the forums of the caravaning magazine ProMobil.
8 | I'm including it here because it contains some really crazy and/or unusual projects and insights.
9 |
--------------------------------------------------------------------------------
/src/great-bugs.md:
--------------------------------------------------------------------------------
1 | # Great Bugs
2 |
3 | An art in its own way.
4 |
5 | ## I can't print on Tuesdays
6 |
7 | I don't want to spoil your fun with this one, so I won't explain it here.
8 | Just follow the links to [Ubuntu bug #255161](https://bugs.launchpad.net/ubuntu/+source/cupsys/+bug/255161/comments/28) or [Ubuntu bug #248619](https://bugs.launchpad.net/ubuntu/+source/file/+bug/248619) that resulted from the investigation there.
9 |
10 | This bug is my favorite example for when people say something like "that's impossible" or "it appears totally at random".
11 |
--------------------------------------------------------------------------------
/src/productivity.md:
--------------------------------------------------------------------------------
1 | # Productivity
2 |
3 | _I'll tidy up this list once I get to it._ 😏
4 |
5 | ## Dealing with email
6 |
7 | [Email Isn't The Thing You're Bad At](https://glyph.twistedmatrix.com/2016/04/email-isnt-the-problem.html) by Glyph is a lengthy article that first talks about the problem and the psychological(?) background of why we're overwhelmed by our inbox, and then goes on to suggest some of the solutions I've heard elsewhere already, like inbox zero, email bankruptcy, the GTD concept of a trusted system etc.
8 | It's a nice read if you've got the time, but not exactly important.
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Some of my Knowledge
2 |
3 | _… hoping that it’ll be useful to others._
4 |
5 | This repository is supposed to collect many of the bits and pieces of “wisdom” I’ve accumulated over the years.
6 | Maybe I’ll turn it into some kind of a website at some point (that’s why [everything is in `src/`](https://github.com/scy/knowledge/tree/master/src)), but for now, the most important thing is that I can write stuff down fast.
7 |
8 | I also have lots of notes in my WorkFlowy (from when I still used that), but most of those are not public.
9 | I’ll migrate them when I find the time …
10 |
--------------------------------------------------------------------------------
/src/git.md:
--------------------------------------------------------------------------------
1 | # Git
2 |
3 | Awesome version control.
4 |
5 | ## Revert changes only to a single file
6 |
7 | As of the time of writing this, Git doesn't have a command that says "revert the changes that commit `C` introduced to file `F`".
8 | You can revert the changes `C` did for _all_ the file it touched, or you can check out `F` as it was before `C`, but reverse-applying the patch, and only to one (or more) files requires a bit more effort, as [this Stack Overflow question](https://stackoverflow.com/q/23068790) points out.
9 |
10 | The easiest command that's provided there is this:
11 |
12 | ```sh
13 | git show $C -- $F | git apply -R
14 | ```
15 |
16 | Note that this won't work if `C` is a merge commit.
17 |
--------------------------------------------------------------------------------
/src/python.md:
--------------------------------------------------------------------------------
1 | # Python
2 |
3 | _Another nice language._
4 |
5 | ## Embed e-mail addresses directly in source code
6 |
7 | If, for whatever reason, you’d like to write e-mail addresses directly into the code (instead of using the boring method of writing them into a string literal), check out the proof of concept called [mailweird](https://gist.github.com/L3viathan/92addec9501969ae628c90b9100f3177) by [L3viathan](https://github.com/L3viathan).
8 |
9 | I didn’t have time to fully understand it, but apparently what it’s doing is using a decorator (`@mail`) to replace function’s code by accessing its bytecode using `fn.__code__` and then iterating over the bytecode primitives, doing some stack magic to replace them.
10 |
--------------------------------------------------------------------------------
/src/intellij.md:
--------------------------------------------------------------------------------
1 | # IntelliJ (or PhpStorm, or WebStorm, or GoLand, …)
2 |
3 | If you like IDEs, this one is pretty good.
4 |
5 | ## Suggested plugins
6 |
7 | * [EditorConfig](https://editorconfig.org/) (should be installed and enabled by default)
8 | * HTTP Client, provides a nice [editor-based way to write your requests](https://www.jetbrains.com/help/webstorm/http-client-in-product-code-editor.html)
9 | * [Php Inspections (EA Extended)](https://plugins.jetbrains.com/plugin/7622-php-inspections-ea-extended-) is awesome if you write PHP, as it provides advanced inspections to keep you from writing bad code
10 | * [String Manipulation](https://plugins.jetbrains.com/plugin/2162-string-manipulation) can convert to UPPER/lower/camelCase (SpOnGeBoBcAsE is missing tho), but more importantly en-/decode Base64, shuffle lines, sort and align text etc.
11 |
--------------------------------------------------------------------------------
/src/software-development/testing.md:
--------------------------------------------------------------------------------
1 | # Testing Software
2 |
3 | ## General Overview
4 |
5 | [The Practical Test Pyramid](https://martinfowler.com/articles/practical-test-pyramid.html) by Martin Fowler provides a detailed overview over the different ways that exist to test software (covering both automatic and manual methods), and when to use which.
6 | Not only that, but also _how many_ tests you should create based on how high- or low-level the tests are:
7 | Many unit tests, but little integration and UI tests.
8 | The article is _really_ long and the examples are focused on Java and Spring magic, which makes some of them hard to read for people who have not much experience with these technologies.
9 | It's a nice introduction to the topic, but also people experienced with testing can learn one or two new things or profit from some of the tools mentioned in it.
10 |
--------------------------------------------------------------------------------
/src/software-development/documentation.md:
--------------------------------------------------------------------------------
1 | # Documenting Software
2 |
3 | ## Architectural Decisions
4 |
5 | This is basically taken from the good ideas at [Documenting architecture decisions, the Reverb way](https://product.reverb.com/documenting-architecture-decisions-the-reverb-way-a3563bb24bd0).
6 |
7 | * Store architectural decisions in your codebase.
8 | * Document _thoughts_ and _decisions_, not the state of things.
9 | Even when the state changes (i.e. documentation drift), you are still able to find out what your plan was and (even more important) _why_ that was your plan.
10 | * Include a summary (tldr) section and one that goes into details.
11 | * Include a tags/SEO section, where you put things like class and function names, business concepts etc.
12 | This allows you (and others!) to find the documentation by accident when grepping.
13 | * Link to these documents from comments in the code.
14 |
--------------------------------------------------------------------------------
/src/package-tracking.md:
--------------------------------------------------------------------------------
1 | # Package Tracking
2 |
3 | Some information on package or mail tracking services and APIs.
4 |
5 | ## Deutsche Post
6 |
7 | ### Einschreiben (Registered Mail)
8 |
9 | Currently (2018-01-28) the tracking web form uses POST requests, so it’s not immediately obvious how to create a tracking URL for registered mail.
10 | However, it’s possible.
11 |
12 | Example link: `https://www.deutschepost.de/sendung/simpleQueryResult.html?form.sendungsnummer=RG083025025DE&form.einlieferungsdatum_tag=23&form.einlieferungsdatum_monat=10&form.einlieferungsdatum_jahr=2012`
13 |
14 | Replace `RG083025025DE` with the tracking number (“Sendungsnummer”), `23` with the day, `10` with the month and `2012` with the year when the Einschreiben was brought to the post office, i.e. the date of the receipt.
15 | Thanks to the JTL Forum for this information.
16 | ([Source thread](https://forum.jtl-software.de/threads/tracking-url-deutsche-post.13981/))
17 |
--------------------------------------------------------------------------------
/src/linux.md:
--------------------------------------------------------------------------------
1 | # Linux
2 |
3 | Just another Unix.
4 | Make sure to have a look at the [Unix](unix.md) page as well.
5 |
6 | ## Keeping a plain-text terminal from blanking
7 |
8 | Just a one-liner.
9 | Can most likely be improved, but kind of works.
10 |
11 | ```sh
12 | TERM=linux setterm -blank 0 | sudo tee /dev/tty0
13 | ```
14 |
15 | ## APT: Installing things noninteractively
16 |
17 | When using `apt` or `apt-get` to install something and you want to make sure that it doesn’t wait for user interaction (for example because you’re calling it from a `Dockerfile`), using `apt install -y` is not enough.
18 | Packages requesting interactive configuration (for example the configuration of the machine’s time zone) will still wait for your reaction, even if there’s nothing connected to stdin (at least that’s how it looked like to me).
19 |
20 | The trick is to also set the environment variable `DEBIAN_FRONTEND=noninteractive`.
21 | So, in a `Dockerfile`, you would write something like:
22 |
23 | ```dockerfile
24 | FROM ubuntu
25 |
26 | ENV DEBIAN_FRONTEND=noninteractive
27 |
28 | RUN apt update \
29 | && apt install -qy whatever
30 | ```
31 |
--------------------------------------------------------------------------------
/src/vim.md:
--------------------------------------------------------------------------------
1 | # Vim
2 |
3 | When reading this, you might be interested in [my Vim config](https://github.com/scy/dotfiles/tree/master/.vim), too.
4 | I also have a [NeoVim config](https://github.com/scy/dotfiles/tree/master/.config/nvim), but it's mainly just loading the Vim one.
5 |
6 | ## Enable folding for Markdown files
7 |
8 | There are some plugins to enable Vim's folding inside Markdown files.
9 | However, in a sufficiently recent Vim, this functionality is already included, it's just not enabled by default.
10 | Try this:
11 |
12 | ```viml
13 | let g:markdown_folding=1
14 | ```
15 |
16 | ## VimWiki
17 |
18 | [VimWiki](https://vimwiki.github.io/) is a nice plugin to create a personal wiki inside of Vim.
19 | Some helpful links:
20 |
21 | * [VimWiki cheatsheet](http://thedarnedestthing.com/vimwiki%20cheatsheet)
22 | * [patrickdavey's config](https://github.com/patrickdavey/dotfiles/blob/682e72e4b7a70e50858d1a3b7f0713ce6b470fb6/vim/.vimrc#L243-L281) demonstrating multiple wikis, nested syntaxes, and using Markdown with folding
23 | * a [Hacker News thread about VimWiki](https://news.ycombinator.com/item?id=13157497) with some additional hints
24 |
--------------------------------------------------------------------------------
/src/http.md:
--------------------------------------------------------------------------------
1 | # HTTP
2 |
3 | ## Bearer Tokens Limited to OAuth?
4 |
5 | There's some confusion about whether doing `Authorization: Bearer …` type tokens requires implementing OAuth or whether any kind of token-based authentication qualifies for this scheme.
6 | Do you have to roll your own scheme (`Authorization: AcmeToken …`) instead?
7 |
8 | Funny enough:
9 | [RFC 6750](https://tools.ietf.org/html/rfc6750) (OAuth 2.0 Bearer Token Usage) answers the question already in its introduction.
10 |
11 | > While designed for use with access tokens resulting from OAuth 2.0 authorization [RFC6749] flows to access OAuth protected resources, this specification actually defines a general HTTP authorization method that can be used with bearer tokens from any source to access any resources protected by those bearer tokens.
12 |
13 | So, if you're doing token-based authentication, feel free to use `Bearer`.
14 |
15 | ## 401 Unauthorized Requires a `WWW-Authenticate` Header
16 |
17 | The headline basically says it all:
18 | When your service responds with a `401` status, [RFC 7235 Section 3.1](https://tools.ietf.org/html/rfc7235#section-3.1) states that it also _"MUST send a WWW-Authenticate header field (Section 4.1) containing at least one challenge applicable to the target resource"_.
19 |
--------------------------------------------------------------------------------
/src/keyboard.md:
--------------------------------------------------------------------------------
1 | # Keyboard(s)
2 |
3 | _The things attached to computers with letters printed on them, not the black-and-white musical variant._
4 |
5 | ## Logitech K810
6 |
7 | This is a Bluetooth keyboard that I use some of the time.
8 | You can switch between three paired devices by the press of a button, it has a backlight and the keys are alright.
9 | However, it has some rather undocumented features:
10 |
11 | ### Switching between ISO and ANSI layout
12 |
13 | Physically, the keyboard uses the ISO layout, i.e. there's a key between Left Shift and Z (or Y, depending on the language it's labeled with).
14 | We'll call that key PinkyKey from now on, to make it easier to refer to.
15 |
16 | You can use the undocumented combination Fn+Alt Gr+PinkyKey to toggle between whether the keyboard sends ISO or ANSI keycodes for PinkyKey and the one left of 1.
17 | For the US layout, which I'm using, this means that you can swap backslash/pipe (generated by the one key) and backtick/tilde (generated by the other).
18 |
19 | Thanks a lot to [David Piggott](https://github.com/dhpiggott) for [his article about it](https://www.dhpiggott.net/2017/02/11/how-to-revert-the-accidental-swapping-of-the-backslash-and-backtick-keycodes-with-the-logitech-k810/)!
20 |
--------------------------------------------------------------------------------
/src/software-development/plone.md:
--------------------------------------------------------------------------------
1 | # Plone
2 |
3 | I'm not _that_ experienced in coding for Plone, so this is basically my collection of notes for things I need regularly or didn't know how to do.
4 |
5 | ## Access ZMI
6 |
7 | Add `/manage` to the base URL.
8 |
9 | ## Allowing a content type to be displayed in a certain view
10 |
11 | ZMI → `portal_types`, then select the type you want to change (e.g. _Folder_).
12 | Then, add the name of your view (the `name` attribute you've used in `configure.zcml`) to the list of _Available view methods_.
13 |
14 | This can also be done programmatically, but I don't know how (yet).
15 |
16 | ## Providing macros for several templates in a common file
17 |
18 | Register a `.pt` file like so:
19 |
20 | ```xml
21 |
27 | ```
28 |
29 | Then, use it by its name (here `ps.plone.mls.macros`) in other templates like so:
30 |
31 | ```xml
32 |
Show list of listings.
33 | ```
34 |
35 | Thanks [Thomas](https://github.com/it-spirit) for showing me [how `ps.plone.mls`](https://github.com/propertyshelf/ps.plone.mls/blob/b902a800f8b0199d8a3d145f582471c38eaae015/src/ps/plone/mls/browser/configure.zcml#L73-L79) [does it](https://github.com/propertyshelf/ps.plone.mls/blob/b902a800f8b0199d8a3d145f582471c38eaae015/src/ps/plone/mls/browser/listings/templates/listing_results_p5.pt#L19)!
36 |
--------------------------------------------------------------------------------
/src/recipes/schneebaelle.md:
--------------------------------------------------------------------------------
1 | # Schneebälle
2 |
3 | Süßes, zartes Biskuitgebäck.
4 | Traditionelles Weihnachtsdessert in meiner Familie.
5 |
6 | *Enthält Eier, Milchprodukte, Alkohol. Glutenfreie Variante mit entsprechendem Mehl einfach zu bewerkstelligen.*
7 |
8 | 
9 |
10 | ## Zutaten
11 |
12 | * 5 EL (60 ml) kaltes Wasser
13 | * 1 Prise Salz
14 | * 7 Eier
15 | * 150 g Kokosflocken
16 | * 2 Päckchen Sahnesteif
17 | * 400 g Sahne
18 | * 125 ml Amaretto
19 | * 150 g Crème fraîche
20 | * 250 g Magerquark
21 | * ½ TL (2 g) Backpulver
22 | * 200 g Mehl
23 | * 200 g Zucker für die Biskuit-Basis
24 | * 50 g Zucker für die Schneebälle
25 | * 2 Päckchen Vanillezucker
26 | * 18 (Papier-)Förmchen
27 |
28 | ## Zubereitung
29 |
30 | ### Biskuit
31 |
32 | * Eier trennen.
33 | * Eiweiß, Salz und Wasser steif schlagen. **Währenddessen** ein Päckchen Vanillezucker und 200 g Zucker einrieseln lassen.
34 | * Eigelb einzeln darunter schlagen.
35 | * Mehl und Backpulver mischen und unterheben.
36 | * Auf ein Backblech streichen und im vorgeheizten Backofen bei 175 °C 25–30 Minuten backen.
37 | * Auskühlen lassen.
38 |
39 | ### Bälle
40 |
41 | * Quark, Crème fraîche, Amaretto, 50 g Zucker und ein Päckchen Vanillezucker glatt verrühren.
42 | * Sahne steif schlagen, dabei Sahnesteif einrieseln lassen. Unter die Amaretto-Quark-Masse ziehen.
43 | * Biskuit in feine Stücke zupfen und zur Amaretto-Quark-Masse geben.
44 | * Alles gut zu einem Teig vermischen.
45 | * Aus der Masse 18 gleichmäßige Bälle formen. In den Kokosflocken wenden und in Förmchen setzen.
46 | * Kühl servieren.
47 |
--------------------------------------------------------------------------------
/src/text-files.md:
--------------------------------------------------------------------------------
1 | # Everything(?) as Text Files
2 |
3 | I'm a huge fan of using plain text files for things, because they
4 |
5 | * can easily be version tracked using Git
6 | * can be edited with builtin (okay, except Android) software on every operating system
7 | * can be modified automatically using a wide variety of tools
8 |
9 | This document is supposed to be a collection of things you can do with text files and tools that work with them.
10 |
11 | ## Diagrams, Graphs etc.
12 |
13 | The [Asciidoctor Diagram](https://asciidoctor.org/docs/asciidoctor-diagram/) page serves quite well as a list of tools to create diagrams ([thanks Martin](https://twitter.com/bountin/status/1184110133749661696)).
14 | Here are those that I found most interesting:
15 |
16 | * Tools that create diagrams based on a textual description:
17 | * [PlantUML](http://plantuml.com/) is nice, even if the “UML” part scares you (SVG, PNG, LaTeX output)
18 | * there's an interesting [article about integration with Markdown, VS Code and GitHub](https://blog.anoff.io/2018-07-31-diagrams-with-plantuml/), [thanks hadez](https://chaos.social/@hadez/102968490447571876)
19 | * [TikZ](http://www.texample.net/tikz/) is a LaTeX package and very advanced and versatile, but also complex
20 | * [GraphViz](http://www.graphviz.org/), but its [DOT](https://en.wikipedia.org/wiki/DOT_(graph_description_language)) language is somewhat limited
21 | * [mermaid](https://mermaidjs.github.io/) can render flowcharts, sequence, Gantt and class diagrams in a browser; [its CLI component](https://github.com/mermaidjs/mermaid.cli) relies on a headless Chrome
22 | * Tools that create diagrams based on ASCII art:
23 | * [Shaape](https://github.com/christiangoltz/shaape)
24 | * [ditaa](http://ditaa.sourceforge.net/)
25 |
--------------------------------------------------------------------------------
/src/databases.md:
--------------------------------------------------------------------------------
1 | # Database Design and Peculiarities
2 |
3 | ## In General
4 |
5 | ### Using "key-set pagination" instead of `OFFSET`
6 |
7 | Markus Winand explains in [Paging Through Results](https://use-the-index-luke.com/sql/partial-results/fetch-next-page) an alternative to `SELECT … LIMIT 10 OFFSET 10`:
8 | Pagination by modifying the `WHERE` clause to simply skip all results that the previous page has returned.
9 |
10 | This requires a deterministic `ORDER BY` and is a bit more complex.
11 | Also, you can't simply jump to arbitrary pages.
12 | However, in return you get significantly better speed (for higher pages) and stable results:
13 | While the results `OFFSET` returns change based when something new is inserted to (or deleted from) the results on previous pages, his method stays stable.
14 |
15 | ## MySQL
16 |
17 | (And MariaDB, I guess.)
18 |
19 | ### Comparing Strings
20 |
21 | When comparing strings with `=`, it sometimes looks like MySQL is removing (or trimming) spaces on the right side of the string, i.e. `'a' = 'a '`.
22 | This can also lead to the impression that MySQL does a trim on string values when storing them in the database.
23 | Strings that differ in space padding only will also cause duplicate key errors, even though they _look_ different – and in fact, they are.
24 |
25 | [According to Stack Overflow](https://stackoverflow.com/a/10495807), the real reason is that `=` is defined in SQL-92 and SQL:2008 with this characteristic:
26 |
27 | > If the length in characters of X is not equal to the length in characters of Y, then the shorter string is effectively replaced, for the purposes of comparison, with a copy of itself that has been extended to the length of the longer string by concatenation on the right of one or more pad characters
28 |
29 | The default pad characters are spaces.
30 |
31 | You can work around this by using `LIKE` (which doesn't pad the strings) or apparently by using `binary 'a' = 'a '`.
32 |
--------------------------------------------------------------------------------
/src/books/7-habits.md:
--------------------------------------------------------------------------------
1 | # The 7 Habits of Highly Effective People
2 |
3 | _I am currently reading the German version of this book (Die 7 Wege zur Effektivität), and I'll try to summarize the most important points here. However, I am doing it in German for the time being, and will most likely translate it once I'm done with the book._
4 |
5 | ## Über das Buch
6 |
7 | „Ohje, noch so ein kapitalistischer Selbstausbeutungs-Ratgeber“ könnte man denken, wenn man sich den Titel ansieht.
8 | _Die 7 Wege zur Effektivität – Prinzipien für persönlichen und beruflichen Erfolg,_ geschrieben von einem US-amerikanischen, hm, _Management-Guru?_
9 | Danke, ich verzichte.
10 |
11 | „Ich hab jetzt schon genug über [GTD](https://de.wikipedia.org/wiki/Getting_Things_Done) und Bullet Journals und so gelesen, ich spiele ~~HabitRPG~~ [Habitica](https://de.wikipedia.org/wiki/Habitica), was soll mir da ein Buch aus den Achtzigern noch beibringen können?“ Produktivitätsmethodiken und -tools haben wir doch alle schon dutzende ausprobiert.
12 |
13 | **Darum geht's in diesem Buch jedoch gar nicht. Im Gegenteil.**
14 |
15 | Die 7 Habits wollen gerade _nicht_ das fünfhundertste Patentrezept sein, wie man seine Zeit besser ~~ausbeutet~~ nutzt.
16 | Es geht nicht um Schritt-für-Schritt-Anleitungen, sondern um **Prinzipien.**
17 | Und es geht auch nicht darum, seinem Boss möglichst effektiv in den Arsch zu kriechen – sondern um **Persönlichkeitsbildung.**
18 | Wer will ich sein?
19 | Wie will ich mich verhalten?
20 | Was soll in meiner Grabrede über mich gesagt werden?
21 |
22 | Es geht hauptsächlich um Ethik.
23 | Um Moral.
24 | Um Integrität.
25 |
26 | Und natürlich auch darum, was man selbst, aber auch die Leute um einen herum, davon haben.
27 | Das alles wird nicht nur, aber auch vor einem kapitalistischen Hintergrund dargelegt.
28 | Wer deshalb jedoch die _7 Wege_ in die Ecke „Selbstausbeutung“ stellt und nie wieder eines Blickes würdigt, handelt zu vorschnell.
29 | Es rentiert sich durchaus, mal etwas genauer reinzulesen.
30 | Wem 400 Seiten dafür zu viel sind:
31 | Genau deshalb baue ich hier eine Zusammenfassung mit den wichtigsten Thesen, und in welchem Kapitel man mehr darüber erfährt.
32 |
--------------------------------------------------------------------------------
/src/community.md:
--------------------------------------------------------------------------------
1 | # Community
2 |
3 | _… building, management etc._
4 |
5 | ## Transparency in decision-making
6 |
7 | In [a Twitter thread about the Go team's abandoning of the `dep` dependency manager](https://twitter.com/_rsc/status/1022588240501661696), Russ Cox writes:
8 |
9 | > We let Dep go its own way and end up somewhere unacceptable, making Go modules seem a very large course correction. Worse, the course correction surprised a lot of people, because we'd only shared concerns with the package management group. [[1]](https://twitter.com/_rsc/status/1022592025919737856)
10 | > I thought I could focus on the technical details and let the pkg mgmt group run community interactions. Somehow that led to the entire community believing that Dep was the official endgame, even though my discussions with the pkg mgmt group were clear it was not on that track. [[2]](https://twitter.com/_rsc/status/1022592076691775489)
11 | >
12 | > In retrospect I made lots of mistakes, but the biggest one was not communicating concerns with Dep and plans for go command integration more widely and publicly. I wanted to let the package management group speak with one voice. [[3]](https://twitter.com/_rsc/status/1022592145369313280)
13 | > It seemed to me most productive to talk directly to Sam and the others about what was needed to bring package management into the go command proper. But those concerns were basically ignored, with the result that Dep is and remains unfit as a design for go command integration. [[4]](https://twitter.com/_rsc/status/1022592179175342080)
14 | > And one way or another, Dep was presented and became known as the final answer for Go package management, even though we'd been clear with Sam as early as December 2016 that it was only a step along the way and should be expected to be replaced. [[5]](https://twitter.com/_rsc/status/1022592255989821440)
15 | >
16 | > Making the discussions public instead probably would have been better for adjusting everyone's expectations, and maybe it would have resulted in Dep's design focusing on the showstoppers for go command integration and being easier to incorporate. [[6]](https://twitter.com/_rsc/status/1022592296615862272)
17 |
--------------------------------------------------------------------------------
/src/chrome-os.md:
--------------------------------------------------------------------------------
1 | # Chrome OS
2 |
3 | Hints for working with Google’s high-security, browser-only operating system.
4 |
5 | ## SSH authentication via SmartCards (YubiKey, Nitrokey etc.)
6 |
7 | I was surprised that this is possible at all, let alone _this_ easy.
8 |
9 | Note that I assume that you already have a key on your SmartCard that can be used for SSH authentication, and that the remote machine has this key in its `authorized_keys` file.
10 |
11 | 1. Get the [Secure Shell](https://chrome.google.com/webstore/detail/secure-shell/pnhechapfaindjhompbnflcldabbghjo) and [Smart Card Connector](https://chrome.google.com/webstore/detail/smart-card-connector/khpfeaanjngmcnplbdlpegiifgpfgdco) applications.
12 | Both are from Google, so you don’t need to trust third party devs.
13 | Thanks, Google!
14 | 2. In the connection settings of the Secure Shell app, add `--ssh-agent=gsc` (for Google Smart Card, I guess?) to the SSH relay server options (not the OpenSSH options).
15 | 3. Connect.
16 | (When you use the Smart Card Connector for the first time, you’ll be asked whether Secure Shell should be allowed to access it.)
17 | You’ll be asked for the SmartCard’s PIN in the terminal while the connection is established.
18 |
19 | Seriously, this is way easier than the stuff you have to do with `gpg-agent` on Linux machines.
20 | And have you tried getting it to work on Windows?
21 | Ha. Good luck with that …
22 |
23 | Oh, and Google even made a [simple, concise document explaining how to do it](https://chromium.googlesource.com/apps/libapps/+/master/nassh/doc/hardware-keys.md) that I’ve basically just could have linked to.
24 |
25 | ## Compose Key
26 |
27 | Google also provides a [Compose Key extension for Chrome OS](https://chrome.google.com/webstore/detail/composekey/iijdllfdmhbmlmnbcohgbfagfibpbgba).
28 | The combinations are not configurable, though, although I guess you could fork [the repo](https://github.com/google/extra-keyboards-for-chrome-os/tree/master/composekey) and make your own version of the extension.
29 |
30 | Please note that you need to add _US English_ to your Chromebook's languages (if you don't have it already); the generic _English_ language is not sufficient and you won't see _Compose_ in the list of available keyboards.
31 |
--------------------------------------------------------------------------------
/src/software-development/estimating.md:
--------------------------------------------------------------------------------
1 | # Estimating Software Development
2 |
3 | ## Coarse estimation
4 |
5 | I found some interesting ideas in [Gib mir eine Zahl – Schätzungen entlang des Entwicklungsprozesses](https://www.heise.de/developer/artikel/Gib-mir-eine-Zahl-Schaetzungen-entlang-des-Entwicklungsprozesses-4119174.html?seite=all) by [Annegret Junker](https://twitter.com/grinseteddy):
6 |
7 | Estimate initial ideas (at the beginning of a project) more roughly, for example with t-shirt sizes (XS, S, M, L, XL).
8 | You can't really estimate them down to the hour anyway, and you shouldn't.
9 |
10 | If you're having trouble assigning a shirt size to a task, simply use "M" for the first one and then place the others relative to it.
11 | If something would need to go below XS or above XL, consider moving all of the tasks a level up or down.
12 |
13 | To get a time estimate from that, take a past reference project or task (not too large, not too small) that you know the time effort for and give it a shirt size.
14 | Now, all of your shirt sizes can be roughly estimated using that reference time.
15 | A suggestion for scaling the sizes is by using a Fibonacci-ish factor, with XS being 1, S being 2, M 3, L 5 and XL 8.
16 | So, if your reference project is placed in "S" and took seven sprints, an XL project would take 8*(7/2)=28 sprints.
17 |
18 | You can use a similar estimation technique for functional parts of a project:
19 | Start with one part and place the others next to it:
20 | above it if they're larger, below if they're smaller.
21 | Place other items in between if that's where they belong.
22 | Same-sized items can be placed next to each other.
23 | Now, start with a part that you can easily estimate and let the team give it a Fibonacci number using planning poker.
24 | Continue pokering in the same row; if you find out that some parts are actually more or less complex than initially thought, move them up or down accordingly.
25 | Continue with the other rows.
26 |
27 | Don't consider risk or additional effort (like learning a technology) in complexity numbers; instead, these two estimates should be additional metadata to write on the cards (or in the tickets).
28 | When prioritizing, these should be considered, but don't skew your estimates by mushing every factor together in one number.
29 |
--------------------------------------------------------------------------------
/src/finance.md:
--------------------------------------------------------------------------------
1 | # Finance
2 |
3 | ## Setting up AqBanking
4 |
5 | [AqBanking](https://www.aquamaniac.de/rdm/) is a set of tools for talking to German banks using the HBCI and FinTS protocols.
6 |
7 | ⚠ **Please note:**
8 | This tutorial, which is based on [the official PIN/TAN tutorial](https://www.aquamaniac.de/rdm/projects/aqbanking/wiki/SetupPinTan), is incomplete.
9 | I couldn't get TANs to work, therefore my AqBanking adventures are currently on hold.
10 |
11 | To begin, create a new user.
12 | You'll need the bank's 8-digit BLZ, which should be the 5th to 12th digit of your IBAN.
13 |
14 | `-N` should apparently be the account holder's name, `-u` is your login user name.
15 | The URL to use for `-s` is specific to your bank; ask them if you don't know it.
16 |
17 | This is an example for my [GLS Bank](https://www.gls.de/) login, with `-u` obviously censored:
18 |
19 | ```sh
20 | aqhbci-tool4 adduser -N 'Tim Weber' -b 43060967 -u 123456789 -t pintan -s 'https://hbci-pintan.gad.de/cgi-bin/hbciservlet' --hbciversion=300
21 | ```
22 |
23 | `aqhbci-tool4 listusers` should return a "unique id" for the user you've just created; make sure to use that in all the places below where I use `-u 1`.
24 |
25 | Next, retrieve some basic information about the user:
26 |
27 | ```sh
28 | aqhbci-tool4 getsysid -u 1
29 | aqhbci-tool4 listitanmodes -u 1
30 | ```
31 |
32 | Set your TAN method to something sensible and marked as "available" in the `listitanmodes` command's output, e.g.:
33 |
34 | ```sh
35 | aqhbci-tool4 setitanmode -u 1 -m 6942
36 | ```
37 |
38 | Retrieve a list of all available bank accounts:
39 |
40 | ```sh
41 | aqhbci-tool4 getaccounts -u 1
42 | aqhbci-tool4 listaccounts -v
43 | ```
44 |
45 | If you'd like to store your PIN in an unencrypted text file in order to run some commands noninteractively (with the `-P` parameter to `aqbanking-cli`), use this command to create an empty example file for you, replacing `gls.pin` with a file name of your choice:
46 |
47 | ```sh
48 | aqhbci-tool4 mkpinlist -o gls.pin
49 | ```
50 |
51 | You’ll need to edit that file using your favorite text editor and put the PIN inside.
52 |
53 | AqBanking uses what they call a "context file" to store results of requests in a machine-readable (to some degree also human-readable) format.
54 | Make sure to provide it when calling `aqbanking-cli request` (by supplying the `-c` parameter), else you'll be left with the contents of the context file dumped to stdout with no easy way to query them.
55 |
56 | ```sh
57 | aqbanking-cli -P gls.pin request -c gls.ctx --transactions --balance --fromdate=20200301
58 | ```
59 |
60 | You can now use commands like `aqbanking-cli listbal -c gls.ctx` or `aqbanking-cli listtrans -c gls.ctx`.
61 |
--------------------------------------------------------------------------------
/src/business.md:
--------------------------------------------------------------------------------
1 | # Business
2 |
3 | ## Lean and Agile Insights
4 |
5 | * Measuring progress as execution of an untested plan is useless.
6 | [Paraphrased from _Scaling Lean_.]
7 | This is basically Eric Ries's _achieving failure_ concept:
8 | You can be very successful in building something, only to then realize that nobody wants it.
9 |
10 | ## Metrics and Marketing
11 |
12 | Just some keywords to interesting methods:
13 |
14 | * AARRR Metrics
15 | * Customer Desire Map
16 | * Design Thinking
17 | * Prioritization using ICE: Impact, Confidence, Ease
18 |
19 | ## Business Models
20 |
21 | _Scaling Lean_'s second chapter, _The Back-of-the-Envelope Business Model Test_, introduces interesting, agile and low-effort ideas on how to design your business model and how to check whether it has a chance to work.
22 |
23 | It also suggests increasing your price by not thinking about your costs plus a decent margin, but about the value you provide to your customers.
24 |
25 | The third chapter points out that the goal is not reaching a one-time success, for example the first ten customers, but building "a system that outputs a repeatable customer throughput rate, for example ten new customers a week".
26 |
27 | ## Quotes
28 |
29 | ### Ash Maurya: Scaling Lean
30 |
31 | * While empirical learning is part of that process [of building a business], unless you can quickly turn that learning into measurable business results, you are just accumulating trivia.
32 | * If you could double your pricing, and not lose more than half of your customers, you would still come out ahead. […] Fewer customers (less inventory) mean fewer customer support requests and lower operating costs to service them.
33 | * Most entrepreneurs price their products like artists. They struggle to place a fair value on their product and fall back on a cost-based pricing approach […]. A more effective approach is thinking in terms of value-based pricing in which you anchor your pricing not against your cost structure but against the potential value your customers stand to derive from your product. Remember that as long as your customers derive more value from your product than it costs them, it's still a fair transaction.
34 | * "I know that $200/month might be higher than most other services you are using, but given what you have seen (the demo), if you feel you can build something similar working just half a day a month, then you come out ahead and shouldn't buy our product."
35 | * A time-boxed traction goal is much more tangible than a revenue goal.
36 | * It's not enough to measure what your prospects say—you have to measure what they do.
37 | * The singularity moment of a product is not when you write your first line of code or raise your first round of funding, but when you create your first customer. You go from nothing to creating value.
38 |
--------------------------------------------------------------------------------
/src/reinstall-guide.md:
--------------------------------------------------------------------------------
1 | # Reinstall Guide
2 |
3 | This document is supposed to list all the stuff you want to think of when reinstalling a machine (or smartphone).
4 | It's basically not that different from “what to back up”, but less focused on long-term, regular processes in favor of a short-term perspective.
5 |
6 | * All **Git repositories** should be clean and pushed. On Windows machines, check repos on the host as well as in WSL.
7 | * Are there any open **browser tabs** you don't want to lose? (In _any_ browser?) Consider screenshotting pinned tabs to be able to restore them in the same order.
8 | * Make sure that this isn't the last machine that allows access to your **password manager**.
9 | * Also, since in **Keybase**, devices invite each other, don't kill the last available device, or at least have a paper key available.
10 | * Check some local directories for stuff you want to keep:
11 | * **Desktop** (if applicable)
12 | * **Downloads**
13 | * **Documents**
14 | * **Music** (unlikely)
15 | * **Pictures**
16 | * **Videos** (again, unlikely)
17 | * On a Windows machine, are there any **PuTTY private keys**?
18 | * Do you have the **password to the backup**?
19 | * Write down or securely store **passwords to auto-unlocking external disks**.
20 | * Keep **game data** that doesn't live in the cloud, e.g.
21 | * screenshots
22 | * Elite: Dangerous journals
23 | * DS4Windows profiles
24 |
25 | Sometimes my machines are backed up automatically, but most aren't.
26 | That's because I keep most of my work
27 |
28 | * in (public or private) Git repos
29 | * on an external SSD that's backed up manually or
30 | * synced to multiple devices, which means there's a copy even if one devices dies.
31 |
32 | Automatic cloud backups are not the best solution when you're [living in a van](https://github.com/scy/jessie) with a (generous but limited) mobile data plan.
33 |
34 | Also, some things are really host-specific and should probably not be backed up (but maybe also not migrated when reinstalling?); others need to be backed up manually anyway, e.g. the keys/passwords to the backup.
35 | These include:
36 |
37 | * GPG keychain
38 | * SSH private keys
39 | * SSH `known_hosts` for hosts that I don't keep in my public dotfiles repo
40 | * Borg config, key, password and metadata
41 | * synced directories when they have not been synced (e.g. because you're reinstalling several devices in short succession and don't want to pair a new device to an old one that's going away tomorrow anyway)
42 |
43 | This is a command line that can be used as a template for a manual backup, but needs to be adapted and refined to the machine you're running it on.
44 |
45 | ```sh
46 | cd \
47 | && tar czv \
48 | b17 my \
49 | .ssh/id_* .ssh/*_hosts .ssh/config.d/* \
50 | .autoborg .*.autoborg \
51 | | gpg --symmetric --no-symkey-cache \
52 | > $(date '+%Y-%m-%d-%H%M%S')-$(hostname)-manual-backup.tar.gz
53 | ```
54 |
--------------------------------------------------------------------------------
/src/usb.md:
--------------------------------------------------------------------------------
1 | # Universal Serial Bus (USB)
2 |
3 | _It was supposed to save us all, now it's part of the problem. ([I've ranted about this.](https://twitter.com/scy/status/987642608649490432))_
4 |
5 | ## Per-Port Power Switching (PPPS)
6 |
7 | Also known as _Hub Port Power Control_ (in the USB 2.0 spec), this is a feature of the USB protocol that allows a host to ask a hub to power down some or all of its device-facing ports.
8 | Surprisingly few people know about this, which might be related to surprisingly few hubs actually implementing this correctly.
9 |
10 | In Linux, using `lsusb -v` and looking for `Per-port power switching`, you can find out if your hub supports it.
11 | Note that this doesn't just mean _external_ hubs; the USB ports built in to your machine are a "hub" on their own.
12 |
13 | You can use a tool like [uhubctl](https://github.com/mvp/uhubctl) to control the power state of your hub(s), but usually, that's where the problems start:
14 |
15 | * Most hubs claim to support this, but only disconnect the data lines, leaving 5V power to the device untouched.
16 | This is usually because the hub manufacturer skipped the necessary switches/transistors on their board and the power lines are hard-wired to 5V.
17 | There are people who [modified their USB hub hardware to fix this](https://befinitiv.wordpress.com/2014/02/02/hacking-per-port-power-switching-to-an-usb-hub-2/), but it requires electronics and soldering skills.
18 | * Backward-compatible USB 3 hubs contain both a USB 3 and a USB 2 controller; _both_ have to be told to switch off the port.
19 | uhubctl should do this automatically though.
20 | * If your hub has more than 4 ports, chances are that it actually consists of multiple hubs daisy-chained to each other.
21 | A seven-port hub would for example contain two four-port controllers, with the second controller connected to port 4 of the first controller.
22 | Disabling one of the ports another controller is connected to can lead to undefined behavior, as the downstream controller(s) might not be re-initialized correctly when you re-enable them.
23 |
24 | If you're still determined you want to try this adventure, the [uhubctl GitHub page](https://github.com/mvp/uhubctl) contains a list of devices that are known to be compatible.
25 | Finding a place to buy them is a quest on its own though.
26 |
27 | ## USB 3 hubs on a Raspberry Pi
28 |
29 | RasPis have a bad reputation when it comes to their USB connectivity.
30 | Using a USB 3 hub on a Pi that only supports USB 2 is one of the reasons for that.
31 | _(Note: As of November 2018, when I wrote this, this problem applied to all Raspberry Pi models, including the 3B+.)_
32 |
33 | According to [raspberrypi/firmware#64](https://github.com/raspberrypi/firmware/issues/64), using USB 1 devices on USB 3 hubs can sometimes cause problems.
34 | One of the symptoms is repeated `device descriptor read/64, error -71` messages in `dmesg` and the device not appearing.
35 | If I understand the thread correctly, it's caused by faulty firmware in the hubs, with the Pi's firmware in turn not being tolerant enough to compensate.
36 |
37 | Funny enough, I seem to have had this problem only with USB **2** devices on a USB 3 hub, so my suggestion is to avoid using USB 3 hubs on a Pi altogether.
38 |
39 | The official workaround by the way is to connect a USB 2 hub to one of the ports of the USB 3 hub and to connect your USB 1 devices to that one instead.
40 | I have not tried this method.
41 |
--------------------------------------------------------------------------------
/src/shell-scripting.md:
--------------------------------------------------------------------------------
1 | # Shell Scripting
2 |
3 | Still one of the best ways to get things done.
4 |
5 | ## In-place processing of files (like `sed -i`)
6 |
7 | On GNU systems, `sed` has a `-i` flag that will cause it to read from the given input file and write the result to the same file.
8 | They call that "in-place editing"; what actually happens is that the output is written to a new file with a generated name and, once completed, that file will be moved over the old one (or, if you use the optional "backup file suffix", the old one will be renamed first).
9 |
10 | On macOS, `sed` also has `-i`, but requires passing the backup suffix.
11 | To be fair, you can pass an empty value using `sed -i ''`, but this style is incompatible with GNU `sed`.
12 | See [sed in-place flag that works both on Mac (BSD) and Linux](https://stackoverflow.com/q/5694228) on Stack Overflow for details.
13 |
14 | This makes it impossible to have a cross-platform `sed` invocation with `-i` and without using a suffix.
15 | ([Using a suffix and deleting the backup file](https://stackoverflow.com/a/22084103) is the obvious workaround.)
16 | However, you can cheat by using a construct like this:
17 |
18 | ```sh
19 | { rm file.txt; sed '…' > file.txt ; } < file.txt
20 | ```
21 |
22 | This works because on Unix, a deleted file will still be available as long as there are still open file handles to it, and the `< file.txt` gets interpreted before `rm file.txt`.
23 |
24 | As you can see, we no longer depend on sed's `-i` flag, so you can use this trick with other commands, too!
25 |
26 | **Be careful though:** If the command that follows the `rm` fails to run for some reason, your file will be gone; there's no safety.
27 |
28 | ## Scripts that can run both on Unix and on Windows
29 |
30 | Apparently it's actually possible to write scripts that are both valid POSIX shell and Windows batch files.
31 | The trick is the colon:
32 | In batch files, a colon at the beginning of the line starts a jump label for `GOTO` commands.
33 | In shell scripts, it's a command that does nothing (like `pass` in Python).
34 | And if you do it like this …
35 |
36 | ```
37 | :; echo This is POSIX.
38 | :; exit
39 | ECHO This is Windows.
40 | ```
41 |
42 | … you have a script that works in both worlds.
43 |
44 | This is possible because `;` is not a valid character in a batch file label, so the rest of the line is simply skipped by Windows.
45 | In POSIX, it denotes the start of a new command.
46 | Without that, the `:` command would eat everything after it as its parameters.
47 |
48 | Thanks to the [Stack Overflow answer that explains the technique](https://stackoverflow.com/a/17623721/417040)!
49 |
50 | ## Best practices and FAQs
51 |
52 | The [shellharden project](https://github.com/anordal/shellharden) has a document on [safe ways to do things in bash](https://github.com/anordal/shellharden/blob/master/how_to_do_things_safely_in_bash.md).
53 | In my opinion, it's too bash-centric.
54 | You should always try to write scripts that conform to POSIX.
55 | If your shell script is _so_ sophisticated that you need bash features like arrays, you maybe should use a "real" programming language anyway.
56 |
57 | The [BashPitfalls](http://mywiki.wooledge.org/BashPitfalls) article on Greg's Wiki contains a _lot_ of detailed knowledge.
58 | The [BashFAQ](https://mywiki.wooledge.org/BashFAQ) in the same wiki is also very useful.
59 |
60 | Google has published their [bash style guide](https://google.github.io/styleguide/shell.xml), and yes, it's explicitly about bash.
61 | They even disallow the use of `/bin/sh`.
62 |
--------------------------------------------------------------------------------
/src/macos.md:
--------------------------------------------------------------------------------
1 | # macOS
2 |
3 | ## Salt
4 |
5 | I try to manage all of my machines using [https://saltstack.com](Salt).
6 | On macOS, I find this a bit harder than I expected.
7 |
8 | ### Setting up as a minion
9 |
10 | In my experience, the `pip` installation method as described in [the official installation docs](https://docs.saltstack.com/en/latest/topics/installation/osx.html) doesn't work.
11 | For one, because on 10.13 you don't have `pip` installed by default, just `easy_install`.
12 | Trying to use that to install `pip` and/or Salt leads down another rabbit hole.
13 |
14 | Since you'll be needing the development command line utilities (or whatever they're called) anyway, it's not the worst plan to install Homebrew manually first and then use it to install Salt.
15 | Something like this:
16 |
17 | ```sh
18 | # Official way to install Homebrew.
19 | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
20 | # Then, get Salt.
21 | brew install saltstack
22 | ```
23 |
24 | However, you then don't have Salt running at the startup automatically.
25 | Plus, it's not configured.
26 | I usually don't configure my minions at all, because they should simply connect to a host called `salt` (the default), but on my machine Salt didn't find out its hostname correctly.
27 | Instead, it used an `.in-addr.arpa` name, which isn't optimal.
28 | You should therefore first set a minion ID manually:
29 |
30 | ```sh
31 | sudo mkdir -p /etc/salt && \
32 | echo put-minion-id-here | sudo tee /etc/salt/minion_id
33 | ```
34 |
35 | Then, in order to start the minion automatically, put [the launchd config they provide](https://github.com/saltstack/salt/blob/develop/pkg/darwin/com.saltstack.salt.minion.plist) to `/Library/LaunchDaemons` and call `launchctl load -w /Library/LaunchDaemons/com.saltstack.salt.minion.plist`.
36 |
37 | ### Installing software using homebrew
38 |
39 | macOS minions will automatically use Homebrew as their `pkg` implementation, so you can simply use `pkg.installed` like you're used to.
40 | _But not quite._
41 |
42 | Homebrew is really flexible when it comes to asking it for a package to install.
43 | You can use `gpg`, `gnupg` and even `gpg2` for the same package.
44 | However, `brew list` will return the canonical name, and `pkg.install` will look in that list to find out whether the installation succeeded.
45 | So, if you don't use the canonical name to install, it'll install the package but will then claim that it wasn't installed and return an error.
46 |
47 | To make things worse, [Casks](https://caskroom.github.io) aren't listed in `brew list` at all.
48 |
49 | There's a [fix coming up](https://github.com/saltstack/salt/pull/45309), but unfortunately it's not released yet (as of 2018-02-07).
50 | I've tried to [patch](https://patch-diff.githubusercontent.com/raw/saltstack/salt/pull/45309.diff) my installation manually with [it](https://github.com/saltstack/salt/pull/45309/files), but the patch won't apply.
51 | Maybe I'll just wait and live with error messages lying to my face.
52 |
53 | Lastly, I don't think there's enough documentation on how to install from a cask.
54 | This causes [all kinds of confusion](https://github.com/saltstack/salt/issues/26414).
55 | I've had success with a SLS file like this:
56 |
57 | ```sls
58 | work stuff:
59 | pkg.installed:
60 | - pkgs:
61 | - caskroom/cask/slack
62 | - caskroom/cask/zoomus
63 | - taps: caskroom/cask
64 | ```
65 |
66 | ## Quirks
67 |
68 | ### DHCP Server Overrides the Local Hostname
69 |
70 | 
71 |
72 | I didn't find out _why_ this happens; I suppose the DHCP server delivers the hostname of a previous device that used the same IP address.
73 | Note that this happens even though a host name has been set in the System Preferences (Sharing section).
74 |
75 | To fix it, the following worked for me:
76 |
77 | ```sh
78 | sudo scutil --set HostName whatever
79 | ```
80 |
--------------------------------------------------------------------------------
/src/unix.md:
--------------------------------------------------------------------------------
1 | # Unix
2 |
3 | A good OS, an even better philosophy.
4 |
5 | ## Is `/dev/urandom` Good Enough?
6 |
7 | tl;dr: Yes.
8 |
9 | There's a lot of debate about whether `/dev/urandom` can be used for things that should be "secure".
10 | After all, it's just pseudo-random data, right?
11 | For crypto keys, secrets, unique IDs etc. you should be using `/dev/random` instead, shouldn't you?
12 | Because that's where the "real" random data is?
13 |
14 | No.
15 | As Thomas Hühn explains in his long but interesting article [Myths about /dev/urandom](https://www.2uo.de/myths-about-urandom/) (found it in this [detailed Information Security Stack Exchange answer](https://security.stackexchange.com/a/3939)), both are implemented as a cryptographically secure pseudorandom number generator (CSPRNG).
16 | Both are seeded from the system's entropy pool, and (and this is important) **reseeded** over time when new entropy becomes available.
17 | The only difference is that `/dev/random` blocks when there is not enough entropy available for (re)seeding.
18 |
19 | `/dev/urandom` is _not_ simply seeded with the date and MAC address or whatever, it's seeded using the same entropy that `random` is.
20 | This means that as soon as there has been enough entropy available during the uptime of the system _once_ since booting, it's seeded using good random data.
21 | (And since Linux stores leftover entropy when shutting down and reuses it when booting, the chance of not having enough randomness is even lower.)
22 | And I repeat:
23 | It will even be reseeded once more entropy comes in.
24 |
25 | So, `urandom` is only not good enough if your system didn't have enough entropy since booting up.
26 | How do you know whether it has?
27 | Well, that's the harder problem.
28 | Luckily, there's the [`getrandom()` syscall](http://man7.org/linux/man-pages/man2/getrandom.2.html) available in Linux ≥3.17 (with glibc ≥2.25).
29 | It will use `/dev/urandom` to get random bytes, but block if it hasn't been initialized with enough entropy yet.
30 |
31 | Oh, and if you're confused by the man pages:
32 | That's totally understandable, and there's been a lot of debate about these as well.
33 | For example, `urandom(4)` once said this:
34 |
35 | > A read from the `/dev/urandom` device will not block waiting for more entropy.
36 | > As a result, if there is not sufficient entropy in the entropy pool, the returned values are theoretically vulnerable to a cryptographic attack on the algorithms used by the driver.
37 | > Knowledge of how to do this is not available in the current unclassified literature, but it is theoretically possible that such an attack may exist.
38 | > If this is a concern in your application, use `/dev/random` instead.
39 |
40 | Thomas is talking about that in his article as well, and even mentions [this message by the respected Dan Bernstein](https://www.mail-archive.com/cryptography@randombit.net/msg04763.html), in which he basically says that the man page is full of shit.
41 | Indeed, [in recent versions](http://man7.org/linux/man-pages/man4/random.4.html) it tells a totally different story.
42 | Quoting:
43 |
44 | > The `/dev/random` device is a legacy interface which dates back to a time where the cryptographic primitives used in the implementation of `/dev/urandom` were not widely trusted.
45 |
46 | and
47 |
48 | > The `/dev/random` interface is considered a legacy interface, and `/dev/urandom` is preferred and sufficient in all use cases, with the exception of applications which require randomness during early boot time; for these applications, `getrandom(2)` must be used instead, because it will block until the entropy pool is initialized.
49 | >
50 | > If a seed file is saved across reboots as recommended below (all major Linux distributions have done this since 2000 at least), the output is cryptographically secure against attackers without local root access as soon as it is reloaded in the boot sequence, and perfectly adequate for network encryption session keys.
51 |
52 | Convinced?
53 | You should be.
54 |
--------------------------------------------------------------------------------
/src/golang.md:
--------------------------------------------------------------------------------
1 | # Go
2 |
3 | … or Golang. A programming language made to do work.
4 |
5 | ## Good Go Code
6 |
7 | Check out [Effective Go](https://golang.org/doc/effective_go.html), [Arne's Styleguide](https://github.com/bahlo/go-styleguide), [Idiomatic Go](https://about.sourcegraph.com/go/idiomatic-go/) and [Writing Great Go Code](https://scene-si.org/2018/07/24/writing-great-go-code/).
8 | Some of the (imho) most important takeaways from the latter:
9 |
10 | * Think about your packages, where to split things and where _not_ to split them. Don't overcomplicate.
11 | * Use [pkg/errors](https://github.com/pkg/errors) and `errors.Wrap`, not the standard library.
12 | * Use a logger package like [sirupsen/logrus](https://github.com/sirupsen/logrus) or [apex/log](https://github.com/apex/log). Try adding the file and line number to each log message (e.g. with `log.SetFlags(log.LstdFlags | log.Lshortfile)`).
13 | * If you want to make sure that one of your types implements an interface, you can do something like `var _ io.Reader = &YourStruct{}`, which will cause an error if it doesn't implement the interface, but will be optimized away during compilation if everything's fine.
14 |
15 | ## Accessing the Mumble Link API for live Guild Wars 2 game data
16 |
17 | GW2 exposes real-time per-frame character and camera position data by using [Mumble's Link API](https://wiki.mumble.info/wiki/Link).
18 | There's a [description on the GW2 Wiki](https://wiki.guildwars2.com/wiki/API:MumbleLink) on what's available, but it's very focused on C code.
19 |
20 | I did some work on creating proof-of-concept quality code in Go.
21 | The challenges have been accessing the API using Windows's File Mapping and parsing the data structure inside.
22 | This is what I've built:
23 |
24 | ```go
25 | package main
26 |
27 | import (
28 | "fmt"
29 | "log"
30 | "syscall"
31 | "time"
32 | "unsafe"
33 | "unicode/utf16"
34 | "unicode/utf8"
35 | "bytes"
36 | )
37 |
38 | type MumbleVector [3]float32
39 | type WChar uint16
40 |
41 | type MumbleIdentity [256]uint16
42 | func (id MumbleIdentity) String() string {
43 | buf := make([]byte, 4)
44 | var ret bytes.Buffer
45 | runes := utf16.Decode(id[:])
46 | count := 0
47 | for _, rune := range runes {
48 | utf8.EncodeRune(buf, rune)
49 | ret.WriteString(string(rune))
50 | count++
51 | }
52 | return ret.String()
53 | }
54 |
55 | type MumblePosition struct {
56 | Position MumbleVector
57 | Front MumbleVector
58 | Top MumbleVector
59 | }
60 |
61 | type MumbleData struct {
62 | Version uint32
63 | Tick uint32
64 | Avatar MumblePosition
65 | Name [256]WChar
66 | Camera MumblePosition
67 | Identity MumbleIdentity
68 | ContextLength uint32
69 | Context [256]byte
70 | Description [2048]WChar
71 | }
72 |
73 | func main() {
74 | file, _ := syscall.UTF16PtrFromString("MumbleLink")
75 | size := 100000 // I’ve tried unsafe.Sizeof(MumbleData{}) but that didn’t work.
76 | handle, err := syscall.CreateFileMapping(0, nil, syscall.PAGE_READWRITE, 0, uint32(size), file)
77 | if err != nil {
78 | log.Fatal(err)
79 | }
80 | defer syscall.CloseHandle(handle)
81 | addr, err := syscall.MapViewOfFile(handle, syscall.FILE_MAP_READ, 0, 0, 0)
82 | if err != nil {
83 | log.Fatal(err)
84 | }
85 | for {
86 | data := (*MumbleData)(unsafe.Pointer(addr))
87 | time.Sleep(1 * time.Second)
88 | fmt.Printf("ava %v cam %v id %v\n", data.Avatar.Position, data.Camera, data.Identity)
89 | }
90 | }
91 | ```
92 |
93 | It's not great Go code, but it works.
94 |
95 | ### Resources I've used
96 |
97 | * [Windows DLLs](https://github.com/golang/go/wiki/WindowsDLLs) on the Go Wiki
98 | * [`mumble.py`](https://github.com/TheTerrasque/gw2lib/blob/master/gw2lib/mumble.py) from [TheTerrasque/gw2lib](https://github.com/TheTerrasque/gw2lib)
99 | * [`MumbleLink.cs`](https://github.com/cvpcs/GuildWars2/blob/master/GuildWars2.ArenaNet.MumbleLink/MumbleLink.cs) from [cvpcs/GuildWars2](https://github.com/cvpcs/GuildWars2)
100 | * [`mmap_windows.go`](https://github.com/etsy/hound/blob/master/codesearch/index/mmap_windows.go) from [etsy/hound](https://github.com/etsy/hound)
101 | * some other questions/answers and examples regarding Python, Java and MumbleLink that are already dead links
102 |
--------------------------------------------------------------------------------
/src/software-development/tech-debt.md:
--------------------------------------------------------------------------------
1 | # Tech Debt
2 |
3 | The thing you amass when doing stuff "quick and dirty".
4 |
5 | ## Taxonomy
6 |
7 | On the Riot Games Engineering Blog there's an excellent, albeit really long, article ([A Taxonomy of Tech Debt](https://engineering.riotgames.com/news/taxonomy-tech-debt) by Bill “LtRandolph” Clark) about three axes of tech debt severity and types of tech debt.
8 | I'll try to summarize it here.
9 |
10 | It introduces three axes to assess the severity of tech debt:
11 |
12 | * **Impact:** How much does it get in the way of people, be it customers or devs?
13 | * **Fix cost:** How hard is it to fix, and how risky?
14 | * **Contagion:** If we allow it to continue to exist, how much will it spread?
15 |
16 | Of these, I consider _contagion_ to be one most people tend not to think about too much, but it's at least as important as the others.
17 |
18 | The article then defines types of debt:
19 |
20 | * **Local:**
21 | Low contagion.
22 | If the impact is higher than the fix cost, it will probably be fixed at some point.
23 | Try not to focus on these too much, even if it hurts the perfectionist in you.
24 | * **MacGyver:**
25 | I don't quite get why it's named like that, but it describes a "bad", legacy way to do things that should be phased out in favor of a new, "better" way, e.g. an old logger class that's used everywhere and that should gradually be replaced with a new interface.
26 | Bill states that they tend to let engineers fix these as they go.
27 | If the new way is easier and nicer, it will win over the old one at some point, like a "good" contagion:
28 | _"If a time-pressured engineer […] chooses to move towards the [new thing], then you’re well on your way."_
29 | * **Foundational:**
30 | A bad design decision in one of the more central parts of the system.
31 | High fix cost, usually high contagion (e.g. if new code often has to use this interface), often high impact as well.
32 | Hard to get rid of, and pretty risky.
33 | Often, due to the high cost, it might be advisable to just leave it like it is.
34 | If you do want to solve this though, the best way might be to create an alternative system/approach of doing things, making it (back-and-forth-)compatible with the old one and that way convert it into MacGyver debt, gradually phasing out the old code.
35 | * **Data:**
36 | A problem in the code that causes users to intentionally enter "wrong" data in order to work around it, e.g. a parameter called `percentage` that expects a value between `0` and `1` instead of `0` and `100`.
37 | Once there's already data with values like `0.9`, it's hard to migrate to the "correct" version (because `0.9` would be a valid value there as well, but with a different meaning).
38 | Highly contagious, often high fix cost.
39 | Often you can't fix it with find/replace.
40 | Since data isn't reviewed as much as code and lots of (maybe only loosely connected) people create it, it's hard to define a moment in time from which on all data will be interpreted in the new way.
41 | To fix it, either introduce a "do it right" flag that toggles how the data is handled, default it to "new way" for new data and set it to "old way" in existing data (i.e. `floatPercentage = true`).
42 | Or, just bite the bullet and fix it once and for all.
43 | Feature toggles in production can make this less terrifying.
44 |
45 | Let me close with two paragraphs of the original article's summary:
46 |
47 | > When measuring a piece of tech debt, you can use impact (to customers and to developers), fix cost (time and risk), and contagion.
48 | > I believe most developers regularly consider impact and fix cost, while I’ve rarely encountered discussions of contagion.
49 | > Contagion can be a developer’s worst enemy as a problem burrows in and becomes harder and harder to dislodge.
50 | > It is possible, however, to turn contagion into a weapon by making your fix more contagious than the problem.
51 | >
52 | > Working on _League,_ most of the tech debt I’ve seen falls into one of the 4 categories I’ve presented here.
53 | > Local debt, like a black box of gross.
54 | > MacGyver debt, where 2 or more systems are duct-taped together with conversion functions.
55 | > Foundational debt, when the entire structure is built on some unfortunate assumptions.
56 | > Data debt, when enormous quantities of data are piled on some other type of debt, making it risky and time-consuming to fix.
57 |
--------------------------------------------------------------------------------
/src/wsl.md:
--------------------------------------------------------------------------------
1 | # Windows Subsystem for Linux (WSL)
2 |
3 | _It’s what makes Windows usable._
4 |
5 | **Note:**
6 | At the time of writing, I’m still using the first-generation WSL.
7 | WSL2 has been released, and it brings several improvements, but as far as I know some things that worked before have not been implemented yet.
8 | Also, I simply lack the time right now to update.
9 |
10 | ## Using GnuPG and SSH with a smartcard (e.g. YubiKey)
11 |
12 | Setting up a YubiKey to use it under WSL is not a particularly easy task, but it’s manageable.
13 | There are some good articles that I’ve used as a basis:
14 | [Yubikey, gpg, ssh and WSL2](https://blog.nimamoh.net/yubi-key-gpg-wsl2/) (although I don’t see anything specific to version 2 of WSL there) and the even larger one [How to use GPG with YubiKey (bonus: WSL)](https://codingnest.com/how-to-use-gpg-with-yubikey-wsl/) explain the setup hands-on.
15 | I’ll show you [my setup](#my-setup) further down below, but first, let’s talk about the issues.
16 |
17 | ### What’s the problem?
18 |
19 | WSL doesn’t support accessing arbitrary USB devices.
20 | Serial ports (`COMn`) can be used (`/dev/ttySn`), but an OpenPGP smartcard like the YubiKey can only be used from the host operating system, i.e. Windows.
21 |
22 | However, [Gpg4win](https://gpg4win.de/) supports `gpg-agent`, which _normally_ allows accessing the smartcard over a socket.
23 | This _can_ be accessed from WSL, but not without a lot of hoops to jump through.
24 |
25 | On Windows, `gpg-agent` doesn’t use Unix sockets, because for a long time, they were not available on Windows.
26 | ([They are now](https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/), by the way.)
27 | Instead, it creates a TCP socket on `localhost`.
28 | To secure this socket, it requires you to send a 16-byte randomly-generated “password” before the normal traffic.
29 | This password, along with the (random) port number the socket is using, is written to the file name that usually would be the Unix socket.
30 | Like this:
31 |
32 | ```
33 | $ hexdump -C /mnt/c/Users/scy/AppData/Roaming/gnupg/S.gpg-agent
34 | 00000000 35 33 30 30 35 0a 2c 82 a3 d0 bb 41 7b 44 a9 28 |53005.,....A{D.(|
35 | 00000010 a6 27 98 2b 7c e7 |.'.+|.|
36 | 00000016
37 | ```
38 |
39 | Of course, when trying to point GPG or SSH from inside WSL to these special files, they expect them to be sockets and it won’t work:
40 |
41 | ```
42 | $ SSH_AUTH_SOCK=/mnt/c/Users/scy/AppData/Roaming/gnupg/S.gpg-agent.ssh ssh-add -L
43 | Error connecting to agent: Connection refused
44 | ```
45 |
46 | Even the version of OpenSSH that’s nowadays included in Windows doesn’t know how to deal with these files:
47 |
48 | ```
49 | > set SSH_AUTH_SOCK=%AppData%\gnupg\S.gpg-agent.ssh
50 | > ssh-add -L
51 | error fetching identities: invalid format
52 | ```
53 |
54 | #### Using SSH only
55 |
56 | If all you care about is using SSH with your YubiKey, there’s [`wsl-ssh-pageant`](https://github.com/benpye/wsl-ssh-pageant).
57 | It uses GnuPG’s Pageant support (that’s PuTTY’s equivalent of `ssh-agent`), i.e. you’ll have to add `enable-putty-support` to `gpg-agent.conf` to provide a socket that can directly be used by the SSH in your WSL.
58 |
59 | However, `wsl-ssh-pageant` doesn’t support doing GPG operations.
60 | For that, you’ll need another tool.
61 |
62 | #### Using GPG only
63 |
64 | If you need to do GPG stuff, you’ll have to use a more universal solution, which is also more messy to set up.
65 | Enter `npiperelay`.
66 | It’s a Go tool to connect to Windows named pipes and provide access to them via stdin/stdout.
67 | In conjunction with `socat`, it can be used to talk to named pipes from WSL.
68 |
69 | `npiperelay` has been originally written [by Microsoft’s John Starks](https://github.com/jstarks/npiperelay), but (as of June 2020) hasn’t been updated for two years.
70 | Support for the GPG-style TCP-socket-pointer files only exists in [pull request #2](https://github.com/jstarks/npiperelay/pull/2), which had been ignored by Starks for about a year.
71 | When he came back with change requests, the author of that pull request, NZSmartie, didn’t react anymore, and currently, the pull request is abandoned.
72 | Another user, Lex Robinson, created [pull request #6](https://github.com/jstarks/npiperelay/pull/6) that takes NZSmartie’s code and adds the fixes requested by Starks.
73 | Now again, Starks didn’t react for over a year and to this day.
74 | Yay open source.
75 |
76 | This means that if you’d like to use `npiperelay`, you’ll have to download the source (preferably [Lex’s `libassuan` branch](https://github.com/Lexicality/wsl-relay/tree/libassuan), I guess), compile it from source (instructions are provided) and use that.
77 | If you’d like to go down that road, check out the howtos I’ve linked above, or have a look at my setup below.
78 |
79 | #### Using both GPG and SSH
80 |
81 | Now you might think “aha, so once I have `npiperelay` and `socat` running for OpenPGP stuff, I can `enable-ssh-support` in the host’s `gpg-agent.conf` and use it without needing a _third_ tool (`wsl-ssh-pageant`) accessing the Pageant socket?”
82 |
83 | Well, I thought so, too.
84 | But you cannot.
85 | Because even if you do the whole dance with nonced TCP instead of Unix sockets, [`enable-ssh-support` in GnuPG’s Windows port is simply broken and won’t be fixed](https://dev.gnupg.org/T4979).
86 | Or, as lead dev Werner Koch explains:
87 |
88 | > […] has never been tested and implemented by me in blind flight mode. I don't think this has any future.
89 |
90 | #### Have GnuPG fix the issue
91 |
92 | All these workaround are only necessary because GnuPG does that strange TCP-pointer-file stuff on Windows.
93 | It’s no longer necessary since `AF_UNIX` sockets are supported under Windows now.
94 |
95 | There’s already [issue T3883](https://dev.gnupg.org/T3883) in the GnuPG project, and Werner Koch seems open to doing something about it.
96 | It has a `gpg23` tag, so I assume they’re planning to implement this in GnuPG 2.3.
97 |
98 | ### My setup
99 |
100 | I’ve written more than enough already.
101 | Let’s keep this short.
102 |
103 | First of all, if you don’t have an OpenPGP-enabled YubiKey (or other smartcard) yet that you’d like to use, do check out [my 2020 OpenPGP setup](https://github.com/scy/knowledge/blob/master/src/openpgp.md#my-2020-openpgp-setup).
104 | Next, let’s get to setting up your machine.
105 |
106 | #### The Windows side
107 |
108 | 1. Install a recent [Gpg4win](https://gpg4win.de/).
109 | 2. Create the file `%APPDATA%\gnupg\gpg-agent.conf` (where `%APPDATA%` is an environment variable on your machine and should be set to something like `C:\Users\your_user\AppData\Roaming`) and put the line `enable-putty-support` into it.
110 | 3. Create a shortcut to `"C:\Program Files (x86)\GnuPG\bin\gpg-connect-agent.exe" /bye` in `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup` so that `gpg-agent` will be started as soon as you log in.
111 | 4. Run that shortcut (or log out and in again, but why would you do that).
112 |
113 | I think that should be it for the Windows side of things.
114 | (Let me know if I’ve missed something!)
115 |
116 | #### The WSL side
117 |
118 | This is where it gets funny.
119 |
120 | 1. Install `git`, `golang`, `socat` and `tmux`.
121 | 2. `git clone https://github.com/Lexicality/wsl-relay.git && cd wsl-relay && git checkout libassuan`
122 | 3. `go get ./...` to install the required dependencies, then `GOOS=windows go build -o "$HOME/bin/npiperelay.exe" .` (assuming you have a `~/bin` directory). In contrast to what other tutorials say, `npiperelay` doesn’t have to be stored in your Windows filesystem (i.e. under `/mnt/c`); the WSL filesystem is just fine. (Technically, the WSL filesystem lives in your Windows filesystem anyway.)
123 | 4. Download the latest [`wsl-ssh-pageant` release](https://github.com/benpye/wsl-ssh-pageant/releases) to `~/bin/wsl-ssh-pageant.exe`.
124 |
125 | You have all of the required ingredients now.
126 | Running all three required parts (`npiperelay` and `socat` for GnuPG operations as well as `wsl-ssh-pageant` for SSH), in the background, and only once (not for every shell you spawn) isn’t easy.
127 | Feel free to use [my `wsl-gpg-agent.sh` script](https://github.com/scy/dotfiles/blob/master/bin/wsl-gpg-agent.sh) or build something similar.
128 | It will check whether existing sockets are still alive and launch every part that isn’t running yet.
129 | Basically, that’s
130 |
131 | 1. Use `socat` to create a Unix socket at `~/.gnupg/S.gpg-agent` that, on connection, spawns `npiperelay.exe` to access `%APPDATA%/gnupg/S.gpg-agent`.
132 | 2. Use `wsl-ssh-pageant.exe` to create a normal Unix socket in the Windows filesystem that translates between GnuPG’s Pageant implementation and OpenSSH’s agent protocol.
133 |
134 | However, I couldn’t for the life of me write it so that it
135 |
136 | * can run from `.bashrc`
137 | * doesn’t freeze your shell on startup and
138 | * doesn’t keep your shell from exiting
139 |
140 | and I’ve tried every combination of `&`, `setsid`, `nohup` and `disown` I could think of.
141 | Instead, I went for a background tmux session, because that way, tmux will make sure that it runs only once, and if something goes wrong, you can always attach to the session and check it.
142 | Run it like this, for example, from your `.bashrc`:
143 |
144 | ```sh
145 | tmux new-session -d -s wsl-gpg-agent wsl-gpg-agent.sh >/dev/null 2>&1
146 | ```
147 |
148 | (Using a systemd user unit would be an alternative, but since this is WSL1, there’s no systemd.)
149 |
150 | Also, while editing the `.bashrc`, don’t forget to tell SSH where to find the socket.
151 | My script puts it in your Windows `gnupg` directory, e.g. `/mnt/c/Users/scy/AppData/Roaming/gnupg/S.wsl-ssh-pageant`, and if you do it like that, you can use a `.bashrc` line like this:
152 |
153 | ```sh
154 | export SSH_AUTH_SOCK="$(wslpath -a "$(cmd.exe /c echo %APPDATA% 2>/dev/null | tr -d '\r')")/gnupg/S.wsl-ssh-pageant"
155 | ```
156 |
157 | (I’m using `cmd.exe` to access Windows environment variables, and `wslpath` to translate. The `tr` is required to get rid of the `CR` character `cmd.exe` will insert.)
158 |
159 | And that’s basically it!
160 | Kill any `gpg-agent` instances that might be running automatically inside your WSL, then reload your `.bashrc`.
161 | `gpg --card-status` should now show your YubiKey (assuming you have it inserted), `ssh-add -L` should list your authentication key (assuming you have that set up), and `gpg --sign` or `--decrypt` or whatever should work.
162 |
163 | #### Issues
164 |
165 | * [Sometimes, `wsl-ssh-pageant` will stop reacting while eating 100 % CPU.](https://twitter.com/scy/status/1282414463090597889) If this happens to me more often, I’ll debug it.
166 | * The PIN window of the `gpg-agent` running on Windows will appear in the foreground, but not have keyboard focus. This is annoying. If you’ve fixed this, please tell me how.
167 | * Neither `wsl-ssh-pageant` nor `npiperelay` are particularly good at removing their sockets once they die. Which is why my script detects that and tries to do the right thing.
168 | * I’d love to get rid of the `tmux` requirement.
169 |
170 | Other than that, I don’t know of any!
171 | It took me _days_ to debug all of this and set it up, but right now, it’s running nicely.
172 |
--------------------------------------------------------------------------------
/src/php.md:
--------------------------------------------------------------------------------
1 | # PHP
2 |
3 | A language I’m proficient in.
4 | A language with horrible design errors.
5 | A language that can be used professionally nevertheless.
6 |
7 | ## Composer
8 |
9 | Composer is PHP's most widely used dependency manager.
10 | [It has some quirks imho](https://twitter.com/scy/status/1014429532860936192), but it's what we have, and if you follow some simple rules and know the answers to the most common problems, it's not so bad anymore.
11 |
12 | ### How to update a requirement (or: change the required version)
13 |
14 | These are basically two different things.
15 | I assume that you know that `composer.json` contains version _constraints_ for your dependencies, while `composer.lock` contains the specific version that has been selected that fulfills the constraint.
16 | Now, _updating_ can mean that you want to get a newer version of a package _without changing the constraints_ or that you want to change the constraints (usually to require a more recent version).
17 | Let's look at these two cases separately.
18 |
19 | #### Updating a package (keeping the constraints)
20 |
21 | That's the easier case.
22 | Suppose you have installed `somedev/theirpackage` in version `1.0.0`, e.g. by running `composer require somedev/theirpackage:^1.0.0`.
23 | The `require` section of your `composer.json` will list something like `"somedev/theirpackage": "^1.0.0"`.
24 | Once a new version gets released that still satisfies your constraint (in this case basically "everything that starts with `1.`"; please read up on [how to specify versions](https://getcomposer.org/doc/articles/versions.md) if you don't know what `^` does), you can run `composer update somedev/theirpackage` and will get the new version.
25 | You can even run `composer update` without any parameters and this will update _all_ packages to the most recent version that still matches the constraints.
26 |
27 | Sometimes you might shy away from that though, because you're scared of breaking anything.
28 | This is usually a sign of badly chosen constraints (like `dev-master` or `0.*`) or lack of automated testing in your application.
29 | You should try to fix that as soon as possible, it'll make your life easier and you'll sleep better.
30 |
31 | #### Changing the constraints (usually to a higher version)
32 |
33 | Suppose you want to update `somedev/theirpackage` to the newly released `2.0.0` version, which no longer matches your `^1.0.0` constraint.
34 | While many people resort to changing the value in `composer.json` using their editor and then running `composer update somedev/theirpackage`, in my experience you end up with less confusion if you use Composer's CLI for as many tasks as possible.
35 | But `composer update somedev/theirpackage:^2.0.0` doesn't work; in fact, the `update` command doesn't understand version constraints at all.
36 |
37 | That's because it's designed for the first use case (from the section directly above) _only_.
38 | Hacking `composer.json` will usually work, but it's easier to use the right command for that.
39 | And since what you're doing is changing the constraints, `require` is the command to do it.
40 |
41 | So, run `composer require somedev/theirpackage:^2.0.0`.
42 | Chances are good that this will not work (even though it's basically the right command) and provide you with a message like
43 |
44 | ```
45 | Your requirements could not be resolved to an installable set of packages.
46 | […]
47 | Problem 1
48 | - somedev/theirpackage 2.0.0 requires someoneelse/a-dependency ^4.0.0 -> satisfiable by someoneelse/a-dependency[4.0.0].
49 | - Can only install one of: someoneelse/a-dependency[3.0.0, 4.0.0].
50 | - Installation request for someoneelse/a-dependency (locked at 3.0.0) -> satisfiable by someoneelse/a-dependency[3.0.0].
51 | ```
52 |
53 | It's definitely not helping that Composer will list the items under "Problem 1" in a seemingly random order.
54 | Try to read the lines as separate findings or statements and don't pay attention to the order in which they appear, and you might recognize the problem here:
55 |
56 | The new `2.0.0` version of `theirpackage` no longer depends on `a-dependency` in version `^3.0.0`, but now `^4.0.0`.
57 | However, since your `composer.lock` not only contains the packages and versions your application depends on, but also those _its dependencies_ depend on, it will also include an entry for `someoneelse/a-dependency` and specifies that this package is (and has to be) installed at version `3.0.0`.
58 | Since the `require` command you just used does not allow any already installed package to change, Composer can't update `a-dependency` to another version.
59 | You simply didn't allow it.
60 |
61 | Luckily, there's a flag for that: `--update-with-dependencies`.
62 | This will allow Composer to update packages that you haven't explicitly named in your command, as long as they are not also a _direct_ dependency of your application (what Composer calls a "root dependency"), i.e. not listed in `require` in your `composer.json`.
63 | (If you need to update one of these in the process as well, there's the `--update-with-all-dependencies` flag.)
64 |
65 | ### When to use `--prefer-dist`
66 |
67 | This flag will choose a "dist" version (e.g. a tarball served over HTTP) over a "source" version (e.g. a Git repo cloned via SSH) when installing or updating _dev versions_ of packages.
68 | Note the emphasis:
69 | `--prefer-dist` only modifies the behavior if your constraints specify something like `dev-master` or a branch name, while a (tagged) version constraint like `^1.3` will be fetched as dist version by default anyway.
70 |
71 | I basically only use this flag for internal packages that are available via a) an internal [Satis](https://github.com/composer/satis) server using IP-based authentication and b) an internal GitLab server that requires an SSH password or key passphrase to use.
72 | In order to be able to run `composer install` without requiring me to enter a password, I use `--prefer-dist`.
73 |
74 | Note that there's also `--prefer-source` that does the opposite, i.e. fetch repositories even for tagged version releases.
75 |
76 | ### When to use `--ignore-platform-reqs`
77 |
78 | This flag makes Composer ignore "platform" requirements, i.e. PHP versions and extensions.
79 | This means that if your `composer.json` says that your application (or one of its dependencies!) requires PHP 7.1, but your local machine only has 7.0, using this flag you can still install the dependencies without Composer complaining.
80 | Be aware though that Composer doesn't know whether that results in a usable application, and it doesn't bother.
81 |
82 | More often, I use this flag when I'm developing on a Mac and I don't have all of the PHP extensions installed that are required.
83 | But since these extensions are only used in a small part of the application (and I'm not working on that), I don't care.
84 | Or, maybe I'm working on that part, but I run the application in a Docker container that _does_ have the extension – but my local machine doesn't (and doesn't need to).
85 |
86 | You should try to use this flag as seldomly as possible, as it disables some safety checks.
87 | You should especially not use it with `require` or `update`, because Composer might then select a version of a package that's incompatible with your target environment.
88 |
89 | Also, when you're using this flag in a staging or even production environment, you're almost certainly doing something wrong.
90 | These environments should really have the PHP version installed that you're developing the app for, and also all required PHP extensions.
91 |
92 | ### Why does Composer require some package?
93 |
94 | When installing or updating, Composer might tell you that it couldn't update some package, or has a version conflict, and you don't even know what this package is or what depends on it.
95 | For this, there's the `composer why` command that, well, explains to you _why_ a certain package is installed.
96 |
97 | ### How to re-calculate the `content-hash` in `composer.lock`
98 |
99 | You usually need to do this when you've had a merge conflict in `composer.lock` and fixed it.
100 | Composer will then show a message like this:
101 |
102 | ```
103 | Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. Run update to update them.
104 | ```
105 |
106 | But what if you don't want to run `composer update`, because you don't want to risk breaking anything in your project?
107 | You just want it to generate a new `content-hash`.
108 |
109 | Some googling may lead you to a command like `composer update nothing`.
110 | This will, well, update nothing, but recreate the lock file.
111 | However, [`nothing` is no special word](https://github.com/composer/getcomposer.org/issues/92), you can also use any other package name that does not exist.
112 | The canonical way to do it though is to run `composer update --lock`, which should be available in every version ≥1.5.0.
113 |
114 | ### Best practices
115 |
116 | * Try to _not_ edit `composer.json` by hand.
117 | For most of the things you do there, there's also a CLI command, and in my experience using that reduces the chance of Composer getting confused.
118 | * Try _really_ hard to not edit `composer.lock` by hand.
119 | The only time you should do it is when you have a merge conflict.
120 | * When you change `composer.json` and/or `composer.lock`, add the command(s) you've used to your commit message.
121 | That way, when your changes cause problems, people can see what you did and have a better chance of fixing it.
122 | * Don't commit `composer.phar` to your application's repository, because it's quite large (several MB) and you need to update it regularly.
123 | There are better ways to do it, [tiny composer installer](https://github.com/fastbill/tiny-composer-installer) being one of them.
124 |
125 | ### When you screw up
126 |
127 | One final hint:
128 | When your Composer command fails and you're left with hundreds of changed lines in `composer.lock` and you don't know why and you want to try something different, please **revert** at least `composer.lock` to a **known-good state** and try again from there.
129 |
130 | The reason behind this is that if your lock file contains broken stuff already, it can happen that the next commands you run, even if they're now the _right_ commands, do something different or bad because of the messed up `composer.lock`.
131 | Or, they might simply not care about it being messed up, but not fix it either, so you're still left with that broken file.
132 |
133 | Use your version control system, go back to where you started, take a deep breath, focus and try again.
134 |
135 | ## Getting Good Random Data
136 |
137 | I see code that uses `mt_rand()` on an array of characters or something like that way too often.
138 | But also a hash over `microtime(true) . gethostname()` isn't really random data.
139 |
140 | Using `/dev/urandom` is nearly always a good idea, and I can recommend [my summary of why you don't need `/dev/random` in most cases, and when you do](unix.md#is-devurandom-good-enough) to get rid of some common misconceptions.
141 | However, in PHP there's an even easier way, and that's [the `random_bytes()` function](http://php.net/manual/en/function.random-bytes.php), which I can highly recommend.
142 | It's available since PHP 7, but if you have some 5.6 code to maintain (or an even older version?) there's a polyfill [paragonie/random_compat](https://github.com/paragonie/random_compat) available all the way down to PHP 5.2.
143 |
144 | You can pass its results directly to something like `base64_encode()` or `hash()`, or concatenate it with date and FQDN of your server or whatever to make it even more "unique".
145 |
146 | ## Xdebug
147 |
148 | ### For PHP 5.6
149 |
150 | Some days ago, [Xdebug 2.6.0](https://derickrethans.nl/xdebug-26.html) was released.
151 | This is the first version that no longer supports PHP 5.6, causing `pecl install xdebug` to break, e.g. in a `php:5.6-apache` Docker container.
152 | To fix this, as [suggested in docker-library/php#566](https://github.com/docker-library/php/issues/566#issuecomment-362094015), ask PECL to install the last version that supported PHP 5.6:
153 |
154 | ```sh
155 | pecl install xdebug-2.5.5
156 | ```
157 |
158 | Note that if there’ll ever be a bugfix version `xdebug-2.5.6`, you won’t get it.
159 | Sadly, as far as I can see there’s no way to tell PECL to install `xdebug-2.5.*` or the like.
160 | If you know of a way, please contact me.
161 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Attribution 4.0 International
2 |
3 | =======================================================================
4 |
5 | Creative Commons Corporation ("Creative Commons") is not a law firm and
6 | does not provide legal services or legal advice. Distribution of
7 | Creative Commons public licenses does not create a lawyer-client or
8 | other relationship. Creative Commons makes its licenses and related
9 | information available on an "as-is" basis. Creative Commons gives no
10 | warranties regarding its licenses, any material licensed under their
11 | terms and conditions, or any related information. Creative Commons
12 | disclaims all liability for damages resulting from their use to the
13 | fullest extent possible.
14 |
15 | Using Creative Commons Public Licenses
16 |
17 | Creative Commons public licenses provide a standard set of terms and
18 | conditions that creators and other rights holders may use to share
19 | original works of authorship and other material subject to copyright
20 | and certain other rights specified in the public license below. The
21 | following considerations are for informational purposes only, are not
22 | exhaustive, and do not form part of our licenses.
23 |
24 | Considerations for licensors: Our public licenses are
25 | intended for use by those authorized to give the public
26 | permission to use material in ways otherwise restricted by
27 | copyright and certain other rights. Our licenses are
28 | irrevocable. Licensors should read and understand the terms
29 | and conditions of the license they choose before applying it.
30 | Licensors should also secure all rights necessary before
31 | applying our licenses so that the public can reuse the
32 | material as expected. Licensors should clearly mark any
33 | material not subject to the license. This includes other CC-
34 | licensed material, or material used under an exception or
35 | limitation to copyright. More considerations for licensors:
36 | wiki.creativecommons.org/Considerations_for_licensors
37 |
38 | Considerations for the public: By using one of our public
39 | licenses, a licensor grants the public permission to use the
40 | licensed material under specified terms and conditions. If
41 | the licensor's permission is not necessary for any reason--for
42 | example, because of any applicable exception or limitation to
43 | copyright--then that use is not regulated by the license. Our
44 | licenses grant only permissions under copyright and certain
45 | other rights that a licensor has authority to grant. Use of
46 | the licensed material may still be restricted for other
47 | reasons, including because others have copyright or other
48 | rights in the material. A licensor may make special requests,
49 | such as asking that all changes be marked or described.
50 | Although not required by our licenses, you are encouraged to
51 | respect those requests where reasonable. More_considerations
52 | for the public:
53 | wiki.creativecommons.org/Considerations_for_licensees
54 |
55 | =======================================================================
56 |
57 | Creative Commons Attribution 4.0 International Public License
58 |
59 | By exercising the Licensed Rights (defined below), You accept and agree
60 | to be bound by the terms and conditions of this Creative Commons
61 | Attribution 4.0 International Public License ("Public License"). To the
62 | extent this Public License may be interpreted as a contract, You are
63 | granted the Licensed Rights in consideration of Your acceptance of
64 | these terms and conditions, and the Licensor grants You such rights in
65 | consideration of benefits the Licensor receives from making the
66 | Licensed Material available under these terms and conditions.
67 |
68 |
69 | Section 1 -- Definitions.
70 |
71 | a. Adapted Material means material subject to Copyright and Similar
72 | Rights that is derived from or based upon the Licensed Material
73 | and in which the Licensed Material is translated, altered,
74 | arranged, transformed, or otherwise modified in a manner requiring
75 | permission under the Copyright and Similar Rights held by the
76 | Licensor. For purposes of this Public License, where the Licensed
77 | Material is a musical work, performance, or sound recording,
78 | Adapted Material is always produced where the Licensed Material is
79 | synched in timed relation with a moving image.
80 |
81 | b. Adapter's License means the license You apply to Your Copyright
82 | and Similar Rights in Your contributions to Adapted Material in
83 | accordance with the terms and conditions of this Public License.
84 |
85 | c. Copyright and Similar Rights means copyright and/or similar rights
86 | closely related to copyright including, without limitation,
87 | performance, broadcast, sound recording, and Sui Generis Database
88 | Rights, without regard to how the rights are labeled or
89 | categorized. For purposes of this Public License, the rights
90 | specified in Section 2(b)(1)-(2) are not Copyright and Similar
91 | Rights.
92 |
93 | d. Effective Technological Measures means those measures that, in the
94 | absence of proper authority, may not be circumvented under laws
95 | fulfilling obligations under Article 11 of the WIPO Copyright
96 | Treaty adopted on December 20, 1996, and/or similar international
97 | agreements.
98 |
99 | e. Exceptions and Limitations means fair use, fair dealing, and/or
100 | any other exception or limitation to Copyright and Similar Rights
101 | that applies to Your use of the Licensed Material.
102 |
103 | f. Licensed Material means the artistic or literary work, database,
104 | or other material to which the Licensor applied this Public
105 | License.
106 |
107 | g. Licensed Rights means the rights granted to You subject to the
108 | terms and conditions of this Public License, which are limited to
109 | all Copyright and Similar Rights that apply to Your use of the
110 | Licensed Material and that the Licensor has authority to license.
111 |
112 | h. Licensor means the individual(s) or entity(ies) granting rights
113 | under this Public License.
114 |
115 | i. Share means to provide material to the public by any means or
116 | process that requires permission under the Licensed Rights, such
117 | as reproduction, public display, public performance, distribution,
118 | dissemination, communication, or importation, and to make material
119 | available to the public including in ways that members of the
120 | public may access the material from a place and at a time
121 | individually chosen by them.
122 |
123 | j. Sui Generis Database Rights means rights other than copyright
124 | resulting from Directive 96/9/EC of the European Parliament and of
125 | the Council of 11 March 1996 on the legal protection of databases,
126 | as amended and/or succeeded, as well as other essentially
127 | equivalent rights anywhere in the world.
128 |
129 | k. You means the individual or entity exercising the Licensed Rights
130 | under this Public License. Your has a corresponding meaning.
131 |
132 |
133 | Section 2 -- Scope.
134 |
135 | a. License grant.
136 |
137 | 1. Subject to the terms and conditions of this Public License,
138 | the Licensor hereby grants You a worldwide, royalty-free,
139 | non-sublicensable, non-exclusive, irrevocable license to
140 | exercise the Licensed Rights in the Licensed Material to:
141 |
142 | a. reproduce and Share the Licensed Material, in whole or
143 | in part; and
144 |
145 | b. produce, reproduce, and Share Adapted Material.
146 |
147 | 2. Exceptions and Limitations. For the avoidance of doubt, where
148 | Exceptions and Limitations apply to Your use, this Public
149 | License does not apply, and You do not need to comply with
150 | its terms and conditions.
151 |
152 | 3. Term. The term of this Public License is specified in Section
153 | 6(a).
154 |
155 | 4. Media and formats; technical modifications allowed. The
156 | Licensor authorizes You to exercise the Licensed Rights in
157 | all media and formats whether now known or hereafter created,
158 | and to make technical modifications necessary to do so. The
159 | Licensor waives and/or agrees not to assert any right or
160 | authority to forbid You from making technical modifications
161 | necessary to exercise the Licensed Rights, including
162 | technical modifications necessary to circumvent Effective
163 | Technological Measures. For purposes of this Public License,
164 | simply making modifications authorized by this Section 2(a)
165 | (4) never produces Adapted Material.
166 |
167 | 5. Downstream recipients.
168 |
169 | a. Offer from the Licensor -- Licensed Material. Every
170 | recipient of the Licensed Material automatically
171 | receives an offer from the Licensor to exercise the
172 | Licensed Rights under the terms and conditions of this
173 | Public License.
174 |
175 | b. No downstream restrictions. You may not offer or impose
176 | any additional or different terms or conditions on, or
177 | apply any Effective Technological Measures to, the
178 | Licensed Material if doing so restricts exercise of the
179 | Licensed Rights by any recipient of the Licensed
180 | Material.
181 |
182 | 6. No endorsement. Nothing in this Public License constitutes or
183 | may be construed as permission to assert or imply that You
184 | are, or that Your use of the Licensed Material is, connected
185 | with, or sponsored, endorsed, or granted official status by,
186 | the Licensor or others designated to receive attribution as
187 | provided in Section 3(a)(1)(A)(i).
188 |
189 | b. Other rights.
190 |
191 | 1. Moral rights, such as the right of integrity, are not
192 | licensed under this Public License, nor are publicity,
193 | privacy, and/or other similar personality rights; however, to
194 | the extent possible, the Licensor waives and/or agrees not to
195 | assert any such rights held by the Licensor to the limited
196 | extent necessary to allow You to exercise the Licensed
197 | Rights, but not otherwise.
198 |
199 | 2. Patent and trademark rights are not licensed under this
200 | Public License.
201 |
202 | 3. To the extent possible, the Licensor waives any right to
203 | collect royalties from You for the exercise of the Licensed
204 | Rights, whether directly or through a collecting society
205 | under any voluntary or waivable statutory or compulsory
206 | licensing scheme. In all other cases the Licensor expressly
207 | reserves any right to collect such royalties.
208 |
209 |
210 | Section 3 -- License Conditions.
211 |
212 | Your exercise of the Licensed Rights is expressly made subject to the
213 | following conditions.
214 |
215 | a. Attribution.
216 |
217 | 1. If You Share the Licensed Material (including in modified
218 | form), You must:
219 |
220 | a. retain the following if it is supplied by the Licensor
221 | with the Licensed Material:
222 |
223 | i. identification of the creator(s) of the Licensed
224 | Material and any others designated to receive
225 | attribution, in any reasonable manner requested by
226 | the Licensor (including by pseudonym if
227 | designated);
228 |
229 | ii. a copyright notice;
230 |
231 | iii. a notice that refers to this Public License;
232 |
233 | iv. a notice that refers to the disclaimer of
234 | warranties;
235 |
236 | v. a URI or hyperlink to the Licensed Material to the
237 | extent reasonably practicable;
238 |
239 | b. indicate if You modified the Licensed Material and
240 | retain an indication of any previous modifications; and
241 |
242 | c. indicate the Licensed Material is licensed under this
243 | Public License, and include the text of, or the URI or
244 | hyperlink to, this Public License.
245 |
246 | 2. You may satisfy the conditions in Section 3(a)(1) in any
247 | reasonable manner based on the medium, means, and context in
248 | which You Share the Licensed Material. For example, it may be
249 | reasonable to satisfy the conditions by providing a URI or
250 | hyperlink to a resource that includes the required
251 | information.
252 |
253 | 3. If requested by the Licensor, You must remove any of the
254 | information required by Section 3(a)(1)(A) to the extent
255 | reasonably practicable.
256 |
257 | 4. If You Share Adapted Material You produce, the Adapter's
258 | License You apply must not prevent recipients of the Adapted
259 | Material from complying with this Public License.
260 |
261 |
262 | Section 4 -- Sui Generis Database Rights.
263 |
264 | Where the Licensed Rights include Sui Generis Database Rights that
265 | apply to Your use of the Licensed Material:
266 |
267 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right
268 | to extract, reuse, reproduce, and Share all or a substantial
269 | portion of the contents of the database;
270 |
271 | b. if You include all or a substantial portion of the database
272 | contents in a database in which You have Sui Generis Database
273 | Rights, then the database in which You have Sui Generis Database
274 | Rights (but not its individual contents) is Adapted Material; and
275 |
276 | c. You must comply with the conditions in Section 3(a) if You Share
277 | all or a substantial portion of the contents of the database.
278 |
279 | For the avoidance of doubt, this Section 4 supplements and does not
280 | replace Your obligations under this Public License where the Licensed
281 | Rights include other Copyright and Similar Rights.
282 |
283 |
284 | Section 5 -- Disclaimer of Warranties and Limitation of Liability.
285 |
286 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
287 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
288 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
289 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
290 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
291 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
292 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
293 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
294 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
295 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
296 |
297 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
298 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
299 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
300 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
301 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
302 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
303 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
304 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
305 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
306 |
307 | c. The disclaimer of warranties and limitation of liability provided
308 | above shall be interpreted in a manner that, to the extent
309 | possible, most closely approximates an absolute disclaimer and
310 | waiver of all liability.
311 |
312 |
313 | Section 6 -- Term and Termination.
314 |
315 | a. This Public License applies for the term of the Copyright and
316 | Similar Rights licensed here. However, if You fail to comply with
317 | this Public License, then Your rights under this Public License
318 | terminate automatically.
319 |
320 | b. Where Your right to use the Licensed Material has terminated under
321 | Section 6(a), it reinstates:
322 |
323 | 1. automatically as of the date the violation is cured, provided
324 | it is cured within 30 days of Your discovery of the
325 | violation; or
326 |
327 | 2. upon express reinstatement by the Licensor.
328 |
329 | For the avoidance of doubt, this Section 6(b) does not affect any
330 | right the Licensor may have to seek remedies for Your violations
331 | of this Public License.
332 |
333 | c. For the avoidance of doubt, the Licensor may also offer the
334 | Licensed Material under separate terms or conditions or stop
335 | distributing the Licensed Material at any time; however, doing so
336 | will not terminate this Public License.
337 |
338 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
339 | License.
340 |
341 |
342 | Section 7 -- Other Terms and Conditions.
343 |
344 | a. The Licensor shall not be bound by any additional or different
345 | terms or conditions communicated by You unless expressly agreed.
346 |
347 | b. Any arrangements, understandings, or agreements regarding the
348 | Licensed Material not stated herein are separate from and
349 | independent of the terms and conditions of this Public License.
350 |
351 |
352 | Section 8 -- Interpretation.
353 |
354 | a. For the avoidance of doubt, this Public License does not, and
355 | shall not be interpreted to, reduce, limit, restrict, or impose
356 | conditions on any use of the Licensed Material that could lawfully
357 | be made without permission under this Public License.
358 |
359 | b. To the extent possible, if any provision of this Public License is
360 | deemed unenforceable, it shall be automatically reformed to the
361 | minimum extent necessary to make it enforceable. If the provision
362 | cannot be reformed, it shall be severed from this Public License
363 | without affecting the enforceability of the remaining terms and
364 | conditions.
365 |
366 | c. No term or condition of this Public License will be waived and no
367 | failure to comply consented to unless expressly agreed to by the
368 | Licensor.
369 |
370 | d. Nothing in this Public License constitutes or may be interpreted
371 | as a limitation upon, or waiver of, any privileges and immunities
372 | that apply to the Licensor or You, including from the legal
373 | processes of any jurisdiction or authority.
374 |
375 |
376 | =======================================================================
377 |
378 | Creative Commons is not a party to its public
379 | licenses. Notwithstanding, Creative Commons may elect to apply one of
380 | its public licenses to material it publishes and in those instances
381 | will be considered the “Licensor.” The text of the Creative Commons
382 | public licenses is dedicated to the public domain under the CC0 Public
383 | Domain Dedication. Except for the limited purpose of indicating that
384 | material is shared under a Creative Commons public license or as
385 | otherwise permitted by the Creative Commons policies published at
386 | creativecommons.org/policies, Creative Commons does not authorize the
387 | use of the trademark "Creative Commons" or any other trademark or logo
388 | of Creative Commons without its prior written consent including,
389 | without limitation, in connection with any unauthorized modifications
390 | to any of its public licenses or any other arrangements,
391 | understandings, or agreements concerning use of licensed material. For
392 | the avoidance of doubt, this paragraph does not form part of the
393 | public licenses.
394 |
395 | Creative Commons may be contacted at creativecommons.org.
396 |
--------------------------------------------------------------------------------
/src/openpgp.md:
--------------------------------------------------------------------------------
1 | # OpenPGP and GnuPG
2 |
3 | ## My 2020 OpenPGP setup
4 |
5 | A lot of this is based on [Eric Severance’s 2015 tutorial](https://www.esev.com/blog/post/2015-01-pgp-ssh-key-on-yubikey-neo/).
6 |
7 | Key features:
8 |
9 | * Master key (used for creating/modifying/signing subkeys and the keys of other persons) lives on a separate, airgapped machine.
10 | * Encryption, decryption and signing happens on a YubiKey, e.g. the YubiKey 5 NFC.
11 | * The OpenPGP key on the YubiKey can also be used as your “SSH key”, i.e. a client-side certificate to log you in to remote servers.
12 | * All of the keys are Ed25519 ones (requires [YubiKey firmware 5.2.3](https://www.yubico.com/blog/whats-new-in-yubikey-firmware-5-2-3/) or higher), not RSA.
13 | * The encryption subkey is _not_ generated on the YubiKey (but signing and authentication are). This allows us to have a backup, should the YubiKey break down.
14 |
15 | ### Alternatives
16 |
17 | Before you start doing this because just because you want to use your YubiKey for authentication:
18 | There’s also [yubikey-agent](https://github.com/FiloSottile/yubikey-agent) by the well-known Filippo Valsorda, which uses the PIV section of modern YubiKeys instead.
19 | He also wrote about why he’s [giving up on PGP](https://blog.filippo.io/giving-up-on-long-term-pgp/); it’s definitely an interesting read.
20 | If you don’t have other places where you’re using OpenPGP, consider ditching it altogether.
21 |
22 | Recent versions of OpenSSH have [FIDO2 support](https://buttondown.email/cryptography-dispatches/archive/cryptography-dispatches-openssh-82-just-works/) now, but introduce a new keytype for it.
23 | Thus, you also need a recent SSH version on the server, and that can become an issue.
24 | As of 2020-07, GitHub doesn’t support it yet, for instance.
25 | But, if you go down that road, you might consider using getting an open source [SoloKey](https://solokeys.com/) instead (because recent YubiKeys are not completely open source anymore).
26 |
27 | ### Building an airgapped machine for key signing
28 |
29 | For this, I use a Raspberry Pi.
30 | You can either boot it, as usual, from an SD card, or improve your defense against [evil maid attacks](https://en.wikipedia.org/wiki/Evil_maid_attack) by booting from a hardware-encrypted USB thumb drive with integrated PIN keyboard like the Kensington DataTraveler 2000.
31 |
32 | The basic idea is, once this device contains your master key, it can’t ever be connected to any network again.
33 | Exchanging key material should then only be done using USB drives or other means.
34 | (Since “rogue USB” attacks against USB stacks exist as well, other ways of exchanging data might be worth exploring, for example RS232 or [QR codes](https://github.com/seiferteric/qrtun).)
35 |
36 | Flash a boot medium for the Pi and start it, connected to a network.
37 | Set up locale, timezone, keyboard layout etc. according to your needs.
38 |
39 | Then, install all packages you’re going to need.
40 | Remember, once you’ve generated your key, you don’t want to connect the machine to the internet ever again, so make sure you have everything you need.
41 | I used these commands to have some basic utilities as well as a GnuPG version that can deal with YubiKeys:
42 |
43 | ```sh
44 | sudo apt update
45 | sudo apt upgrade
46 | sudo apt install build-essential git links qrencode rsync scdaemon vim yubikey-manager yubikey-personalization zip
47 | ```
48 |
49 | Then, configure GnuPG with some sane defaults, e.g. those from [Riseup’s best practices](https://riseup.net/en/security/message-security/openpgp/gpg-best-practices):
50 |
51 | ```
52 | default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
53 | personal-digest-preferences SHA512 SHA384 SHA256 SHA224
54 | personal-cipher-preferences AES256 AES192 AES CAST5
55 | cert-digest-algo SHA512
56 | ```
57 |
58 | ### Setting up the YubiKey
59 |
60 | #### Key types
61 |
62 | Run `gpg --edit-card` to modify the YubiKey’s OpenPGP settings.
63 | An interactive prompt will appear.
64 | The first command you enter there should be `admin`; it unlocks all of the commands.
65 |
66 | First of all, make sure to switch the YubiKey to use Ed25519 keys:
67 |
68 | ```
69 | gpg/card> admin
70 | Admin commands are allowed
71 |
72 | gpg/card> key-attr
73 | Changing card key attribute for: Signature key
74 | Please select what kind of key you want:
75 | (1) RSA
76 | (2) ECC
77 | Your selection? 2
78 | Please select which elliptic curve you want:
79 | (1) Curve 25519
80 | (4) NIST P-384
81 | Your selection? 1
82 | The card will now be re-configured to generate a key of type: ed25519
83 | Note: There is no guarantee that the card supports the requested size.
84 | If the key generation does not succeed, please check the
85 | documentation of your card to see what sizes are allowed.
86 | Changing card key attribute for: Encryption key
87 | Please select what kind of key you want:
88 | (1) RSA
89 | (2) ECC
90 | Your selection? 2
91 | Please select which elliptic curve you want:
92 | (1) Curve 25519
93 | (4) NIST P-384
94 | Your selection? 1
95 | The card will now be re-configured to generate a key of type: cv25519
96 | Changing card key attribute for: Authentication key
97 | Please select what kind of key you want:
98 | (1) RSA
99 | (2) ECC
100 | Your selection? 2
101 | Please select which elliptic curve you want:
102 | (1) Curve 25519
103 | (4) NIST P-384
104 | Your selection? 1
105 | The card will now be re-configured to generate a key of type: ed25519
106 | ```
107 |
108 | #### Metadata
109 |
110 | Next, enter some basic data about yourself.
111 | Note that the “language preference” is defined as up to four two-byte lower-case ISO 639-1 language codes.
112 | So, if your most preferred language is German (`de`), followed by English (`en`), you should set your language preference to `deen`.
113 | No, there are no spaces, commas or whatever in between.
114 | Just a string of up to eight characters.
115 |
116 | ```
117 | gpg/card> name
118 | Cardholder's surname: Weber
119 | Cardholder's given name: Tim
120 |
121 | gpg/card> login
122 | Login data (account name): scy
123 |
124 | gpg/card> lang
125 | Language preferences: deen
126 |
127 | gpg/card> sex
128 | Sex ((M)ale, (F)emale or space): m
129 | ```
130 |
131 | Recent GnuPG versions call the `sex` command `salutation` instead. `<3`
132 |
133 | #### Security features
134 |
135 | **Do not enable KDF** unless you know what you’re up against.
136 | KDF hashes your PIN, so that it’s no longer transferred in plain text via USB or NFC when communicating with the YubiKey.
137 | This sounds good in theory, but currently, software support is poor:
138 |
139 | * Yubico’s own `ykman` doesn’t support it in the current 3.1.1 version, keeping you from setting touch policies etc using `ykman openpgp set-touch` and causing “invalid admin PIN” errors which could possibly force you to reset the YubiKey. It will probably be included in the next release though, see [#279](https://github.com/Yubico/yubikey-manager/issues/279) and [#325](https://github.com/Yubico/yubikey-manager/pull/325).
140 | * The popular Android OpenPGP application OpenKeychain doesn’t support it either. The corresponding issue [#2368](https://github.com/open-keychain/open-keychain/issues/2368) is open for two years now.
141 | * GnuPG itself apparently [doesn’t update the PINs automatically after enabling KDF](https://dev.gnupg.org/T3891), which is something between messy and dangerous.
142 |
143 | Also, enabling KDF is a one-way street:
144 | As far as I know, the only way to disable it again is by resetting the YubiKey’s OpenPGP section (i.e. the `gnupg --edit-card` subcommand `factory-reset`).
145 |
146 | Once the KDF situation improves, let me know, and I’ll update this guide accordingly.
147 |
148 | Next, if you think it’s better, configure the YubiKey to ask for the pin for each signature.
149 | **Note:** This _toggles_ the flag.
150 | Make sure that the `Signature PIN:` setting in the output of `list` is set to what you want afterwards.
151 |
152 | ```
153 | gpg/card> forcesig
154 | ```
155 |
156 | ### Creating the keys
157 |
158 | First, generate only the primary key.
159 | This one will not be used in your day-to-day interactions with GnuPG, but only for modifying subkeys and signing the keys of other people.
160 | It will exist only on the airgapped Raspberry Pi.
161 |
162 | By default, it will have _Sign_ and _Certify_ capabilities, but we’re going to change that to _Certify_ only.
163 | Basically, _Certify_ is about key management (adding/modifying/revoking subkeys, signing the keys of others etc.) and _Sign_ is about signing emails or files.
164 |
165 | Also, [you most likely should leave the “comment” field of your user ID empty](https://debian-administration.org/users/dkg/weblog/97).
166 | Remember, if you want other people to sign your key, they will need to verify that you are who (and what!) the key says, including the comment.
167 |
168 | ```
169 | $ gpg --expert --full-gen-key
170 | gpg (GnuPG) 2.2.12; Copyright (C) 2018 Free Software Foundation, Inc.
171 | This is free software: you are free to change and redistribute it.
172 | There is NO WARRANTY, to the extent permitted by law.
173 |
174 | Please select what kind of key you want:
175 | (1) RSA and RSA (default)
176 | (2) DSA and Elgamal
177 | (3) DSA (sign only)
178 | (4) RSA (sign only)
179 | (7) DSA (set your own capabilities)
180 | (8) RSA (set your own capabilities)
181 | (9) ECC and ECC
182 | (10) ECC (sign only)
183 | (11) ECC (set your own capabilities)
184 | (13) Existing key
185 | Your selection? 11
186 |
187 | Possible actions for a ECDSA/EdDSA key: Sign Certify Authenticate
188 | Current allowed actions: Sign Certify
189 |
190 | (S) Toggle the sign capability
191 | (A) Toggle the authenticate capability
192 | (Q) Finished
193 |
194 | Your selection? s
195 |
196 | Possible actions for a ECDSA/EdDSA key: Sign Certify Authenticate
197 | Current allowed actions: Certify
198 |
199 | (S) Toggle the sign capability
200 | (A) Toggle the authenticate capability
201 | (Q) Finished
202 |
203 | Your selection? q
204 | Please select which elliptic curve you want:
205 | (1) Curve 25519
206 | (3) NIST P-256
207 | (4) NIST P-384
208 | (5) NIST P-521
209 | (6) Brainpool P-256
210 | (7) Brainpool P-384
211 | (8) Brainpool P-512
212 | (9) secp256k1
213 | Your selection? 1
214 | Please specify how long the key should be valid.
215 | 0 = key does not expire
216 | = key expires in n days
217 | w = key expires in n weeks
218 | m = key expires in n months
219 | y = key expires in n years
220 | Key is valid for? (0) 6m
221 | Key expires at Fri 01 Jan 2021 10:12:34 PM CET
222 | Is this correct? (y/N) y
223 |
224 | GnuPG needs to construct a user ID to identify your key.
225 |
226 | Real name: Tim Weber
227 | Email address: scy@scy.name
228 | Comment:
229 | You selected this USER-ID:
230 | "Tim Weber "
231 |
232 | Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
233 | We need to generate a lot of random bytes. It is a good idea to perform
234 | some other action (type on the keyboard, move the mouse, utilize the
235 | disks) during the prime generation; this gives the random number
236 | generator a better chance to gain enough entropy.
237 | [passphrase dialog]
238 | gpg: /home/pi/.gnupg/trustdb.gpg: trustdb created
239 | gpg: key 38ACA93052B3EB9A marked as ultimately trusted
240 | gpg: directory '/home/pi/.gnupg/openpgp-revocs.d' created
241 | gpg: revocation certificate stored as '/home/pi/.gnupg/openpgp-revocs.d/144FEA6B3FF0F6EFD035963B38ACA93052B3EB9A.rev'
242 | public and secret key created and signed.
243 |
244 | pub ed25519 2020-07-05 [C] [expires: 2021-01-01]
245 | 144FEA6B3FF0F6EFD035963B38ACA93052B3EB9A
246 | uid Tim Weber
247 | ```
248 |
249 | Next, we’ll create an encryption-only subkey.
250 | We will _not_ generate it on the YubiKey, but on the Raspberry Pi.
251 | That way, we can have a backup in case the YubiKey is lost or breaks or something.
252 |
253 | **Note:** I’m using `@.` here to specify which key to edit.
254 | This actually means “find a key with an email address that contains `.`”.
255 | You can only use this if this is a fresh installation of GnuPG and your keyring only contains your own key and no others.
256 |
257 | ```
258 | $ gpg --expert --edit-key @.
259 | gpg (GnuPG) 2.2.12; Copyright (C) 2018 Free Software Foundation, Inc.
260 | This is free software: you are free to change and redistribute it.
261 | There is NO WARRANTY, to the extent permitted by law.
262 |
263 | Secret key is available.
264 |
265 | sec ed25519/38ACA93052B3EB9A
266 | created: 2020-07-05 expires: 2021-01-01 usage: C
267 | trust: ultimate validity: ultimate
268 | [ultimate] (1). Tim Weber
269 |
270 | gpg> addkey
271 | Please select what kind of key you want:
272 | (3) DSA (sign only)
273 | (4) RSA (sign only)
274 | (5) Elgamal (encrypt only)
275 | (6) RSA (encrypt only)
276 | (7) DSA (set your own capabilities)
277 | (8) RSA (set your own capabilities)
278 | (10) ECC (sign only)
279 | (11) ECC (set your own capabilities)
280 | (12) ECC (encrypt only)
281 | (13) Existing key
282 | Your selection? 12
283 | Please select which elliptic curve you want:
284 | (1) Curve 25519
285 | (3) NIST P-256
286 | (4) NIST P-384
287 | (5) NIST P-521
288 | (6) Brainpool P-256
289 | (7) Brainpool P-384
290 | (8) Brainpool P-512
291 | (9) secp256k1
292 | Your selection? 1
293 | Please specify how long the key should be valid.
294 | 0 = key does not expire
295 | = key expires in n days
296 | w = key expires in n weeks
297 | m = key expires in n months
298 | y = key expires in n years
299 | Key is valid for? (0) 6m
300 | Key expires at Fri 01 Jan 2021 10:28:41 PM CET
301 | Is this correct? (y/N) y
302 | Really create? (y/N) y
303 | [passphrase dialog]
304 | We need to generate a lot of random bytes. It is a good idea to perform
305 | some other action (type on the keyboard, move the mouse, utilize the
306 | disks) during the prime generation; this gives the random number
307 | generator a better chance to gain enough entropy.
308 |
309 | sec ed25519/38ACA93052B3EB9A
310 | created: 2020-07-05 expires: 2021-01-01 usage: C
311 | trust: ultimate validity: ultimate
312 | ssb cv25519/278254E6B4D6F0F6
313 | created: 2020-07-05 expires: 2021-01-01 usage: E
314 | [ultimate] (1). Tim Weber
315 |
316 | gpg> save
317 | ```
318 |
319 | Now is a good time to make a backup of your public and private keys, since transferring the encryption key to the YubiKey will remove it from your on-disk keyring.
320 |
321 | ```
322 | $ gpg --export --armor > ~/2020-07-06-public.asc
323 | $ gpg --export-secret-keys --armor > ~/2020-07-06-secret.asc
324 | [passphrase dialog]
325 | ```
326 |
327 | Next, move the encryption key to the YubiKey.
328 |
329 | ```
330 | $ gpg --edit-key @.
331 | gpg (GnuPG) 2.2.12; Copyright (C) 2018 Free Software Foundation, Inc.
332 | This is free software: you are free to change and redistribute it.
333 | There is NO WARRANTY, to the extent permitted by law.
334 |
335 | Secret key is available.
336 |
337 | sec ed25519/38ACA93052B3EB9A
338 | created: 2020-07-05 expires: 2021-01-01 usage: C
339 | trust: ultimate validity: ultimate
340 | ssb cv25519/278254E6B4D6F0F6
341 | created: 2020-07-05 expires: 2021-01-01 usage: E
342 | [ultimate] (1). Tim Weber
343 |
344 | gpg> key 1
345 |
346 | sec ed25519/38ACA93052B3EB9A
347 | created: 2020-07-05 expires: 2021-01-01 usage: C
348 | trust: ultimate validity: ultimate
349 | ssb* cv25519/278254E6B4D6F0F6
350 | created: 2020-07-05 expires: 2021-01-01 usage: E
351 | [ultimate] (1). Tim Weber
352 |
353 | gpg> keytocard
354 | Please select where to store the key:
355 | (2) Encryption key
356 | Your selection? 2
357 | [passphrase dialog]
358 | [admin pin dialog]
359 | sec ed25519/38ACA93052B3EB9A
360 | created: 2020-07-05 expires: 2021-01-01 usage: C
361 | trust: ultimate validity: ultimate
362 | ssb* cv25519/278254E6B4D6F0F6
363 | created: 2020-07-05 expires: 2021-01-01 usage: E
364 | [ultimate] (1). Tim Weber
365 | ```
366 |
367 | Now, we need to create signature and authentication subkeys.
368 |
369 | ```
370 | gpg> addcardkey
371 | Signature key ....: [none]
372 | Encryption key....: F2F4 492E 6061 5C4B EB63 A447 2782 54E6 B4D6 F0F6
373 | Authentication key: [none]
374 |
375 | Please select the type of key to generate:
376 | (1) Signature key
377 | (2) Encryption key
378 | (3) Authentication key
379 | Your selection? 1
380 | [admin pin dialog]
381 | Please specify how long the key should be valid.
382 | 0 = key does not expire
383 | = key expires in n days
384 | w = key expires in n weeks
385 | m = key expires in n months
386 | y = key expires in n years
387 | Key is valid for? (0) 6m
388 | Key expires at Fri 01 Jan 2021 11:51:45 PM CET
389 | Is this correct? (y/N) y
390 | Really create? (y/N) y
391 | [passphrase dialog]
392 | sec ed25519/38ACA93052B3EB9A
393 | created: 2020-07-05 expires: 2021-01-01 usage: C
394 | trust: ultimate validity: ultimate
395 | ssb* cv25519/278254E6B4D6F0F6
396 | created: 2020-07-05 expires: 2021-01-01 usage: E
397 | card-no: 0006 13316619
398 | ssb ed25519/1E99BDD0B41C156B
399 | created: 2020-07-05 expires: 2021-01-01 usage: S
400 | card-no: 0006 13316619
401 | [ultimate] (1). Tim Weber
402 |
403 | gpg> addcardkey
404 | Signature key ....: 44A6 CD9F 0427 8CCB 1DB3 467D 1E99 BDD0 B41C 156B
405 | Encryption key....: F2F4 492E 6061 5C4B EB63 A447 2782 54E6 B4D6 F0F6
406 | Authentication key: [none]
407 |
408 | Please select the type of key to generate:
409 | (1) Signature key
410 | (2) Encryption key
411 | (3) Authentication key
412 | Your selection? 3
413 | Please specify how long the key should be valid.
414 | 0 = key does not expire
415 | = key expires in n days
416 | w = key expires in n weeks
417 | m = key expires in n months
418 | y = key expires in n years
419 | Key is valid for? (0) 6m
420 | Key expires at Fri 01 Jan 2021 11:52:39 PM CET
421 | Is this correct? (y/N) y
422 | Really create? (y/N) y
423 |
424 | sec ed25519/38ACA93052B3EB9A
425 | created: 2020-07-05 expires: 2021-01-01 usage: C
426 | trust: ultimate validity: ultimate
427 | ssb* cv25519/278254E6B4D6F0F6
428 | created: 2020-07-05 expires: 2021-01-01 usage: E
429 | card-no: 0006 13316619
430 | ssb ed25519/1E99BDD0B41C156B
431 | created: 2020-07-05 expires: 2021-01-01 usage: S
432 | card-no: 0006 13316619
433 | ssb ed25519/A8C037AB8AE97089
434 | created: 2020-07-05 expires: 2021-01-01 usage: A
435 | card-no: 0006 13316619
436 | [ultimate] (1). Tim Weber
437 |
438 | gpg> save
439 | ```
440 |
441 | ### Finalizing the YubiKey
442 |
443 | #### PIN retries
444 |
445 | After you enter a wrong user or admin PIN several times in a row, that PIN will be locked.
446 | If it was the user PIN, you can unlock it using the admin PIN.
447 | However, if it was the admin PIN, you’ll have to reset the GnuPG part of the YubiKey, using all of the key material.
448 |
449 | If you’re like me, you’re fine with the default of three retries for the user PIN, but I prefer a few more tries for the admin PIN.
450 |
451 | ```
452 | $ ykman openpgp set-pin-retries 3 1 5
453 | ```
454 |
455 | (The reset code is disabled by default and comes with 0 retries, but you the value you set it do needs to be between 1 and 99.)
456 |
457 | #### Touch policy
458 |
459 | If you want, you can configure the YubiKey to require touches for certain OpenPGP operations.
460 | This is useful if you want to make sure that a signing operation (or authenticate, or decrypt) is only signing one thing, and that there’s not some kind of malware signing something else without you noticing.
461 | `ykman openpgp info` will show the current policies:
462 |
463 | ```
464 | OpenPGP version: 3.4
465 | Application version: 5.2.6
466 |
467 | PIN tries remaining: 3
468 | Reset code tries remaining: 0
469 | Admin PIN tries remaining: 3
470 |
471 | Touch policies
472 | Signature key Off
473 | Encryption key Off
474 | Authentication key Off
475 | Attestation key Off
476 | ```
477 |
478 | You can modify these settings with `ykman openpgp touch` (on older versions of `ykman`) or `ykman openpgp set-touch` (on more recent ones) and set different policies for each type of operation.
479 | Old versions of `ykman` let you choose between `on`, `off` or `fixed` (where `fixed` means “can’t be changed anymore, except by resetting the OpenPGP part of the YubiKey, deleting all keys”), while newer versions also allow a `cached` setting that will not require another touch up to 15 seconds after the previous operation.
480 |
481 | I prefer to require touches for each sign and auth operation, but prefer to allow caching for decryption to support batch operations.
482 |
483 | ```
484 | $ ykman openpgp set-touch sig on
485 | Enter admin PIN:
486 | Set touch policy of signature key to on? [y/N]: y
487 | $ ykman openpgp set-touch aut on
488 | Enter admin PIN:
489 | Set touch policy of authentication key to on? [y/N]: y
490 | $ ykman openpgp set-touch enc cached
491 | Enter admin PIN:
492 | Set touch policy of encryption key to cached? [y/N]: y
493 | ```
494 |
495 | I’m not ready to set any of these to `fixed` yet because I’m scared of forgetting a scenario where it can be cumbersome.
496 |
497 | Note that modifying (i.e. creating or replacing) one of the OpenPGP key slots on the YubiKey will apparently reset the touch policy for that key to `off`.
498 | Therefore you should set them _after_ creating the keys.
499 |
500 | ### Publishing the keys
501 |
502 | First of all, let’s store the keyid in an environment variable for easy access in the future.
503 |
504 | ```
505 | $ gpg --list-keys --keyid-format 0xshort
506 | /home/pi/.gnupg/pubring.kbx
507 | ---------------------------
508 | pub ed25519/52B3EB9A 2020-07-05 [C] [expires: 2021-01-01]
509 | 144FEA6B3FF0F6EFD035963B38ACA93052B3EB9A
510 | uid [ultimate] Tim Weber
511 | sub cv25519/B4D6F0F6 2020-07-05 [E] [expires: 2021-01-01]
512 | sub ed25519/B41C156B 2020-07-05 [S] [expires: 2021-01-01]
513 | sub ed25519/8AE97089 2020-07-05 [A] [expires: 2021-01-01]
514 | $ echo export KEYID=52B3EB9A >> ~/.bashrc # or whereever you store your environment variables
515 | $ . ~/.bashrc
516 | ```
517 |
518 | Next, export the authentication key in SSH format.
519 | How to actually _use_ this exported key then for authentication is currently beyond the scope of this tutorial.
520 | Basically, you’d need to add `enable-ssh-support` in `gpg-agent.conf` and then use `gpg-agent` as a replacement for `ssh-agent`.
521 | Google around, there are lots of howtos.
522 |
523 | Also note:
524 | There was a time when there was a utility called `gpgkey2ssh`, but nowadays GnuPG has replaced that tool with a command line option.
525 | So, in order to extract the key _and_ replace GnuPG’s default comment that specifies the serial number of your YubiKey with something more human-readable, try this:
526 |
527 | ```
528 | $ gpg --export-ssh-key "$KEYID" | awk '{ print $1, $2, "user@yubikey" }' > ~/$KEYID.pub
529 | ```
530 |
531 | Of course feel free to replace `user@yubikey` with whatever you like, it’s just the SSH key’s comment field.
532 |
533 | If you’d like to publish your GPG key on your own webserver, you should write that URL into the key itself:
534 |
535 | ```
536 | $ gpg --edit-key $KEYID
537 | gpg (GnuPG) 2.2.12; Copyright (C) 2018 Free Software Foundation, Inc.
538 | This is free software: you are free to change and redistribute it.
539 | There is NO WARRANTY, to the extent permitted by law.
540 |
541 | Secret key is available.
542 |
543 | sec ed25519/38ACA93052B3EB9A
544 | created: 2020-07-05 expires: 2021-01-01 usage: C
545 | trust: ultimate validity: ultimate
546 | ssb cv25519/278254E6B4D6F0F6
547 | created: 2020-07-05 expires: 2021-01-01 usage: E
548 | card-no: 0006 13316619
549 | ssb ed25519/1E99BDD0B41C156B
550 | created: 2020-07-05 expires: 2021-01-01 usage: S
551 | card-no: 0006 13316619
552 | ssb ed25519/A8C037AB8AE97089
553 | created: 2020-07-05 expires: 2021-01-01 usage: A
554 | card-no: 0006 13316619
555 | [ultimate] (1). Tim Weber
556 |
557 | gpg> keyserver
558 | Enter your preferred keyserver URL: http://scy.name/keys/52B3EB9A.asc
559 | [passphrase dialog]
560 | sec ed25519/38ACA93052B3EB9A
561 | created: 2020-07-05 expires: 2021-01-01 usage: C
562 | trust: ultimate validity: ultimate
563 | ssb cv25519/278254E6B4D6F0F6
564 | created: 2020-07-05 expires: 2021-01-01 usage: E
565 | card-no: 0006 13316619
566 | ssb ed25519/1E99BDD0B41C156B
567 | created: 2020-07-05 expires: 2021-01-01 usage: S
568 | card-no: 0006 13316619
569 | ssb ed25519/A8C037AB8AE97089
570 | created: 2020-07-05 expires: 2021-01-01 usage: A
571 | card-no: 0006 13316619
572 | [ultimate] (1). Tim Weber
573 |
574 | gpg> save
575 | ```
576 |
577 | Also, the YubiKey has a metadata field for the public key, too.
578 | Set it using the `url` command of `gpg --edit-card`.
579 |
580 | Then, export the public key and all of its signatures:
581 |
582 | ```
583 | $ gpg --armor --export "$KEYID" > ~/$KEYID.asc
584 | ```
585 |
586 | #### Changing the PINs
587 |
588 | If you didn’t do it already, now would be a good time to change the PIN (default `123456`) and admin PIN (default `12345678`).
589 | Both are not limited to numbers!
590 | You can usually enter up to 127 bytes of UTF-8.
591 |
592 | There’s also the “unblock” feature where you can set a “reset code”.
593 | As far as I understand it, it allows to to set a code that can _only_ be used to reset a blocked PIN.
594 | This could be useful if you want to allow someone to reset the PIN without disclosing the admin PIN.
595 | I chose not to use it.
596 |
597 | ```
598 | gpg/card> passwd
599 | gpg: OpenPGP card no. D276000124[…] detected
600 |
601 | 1 - change PIN
602 | 2 - unblock PIN
603 | 3 - change Admin PIN
604 | 4 - set the Reset Code
605 | Q - quit
606 |
607 | Your selection? 3
608 | [pin dialog]
609 | PIN changed.
610 |
611 | 1 - change PIN
612 | 2 - unblock PIN
613 | 3 - change Admin PIN
614 | 4 - set the Reset Code
615 | Q - quit
616 |
617 | Your selection? 1
618 | [pin dialog again]
619 | PIN changed.
620 |
621 | 1 - change PIN
622 | 2 - unblock PIN
623 | 3 - change Admin PIN
624 | 4 - set the Reset Code
625 | Q - quit
626 |
627 | Your selection? q
628 | ```
629 |
630 | And this is where the tutorial ends, at least for now.
631 |
632 | ## Adding a size-optimized photo to your public key
633 |
634 | I have just finished writing the tutorial above, which is why, for now, I’ll just link to [Creating a small JPEG photo for your OpenPGP key](https://blog.josefsson.org/2014/06/19/creating-a-small-jpeg-photo-for-your-openpgp-key/) by Simon Josefsson.
635 | There are some valuable pointers in it.
636 |
--------------------------------------------------------------------------------
/src/event-notes/2018/international-php-conference.md:
--------------------------------------------------------------------------------
1 | # International PHP Conference 2018
2 |
3 | October 15 to 19, Munich; [phpconference.com](https://phpconference.com/)
4 |
5 | These are some notes I took during the talks. They don't necessarily contain everything the presentation was about, but what I personally found noteworthy. I didn't check facts and figures for accuracy. I might provide sources to some statistics, links to slides or whatever at a later point.
6 |
7 | ## My personal tl;dr
8 |
9 | The things I found most interesting or enlightening or relevant to my current work. The "from" links point to the respective talk's section on this page.
10 |
11 | * Having PhpStorm's code inspections is nice, but there are also awesome **static analysis** tools like Psalm available to integrate into your CI pipeline. ([from](#squash-bugs-with-static-analysis))
12 | * Instead of using a cache to speed up page generation, why don't you transform your site into a **push architecture** by pre-rendering everything, even personalized results, based on events? ([from](#performance-in-a-personalized-world))
13 | * **Self-governing teams** can solve organizational and speed issues for startups that grow. Involve people, but don't be forced to wait for them. ([from](#working-in-autonomous-teams-why--how))
14 | * Not accidentally fucking up **cryptography in PHP** is finally getting easy, because libsodium is now bundled with PHP 7.2. Previous versions can get it from PECL or even use a PHP implementation. Use Argon2id for password hashing. SHA-512 is faster than SHA-256 (on 64-bit processors). ([from](#crypto-for-everyone--libsodium-in-php-72))
15 | * Check out **Domain-Driven Design**. The concept isn't new, but by focusing on the _problem_ instead of the solution, it helps solve some of the complexity that software projects often have, and it helps you to better understand your users and stakeholders. ([from](#metamorphosis-from-database-driven-to-ddd))
16 | * Of course there are also **patterns** to help you make the right decisions, both while understanding the problem and while solving it. ([from](#a-journey-into-strategic-domain-design))
17 | * **Pair programming** is actually faster than programming alone _and_ produces less bugs. **Code reviews** should regularly also be done as a team. And your **coding styleguide** should focus on things that can _not_ be validated by tools. ([from](#effective-code-reviews))
18 | * There's actually a name for modeling your data to not keep _state,_ but all of the _history_ and derive the state from it. It's called **event sourcing** and there are tools for it. Also, it plays nice with one of today's buzzwords, **CQRS**. (And DDD.) ([from](#ddd-event-sourcing-and-cqrs--theory-and-practice))
19 | * **View models** are a really exciting concept to make your view code (and only it, not even including the template engine) unit-testable. Check out [Templado](https://templado.io/) for a templating engine based on this idea. ([from](#do-you-verify-your-views))
20 | * If that's too radical for you, at least use techniques like service layers and dependency injection to **decouple your code** for easier testing. ([from](#asserttrueisdecoupledmy-tests))
21 | * [whatwebcando.today](https://whatwebcando.today/) and [PWA Stats](https://www.pwastats.com/) are awesome resources if you think about writing a **progressive web application** (PWA). ([from](#is-a-new-cross-platform-development-era-coming))
22 | * And **web components** help you structure it or even integrate legacy frontends. ([from](#angular-react-vue-and-co--peacefully-united-thanks-to-web-components-and-micro-apps))
23 | * Be really careful when running **shell** commands from within PHP. ([from](#tales-from-the-wrong-end--a-maintainers-story-of-open-source--cves))
24 | * **WebAuthn** is a new technology for logging users in without a password. It's not quite there yet, but worth watching. ([from](#webauthn-passwords-are-legacy))
25 |
26 | ## [Is a new Cross-platform Development Era coming?](https://phpconference.com/web-development/is-a-new-cross-platform-development-era-coming/)
27 |
28 | by Maxim Salnikov ([@webmaxru](https://twitter.com/webmaxru)), [recording](https://youtu.be/QMyo7UVfg_A?t=868), [slides](https://slides.com/webmax/pwa-ijs-2018/)
29 |
30 | * PWAs use modern web APIs along with traditional progressive enhancement to create cross-platform applications.
31 | * more mobile usage than desktop
32 | * more than 50% of the users install **zero** apps per month
33 | * web technologies on mobile are improving: performance, access to hardware, auth & payments, integration with the OS: [whatwebcando.today](https://whatwebcando.today/)
34 | * offline PWAs work since Service Worker API
35 | * Why should we call a PWA an "app" and not a website? There are 3x more mobile web unique visitors compared to app users, but people spend 20x more time in apps than on the web
36 | * check out: [Workbox](https://workboxjs.org/), [PWA Builder](https://www.pwabuilder.com/)
37 | * problems:
38 | * breaking changes in the APIs do happen
39 | * browser support is still spotty
40 | * there's no shared roadmap between browser vendors
41 | * success stories (see [PWA Stats](https://www.pwastats.com/))
42 | * Twitter Lite: 3% size of Android version, 70% data usage reduction
43 | * mobile Uber: 50k gzipped, takes less than 3s on 2G networks
44 | * Lancome: 17% conversion increase
45 | * Gartner predicts that PWAs will have replaced 50% of general-purpose, consumer-facing apps by 2020
46 |
47 | ## [Do you verify your Views?](https://phpconference.com/testing-quality/do-you-verify-your-views/)
48 |
49 | by Arne Blankerts ([@arneblankerts](https://twitter.com/arneblankerts)) and Sebastian Bergmann ([@s_bergmann](https://twitter.com/s_bergmann))
50 |
51 | * a view could be a HTML page, XML doc, JSON blob; the markup; a fragment; the data and content shown – or the code that generates them
52 | * a view is **code**
53 | * if it's code, we can test it, and if we can test it, we _should_ test it
54 | * could be done manually by looking at pages (technically an end-to-end test)
55 | * automated with Selenium (still end-to-end)
56 | * end-to-end tests relies on a lot of things in addition to the actual view: webserver, database, routing, template engine, …
57 | * they are also slow and have a high maintenance cost, and it's harder to find the cause of an error if a test fails (compared to a unit test for example)
58 | * edge-to-edge testing would leave out the webserver and browser, i.e. fake a request programmatically
59 | * works even with legacy code by setting `$_REQUEST` etc.
60 | * there are XPath extensions for PHPUnit, see [Domain-Specific Assertions presentation](https://thephp.cc/dates/2017/10/symfony-live-berlin/domain-specific-assertions)
61 | * invoking the action directly, you got rid of (most of the) framework code and routing
62 | * next, we could mock the database
63 | * there's DBUnit if you're using for PHPUnit, but it's unmaintained and its future not too bright, all large frameworks don't use it
64 | * Sebastian tends to suggest using your own custom solution
65 | * skip the action; you now provide sample data and pass it to the template engine that provides it to the view
66 | * skipping the template engine as well usually isn't going to work
67 | * templating today usually has a "push architecture", where the view receives basically an array of crap`^W`data
68 | * templates are code: formatting logic, decision logic, iterations, security (escaping)
69 | * you have no idea what data that you pass in will actually be _used_ and which will be considered for decisions
70 | * side note: do you filter information that you pass into a template (like user data that should only be admin-visible)?
71 | * there's [this blog post from 2011](http://www.workingsoftware.com.au/page/Your_templating_engine_sucks_and_everything_you_have_ever_written_is_spaghetti_code_yes_you) showing that this problem isn't new …
72 | * calling additional business logic from _inside_ the template makes it even worse
73 | * there's a presentation by Nikolas Martens: [Templating – You're doing it wrong](https://www.youtube.com/watch?v=bi0Cb97f7G4)
74 | * it sparked [Tempan](https://github.com/watoki/tempan), which uses HTML annotated using [RDFa](https://en.wikipedia.org/wiki/RDFa)
75 | * the view "model" then contains methods for every `attribute` in the HTML template that returns whatever data the template needs
76 | * these methods can also return objects like users, links etc.
77 | * the view models are now unit-testable!
78 | * pull architecture, no template engine needed, no template required, no framework needed
79 | * you can just instantiate the view
80 | * CQRS is basically going in the same direction
81 | * Arne created [Templado](https://templado.io/) that uses that "view model" concept
82 | * it also has an in-development PHPUnit integration that traces which methods are being called, to make sure you don't have unconsumed data in your view model
83 | * it also has JSON view support upcoming
84 | * **to summarize:** separate HTML/JSON and template logic; use "view models" that can be tested without templating engines and can be traced
85 |
86 | > If you cannot unit-test it, you cannot reuse it.
87 | > — Sebastian Bergmann
88 |
89 | ## [Ten standards a PHP developer should know](https://phpconference.com/php-development/10-standards-a-php-developer-should-know/)
90 |
91 | by Sebastian Feldmann
92 |
93 | * [PHP Framework Interop Group](https://www.php-fig.org/)
94 | * PSR vs [RFC](https://wiki.php.net/rfc/howto)
95 | * PSR-1/PSR-2/PSR-12: coding standards
96 | * PSR-0/PSR-4: autoloading
97 | * [`__autoload`](http://php.net/manual/en/function.autoload.php) is deprecated since 7.2
98 | * PSR-3: logging
99 | * PSR-6/PSR-16: caching
100 | * PSR-16 is "simpler" than PSR6
101 | * PSR-11: dependency injection container interface
102 | * PSR-7: HTTP messages
103 | * PSR-17: HTTP factory interface (to make creating PSR7 objects independent of the framework)
104 | * PSR-18: HTTP client interface
105 | * PSR-15: HTTP request handler and middleware interfaces
106 | * PSR-14: event managing (draft!)
107 | * PSR-8: huggable 😂
108 |
109 | ## [WebAuthn: Passwords are Legacy](https://phpconference.com/performance-security/webauthn-passwords-are-legacy/)
110 |
111 | by Arne Blankerts
112 |
113 | * (a lot of introduction)
114 | * HTML5 actually specifies a [`` element](https://en.wikipedia.org/wiki/SPKAC) to generate client side certificates
115 | * (more stuff that's not about WebAuthn either, but about why passwords suck)
116 | * WebAuthn is an official W3C standard, a sub-spec of FIDO2
117 | * "public keys for authentication in browsers"
118 | * supported in Firefox 60+, Chrome 65+ and Edge starting in October
119 | * needs JavaScript to work
120 |
121 | ## [Zend\Expressive 3 – The Next Generation](https://phpconference.com/php-development/zendexpressive-3-the-next-generation/)
122 |
123 | by Ralf Eggert
124 |
125 | * based on PSRs, PSR-7 was the kick-off for the Expressive project, ZE2 focused on PSR-11, ZE3 on PSR-15
126 | * ZE3 now has components for sessions, CSRF protection and flash messages
127 | * (talk focuses on differences between the ZE versions and the migration paths, I was hoping to get at least some kind of introduction since I'm new to the framework)
128 | * check out: [Swoole](https://www.swoole.co.uk/), PHP C extension that provides coroutines for PHP
129 | * Expressive is the future, Zend Framework has lost momentum
130 |
131 | ## [Metamorphosis: From Database-Driven to DDD](https://phpconference.com/web-development/metamorphosis-from-database-driven-to-ddd/)
132 |
133 | by Julie Lerman, [recording on YouTube](https://youtu.be/ndA-00usnS4?t=651)
134 |
135 | * in DBase, you built databases and put a form on top of it
136 | * even when using .NET, she was focusing on the data instead of the business domain
137 | * path to her transformation
138 | * ALT.NET vs Entity Framework
139 | * start with POCOs
140 | * automated tests (TDD came later)
141 | * Eric Evan's DDD book
142 | * the book starts not about programming, but about communicating with clients, about understanding their domain
143 | * in DDD, it's more about the business logic, and storing and retrieving data becomes more of a secondary concern
144 | * DDD helps you solving the messy complex things by breaking them down into small, solvable, contained and interconnected problems
145 | * the book even helps the more "introverted" people by walking them through client conversations
146 | * strategic design: analyze and model the business problem, identify the best strategies for solving the problem
147 | * bounded context (against leaky abstractions)
148 | * ubiquitous language isn't cross-context
149 | * use the same words from the time when talking to the client all the way down to the code
150 | * duplication can be hard to accept, but is required so that changes in one bounded context don't mess up another
151 | * tactical design: …
152 | * reducing side-effects of relationships
153 | * one-way by default
154 | * value objects by default vs 1:1 relationships
155 | * avoid overkill: not everything needs to be modeled with DDD patterns
156 | * if the application is just data in and data out, that's fine as well; keep things simple
157 |
158 | ## [Angular, React, Vue and Co. – peacefully united thanks to Web Components and Micro Apps](https://phpconference.com/web-architecture/angular-react-vue-and-co-peacefully-united-thanks-to-web-components-and-micro-apps/)
159 |
160 | by Manfred Steyer ([@ManfredSteyer](https://twitter.com/ManfredSteyer)), [recording on YouTube](https://youtu.be/kGwNemiInIw?t=549)
161 |
162 | * web components
163 | * framework independent
164 | * there are several standards for them
165 | * templates
166 | * HTML imports
167 | * forget about them, the browser vendors won't import them because there's already ES imports
168 | * custom HTML elements
169 | * shadow DOM
170 | * can be polyfilled down to IE11
171 | * simply extend `HTMLElement` and use `attachShadow` and `shadowRoot`
172 | * doesn't influence the main DOM
173 | * Angular has "Elements" for custom HTML elements, Vue.js has a CLI switch, in React you have to do this by hand
174 | * micro apps aka micro frontends
175 | * basically what's called "microservices" in the backend
176 | * increase maintainability, reduce communication overhead
177 | * flexible to choose architechtures and frameworks for the apps instead of having to use one for everything
178 | * pros: separate deployment, mix different technologies, less coordination, less complexity
179 | * cons: distributed system, distributed data, UI composition
180 | * UI composition is the important thing to solve
181 | * hyperlinks between several tiny single-page applications?
182 | * sounds cheap, but e.g. Google does this: there's the menu to switch between SPAs (Gmail, Maps etc.)
183 | * simple, but you're losing state, and it's hard to have a consistent UI between them
184 | * (SPA based) shell
185 | * ugliest way to do this: iframes (but: provides the best amount of isolation!)
186 | * bootstrapping several SPAs in one `index.html`
187 | * in this case, consider wrapping the apps into web components
188 | * choosing a solution
189 | * do you have shared state and a lot of navigation between applications?
190 | * little: use hyperlinks
191 | * much: do you have to integrate legacy apps (PHP, Java) or need very strong isolation?
192 | * yes: iframes (not so awesome for public websites, you'll not win an award for this though)
193 | * no: do you need separate deployments or mix technologies?
194 | * yes: try web components to bootstrap several frameworks as web components
195 | * no: go with a monolith and libs in a monorepo
196 | * conclusion: web components allow decoupling from your framework, micro apps allow decoupling teams and projects
197 |
198 | ## [A Journey Into Strategic Domain Design](https://phpconference.com/agile-devops/a-journey-into-strategic-domain-design/)
199 |
200 | by Leandro Lages ([@leandrolages](https://twitter.com/leandrolages))
201 |
202 | * Why DDD?
203 | * over time without care and consideration, software turns into a ball of mud
204 | * it's not only an engineering problem, but also of communication and company structure
205 | * if your domain is already complex, your software usually adds complexity to it
206 | * how can we decouple this into smaller parts?
207 | * problem space: domain, subdomains
208 | * solution space: context map, bounded contexts
209 | * the language between these should be common: ubiquitous language
210 | * tactical patterns: entities, value objects, repositories, factories (mainly code)
211 | * strategic patterns: focus on organization structure and technical aspects; not limited to source code
212 | * Context Mapping
213 | * reality map with contexts and models in them
214 | * legacy system can be viewed as another context in that map
215 | * when you notice that the language is changing, you're most likely in another bounded context
216 | * (this is before ubiquitous language)
217 | * you can also draw maps for organizational and technical reality
218 | * focus on the domain, not on the data, even if you already have technical realities (certain databases etc.)
219 | * all bounded contexts usually have upstream and downstream
220 | * Strategic Patterns
221 | * shared kernel
222 | * e.g. employee model is common to payroll and HR contexts
223 | * but usually in the same subdomain
224 | * anticorruption layer
225 | * before a model comes into my context, it will go through a layer that does the translation into my language
226 | * instead of using another context's language
227 | * open host service
228 | * on top of anticorruption layer (ACL)
229 | * if you use the same ACL between several contexts to a common upstream, instead try to provide a service/translation layer at the upstream
230 | * downstreams don't need to implement ACLs anymore
231 | * customer/supplier
232 | * conformist
233 | * similar to customer/supplier, but no communication
234 | * usually used if the ACL is expensive to implement
235 | * downstreams simply use what upstreams provide
236 | * bad if you have it _inside_ of your organization
237 | * separate ways
238 | * no relation between contexts at all
239 | * instead of using a model from another context, you're building your own model
240 | * starting point when experimenting (no need to use data from another context, just fake your own)
241 | * partnerships
242 | * two teams work together to create a common context without upstream/downstream relation between them
243 | * usually to create a shared kernel
244 | * communicating the context map
245 | * don't try to find a tool for drawing your context map, just draw on paper or something
246 | * you don't need to include _all_ of your company, focus on the important parts
247 | * never think of the data first!
248 | * example: Get Your Guide
249 | * (I'm not noting down all the details of the example)
250 | * trying to create teams on top of _domains_
251 | * strategic importance of context maps:
252 | * retaining integrity (nobody changes your model in a context)
253 | * a plan for attack, how to distribute team
254 | * understanding ownership and responsibility
255 | * revealing areas of confusion in business workflow
256 | * identifying nontechnical obstacles (e.g. team structure)
257 | * encourages good communication
258 | * helps on-board new starters
259 | * DDD is not only for engineers!
260 | * you need to involve domain experts, UX people, etc.
261 | * event storming is a good method for aligning the company
262 | * suggestions:
263 | * book: Patterns, Principles and Practices of Domain-Driven Design (Scott Millett / Nick Tune)
264 | * check YouTube for talks by Tune
265 | * developers might be sceptical: where's a successful example?
266 | * Microsoft is using DDD heavily
267 |
268 | ## [Performance in a Personalized World](https://phpconference.com/performance-security/performance-in-a-personalized-world/)
269 |
270 | by Stefan Priebsch ([@spriebsch](https://twitter.com/spriebsch))
271 |
272 | * interesting words from the introduction: edge site includes
273 | * I didn't take notes during the introduction
274 | * let's build a new frontend that builds a page based on some snippets of data
275 | * these snippets are created by components
276 | * a component publishes content in reaction to an event (push on "publish", change in price etc.)
277 | * now we have a push model
278 | * what if some of the snippets aren't there?
279 | * if the frontend dynamically asks a component to generate the data, we can't generate response times on cache misses anymore
280 | * it also makes the system way more complicated
281 | * so we don't do that
282 | * we just _define_ that the snippets are always there
283 | * where do we store these snippets?
284 | * file system? doesn't work good with parts running on different servers
285 | * key-value store: works if you don't have to do "queries" like "all items for user X"
286 | * search engine?
287 | * implementing this kind of push architecture can be hard because of organizational structures
288 | * instead of personalizing to a single user, why don't you personalize to personas?
289 | * many organizations don't even have enough data for per-user personalization
290 | * doesn't stop you from personalizing further over time
291 | * this "new frontend" can of course also proxy the legacy system
292 | * the frontend can even replace parts of the legacy page with something from the new push architecture → personalized legacy pages!
293 |
294 | ## [Squash Bugs with Static Analysis](https://phpconference.com/php-development/squash-bugs-with-static-analysis/)
295 |
296 | by Dave Liddament ([@DaveLiddament](https://twitter.com/daveliddament))
297 |
298 | * statis analysis can only tell you that your code is incorrect
299 | * tests tell you that a particular scenario is working correctly
300 | * CI and static analysis helps you to reduce the costs of bugs by moving their detection earlier, at least before roll-out
301 | * tool suggestions
302 | * `jakub-onderka/php-parallel-lint`
303 | * `composer validate`
304 | * `friendsofsymfony/php-cs-fixer`
305 | * `jakub-onderka/php-var-dump-check`
306 | * `sensiolabs/security-checker`
307 | * https://github.com/exakat/php-static-analysis-tools
308 | * four types of bugs
309 | * bug
310 | * deferred bug: things that work until you put invalid/unexpected/future values in there
311 | * evolvability defect: roughly equivalent to "technical debt"
312 | * (false positives)
313 | * you can also rewrite your code so that static analysis doesn't trigger, it's often even clearer that way
314 | * "do you really expect me to fix the 3183 bugs I currently have in the code?"
315 | * no, but set these as your baseline
316 | * static analysis in real time can reduce your bug cost to 0 since you fix it before even writing it
317 | * but the advanced checks from the IDE are not running in CI
318 | * other tools: Psalm, Phan, PHPStan
319 | * all have levels (strictest to least strict)
320 | * "generics" (e.g. `@return Employee[]` instead of just `array`), even syntax `@return array` to specify key type
321 | * key/value syntax currently not understood in PhpStorm?
322 | * you can do `@return Employee[]` _and_ `@psalm-return array`
323 | * PSR-5 is being resurrected and will probably advocate that syntax too
324 | * ignoring violations
325 | * reducing the number of bugs
326 | * focus on your business logic (strict), not the framework (less strict or not at all)
327 | * create wrapper code for calling "faulty" 3rd party code
328 | * `@psalm-param class-string $name` can for example make sure that a valid class name is passed in
329 | * `@psalm-assert !null $expression` means "this method will assert that `$expression` is not null"
330 | * you can use stubs to add annotations to 3rd party libraries
331 | * Dave will soon publish [sarb](https://github.com/DaveLiddament/sarb), which can create a "baseline" of all problems at one point in time and only shows new one you introduced
332 | * Psalm also supports `@template` for "real" generics
333 | * see also
334 | * https://medium.com/vimeo-engineering-blog/fixing-code-that-aint-broken-a99e05998c24
335 | * CircleCI (1500 minutes per month for free)
336 |
337 | ## [Event-Sourcing vs CRUD](https://phpconference.com/web-architecture/event-sourcing-vs-crud/)
338 |
339 | by Golo Roden ([@goloroden](https://twitter.com/goloroden))
340 |
341 | * CRUD is so familiar to us that we don't think about it anymore
342 | * an update loses previous value, a delete loses all values; they are destructive actions
343 | * it's hard to answer questions about the past
344 | * event sourcing limits us to create and read; we add to the list of events, but we never remove from it or edit it
345 | * simple example: bank account with income and expense events
346 | * in order to get the balance, all relevant changes have to be _replayed_
347 | * allows us to in the future answer questions about things that happened in the past
348 | * you can do _snapshots_ in order to not have to replay everything from the beginning
349 | * pros: (some obvious ones), semantic expressiveness: your stored events state the _intention_, not only the result
350 | * cons: (obvious ones), hard to check for uniqueness, GDPR
351 | * solving GDPR issues
352 | * simple idea: don't store anything related to humans directly in the events; instead, store it in another system and just refer to it
353 | * if your domain isn't "story-telling" and the history isn't relevant, you don't need to use event sourcing
354 |
355 | ## [AssertTrue(isDecoupled(“my tests”))](https://phpconference.com/testing-quality/asserttrueisdecoupledmy-tests/)
356 |
357 | by Dave Liddament ([@DaveLiddament](https://twitter.com/daveliddament))
358 |
359 | * we want to reduce development and maintenance costs of the test suite
360 | * value of tests = (cost of bugs found by suite) - (cost of suite)
361 | * sometimes you do a small change and half of the test suite fails
362 | * _coupling_ is the degree to which two objects know about each other
363 | * ideally, object A only knows about object B's interface
364 | * if it knows internal details, changes in B may force us to change A as well
365 | * the more loosely coupled, the easier it is to create a test double for B
366 | * first example
367 | * automated testing via Selenium
368 | * request "can we change the layout of page X"? now half of his Selenium tests failed, because they relied on the interface, even if they weren't _testing_ it
369 | * UI changes often
370 | * reduce coupling to the UI!
371 | * idea: page object translates high-level request ("login") to actual UI steps ("find text box, click here, enter this, …)
372 | * changes in the UI only require the page object to be updated
373 | * request: can we change the page a user goes to after logging in?
374 | * idea: introduce a DSL layer with high-level things like"log in", "get score" and talks to correct page objects
375 | * tests now contain `assignUserToTeam`, `answerQuestion` etc. and are more decoupled and actually easier to read
376 | * lessons:
377 | * testing business logic via UI is difficult, time consuming and brittle
378 | * introduce layers between tests and software under test
379 | * but what happens if we replace the entire site with an app or an API?
380 | * second example
381 | * layered architecture: you want to test the core, the business logic
382 | * put a service layer around
383 | * the business logic doesn't even know whether it's a web application
384 | * the core of course has to know interfaces to things like the payment service or email gateway (e.g. via adaptor)
385 | * you might even get rid of the DSL and talk directly to the service layer from the tests
386 | * what do we test at UI level?
387 | * maybe you don't even need automated UI tests anymore
388 | * summary:
389 | * testing business logic at integration level is much easier
390 | * we need to architect our code to make this possible
391 | * but this has other benefits as well
392 | * third example
393 | * we sell our service to different companies, so login now requires username, password and subdomain
394 | * tests were putting data directly into the database, this now failed
395 | * db should be treated like another third-party service
396 | * idea: object mother pattern for users
397 | * also: builder pattern (creates user with default values set, you can change to only what you need)
398 | * repository pattern for db?
399 |
400 | ## [Effective Code Reviews](https://phpconference.com/testing-quality/effective-code-reviews/)
401 |
402 | by Frank Sons ([@FrankS](https://twitter.com/FrankS))
403 |
404 | > Peer code reviews are the single biggest thing you can do to improve your code.
405 | > – Jeff Atwood
406 |
407 | * Why are you doing code reviews? What are you looking for? Can you answer these questions?
408 | * cf [Microsoft survey](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/ICSE202013-codereview.pdf)
409 | * Are you tracking your results? (Fixing right now or later? Different opinions? How successful are your reviews?)
410 | * different opinions: Asking a third person to decide may not be helpful (cf Joel Spolsky: "I am the person with the least knowledge about the problem, I won't decide for you")
411 | * results depend on experience, timing and motivation of the reviewing dev
412 | * Let's make it effective.
413 | * mindset
414 | * Leave your ego at the door. If nobody will tell you how bad you are, how can you improve?
415 | * There's _always_ something that can be improved.
416 | * Forget about "your" code. Collective ownership. Share the knowledge. The team is responsible.
417 | * If you reviewed code, you should be knowledgeable enough to fix bugs in it later on.
418 | * Maybe don't do only pull request reviews. Large pull requests will be left lying around the longest.
419 | * try team reviews
420 | * no longer than 90 minutes
421 | * maybe one review meeting per 2-week sprint
422 | * don't pick random code, but something that's _important_
423 | * don't have the author explain the code: pick someone at random to explain it
424 | * solves the problem of people not asking questions
425 | * _understand_ every line, especially complicated ones with formulas or something
426 | * intention
427 | * if you're writing an experiment, _learnings_ are more important than quality
428 | * if you're writing a payment system, quality and security are more important
429 | * pick the right method
430 | * for style, pull requests are fine
431 | * for finding the best solution, pair programming is probably better, or team reviews
432 | * create a coding guideline
433 | * don't add stuff you can check with tools
434 | * let the team create it
435 | * it's a living document
436 | * how are we going to do things? what are the "best" solutions to things? best practices?
437 | * keep track of results
438 | * make sure you have follow ups, e.g. architectural flaws
439 | * don't discuss longer than 5 minutes in the review
440 | * not everything needs to be fixed at once
441 | * of course, if it just takes you only one minute, do it
442 | * create a review checklist based on the guideline you've created
443 | * "The only difference between screwing around and science is writing it down."
444 | * reviews are part of the development and shouldn't be second-class citizens
445 | * don't document what the code is doing, but _why_ it's doing that
446 | * "yeah this code is complicated but it's working" is no excuse: other people need to _understand_ it in order to be able to fix bugs in it
447 | * if nodoby can reason _why_ solution A is better than B, it's opinion
448 | * otoh, if there is a reason why one of these is better, choose that one
449 | * try to not care about things that are opinion
450 | * for opinion things, vote as a team, or in the worst case, flip a coin
451 | * there are studies showing that pair programming is faster
452 | * in the beginning it's slower but produces far less bugs
453 | * later on it's even faster than doing it on your own _and_ produces less bugs
454 | * see Johann Peter Hartmann's [Management Brainfucks](https://de.slideshare.net/johannhartmann/management-brainfucks)
455 | * try mob programming at some time, but it's not something to do regularly
456 |
457 | > I've seen companies where they don't put all of the developers on the same plane, because if it crashed, the company would go bankrupt.
458 | > – Frank Sons
459 |
460 | ## [DDD, event sourcing and CQRS – theory and practice](https://phpconference.com/web-architecture/ddd-event-sourcing-and-cqrs-theory-and-practice/)
461 |
462 | by Golo Roden ([@goloroden](https://twitter.com/goloroden))
463 |
464 | * example he used: [Never Completed Game](https://www.nevercompletedgame.com/)
465 | * the hard thing is not to create the game, but to come up with the questions
466 | * experts do that
467 | * language is a core thing: open game, start game, create game?
468 | * a command will either be fulfilled or denied and cause events
469 | * you can have the discussion about how to name things with anyone on the team, there's no technology involved
470 | * in DDD, a thing that has a state and reacts to commands is called an aggregate
471 | * a word ("game") can mean different things to different people
472 | * DDD solves this by introducing a bounded context, and in that context the word has a single, well-defined meaning
473 | * so the context has a ubiquitous language and one or more contexts
474 | * event sourcing can be done without DDD, but they play nice with each other, since DDD has events, too
475 | * CQRS is CQS on the application level
476 | * CQS means that each method is either a command or a query
477 | * this may seem easy, but think of `stack.pop()`
478 | * so CQRS splits for example an API into a read and a write API
479 | * joins are expensive, reading is important: 5NF databases are not always the goal
480 | * have a write and a read database
481 | * can now be scaled independently
482 | * writes to the write DB are pushed to the read DB using a message queue, which of course causes eventual consistency
483 |
484 | ## [Tales from the wrong end – a maintainer’s story of open source & CVEs](https://phpconference.com/php-development/tales-from-the-wrong-end-a-maintainers-story-of-open-source-cves/)
485 |
486 | by Marcus Bointon ([@SynchroM](https://twitter.com/SynchroM))
487 |
488 | * presenter is maintainer (not original author) of PHPMailer, really popular package existing since 2001
489 | * once upon a time: [security issue in PHPMailer](https://github.com/PHPMailer/PHPMailer/issues/903)
490 | * coordinated disclosure:
491 | * someone finds a vulnerability, reports it to vendor
492 | * vendor develops and releases patch
493 | * vulnerability disclosed
494 | * PHPMailer: CVE-2016-10033, CVE-2016-10045
495 | * CVE type alone doesn't tell you a lot, even if it's a remote code execution
496 | * there's an additional severity rating
497 | * the PHPMailer issues were rated 9.8/critical
498 | * CVE-2016-10033
499 | * `$params = sprintf('-f%s', $this->Sender)`
500 | * attack string can be a valid email address! so we escape it, right?
501 | * `$params = sprintf('-f%s', escapeshellarg($this->Sender))`
502 | * before release, someone posted an exploit → we had become a zero-day
503 | * so fix was rushed out as 5.2.18
504 | * also known as CVE-2016-10045
505 | * CVE-2016-10045
506 | * we think we're doing `$mailcommand . escapeshellarg($param)`
507 | * but `mail()` applies `escapeshellcmd()` internally
508 | * `escapeshellcmd($command . escapeshellarg($param))`
509 | * result is undefined and exploitable
510 | * workaround released in PHPMailer 5.2.20
511 | * this is fundamentally a PHP bug
512 | * we ended up checking whether the escaped version is different than the non-escaped version and if it is, we refuse to use it
513 | * affected Wordpress, Joomla, Drupal etc; articles in press
514 | * but comments on handling the vuln were generally positive
515 | * how did other mailers fix this?
516 | * they didn't!
517 | * Zend_Mail, SwiftMailer, RoundCube all vulnerable
518 | * similar bugs in Python, Ruby, Node
519 | * researcher wrote a long article about the general vulnerability of PHP's `mail()` function
520 | * lessons learned
521 | * don't use `mail()`, use SMTP to localhost
522 | * open source is awesome
523 | * when it matters, people will show up to help, it's not all left to you
524 | * security researchers and whistleblowers need effective legal protection as a matter of national policy
525 | * donations to open source?
526 | * there's a lot of time, effort and enthusiasm going into it
527 | * differentiate between project and personal
528 | * PHPMailer could use a code audit
529 | * Patreon is great for writers
530 | * open source maintainers will almost need to _become_ a writer or regular blogger to benefit from it
531 | * PayPal is probably better
532 | * make it easy!
533 | * his experience: one 0.25 EUR donation, another one with 0.75 EUR and after mentioning it in a talk another one: 9 EUR so that he is now at a nice round 10
534 | * thanks to
535 | * security researches - we need them!
536 | * those who comment, submit PRs, review code
537 | * everyone providing feedback and bug reports
538 | * those that answer questions on Stack Overflow
539 |
540 | ## [Working in Autonomous Teams: Why & How](https://phpconference.com/agile-devops/working-in-autonomous-teams-why-how/)
541 |
542 | by Tina Dreimann ([@tina_3men](https://twitter.com/tina_3men))
543 |
544 | My notes don't reflect how inspiring this talk was. At some point, I even stopped taking notes and just listened. I'm afraid that even once the slides are available, they won't give a lot more insight. Talk to Tina if you can, she has a lot of experience and new solutions.
545 |
546 | > Control leads to compliance; autonomy leads to engagement.
547 | > – Daniel Pink
548 |
549 | * often autonomy is mistaken for anarchy
550 | * they work in squads and tribes
551 | * main reason for that was growth
552 | * they were in a place were they were slowed down by their size
553 | * CTO: "I was looking so long for ways to motivate people, and then I realized that I simply have to get out of their way and not _demotivate_ them."
554 | * diversity: every important role in one team; don't lock developers away in a room
555 | * growth mindset is everything
556 | * every team needs their missing & vision
557 | * mission should be the north star, never be reachable
558 | * vision should be a clear playground, a picture of the "preferred future"
559 | * self-governing teams set their own objectives & key results
560 | * OKRs: ambitious, focused, team-driven, fully integrated
561 | * useless if it's not defined by the team together
562 | * don't just name a person to do it, you're missing the diversity!
563 | * at least have a team discussion
564 | * advice processes are not optional
565 | * before you make a decision, get advice from someone
566 | * (even in an autonomous team!)
567 | * decision making is more than yes or no
568 | * difference between consent and consensus
569 | * not everyone needs to say yes, they just need to not object
570 | * else it slows you down
571 | * squad size
572 | * if you focus on the business priorities, you will be able to do a natural split
573 | * try slicing by customer groups
574 | * in the beginning they started with fully-stacked teams
575 | * teams got too big
576 | * give the teams the time to get rid of legacy!
577 | * put people into the same team to make them understand each other better (e.g. devs/marketing)
578 |
579 | ## [Crypto for Everyone – Libsodium in PHP 7.2](https://phpconference.com/php-development/crypto-for-everyone-libsodium-in-php-7-2/)
580 |
581 | by Marcus Bointon ([@SynchroM](https://twitter.com/SynchroM))
582 |
583 | > If you type `mcrypt` in your editor, stop. You're doing it wrong.
584 | > – Marcus Bointon
585 |
586 | * crypto functions by number of keys involved
587 | * 0 keys: hashes, PRNGs, key derivation
588 | * 1 key: MACs, secret key encryption
589 | * 2 keys: key exchange, public key encryption, digital signatures
590 | * core & extension crypto in PHP: mhash, mcrypt, openssl, pecl-gnupg, hash, pecl-scrypt, password_hash, hash_equals, CSPRNG, pecl-libsodium, sodium
591 | * so sodium came into PHP 7.2 after being available as a PECL extension since 5.6
592 | * OpenSSL is okay, depending on which part of it you're using
593 | * libraries: zend-crypt is okay, phpseclib is based on mcrypt, don't use it
594 | * sodium_compat is libsodium reimplemented in PHP
595 | * ciphersweet allows searches on encrypted data
596 | * libsodium is a fork of NaCl, supported on more platforms (also in JS and WASM), multiple language bindings, audited code
597 | * NaCl is by DJB, Tanja Lange etc., libsodium by Frank Denis (pure-ftpd), Scott Arciszewski
598 | * sodium takes away choices so that you can't choose the wrong thing
599 | * side channel attacks: timing, thermal, RF emissions, light, sound power (Spectre/Meltdown, password hash timing, Ethernet switch LEDs connected to the data lines)
600 | * instead of `WHERE email='…' AND password='…'`, do the comparison in PHP, but use `password_verify` instead of a string comparison, which will return early on the first character mismatch
601 | * or `sodium_crypto_pwhash_str_verify`
602 | * SHA-512 is more efficient on a 64-bit processor than SHA-256
603 | * Argon2i is resistant against timing attacks, Argon2d against GPUs/parallelization, Argon2id against both
604 | * when writing new apps, use the best hash currently available (rehash on login to upgrade)
605 | * hashing with sodium
606 | * `sodium_crypto_shorthash` for verification and non-crypto purposes
607 | * `sodium_crypto_generichash` (uses e.g. BLAKE2b)
608 | * `sodium_crypto_pwhash_str` for passwords (e.g. Argon2id)
609 | * message authentication codes
610 | * `sodium_crypto_auth`, `sodium_crypto_auth`
611 | * the CSPRNG in PHP7 is good, which is why sodium doesn't replace it
612 | * secret-key encryption
613 | * `sodium_crypto_secretbox_*` for combined encrypt-then-MAC
614 | * `sodium_crypto_aead_*` (authenticated encryption with associated data)
615 | * ChaCha20-Poly1305 is efficient on low-power machines, AES256-GCM is basically less efficient, except on most modern processors since it's hardware-accelerated
616 | * public-key encryption
617 | * `sodium_crypto_box_*` functions
618 | * key derivation: `sodium_crypto_pwhash`
619 | * key exchange: `sodium_crypto_kx_*`
620 | * `sodium_memzero` to clear the key etc. from memory
621 | * `password_hash` in 7.2 supports Argon2i, but requires libargon2, which isn't included by default; will be fixed in 7.3
622 |
--------------------------------------------------------------------------------