├── LICENSE.txt ├── README.md └── src ├── _img └── macos-dhcp-hostname-override.png ├── artificial-intelligence.md ├── books └── 7-habits.md ├── business.md ├── chrome-os.md ├── community.md ├── databases.md ├── event-notes └── 2018 │ └── international-php-conference.md ├── finance.md ├── git.md ├── golang.md ├── great-bugs.md ├── hardware-hacking.md ├── http.md ├── intellij.md ├── ios.md ├── keyboard.md ├── linux.md ├── macos.md ├── music.md ├── nomad-life.md ├── openpgp.md ├── package-tracking.md ├── php.md ├── privacy.md ├── productivity.md ├── python.md ├── quotes.md ├── recipes ├── schneebaelle.jpg └── schneebaelle.md ├── reinstall-guide.md ├── shell-scripting.md ├── software-development ├── documentation.md ├── estimating.md ├── learning.md ├── plone.md ├── releasing.md ├── tech-debt.md └── testing.md ├── termux.md ├── text-files.md ├── unix.md ├── usb.md ├── vim.md └── wsl.md /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 | -------------------------------------------------------------------------------- /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/_img/macos-dhcp-hostname-override.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scy/knowledge/63852247f33d35cf321e8508ace546f1e44f95d7/src/_img/macos-dhcp-hostname-override.png -------------------------------------------------------------------------------- /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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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 | ![Screenshot demonstrating the problem](_img/macos-dhcp-hostname-override.png) 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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/quotes.md: -------------------------------------------------------------------------------- 1 | # Quotes 2 | 3 | ## Focus 4 | 5 | > The essence of strategy is choosing what not to do. 6 | > — Michael Porter 7 | -------------------------------------------------------------------------------- /src/recipes/schneebaelle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scy/knowledge/63852247f33d35cf321e8508ace546f1e44f95d7/src/recipes/schneebaelle.jpg -------------------------------------------------------------------------------- /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 | ![Schneeball auf einem weißen Teller, dekoriert mit Heidelbeeren und Physalis](schneebaelle.jpg) 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 | --------------------------------------------------------------------------------