`` elements. The resulting
-HTML for a code block then looks like this:
-
-.. code:: html
-
-
-
-
- The code!
-
-
-
-
-You can find the (rather short) source of the ``rst2html`` wrapper `below <#rst2html-wrapper>`_.
-
-The CSS
--------
-
-This modified HTML structure of the code listing gets accompanied by some CSS to make it flow nicely. Here is a listing
-of the complete CSS controlling the listing. The only bit that isn't included here is the actual syntax styling rules
-for the pygments tokens.
-
-.. code:: css
-
- /*****************************************************/
- /* Code block formatting / syntax highlighting rules */
- /*****************************************************/
-
- .code {
- font-family: "Fira Code";
- font-size: 13px;
- text-align: left; /* Override default content "justify" alignment */
- white-space: pre-wrap;
- word-wrap: break-word;
- overflow-x: auto;
- display: grid;
- align-items: start;
- grid-template-columns: min-content 1fr;
- }
-
- .code > .line {
- padding-left: calc(2em + 5px);
- text-indent: -2em;
- padding-top: 2px;
- min-width: 15em;
- }
-
- /* Make individual syntax tokens wrap anywhere */
- .code > .line > span {
- overflow-wrap: anywhere;
- white-space: pre-wrap;
- }
-
- /* We render line numbers in CSS! */
- .code > .lineno {
- counter-increment: lineno;
- word-break: keep-all;
- margin: 0;
- padding-left: 15px;
- padding-right: 5px;
- overflow: clip;
- position: relative;
- text-align: right;
- color: var(--c-text-muted);
- border-right: 1px solid var(--c-fg-highlight);
- align-self: stretch;
- }
-
- /* We also handle line continuation markers in CSS. */
- .code > .lineno::after {
- position: absolute;
- right: 5px;
- content: "\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳";
- white-space: pre;
- color: var(--c-text-muted);
- }
-
- /* Insert the actual line number */
- .code > .lineno::before {
- content: counter(lineno);
- }
-
- .code::before {
- counter-reset: lineno;
- }
-
- .code .hll {}
- /* Following are about 50 lines that define the styling of each kind of pygments syntax highlight token. These lines
- all look like the following: */
- .code .c { color: var(--c-text); font-weight: 400 } /* Comment */
-
-This CSS does a few things:
-
- 1. It renders the ```` code listing element using a two-column CSS ``display: grid`` layout. The left column is
- used for the line numbers, and the right column is used for the code lines.
- 2. It numbers the lines using a `CSS Counter`_. CSS counters are meant for things like numbering headings and such, but
- they are a perfect fit for our purpose.
- 3. It inserts the counter value as the line number into the ```` element's ``::before``
- pseudo-element. A side effect of using the ``::before`` pseudo-element is that without doing anything extra, the
- line numbers will remain outside of the normal text selection so they will neither be highlighted when selecting
- listing content, nor will they be copied when copy/pasting the listing content.
- 4. It inserts a string of ``"\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳"`` into the line number span's
- ``::after`` pseudo-element. This string evaluates to a sequence of unicode arrows separated by line breaks, and
- starting with an empty line. The ``::after`` pseudo-element is positioned using ``position: absolute``, and the
- parent ```` has ``position: relative`` set. This way, the arrow pseudo-element gets placed on
- top of the lineno span without affecting the layout at all. By setting ``overflow: clip`` on the parent ````, the arrow pseudo-element gets cut off vertically wherever the parent lineno element naturally
- ends.
-
-The line number span is inserted into the parent ```` element's CSS grid using ``align-self: stretch``, which
-causes it to vertically stretch to fill the available space. Since the line number span only contains the line number,
-its minimum height is a single line. As a result, it will stretch higher only when the corresponding code line in the
-right grid column stretches vertically because of line wrapping. When that happens, part of the arrow pseudo-element
-starts showing through from behind the ``overflow: clip`` of the line number span, and one arrow gets rendered for each
-wrapped listing line.
-
-When the page is too narrow, we don't want the code listing's lines to wrapp into a column of single characters. To
-prevent that, we simply set a ``min-width`` on the ```` in the right column, and set ``overflow-x:
-auto`` on the listing ````. This results in a horizontal scroll bar appearing whenever the listing gets too narrow.
-
-You can try out the line wrapping by resizing this page!
-
-rst2html wrapper
-----------------
-
-Here is the python ``rst2html`` wrapper that monkey-patches code rendering. I made hugo invoke this while building the
-page by simply overriding the ``PATH`` environment variable.
-
-.. code:: python
-
- #!/usr/bin/env python3
- # Based on https://gist.github.com/mastbaum/2655700 for the basic plugin scaffolding
-
- import sys
- import re
-
- import docutils.core
- from docutils.transforms import Transform
- from docutils.nodes import TextElement, Inline, Text
- from docutils.parsers.rst import Directive, directives
- from docutils.writers.html4css1 import Writer, HTMLTranslator
-
-
- class UnfuckedHTMLTranslator(HTMLTranslator):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.in_literal_block = False
-
- def visit_literal_block(self, node):
- # Insert an empty "lineno" span before each line. We insert the line numbers using pure CSS in a ::before
- # pseudo-element. This has the added advantage that the line numbers don't get included in text selection.
- # These line number spans are also used to show line continuation markers when a line is wrapped.
- self.in_literal_block = True
- self.body.append(self.starttag(node, 'pre', CLASS='literal-block'))
- self.body.append('')
-
- def depart_literal_block(self, node):
- self.in_literal_block = False
- self.body.append('\n\n')
-
- def visit_Text(self, node):
- if self.in_literal_block:
- for match in re.finditer('([^\n]*)(\n|$)', node.astext()):
- text, end = match.groups()
-
- if text:
- super().visit_Text(Text(text))
-
- if end == '\n':
- if isinstance(node.parent, Inline):
- self.depart_inline(node.parent)
- self.body.append(f'\n')
- if isinstance(node.parent, Inline):
- self.visit_inline(node.parent)
-
- else:
- super().visit_Text(node)
-
-
- html_writer = Writer()
- html_writer.translator_class = UnfuckedHTMLTranslator
- docutils.core.publish_cmdline(writer=html_writer)
-
-.. _Hugo: https://gohugo.io/
-.. _RestructuredText: https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html
-.. _Pygments: https://pygments.org/
-.. _`monkey-patched`: https://en.wikipedia.org/wiki/Monkey_patch
-.. _`CSS Counter`: https://developer.mozilla.org/en-US/docs/Web/CSS/counter
diff --git a/content/blog/epa-sgd-crypto/index.rst b/content/blog/epa-sgd-crypto/index.rst
deleted file mode 100644
index ccbf648..0000000
--- a/content/blog/epa-sgd-crypto/index.rst
+++ /dev/null
@@ -1,100 +0,0 @@
----
-title: "75 Million Lives, Two Keys"
-date: 2025-01-05T23:42:00+01:00
-draft: true
----
-
-2025 has begun. In this new year, with its new national healthcare record system, the country of Germany will start one
-of the largest rollouts of a cryptographic system in history. While the system has received scrutiny as well as
-resulting harsh criticism from a number of parties ranging from NGOs to everyday civilians, the system has received
-surprisingly little attention from the academic applied cryptography crowd. Additionally, previous criticism of
-the system has largely revolved around organizational issues. While valid, we belive that some cryptographic issues at
-the core of the system have escaped attention unitl now. In particular, at the core of the system is a key escrow system
-that contains several questionable design choices and that in its overall design seems out of place in 2025.
-
-The aim of the system is to serve as a shared storage for all healthcare records of a person. In the system, a person's
-entire patient file with all documentation on the treatment process including test results, images and other raw data
-will be stored in something vaguely resembling cloud storage such that all healthcare providers that the person visits
-can access the entire file. This centralized, synchronized storage eliminates the need for transferring this data
-between hospitals and doctors offices by fax, mail or physical media as it was common practice until now. After a
-development and testing phase lasting approximately five years, the German government decided to roll out the system to
-everybody insured under Germany's mandatory national health insurance scheme, totalling approximately 75 million people,
-on January 15th 2025.
-
-In this article, we will give an overview of the system's cryptographic design before highlighting a few odd
-design choices that could amount to a viable attack vector to the powerful adversaies
-
-## Context and involved parties
-
-Germany has a national, mandatory health insurance system. The system is open to any permanent resident of the country
-irrespective of citizenship. The system is mandatory in that while residents can choose between a number of both
-publically owned as well as private healthcare providers, it is not possible to opt out of the system. The public health
-insurance providers cover approximately 90% of German residents. These providers are organized in an umbrella
-organization named "GKV Spitzenverband". The resposibility of this umbrella organization largely revolves around
-negotiating prices with pharmaceutical companies and with healthcare providers as a publically sanctioned cartel, but
-also includes the specification and operation of shared IT infrastructure for billing and data exchange between
-healthcare providers.
-
-While GKV Spitzenverband is the party that ultimately holds responsibility for the regulatory administration of national
-healthcare IT infrastructure, it has delegated large parts of both the technical specification of this infrastructure as
-well as its day-to-day operation to Gematik GmbH, a state-owned limited liability corporation created specifically for
-the purpose of developing and implementing national healthcare IT standards. The electronic healthcare record system we
-describe in this article was standardized and implemented by Gematik GmbH under the direction of GKV Spitzenverband.
-
-Healthcare providers in Germany need to be registered with GKV Spitzenverband to serve members of public health
-insurance providers. Since these public providers constitute approximately 90% market share, the vast majority of
-healthcare providers are registered this way.
-
-Before the new national health record system, a number of healthcare IT processes have already been standardized and
-implemented by the parties above. In particular, every insured person already owns a cryptographic smartcard that acts
-as their proof of identity when accessing healthcare services. On the other side of such transactions, healthcare
-providers are likewise identified by cryptographic smartcards. Until now, these cards were used to facilitate billing of
-services from healthcare providers to insurers and to transfer prescriptions from prescribing doctors to pharmacies.
-
-A central role in this existing infrastructure is assumed by VPN gateways that link healthcare providers to
-the centrally-run backend infrastructure. Gematik GmbH calls these devices "Konnektor". They are specially-built
-hardware devices that contain multiple smart cards to authenticate the VPN connection towards the backend, and besides
-acting as a standard VPN gateway for client applications in the healthcare provider's network to tunnnel their backend
-requests through, the Konnektors also perform cryptographic operations in some of Gematik GmbH's protocols, such as
-authenticating certain requests using signatures.
-
-## Design principles
-
-The new health record system was built on top of the existing infrastructure described above. In particular, access to
-health records is managed through keys stored in the patient's and the healthcare provider's existing smartcards, and
-all backend communication is tunneled through the existing VPN. Access to the files is mediated through the healthcare
-provider's existing patient management software. While in early drafts of the system, access to healthcare records
-through the patient's smartcard was gated behind a PIN, the impracticality of making the entire patient populace
-remember PINs led the implementors to scrap this provision, meaning that the patient's smartcard is all a healthcare
-provider needs to access the patient's record.
-
-A critical cornerstone in the system's design is that the system's designers decided that a lost smartcard should not
-lead to any data loss. As a consequence of this decision, while some of the record's access keys are kept on the
-patient smartcard, in contravention to conventional smartcard designs the same keys are kept accessible in a centralized
-key escrow system named "Schlüsselgenerierungsdienst" and abbreviated as SGD. Furthermore, these keys are not generated
-on the smartcard either -- instead, the key escrow system generates these access keys, one copy of which is then
-transmitted and stored inside the smartcard.
-
-The system supports re-issuing a smartcard to gain access to a healthcare record. Since the record's privacy pivots on
-this process, the system incorporates some organziational countermeasures that aim to make it hard to gain access to a
-re-issued copy of a patient smartcard without the patient's help or otherwise multiple colluding parties.
-
-## Cryptographic design
-
-
-
-## The implied adversary model
-
-While Gematik GmbH publishes detailed specifications of the systems they standardize, these specifications and some
-associated implementation guidelines are about the extent of public information. Software implementations are being kept
-secret, and while standardization results are available, a large fraction of design rationale is discussed behind closed
-doors. From an academic perspective, the most glaring omission in Gematik GmbH's public documents is any definition of a
-threat model or an adversary model. As a result of this, we will deduce an adversary model below by contextualizing the
-published standards in the national healthcare setting. We will base our further analysis of the system on this
-adversary model.
-
-
-
-## Previous reviews and audits of the system
-
-[0] https://www.destatis.de/DE/Themen/Arbeit/Arbeitsmarkt/Qualitaet-Arbeit/Dimension-2/krankenversicherungsschutz.html
diff --git a/content/blog/hsm-basics/index.rst b/content/blog/hsm-basics/index.rst
deleted file mode 100644
index 74b7275..0000000
--- a/content/blog/hsm-basics/index.rst
+++ /dev/null
@@ -1,214 +0,0 @@
----
-title: "Hardware Security Module Basics"
-date: 2019-05-17T15:29:20+08:00
-summary: >
- I gave a short introduction into Hardware Security Modules at our university workgroup, including an overview on
- interesting research directions.
----
-
-On May 17 2019 I gave a short presentation on the fundamentals of hardware security modules at the weekly seminar of
-Prof. Mori's security research working group at Waseda University. The motivation for this was that outside of low-level
-hardware security people and people working in the financial industry HSMs are not thought about that often. In
-particular most network or systems security people would not consider them an option. Also it could turn out to be
-really interesting to think about what could be done with an HSM in conjunction with modern cryptography (instead of
-just plain old RSA-OAEP and AES-CBC).
-
-`Click here to download a PDF with the slides for this talk. `__
-
-Ideas for research in HSMs
-==========================
-
-Preparing for this talk brought me back to some research ideas I've been working on for a while now. Since I'm not sure
-I'll find the time to properly research this topic, I thought it would be great to write down some rought outlines first
-for future reference.
-
-The Problem with current HSM tech
----------------------------------
-
-Currently, HSMs are only used in certain specific niche applications such as certificate authority key management and
-financial transaction data handling. One key reason for this is that HSMs currently don't provide the affordances that
-would be needed for them to be adopted more widely by the cryptographic and security engineering community. As far as I
-can tell, the two core missing affordances are:
-
-1. To be more widely adopted, HSMs must become less expensive. Currently, they go for several tens of thousands of Euro,
- which puts them outside most budgets.
-2. To be more widely adopted, HSMs must provide the standardized programming interfaces familiar to cryptographic
- developers. Currently, every HSM vendor has their own custom cryptographic API and a developer will have to train on
- one specific vendor's tooling. Furthermore, any documentation of these internals is kept secret behind NDAs. This
- constitutes a high barrier to entry, decreasing adoption in particular with young developers accustomed to
- open-source ecosystems.
-
-Attacking cost of implementation
---------------------------------
-
-The first issue can be addressed by simply creating a viable low-cost alternative. There is no fundamental technical
-reason for the high cost of HSMs. This cost is instead due to manufacturers trying to recoup their expenses for R&D as
-well as certification from the small volumes HSMs are sold in.
-
-Compared to system integration and certification the pure R&D cost of HSM defense mechanisms themselves is not too high
-in an academic context it should be feasible to develop a sort of HSM blueprint that can then be cheaply produced by
-anyone in need. Since the application areas outlined here are far from the core business areas of the clients of
-established HSM vendors this would most likely not be a realistic threat to any established vendor's business and a
-co-existence of both should not pose any problems in the short term.
-
-Benefits of an academic HSM standard
-------------------------------------
-
-Tackling the high cost of current HSM hardware with an open-source HSM blueprint would yield
-several academic advantages beyond cost reduction.
-
-1. An open-source blueprint could serve as an academic reference design to evaluate and compare other HSM designs
- against. For instance this would not only allow quantifying the effectiveness of academic security measures but also
- allow an evaluation of commercial HSMs.
-2. An open-source blueprint could stimulate academic research in this academically very quiet albeit commercially
- important area. This research would ultimately benefit everyone employing HSMs by raising security standards in the
- field. Since HSMs are never solely relied upon for overal system security both defensive and offensive security
- research would yield these benefits.
-3. An open-source blueprint would encourage new people to get into the field and both apply HSMs to practical problems
- as well as improve HSMs themselves. Currently, this is highly discouraged due to the strictly proprietary nature of
- all available systems.
-4. Finally, developing an open-source HSM blueprint might yield new findings in adjacent academic areas due to the
- hightly multi-disciplinary nature of security research in general and HSM design in particular.
-
-Scope of an academic HSM standard
----------------------------------
-
-An academic HSM blueprint would need to be flexible so that researchers can adapt it to their particular problem. A
-modular architecture would lend itself to this flexibility. Fundamentally, there would be three components to this
-architecture. First, a **base** containing infrastructure such as the surveillance microcontroller, power supplies,
-power supply filtering and hardware DPA countermeasures, and possibly a standardized mechanical and electrical
-interface.
-
-Next to the base, a system integrator would put their *payload*. The nature of this payload is intentionally kept
-unspecified, and it might be anything from a cryptographic microcontroller to a small embedded system such as a
-raspberry pi single board computer. Keeping the *payload* open like this achieves two benefits: It gives the HSM
-blueprint's user *their* familiar tooling and the hardware *they* need, allowing fast adoption. Someone well-versed in
-e.g. Javascript could literally implement their cryptography in Javascript, run it on an off-the-shelf raspberry pi and
-just apply the HSM blueprint around it. In addition, keeping the *payload* open reduces the scope of what needs to be
-implemented. Building a general SDK on top of something like a bare ARM SoC such as a TI OMAP or a Freescale/NXP IMX
-would be a considerable additional burden, on top of the actual HSM design. Keeping the *payload* open allows research
-to concentrate on the actual point, the HSM design.
-
-The final and most important component would be a set of *security measures* that can be combined with the base to
-form the final HSM. Each of these *security measures* would entail a detailed specification of its design, manufacture
-and security properties. These *security measures* could be simple things like tamper switches or potting, but could
-also be complex things like security meshes.
-
-Given these three components -- *base*, *payload* and *security measures* as detailed specifications any engineer should
-be able to design and manufacture a HSM customized to their needs. Unifying these three components within the HSM
-blueprint would be a set of reference designs. Each reference design would implement a particular parametrization of the
-three architectural components with a physical hole cut out where the payload would go.. These reference designs would
-for one serve to guide any implementer on the customization and integration of their own derivation from the blueprint.
-In addition it would serve as an extremely simple, low-cost point of entry into the ecosystem. A curious researcher
-could simply replicate the reference design and put their existing payload inside. Practically this might mean e.g. a
-researcher having PCBs produced according to the design files for a reference design for a mesh-based HSM, producing
-their own mesh, physically glueing a raspberry pi SBC into the middle of it, and potting the resulting system. Given the
-ease of prototype PCB fabrication today this would realistically allow evaluation of HSM technologies on a budget that
-is orders of magnitude less than the cost of current HSMs.
-
-Research ideas for tamper detection mechanisms
-==============================================
-
-The core component of an HSM blueprint would be a suite of tamper detection mechanisms. Following are a few ideas on how
-to improve on the current state of the art of membrane tamper switches plus temperature sensors plus PCB and printed
-security meshes plus potting.
-
-DIY or small lab mesh production
---------------------------------
-**Analog sensing** meshes are a proven technology where instead of just monitoring for continuity and shorts, analog
-parameters of the mesh traces such as inductance and mutual capacitance are monitored. In 2019, `Immler et al. published
-a paper `__ where took this principle and turned it all the
-way up. They directly derived a cryptographic secret from the analog properties of their HSM's security mesh in an
-attempt to built a `Physically Unclonable Function, or PUF
-`__. The idea with PUFs is that they reproduce some entropy
-that comes from random tolerances of their production process. The same PUF will always yield (approximately) the same
-key, but since you cannot control these random production variations, in practice the resulting PUF cannot be cloned.
-Note however, that its secrets can of course be copied if you find a way to read them out.
-
-As Immler et al. demonstrated in their paper, you don't need any secret sauce to create an analog mesh sensing circuit.
-All you need are a bunch of (admittedly, expensive) off-the-shelf analog ICs. The interesting bit here is that by
-applying more advanced analog sensing, weaknesses of an otherwise coarse mesh desing could maybe be alleviated. That is,
-instead of monitoring a very fine mesh for continuity, you could instead closely monitor inductance and capacitance of a
-more coarse mesh. This trade-off between sensing circuit complexity (resp. cost) and mesh production capabilities may
-allow someone with a poorly equipped lab to still make a decent HSM. The question is, how do you produce a "decent" mesh
-given only basic tools? Here are some ideas.
-
-**3D metal patterning techniques** refers to any technique for producing thin, patterned metal structures on a
-three-dimensional plastic substrate. The basic process would consist of 3D-printing the polymer substrate, depositing a
-thin metal layer on top and then patterning this metal layer. A good starting point here would be the recent work of
-`Ben Kraznow`_ on this exact thing.
-
-**Copper filament methods** would be any method embedding copper wire from a spool in some resin or other matrix. This
-could mean either of a systematic approach of carefully winding or folding the copper wire into patterns or a
-non-systematic approach of simply stuffing a large tangle of copper wire into a small space. The main challenge with the
-former would be to find a non-tedious way of production. The main challenge with the latter would be to find process
-parameters that guarantee complete coverage of the HSM without holes or other areas of lower sensitivity to intrusions.
-Both approaches would require careful consideration of the overall design including the polymer resin supporting
-structure to ensure sensitivity against attacks since copper wire is mechanically much stronger than the micrometre-thin
-metal coatings used in patterning techniques.
-
-Envelope measurement
---------------------
-
-Finally, I think there is another set of currently under-utilized tamper-detection methods that would be very
-interesting to explore. I am not aware of an academic term for these, so I am just going to dub them *envelope
-measurement* here.
-
-The fundamental apporach of a mesh is to build a physical security envelope (the mesh) that physically detects when it
-is disturbed (open or short circuits). This approach works well but has the disadvantage that these meshes are rather
-complex to manufacture since effectively every part of them is acting as a sensing element. A conceptually more complex
-but in practice potentially simpler approach might be to split the functions of security envelope and sensing element.
-This would mean that in place of the mesh, some form of passive element such as metal foil forms the security envelope
-which is then checked for tampering using a very sensitive sensor inside. This remote-sensing approach might simplify
-the manufacture of the envelope itself and thus yield a design that is more easily customized. Following are a few ideas
-on how to approach this envelope measurement problem.
-
-**Ultrasonic** If the HSM is potted, a few ultrasonic transducers could be added inside the potting. With several
-transducers, any one could be used to transmit ultrasound while the others measure complex phase and energy of the
-signal they receive. The circuitry for this could be made fairly simple if using a static transmit frequency or a low
-chirp rate by using a homodyne receiver built around a comparator fed into some timers. This approach would likely
-detect any mechanical attack and would also rule out chemical attacks involving liquids (though starting from which
-amount of liquid depends on receiver sensitivity). The main disadvantages might be high power consumption and cost and
-size of the ultrasonic transducers. Traditional cheap transducers made for air as a transmission medium are fairly large
-and might not adequately couple into potting compound. If somehow one could convince a standard small piezo element to
-do the same job that would be great as far as cost and size are concerned. A concern in some fringe use cases might be
-suceptibility to ambient noise, though this could easily be reduced at the expense of space and heat dissipation
-capacity by adding sound dampening on the outside. A likely attack vector against this approach might be using a laser
-cutter to drill a hole through the potting compound, then inserting probes carefully chosen to not couple too much
-to the potting compound ultrasonically.
-
-**Light** In either an unpotted HSM or one potted with a transparent (at some wavelengths) potting compound one could
-embed LEDs and photodiodes in a similar setup to the ultrasonic setup described above. In contrast to the ultrasound,
-the LEDs would literally have to light up the HSM's interior and shadows might be an issue since the HSM is likely some
-flat rectangular shape. A possible solution to this would be to coat both the embedded payload and the lid with some
-highly reflective paint such as some glossy silver paint or simple white paint. The basic approach might be as simple as
-simply turning on several LEDs distributed throughout the HSM in turn and measuring amplitude at several photodetectors,
-or as complex as doing a LIDAR-like phase measurement sweeping through a range of frequencies to determine not only
-absorption but also phase/distance characteristics between emitter LED and detector photodiode. Using some high-gain TIA
-along with a homodyne detector (lock-in amplifier) and changing emitter intensity, very precise measurements of both
-absorption and phase might be possible, as might be measurements through almost opaque, diffuse potting compounds such
-as a grey epoxide resin. The main disadvantages of this method would likely be the need to thoroughly light-proof the
-entire HSM (likely by wrapping it in metal foil) and the potentially high cost of transmitter and receiver circuitry
-(nice TIAs aren't cheap). To be effective against attacks using e.g. very fine drills and probes the system would likely
-have to be very sensitive.
-
-**Radar** Finally, one could turn to standard radar techniques to fingerprint the inside of the HSM. The goal here would
-be fingerprinting instead of mapping since only changes need to be detected. In this approach one could use homodyne
-detection to improve sensitivity and reduce receiver complexity, and sweep frequencies similar to an FMCW radar (but
-probably without exploiting the self-demodulation effect). Besides high cost, this approach has two disadvantages.
-First, such a system would likely not go beyond 24GHz or maybe 40GHz due to component availability issues. Even at 40GHz
-the wavelength in the potting compound would be in the order of magnitude of several millimeters. Fine intrusions using
-some tool chosen to not interact too much with the EM field inside the HSM such as a heated ceramic needle or simply a
-laser cutter might not be detectable using this approach. In any case, this system would certainly not be able to detect
-small holes piercing the HSM enclosure. The HSM enclosure would have to be made into an RF shield, likely by using some
-metal foil in it.
-
-Overall in the author's opinion these three techniques are most promising in order *Light*, *Ultrasonic*, *Radar*. Light
-would prbably provide the best sensitivity at expense of some cost. Ultrasonic might be used in conjunction with light
-to cover some additional angles since it is potentially very low-cost. Radar seems hard to engineer into a solution that
-works reliably and also would likely be at least an order of magnitude more expensive than the other two technologies
-while not providing better sensitivity.
-
-.. _`Ben Kraznow`: https://www.youtube.com/watch?v=Z228xymQYho
-.. _affordances: https://en.wikipedia.org/wiki/Affordance
-
diff --git a/content/blog/ihsm-worlds-first-diy-hsm/index.rst b/content/blog/ihsm-worlds-first-diy-hsm/index.rst
deleted file mode 100644
index 22cf8e4..0000000
--- a/content/blog/ihsm-worlds-first-diy-hsm/index.rst
+++ /dev/null
@@ -1,44 +0,0 @@
----
-title: "New Paper on Inertial Hardware Security Modules"
-date: 2021-11-23T23:42:20+01:00
-summary: >
- Paper announcement: We have published a paper on how you can DIY a tamper-sensing hardware security module from any
- single-board computer using a moving tamper-sensing mesh made from cheap PCBs.
----
-
-World's First DIY HSM
-=====================
-
-Last week, Prof. Dr. Björn Scheuermann and I have `published our first joint paper on Hardware Security Modules
-`__. In our paper, we introduce Inertial Hardware Security
-Modules (IHSMs), a new way of building high-security HSMs from basic components. I think the technology we demonstrate
-in our paper might allow some neat applications where some civil organization deploys a service that no one, not even
-they themselves, can snoop on. Anyone can built an IHSM without needing any fancy equipment, which makes me optimistic
-that maybe the ideas of the `Cypherpunk movement `__ aren't obsolete
-after all, despite even the word "crypto" having been co-opted by radical capitalist environmental destructionists.
-
-An IHSM is basically an ultra-secure enclosure for something like a server or a raspberry pi that even someone with
-unlimited resources would have a really hard time cracking without destroying all data stored in it. The principle of an
-IHSM is the same as that of a `normal HSM`_. You have a payload that contains really secret data. There's really no way
-to prevent an attacker with physical access to the thing from opening it given enough time and abrasive discs for their
-angle grinder. So what you do instead is that you make it self-destruct its secrets within microseconds of anyone
-tampering with it. Usually, such HSMs are used for storing credit card pins and other financial data. They're expensive
-as fuck, all the while being about the same processing speed as a smartphone. Traditional HSMs use printed or
-lithographically patterned conductive foils for their security mesh. These foils are not an off-the-shelf component and
-are made in a completely custom manufacturing process. To create your own, you would have to re-engineer that entire
-process and probably spend some serious money on production machines.
-
-Inertial HSMs take the concept of traditional HSMs, but replace the usual tamper detection mesh with a few security mesh
-PCBs. These PCBs are coarser than traditional meshes by orders of magnitude, and would alone not even be close to enough
-to keep out even a moderately motivated attacker. IHSMs fix this issue by spinning the entire tamper detection mesh at
-very high speed. To tamper with the mesh, an attacker would have to stop it. This, in turn, can be easily detected by
-the mesh's alarm circuitry using a simple accelerometer as a rotation sensor.
-
-In our paper, we have shown a working prototype of the core concepts one needs to build such an IHSM. To build an IHSM
-you only need a basic electronics lab. I built the prototype in our paper at home during one of Germany's COVID
-lockdowns. You can have a look at our code and CAD on `my git `__. What is missing right
-now is an integration of all of these fragments into something cohesive that an interested person with the right tools
-could go out and build. We are planning to release this sort of documentation at some point, but right now we are
-focusing our effort on the next iteration of the design instead. Stay tuned for updates ;)
-
-.. _`normal HSM`: {{[}}
diff --git a/content/blog/jupyterlab-notebook-file-oneliner/index.rst b/content/blog/jupyterlab-notebook-file-oneliner/index.rst
deleted file mode 100644
index 5a543de..0000000
--- a/content/blog/jupyterlab-notebook-file-oneliner/index.rst
+++ /dev/null
@@ -1,21 +0,0 @@
----
-title: "Getting the .ipynb Notebook File Location From a Running Jupyter Lab Notebook"
-date: 2025-06-29T23:42:00+01:00
-summary: >
- If you need to get the path of the ipynb file in a running #Jupyter notebook, this one-liner will do the trick. It
- seems chatgpt is confused, and a bunch of other approaches on the web look fragile and/or unnecessarily complex to
- me.
----
-
-If you need to get the path of the ipynb file in a running #Jupyter notebook, this one-liner will do the trick. It seems
-chatgpt is confused, and a bunch of other approaches on the web look fragile and/or unnecessarily complex to me.
-
-.. code:: python
-
- import sys
- Path(json.loads(Path(sys.argv[-1]).read_bytes())['jupyter_session'])
-
-The way this works is that for each notebook, jupyter starts a python "kernel" process that actually runs the notebook's
-code. That kernel gets a json file with info on the notebook's location on the disk passed through its command line.
-Since we're running code in that exact python process, we can just grab that json file from sys.argv, and read it
-ourselves.
diff --git a/content/blog/kicad-mesh-plugin/index.rst b/content/blog/kicad-mesh-plugin/index.rst
deleted file mode 100644
index 0969bf3..0000000
--- a/content/blog/kicad-mesh-plugin/index.rst
+++ /dev/null
@@ -1,229 +0,0 @@
----
-title: "Kicad Mesh Plugin"
-date: 2020-08-18T13:15:39+02:00
-summary: >
- I wrote a little KiCad plugin that you can use to create security meshes, heaters and other things where you need
- one or more traces cover the entire surface of a PCB. The plugin supports arbitrary PCB shapes, cutouts, and can
- route around existing footprints and traces on the PCB.
----
-
-.. raw:: html
-
-
- ]
-
-
-Tamper Detection Meshes
-=======================
-
-Cryptography is at the foundation of our modern, networked world. From email to card payment infrastructure in brick and
-mortar stores, cryptographic keys secure almost every part of our digital lives againts cybercriminals or curious
-surveillance capitalists. Without cryptography, many of the things we routinely do in our lives such as paying for
-groceries with a credit card, messaging a friend on `Signal `_ or unlocking a car with its keyfob
-would not be possible. The security of all of these systems in its core lies on the secrecy of cryptographic keys.
-Systems differ in what kind of keys they use, how often these keys are replaced and the intricacies of the cryptographic
-operations these keys fit into but all have in common that their security relies on keeping the keys secret.
-
-In practice, this secrecy has been implemented in many different ways. Mass-market software such as browsers or
-messenger apps usually relies on some operating system facility to tell the computer "*please keep this piece of memory
-away from all other applications*". While on desktop operating systems usually this does not provide much of a barrier
-to other programs on the same computer, on modern mobile operating systems this approach is actually quite secure.
-However, given sufficient resources no security is perfect. All of these systems can be compromised if the host
-operating system is compromised sufficiently, and for organizations with considerable resources a market has sprung up
-that offers turn-key solutions for all wiretapping needs.
-
-In some applications, this level of security has not been considered sufficient. Particularly financial infrastructure
-is such a high-profile target that a lot of effort has been put into the security of cryptographic implementations. The
-best cryptographic algorithm is useless if it is run on a compromised system (from that system's point of view anyway).
-One of the core cryptographic components in financial applications are smartcards like they are used as payment cards in
-most countries nowadays. These smartcards contain a small, specialized cryptographic microcontroller that is designed to
-be hard to tamper with. Though one of the design goals of the system is to reduce the amount of sensitive information
-stored on the card, things such as copying of a card can only be hindered by making the chip hard to read out.
-
-.. raw:: html
-
-
-
-
-
-With smartcards being the means of choice on one side of the counter in electronic payments, on the other side of the
-counter a different technology prevails. Attacks on payment terminals are bound to have much more dire consequences than
-attacks on individual cards since one terminal might see hundreds of cards being read every day. For this reason, the
-level of attack countermeasures employed in these terminals is a considerable step up from bare smartcards. While a
-smartcard is made physically hard to tamper, it does not have a battery and it can only detect tampering once it is
-powered by a reader. This allows for well-equipped attackers to use tools such as Focused Ion Beam (FIB) workstations to
-circumvent the smartcard's defences while it is powered down, and then power up the card to carry out the actual attack.
-
-The answer to this problem in electronic payment infrastructure is called *Hardware Security Module*, or HSM. An HSM is
-similar to a smartcard in its function (cryptographic processing using keys that are meant to never leave the protection
-of the HSM). The one major between the two is that an HSM has its own battery and is continuously powered from its
-manufacture to the day it is scrapped. If the HSM looses power at any point in time, it uses a small amount of energy
-stored internally to securely wipe all cryptographic secrets from its memory within a few milliseconds.
-
-Being powered at all times allows the HSM to actively detect and respond to attacks. The most common way this is done is
-by wrapping the juicy secret parts in a foil or a printed circuit board that is patterned with a long and convoluted
-maze of wires, called a *mesh*. The HSM is continuously monitoring these wires for changes (such as shorts, breaks or
-changes in resistance) and will sound the alarm when any are detected. Practically, this presents a considerable hurdle
-to any attacker: They have to find a way to disable or circumvent the mesh while it is being monitored by the HSM. In
-practice, often this is no insurmountable challenge but it again increases attack costs.
-
-DIY Meshes
-==========
-
-Throughout my studies in security research I have always had an interest in HSMs. I have taken apart my fair share of
-HSMs and at this point, to understand the technology more, I want to experiment with building my own HSM. In last year's
-`HSM basics <{{[}}>`_ post I have lined out some ideas for a next generation design that
-deviates from the bread-and-butter apporoach of using a mesh as the primary security feature. Before embarking on
-practical experiments with these ideas, I want to first take a stab at replicating the current state of the art as best
-I can. State of the art meshes often use exotic substrates such as 3D plastic parts with traces chemically deposited on
-their surface or special flexible substrates with conductive ink traces. These technologies will likely be too
-cumbersome for me to implement myself only for a few prototypes, and industrial manufacturers will most likely be too
-expensive. Thus, I will concentrate on regular PCB technology for now.
-
-The idea of a mesh on a PCB is pretty simple: You have one or several traces that you try to cover every corner of the
-mesh PCB's area with. To be most effective, the traces should be as thin and as close together as possible. To increase
-the chances of a manipulation being detected, multiple traces can also be used that can then be monitored for shorts
-between them.
-
-While one can feasibly lay out these traces by hand, this really is an ideal application of a simple auto-router. While
-general PCB autorouting is *hard*, auto-routing just a few traces to approximate a space-filling curve is not. Since I
-am just starting out, I went with the simplest algorithmic solution I could think of. I first approximate the area
-designated to the mesh with a square grid whose cells are a multiple of my trace/space size. The mesh will only be drawn
-into grid cells that are fully inside the set boundaries. All cells outside or going across the border are discarded in
-this step.
-
-I decided to implement this auto-router in a KiCAD plugin. Though KiCADs plugin API is not the best, it was just about
-usable for this task.
-
-.. raw:: html
-
-
- ]
- The process starts out with the mesh shape being defined inside KiCAD. The mesh's outline is drawn
- onto one of the graphical "Eco" layers. A footprint is placed to serve as a placeholder for the mesh's
- connections to the outside world. This footprint is later used as the starting point for the mesh generation
- algorithm.
-
-
-.. raw:: html
-
-
-
- A visualization of the grid fitting process. First, a grid large enough to contain the mesh border
- is generated. Then, every cell is checked for overlap with the mesh border area. If the cell is fully inside, it
- (yellow), it is considered in the mesh generation later. Cells outside (gray) or on the border (red) are
- discarded.
-
-
-After generating the grid, starting from the place I want to connect to the mesh, I walk the grid's cells one by one to
-generate a tree that covers the entire grid's area. To set the mesh's starting place I place a footprint on the board
-(dark gray in the picture above) whose designator I then tell my script. The tree generation algorithm looks like a
-depth-first search, except all checks are random. Depending on the level of randomness used at each step of the
-algorithm it yields more or less organized-looking results. Below are five example runs of the algorithm at differing
-levels of randomness with the cells colored according to their distance from the tree root. 0% randomness means that the
-algorithm is going to try cells in forward direction first on every step, and only then try out left and right. 100%
-means that on every step, the algorithm is choosing a new direction at random.
-
-.. raw:: html
-
-
-
-After I have built this tree like you would do in a depth-first search, I draw my one or several mesh mesh traces into
-it. The core observation here is that there is only 16 possible ways a cell can be connected: It has four neighbors,
-each of which it can either be connected to or not, which results in 2^4 options. If you consider rotations and
-mirroring, this works out to rotations or mirrored versions of only six base tiles: The empty tile, a tile with all four
-sides connected, a straight through, a 90 degree bend, and a "T"-junction—see the illustration below.
-
-.. raw:: html
-
-
-
-
- There are six possible tile types in our connectivity graph inside its square tiling. This graphic illustrates
- all sixteen rotations of these with how they would look in a two-conductor mesh.
-
-
-
-After tiling the grid according to the key above, we get the result below.
-
-.. raw:: html
-
-
-
-
- An auto-routed mesh with traces colored according to tile types.
-
-
-
-.. raw:: html
-
-
-
-
- The same mesh, but with traces all black.
-
-
-
-Putting it all together got me the KiCAD plugin you can see in the screenshot below.
-
-.. raw:: html
-
-
-
-
- The plugin settings window open.
-
-
-
-.. raw:: html
-
-
-
-
- After runing the plugin, the generated mesh looks like this in pcbnew.
-
-
-
-I am fairly happy with the result, but getting there was a medium pain. Especially KiCAD's plugin API is still very
-unfinieshed. It is hard to use, most parts are completely undocumented and if you use anything but its most basic parts
-things tend to break. One particular pain point for me was that after generating the mesh, the traces have been added to
-the board, but are still invisible for some reason. You have to save the board first, then re-load the file for them to
-become visible. Also KiCAD crashes whenever the plugin tries to remove a trace, so currently my workflow involves always
-making a copy of the board file first and treating mesh generation as a non-reversible finishing step.
-
-`Check out the code on my cgit `_.
-
-.. ::
-
- .. raw:: html
-
-
-
-
-
-
diff --git a/content/blog/led-characterization/index.rst b/content/blog/led-characterization/index.rst
deleted file mode 100644
index 31e81cc..0000000
--- a/content/blog/led-characterization/index.rst
+++ /dev/null
@@ -1,510 +0,0 @@
----
-title: "LED Characterization"
-date: 2018-05-02T11:18:38+02:00
-summary: >
- Recently, I have been working on a small driver for ambient lighting using 12V LED strips like you can get
- inexpensively from China. I wanted to be able to just throw one of these somewhere, stick down some LED tape, hook
- it up to a small transformer and be able to control it through Wifi. When I was writing the firmware, I noticed that
- when fading between different colors, the colors look *all wrong*! This observation led me down a rabbit hole of
- color perception and LED peculiarities.
----
-
-Preface
--------
-
-Recently, I have been working on a `small driver`_ for ambient lighting using 12V LED strips like you can get
-inexpensively from China. I wanted to be able to just throw one of these somewhere, stick down some LED tape, hook it up
-to a small transformer and be able to control it through Wifi. When I was writing the firmware, I noticed that when
-fading between different colors, the colors look *all wrong*! This observation led me down a rabbit hole of color
-perception and LED peculiarities.
-
-The idea of the LED driver was that it can be used either with up to eight single-color LED tapes or, much more
-interesting, with up to two RGB or RGBW (red-green-blue-white) LED tapes. For ambient lighting high color resolution was
-really important so you could dim it down a lot without flickering. I ended up using the same driver stage I used in the
-`multichannel LED driver`_ project for its great color resolution and low hardware requirements.
-
-.. raw:: html
-
-
-
- An illustration of the RGB color cube.
- Picture by
- Maklaan from Wikimedia Commons,
- CC-BY-SA 3.0
-
-
-
-To make setting colors over Wifi more intuitive I implemented support for HSV colors. RGB is fine for communication
-between computers, but I think HSV is easier to work with when manually inputting colors from the command line. RGB is
-close to how most monitors, cameras and the human visual apparatus work on a very low level but doesn't match
-higher-level human color perception very well. When we describe a color we tend to think in terms of "hue" or
-"brightness", and computing a measure of those from RGB values is not easy.
-
-Colors and Color Spaces
------------------------
-
-`Color spaces`_ are a mathematical abstraction of the concept of color. When we say "RGB", most of the time we actually
-mean `sRGB`_, a standardized notion of how to map three numbers labelled "red", "green" and "blue" onto a perceived
-color. `HSV`_ is an early attempt to more closely align these numbers with our perception. After HSV, a number of other
-*perceptual* color spaces such as `XYZ (CIE 1931)`_ and `CIE Lab/LCh`_ were born, further improving this alignment. In
-this mathematical model, mapping a color from one color space into another color space is just a coordinate
-transformation.
-
-.. raw:: html
-
-
-
- An illustration of the HSV color space as a cylinder.
- Picture by
- SharkD from Wikimedia Commons,
- CC-BY-SA 3.0
-
-
-
-CIE 1931 XYZ is much larger than any other color space, which is why it is a good basis to express other color spaces
-in. In XYZ there are many coordinates that are outside of what the human eye can perceive. Below is an illustration of
-the sRGB space within XYZ. The wireframe cube is (0,0,0) to (1,1,1) in XYZ. The colorful object in the middle is what
-of sRGB fits inside XYZ, and the lines extending out from it indicate the space that can be expressed in sRGB but not in
-XYZ. The fat white curve is a projection of the *monochromatic spectral locus*, that is the curve of points you get in
-XYZ for pure visible wavelengths.
-
-As you can see, sRGB is *much* smaller than XYZ or even the part within the monochromatic locus that we can perceive. In
-particular in the blues and greens we loose *a lot* of colors to sRGB.
-
-.. raw:: html
-
-
-
- Illustration of the measured sRGB color space within XYZ. The thick, white line is the spectral
- locus.
-
- mkv/h264 download /
- webm download
-
-
-
-The wrong colors I got when fading between colors were caused by this coordinate transformation being askew. Thinking
-over the problem, there are several sources for imperfections:
-
-* The LED driver may not be entirely linear. For most modulations such as PWM the brightness will be linear starting
- from a certain value, but there is probably an offset caused by imperfect edges of the LED current. This offset can be
- compensated with software calibration. I built a calibration setup for driver linearity in the `multichannel LED
- driver`_ project. Below are pictures of ringing on the edges of an LED driver's waveform.
-
-* The red, green and blue channels of the LEDs used on the LED tape are not matched. This skews the RGB color space.
- In practice, the blue channel of my RGB tape to me *looks* much brighter than the red channel.
-
-* The precise colors of the red, green and blue channels of the LEDs are unknown. Though the red channel *looks* red, it
- may be of a slightly different hue compared to the reference red used in `sRGB`_ which would also skew the RGB color
- space.
-
-.. raw:: html
-
-
-
-These last two errors are tricky to compensate. What I needed for that was basically a model of the *perceived* colors
-of the LED tape's color channels. A way of doing his is to record the spectra of all color channels and then evaluate
-their respective XYZ coordinates. If all three channels are measured in one go with the same setup the relative
-magnitudes of the channels in XYZ will be accurate.
-
-To map any color to the LEDs, the color's XYZ coordinates simply have to be mapped onto the linear coordinate system
-produced by these three points within XYZ. LEDs are mostly linear in their luminous flux vs. current characteristic so
-this model will be adequate. The spectral integrals mapping the channels' measured responses to XYZ need only be
-calculated once and their results can be used as scaling factors thereafter.
-
-Measuring the spectrum
-----------------------
-
-In order to compensate for the cheap LED tape's non-ideal performance I had to measure the LED's red, green and blue
-channels' spectra. The obvious thing would be to go out and buy a `spectrograph`_, or ask someone to borrow theirs. The
-former is kind of expensive, and I did not want to wait two weeks for the thing to arrive. The latter I could probably
-not do every time I got new LED tape. Thus the only choice was to build my own.
-
-Luckily, building your own spectrometer is really easy. The first thing you need is something that splits incident light
-into its constituent wavelengths. In professional devices this is called the *`monochromator`_*, since it allows extraction
-of small color bands from the spectrum. The second thing is some sort of optics that project the incident light onto a
-screen behind the monochromator. In professional devices lenses or curved mirrors are used. In a simple homebrew job a
-pinhole as you would use in a `camera obscura`_ does a remarkably nice job.
-
-For the monochromator component several things could be used. A prism would work, but I did not have any. The
-alternative is a `diffraction grating`_. Professional gratings are quite specialized pieces of equipment and thus
-rather expensive. Luckily, there is a common household item that works almost as well: A regular CD or DVD. The
-microscopic grooves that are used to record data in a CD or DVD work the same as the grooves in a professional
-diffraction grating.
-
-Household spectra
------------------
-
-From this starting point, a few seconds on my favorite search engine yielded an `article by two researchers from the
-National Science Museum in Tokyo`_ providing a nice blueprint for a simple cardboard-and-DVD construction for use in
-classrooms. I replicated their device using a DVD and it worked beautifully. Daylight and several types of small LEDs I
-had around did show the expected spectra. Small red, yellow, green, and blue LEDs showed narrow spectra, daylight one
-continuous broad one, and white LEDs a continuous broad one with a distinct bright spot in the blue part. The
-single-color LED spectra are quite narrow since they are determined by the LED's semiconductor's band gap, which is
-specific to the semiconductor used and is quite precise. White LEDs are in fact a blue LED chip covered with a so-called
-*phosphor*. This phosphor is not elementary phosphorus but an anorganic compound that absorbs the LED chip's blue light
-and re-emits a broader spectrum of more yellow-ish wavelengths instead. The final LED spectrum is a superposition of
-both spectra, with some of the original blue light leaking through the phosphor mixing with the broadband yellow
-spectrum of the phosphor.
-
-.. raw:: html
-
-
-
-
-Now that I had a spectrograph, I needed a somewhat predictable way of measuring the spectrum it gave me.
-
-Measuring a spectrum
---------------------
-
-Pointing a camera at the spectrograph would be the obvious thing to do. This produces pretty images but has one critical
-flaw: I wanted to acquire quantitative measurements of brightness across the spectrum. Since I don't have a precise
-technical datasheet specifying the spectral response of any of my cameras I can't compare the absolute brightness of
-different colors on their pictures. Some other sensor was needed.
-
-.. raw:: html
-
-
-
- The daylight spectrum as seen using a DVD as a grating.
- Picture by
- Xofc from Wikimedia Commons,
- CC-BY-SA 4.0
-
-
-
-
-Measuring light intensity
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Looking around my lab, I found a bag of `SFH2701`_ visible-light photodiodes. Their
-datasheet includes their spectral response so I can compensate for that, allowing precise-ish absolute intensity
-measurements. Just like LEDs, photodiodes are extremely linear across several orders of magnitude. The datasheet of the
-classic `BPW34`_ photodiode shows that this photodiode's light current is exactly proportional to illuminance over at
-least three orders of magnitude. The `SFH2701`_ datasheet does not include a similar graph but its performance will be
-similar. The `SFH2701`_ photodiodes I had at hand were perfect for the job compared to the vintage `BPW34`_ since their
-active sensing area is really small (0.6mm by 0.6mm) compared to the BPW34 (a whopping 3mm by 3mm). If I were to use a
-`BPW34`_ I would have to insert some small apterture in front of it so it does not catch too broad a part of the
-spectrum at once. The `SFH2701`_ is small enough that if I just point it at the projected spectrum directly I will
-already get only a small part of the spectrum inside its 0.6mm active area.
-
-To convert the photodiode's tiny photocurrent into a measurable voltage I built another copy of the `transimpedance
-amplifier`_ circuit I already used in the `multichannel LED driver`_. A `transimpedance amplifier`_ is an
-amplifiert that produces a large voltage from a small current. The weird name comes from the fact that it works kind of
-like an amplified resistor (which can be generalized as an *impedance* electrically). Apply a current to a resistor and
-you get a voltage. A transimpedance amplifiert does the same with the difference that its input always stays at 0V,
-making it look like an ideal current sink to the connected current source.
-
-Transimpedance amplifiers are common in optoelectronics to convert small photocurrents to voltages. In this instance I
-built a very simple circuit with a dampened transimpedance amplifier stage followed by a simple RC filter for noise
-rejection and a regular non-inverting amplifier using another op-amp from the same chip to further boost the filtered
-transimpedance amplifier output. I put all the passives setting amplifier response (the gain-setting resistors and the
-filter resistor and capacitors) on a small removable adapter so I could easily change them if necessary. I put a small
-trimpot on the virtual ground both amplifers use as a reference so I could trim that if necessary.
-
-.. raw:: html
-
-
-
- The photodiode preamplifier schematic. Schematic drawn with an unlicensed copy of
- DaveCAD.
-
-
-Following are pictures of the preamplifier board. The connectors on the top-left side are two copies of the analog
-signal for the ADC and a small panel meter. The SMA connector is used as the photodiode input since coax cables are
-generally low-leakage and have built-in shielding. The circuit is powered via the micro-USB connector and the analog
-ground bias voltage can be adjusted using the trimpot.
-
-For easy replacement, all passives setting gain and frequency response are on a small, pluggable carrier PCB made from a
-SMD-to-DIP adapter.
-
-Flying-wire construction is just fine for this low-frequency circuit. In a high-speed photodiode preamp, the
-transimpedance amplifier circuit would be highly sensitive to stray capacitance, but we're not aiming at high speed
-here.
-
-.. raw:: html
-
-
-
-Given a way to measure intensity what remains missing is a way to scan a single photodiode across the spectrum.
-
-Scanning the projection
-~~~~~~~~~~~~~~~~~~~~~~~
-
-A cheap linear stage can be found in any old CD or DVD drive. These drives use a small linear stage based on a
-stepper-driven screw to move the laser unit radially. Removing the laser unit and connecting a leftover stepper driver
-module I was left with a small linear stage with about 45 steps per cm without microstepping enabled. The driver I used
-was an `A4988`_ module that required at least 8V motor drive voltage. I used a small micro USB-input boost converter
-module to generate a stable 10V supply for the motor driver, with the USB's 5V rail used as a logic supply for the motor
-driver.
-
-The `SFH2701`_ can easily be mounted to the linear stage using a small SMD breakout board glued in place with thin wires
-connecting it to the transimpedance amplifier. The DVD drive linear stage is not very strong so it is important that
-this wire does not put too much strain on it.
-
-Above the photodiode, I mounted a small piece of paper on the linear stage to be used as a projection screen to align
-the linear stage in front of the spectrometer viewing window. A line on the screen paper points to the photodiode die in
-parallel to the linear stage allowing precise alignment.
-
-The whole unit with photodiode preamplifier, linear stage, photodiode and stepper motor driver finally looks like this:
-
-.. raw:: html
-
-
-
- The complete electronics setup. The buspirate on the right interfaces to a computer and controls the
- stepper driver and ADC'es the preamp output. The two panel meters show the preamp output and stepper voltage for
- setup.
-
-
-The projection of the spectrum can be adjusted by moving the light source relative to the entry slot and by moving
-around the grating DVD.
-
-The capture process
-~~~~~~~~~~~~~~~~~~~
-
-To capture a spectrum, first the light source has to be mounted near the spectrograph's entry slot. The LED tape I
-tested I just taped face-down directly into it. Next, the grating DVD has to be adjusted to make sure the spectrum
-covers a sensible part of the photodiode's path. Mostly, this boils down to adjusting the photodiode distance and height
-to match the vertical extent and wiggling the grating DVD to adjust the projection's horizontal position.
-
-After the optics are set-up, the photodiode preamplifier has to be adjusted. In my experiments, most LED tape at 5GΩ
-required a high-ish amplification. The goal in this step is to maximize the peak response of the preamp to be just
-shy of its VCC rail to make best use of its dynamic range. To adjust the pre-amp, I took several very coarsely-spaced
-measurements to give me an estimate of the peak while I did not yet know its precise location.
-
-Since stray daylight totally swamped out the weak projection of the LED's spectrum I shielded the entire setup with a
-small box made of black cardboard and two black t-shirts on top. This shielding proved adequate for all my measurements
-but I had to be careful not to accidentially move the DVD that was stuck into the spectrograph with the shielding
-t-shirts.
-
-For capturing a single spectrum I wrote a small python script that will automatically move the stepper in adjustable
-intervals and take two measurements at each point, one with the LED tape off that can be used for offset calibration and
-one with the LED tape on. All measurements are stored in a sqlite database that can then be accesssed from other
-scripts.
-
-I built a small script that shows the progress of the current run and an jupyter notebook for data analysis. The jupyter
-notebook is capable of live-updating a graph with the in-progress spectrum's data. This was quite useful as a sanity
-check for when I made some mistake easy to spot in the resulting data.
-
-After one color channel is captured, the LED tape has to be manually set to the next color and the next measurement can
-begin.
-
-.. raw:: html
-
-
-
- A plot of the raw preamp output voltage versus stepper position. From left to right, the three peaks
- are blue, green and red. Step 0 corresponds to the bottommost stepper position and the shortest wavelength.
-
-
-
-
-Data analysis
-~~~~~~~~~~~~~
-
-Data analysis consists of three major steps: Offset- and stray light removal, wavelength and amplitude calibration and
-color space mapping.
-
-Offset removal
-**************
-The first task is to remove the offset caused by dark current as well as stray light of the LED's bright primary
-reflection on the DVD. The LED is very bright and only a small part of its light gets reflected by the grating towards
-the photodiode screen. The remaining part of the light is reflected onto the table in front of the DVD spectrograph.
-Though I covered all of this with black cardboard, some of that light ultimately gets reflected onto the photodiode.
-This causes a large offset, in particular in the blue part of the spectrum since in this part the photodiode is closest
-to the spectrograph's opening.
-
-The composite offset can be approximated with a second-order polynomial that is fitted to all the data outside of the
-main peak's area. Since at this point the wavelength of each data point is still unknown this is done with a rough first
-estimate of the three colors' peaks' locations and widths.
-
-Wavelength- and amplitude calibration
-*************************************
-The photodiode's response is strongly wavelength-dependent. In particular in the blue band, the photodiode's sensitivity
-gets very poor down to about 20% at the edge to ultraviolet. This effect is strong enough to move the apparent location
-of the blue peak towards red.
-
-.. raw:: html
-
-
-
- A plot of the photodiode's relative sensitivity in the visible spectrum. The sensitivity is
- normalized against its peak at 820nm.
-
-
-
-The problem is that in order to remove this non-linearity, we would already have to know the wavelength of the measured
-light. Since I don't, I settled for a two-step process. First, a coarse wavelength calibration is done relative to the
-red peak and the short-wavelength edge of the blue peak. The photodiode measurements are then sensitivity-corrected
-using this coarse measurement. Then all three channel peaks are measured in the resulting data and a fine wavelength
-estimate is produced by a least-squares fit of a linear function. This fine estimate is then used for a second
-sensitivity correction of all original measurements and the scale is changed from stepper motor step count to
-wavelength in nanometers.
-
-.. raw:: html
-
-
-
- A plot of the processed measurements. From left to right, the three peaks are blue, green and red.
-
-
-
-.. FIXME re-do these measurements, avoiding clipping
-.. FIXME re-do calibration using CCFL
-.. FIXME calibration for brightness imbalance due to wedge-shaped projection of spectrum
-
-Color space mapping
-*******************
-Finally, to achieve the objective of measuring the LED tape's channels' precise color coordinates the measured spetra
-have to be matched against the color spaces' *color matching functions*. The color matching functions describe how
-strong the color space's idealized *standard observer* would react to light at a particular wavelength. Going from a
-measured spectrum to color coordinates XYZ works by integrating over the product of the measurement and each color
-coordinate's color matching function.
-
-The result are three color coordinates X, Y and Z for each channel R, G and B yielding nine coordinates in total. When
-written as a matrix conversion between XYZ color space and LED-RGB color space is as simple as multiplying that matrix
-(or its inverse) and a vector from one of the color spaces.
-
-In XYZ space, the set of colors that can be produced with this LED tape is described by the `parallelepiped`_ spanned by
-the three channel's XYZ vectors. In the following figures, you can see a three-dimensional model of the RGB LED's color
-space (colorful) as well as sRGB (white) for comparison plotted within CIE 1931 XYZ. There is no natural map to scale
-both so for this illustration the LED color space has been scaled to fit. These figures were made with blender and a few
-lines of python. The blender project file including all settings and the python script to generate the color space
-models can be found in the `project repo`_.
-
-.. raw:: html
-
-
-
- Illustration of the measured LED color space scaled to fit within XYZ with sRGB (light gray) for
- comparison. The thick, white line is the spectral locus.
-
- mkv/h264 download /
- webm download
-
-
-
-As you can see, the result is pretty disappointing. The LED's color space parallepiped is very narrow, which is because
-the blue channel is much brighter than the other two channels. An easy fix for this is to scale-up the RGB space and
-drop any values outside XYZ. The scaling factor is a trade-off between color space coverage and brightness. You can
-produce the most colors when you clip all channels to brightness of the weakest channel (green in this case), but that
-will make the result very dim. Scaling brightness like that stretches the RGB parallelepiped along its major axis. Up to
-a point the number of possible colors (the gamut) increases at expense of maximum brightness. When the parallelepiped is
-stretched far enought for all three channel vectors to be outside the 1,1,1 XYZ-cube, maximum brightness continues to
-decrease but the gamut stays constant. I don't know a simple scientific way to solve this problem, so I just played
-around with a couple of factors and settled on 2.5 as a reasonable compromise. Below is an illustration.
-
-.. raw:: html
-
-
-
- Illustration of the measured LED color space at scale factor 2.5 within XYZ with sRGB (light gray)
- for comparison. The thick, white line is the spectral locus.
-
- mkv/h264 download /
- webm download
-
-
-
-Firmware implementation
------------------------
-In the end, the above measurements yield two matrices: One for mapping XYZ to RGB, and one for mapping RGB to XYZ. Of
-the several versions of CIE XYZ I chose the CIE 1931 XYZ color space as a basis for the firmware because it is most
-popular. Mapping a color coordinate in one color space to the other is as simple as performing nine floating-point
-multiplications and six additions. Mapping Lab or Lch to RGB is done by first mapping Lab/Lch to XYZ, then XYZ to RGB.
-Lab to XYZ is somewhat complex since it requires a floating-point power for gamma correction, but any self-respecting
-libc will have one of those so this is still no problem. Lch also requires floating-point sine and cosine functions, but
-these should still be no problem on most hardware.
-
-My implementation of these conversions in the ESP8266 firmware of my `Wifi LED driver`_ can be found `on Github`_. You
-can view the Jupyter notebook most of the analysis above `here `__.
-
-.. _`on Github`: https://github.com/jaseg/esp_led_drv/blob/master/user/led_controller.c
-.. _`project repo`: https://github.com/jaseg/led_drv
-.. _`Wifi LED driver`: {{[}}
-.. _`small driver`: {{][}}
-.. _`multichannel LED driver`: {{][}}
-.. _`sRGB`: https://en.wikipedia.org/wiki/SRGB
-.. _`CC BY-SA 3.0`: https://creativecommons.org/licenses/by-sa/3.0
-.. _`Color spaces`: https://en.wikipedia.org/wiki/Color_space
-.. _`HSV`: https://en.wikipedia.org/wiki/HSL_and_HSV
-.. _`CIE Lab/LCh`: https://en.wikipedia.org/wiki/Lab_color_space
-.. _`XYZ (CIE 1931)`: https://en.wikipedia.org/wiki/CIE_1931_color_space
-.. _`camera obscura`: https://en.wikipedia.org/wiki/Pinhole_camera
-.. _`article by two researchers from the National Science Museum in Tokyo`: http://www.candac.ca/candacweb/sites/default/files/BuildaSpectroscope.pdf
-.. _`spectrograph`: https://en.wikipedia.org/wiki/Ultraviolet%E2%80%93visible_spectroscopy
-.. _`monochromator`: https://en.wikipedia.org/wiki/Monochromator
-.. _`diffraction grating`: https://en.wikipedia.org/wiki/Diffraction_grating
-.. _`SFH2701`: https://dammedia.osram.info/media/resource/hires/osram-dam-2495903/SFH%202701.pdf
-.. _`BPW34`: http://www.vishay.com/docs/81521/bpw34.pdf
-.. _`transimpedance amplifier`: https://en.wikipedia.org/wiki/Transimpedance_amplifier
-.. _`A4988`: https://www.pololu.com/file/0J450/A4988.pdf
-.. _`parallelepiped`: https://en.wikipedia.org/wiki/Parallelepiped
diff --git a/content/blog/make-cgit-serve-pdfs-directly/index.rst b/content/blog/make-cgit-serve-pdfs-directly/index.rst
deleted file mode 100644
index 5da8dc3..0000000
--- a/content/blog/make-cgit-serve-pdfs-directly/index.rst
+++ /dev/null
@@ -1,26 +0,0 @@
----
-title: "How to make cgit serve PDF files as direct downloads"
-date: 2025-11-17T23:42:00+01:00
-summary: >
- cgit is great, but by default when you click on a PDF file in a repository content listing it will show you a
- hexdump of the file. You can access the actual file by clicking the "plain" link on top of the listing, but that's
- not only annoying, for large PDF files rendering the hexdump can also hang browser tabs.
----
-
-cgit is great, but by default when you click on a PDF file in a repository content listing it will show you a
-hexdump of the file. You can access the actual file by clicking the "plain" link on top of the listing, but that's
-not only annoying, for large PDF files rendering the hexdump can also hang browser tabs.
-
-I found a quick and easy solution to this problem, which I'm documenting here because it seems nobody on the
-internet has really done this before, and the usual AI assistants (ChatGPT and Claude) are both deeply confused.
-
-You just add a simple rewrite rule to your nginx config that 302-redirects requests to ``/tree/.../foobar.pdf`` to
-``/plain/.../foobar.pdf``. Here's the rule, make sure you put them in your nginx config *before* the location directive
-proxying requests to cgit.
-
-.. code:: nginx
-
- location ~ ^/([^/]+)/tree/(.*\.pdf)$ {
- return 302 /$1/plain/$2;
- }
-
diff --git a/content/blog/multichannel-led-driver/index.rst b/content/blog/multichannel-led-driver/index.rst
deleted file mode 100644
index f157458..0000000
--- a/content/blog/multichannel-led-driver/index.rst
+++ /dev/null
@@ -1,462 +0,0 @@
----
-title: "32-Channel LED tape driver"
-date: 2018-05-02T11:31:14+02:00
-summary: >
- Together, a friend and I outfitted the small staircase at Berlin's Chaos Computer Club with nice, shiny RGB-WW LED
- tape for ambient lighting. For this installation, I made a 32-channel LED driver that achieves high dynamic range on
- all 32 channels using a cheap microcontroller by using Binary Code Modulation.
----
-
-Theoretical basics
-==================
-
-Together, a friend and I outfitted the small staircase at Berlin's Chaos Computer Club with nice, shiny RGB-WW LED tape
-for ambient lighting. This tape is like regular RGB tape but with an additional warm white channel, which makes for much
-more natural pastels and whites. There are several variants of RGBW tape. Cheap ones have separate RGB and white LEDs,
-which is fine for indirect lighting but does not work for direct lighting. Since we wanted to mount our tape in channels
-at the front of the steps, we had to use the slightly more expensive variant with integrated RGBW LEDs. These are LEDs
-in the 5050 (5.0mm by 5.0mm) form factor common with RGB LEDs that have a small section divided off for the white
-channel. The red, green and blue LED chips sit together in the larger section covered with clear epoxy and the white
-channel is made up from the usual blue LED inside a yellow phosphor in the smaller section.
-
-Since we wanted to light up all of 15 steps, and for greatest visual effect we would have liked to be able to control
-each step individually we had to find a way to control 60 channels of LED tape with a reasonable amount of hardware.
-
-LED tape has integrated series resistors and runs off a fixed 12V or 24V constant-voltage supply. This means you don't
-need a complex constant-current driver as you'd need with high-power LEDs. You can just hook up a section of LED tape
-to a beefy MOSFET to control it. Traditionally, you would do *Pulse Width Modulation* (PWM) on the MOSFET's input to
-control the LED tape's brightness.
-
-Pulse Width Modulation
-----------------------
-
-`Pulse Width Modulation`_ is a technique of controlling the brightness of a load such as an LED with a digital signal.
-The basic idea is that if you turn the LED on and off much too fast for anyone to notice, you can control its power by
-changing how long you turn it on versus how long you leave it off.
-
-PWM divides each second into a large number of periods. At the beginning of each period, you turn the LED on. After
-that, you wait a certain time until you turn it off. Then, you wait for the next period to begin. The periods are always
-the same length but you can set when you turn off the LED. If you turn it off right away, it's off almost all the time
-and it looks like it's off to your eye. If you turn it off right at the end, it's on almost all the time and it looks
-super bright to your eye. Now, if you turn it off halfway into the cycle, it's on half the time and it will look to your
-eye as half as bright as before. This means that you can control the LED's brightness with only a digital signal and
-good timing.
-
-.. raw:: html
-
-
- ]
- Waveforms of two PWM cycles at different duty cycles.
-
-
-PWM works great if you have a dedicated PWM output on your microcontroller. It's extremely simple in both hardware and
-software. Unfortunately for us, controlling 32 channels with PWM is not that easy. Cheap microcontrollers only have `a
-handful of hardware PWM outputs`_, so we'd either have to do everything in software, bit-banging our LED modulation, or
-we'd have to use a dedicated chip.
-
-Doing PWM in software is both error-prone and slow. Since the maximum dynamic range of a PWM signal is limited by the
-shortest duty cycle it can do, software PWM being slow means it has poor PWM resolution at maybe 8 bits at most. Poor
-color resolution is not a problem if all you're doing is to fade around the `HSV rainbow`_, but for ambient lighting
-where you *really* want to control the brightness down to a faint shimmer you need all the color resolution you can get.
-
-If you rule out software PWM, what remains are dedicated `hardware PWM controllers`_. Most of these have either of three
-issues:
-
-* They're expensive
-* They don't have generous PWM resolution either (12 bits if you're lucky)
-* They're meant to drive small LEDs such as a 7-segment display directly and you can't just hook up a MOSFET to their
- output
-
-This means we're stuck in a dilemma between two poor solutions if we'd want to do PWM. Luckily for us, PWM is not the
-only modulation in town.
-
-.. _`Pulse Width Modulation`: https://en.wikipedia.org/wiki/Pulse-width_modulation
-.. _`a handful of hardware PWM outputs`: https://www.nxp.com/parametricSearch#/&c=c731_c380_c173_c161_c163&page=1
-.. _`HSV rainbow`: https://en.wikipedia.org/wiki/HSL_and_HSV
-.. _`hardware PWM controllers`: http://www.ti.com/lit/ds/symlink/tlc5940.pdf
-
-Binary Code Modulation
-----------------------
-
-PWM is the bread-and-butter of the maker crowd. Everyone and their cat is doing it and it works really well most of the
-time. Unbeknownst to most of the maker crowd, there is however another popular modulation method that's mostly used in
-professional LED systems: Enter `*Binary Code Modulation* (BCM) `_.
-
-BCM is to PWM sort of what barcodes are to handwriting. While PWM is easy to understand and simple to implement if all
-you have is a counter and an IO pin, BCM is more complicated. On the other hand, computers can do complicated and BCM
-really shines in multi-channel applications.
-
-Similar to PWM, BCM works by turning on and off the LED in short periods fast enough to make your eye perceive it as
-partially on all the time. In PWM the channel's brightness is linearly dependent on its duty cycle, i.e. the percentage
-it is turned on. In PWM the duty cycle D is the total period T divided by the on period T_on. The issue with doing PWM
-on many channels at once is that you have to turn off each channel at the exact time to match its duty cycle.
-Controlling many IO pins at once with precise timing is really hard to do in software.
-
-BCM avoids this by further dividing each period into smaller periods which we'll call *bit periods* and splitting each
-channel's duty cycle into chunks the size of these bit periods. The amazingly elegant thing in BCM now is that as you
-can guess from the name these bit periods are weighted in powers of two. Say the shortest bit period lasts 1
-microsecond. Then the second-shortest bit period is 2 microseconds and the third is 4, the fifth 8, the sixth 16 and so
-on.
-
-.. raw:: html
-
-
-
- Waveforms of a single 4-bit BCM cycle at different duty cycles. This BCM can produce 16 different
- levels.
-
-
-Staggered like this, you turn on the LED for integer value of microseconds by turning it on in the bit periods
-corresponding to the binary bits of that value. If I want my LED to light for 19 microseconds every period, I turn it on
-in the 16 microsecond bit period, the 2 microsecond bit period and the 1 microsecond bit period and leave it off for the
-4 and 8 mircosecond bit periods.
-
-Now, how this is better instead of just more complicated than plain old PWM might not be clear yet. But consider this:
-Turning on and off a large number of channels, each at its own arbitrary time is hard because doing the timing in
-software is hard. We can't use hardware timers since we only have two or three of those, and we have 32 channels.
-However, we can use one hardware timer to trigger a really cheap external latch to turn on or off the 32 channels all at
-once. With this setup, we can only controll all channels at once, but we can do so with very precise timing.
-
-All we need to do is to set our timer to the durations of the BCM bit periods, and we can get the same result as we'd
-get with PWM with only one hardware timer and a bit of code that is not timing-critical anymore.
-
-Applications of Binary Code Modulation
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-BCM is a truly wondrous technique, and outside of hobbyist circles it is in fact very widely known. Though we're using
-it to control just 32 channels here, you can do much more channels without any problems. The most common application
-where BCM is invariably used is *any* kind of LED screen. Controlling the thousands and thousands of LEDs in an LED
-screen with PWM with a dedicated timer for each LED would not be feasible. With BCM, all you need to dedicate to a
-single LED is a flipflop (or part of one if you're multiplexing). In fact, there is a whole range of `ICs with no other
-purpose than to enable BCM on large LED matrices `_. Basically, these are a
-high-speed shift register with latched outputs much like the venerable 74HC595_, only their outputs are constant-current
-sinks made so that you can directly connect an LED to them.
-
-.. _74HC595: http://www.ti.com/lit/ds/symlink/sn74hc595.pdf
-
-Running BCM on LED tape
-~~~~~~~~~~~~~~~~~~~~~~~
-
-In our case, we don't need any special driver chips to control our LED tape. We just connect the outputs of a 74HC595_
-shift register to one MOSFET_ each, and then we directly connect the LED tape to these MOSFETs. The MOSFETs allow us to
-drive a couple of amps into the LED tape from the weak outputs of the shift register.
-
-The BCM timing is done by hooking up two timer channels of our microcontroller to the shift registers *strobe* and
-*reset* inputs. We set the timer to PWM mode so we can generate pulses with precise timing. At the beginning of each
-bit period, a pulse will strobe the data for this bit period that we shifted in previously. At the end of the bit
-period, one pulse will reset the shift register and one will strobe the freshly-reset zeros into the outputs.
-
-.. raw:: html
-
-
-
-
- The schematic of a single output of this LED driver. Multiple shift register stages can be cascaded.
-
-
-
-
-Our implementation of this system runs on an STM32F030F4P6_, the smallest, cheapest ARM microcontroller you can get from
-ST. This microcontroller has only 16kB of flash and 1kB of RAM, but that's plenty for our use. We use its SPI controller
-to feed the modulation data to the shift registers really fast, and we use two timer channels to control the shift
-registers' reset and strobe.
-
-We can easily cascade shift registers without any ill side-effects, and even hundreds of channels should be no problem
-for this setup. The only reason we chose to stick to a 32-channel board is the mechanics of it. We thought it would be
-easier to have several small boards instead of having one huge board with loads of connectors and cables coming off it.
-
-The BOM cost per channel for our system is 3ct for a reasonable MOSFET, about 1ct for one eighth of a shift register
-plus less than a cent for one resistor between shift register and MOSFET. In the end, the connectors are more expensive
-than the driving circuitry.
-
-.. _MOSFET: https://en.wikipedia.org/wiki/MOSFET
-.. _STM32F030F4P6: http://www.st.com/resource/en/datasheet/stm32f030f4.pdf
-
-Hardware design
-===============
-
-From this starting point, we made a very prototype-y hardware design for a 32-channel 12V LED tape driver. The design is
-based on the STM32F030F4P6_ driving the shift registers as explained above. The system is controlled through an RS485_
-bus that is connected up to the microcontroller's UART using an MAX485_-compatible RS485 transceiver. The LED tape is
-connected using 9-pin SUB-D_ connectors since they are cheap and good enough for the small current of our short segments
-of LED tape. The MOSFETs we use are small SOT-23_ logic-level MOSFETs. In various prototypes we used both International
-Rectifier's IRLML6244_ as well as Alpha & Omega Semiconductor's AO3400_. Both are good up to about 30V/5A. Since we're
-only driving about 2m of LED tape per channel we're not going above about 0.5A and the MOSFETs don't even get warm.
-
-.. _RS485: https://en.wikipedia.org/wiki/RS-485
-.. _MAX485: https://datasheets.maximintegrated.com/en/ds/MAX1487-MAX491.pdf
-.. _IRLML6244: https://www.infineon.com/dgdl/?fileId=5546d462533600a4015356686fed261f
-.. _AO3400: http://aosmd.com/pdfs/datasheet/AO3400.pdf
-.. _SUB-D: https://en.wikipedia.org/wiki/D-subminiature
-.. _SOT-23: http://www.nxp.com/documents/outline_drawing/SOT23.pdf
-
-Switching nonlinearities
-------------------------
-During testing of our initial prototype, we noticed that the brightness seemed to jump around when fading to very low
-values. It turned out that our extremely simple LED driving circuit consisting of only the shift register directly
-driving a MOSFET, which in turn directly drives the LED tape was maybe a little bit too simple. After some measurements
-it turned out that we were looking at about 6Vpp of ringing on the driver's output voltage. The picture below is the
-voltrage we saw on our oscilloscope on the LED tape.
-
-.. raw:: html
-
-
-
- Bad ringing on the LED output voltage caused by wiring inductance. Note that the effect on the
- actual LED current is less bad than this looks since the LED's V/I curve is nonlinear.
-
-
-
-Dynamic switching behavior: Cause and Effect
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-A bit of LTSpice_ action later we found that the inductance of the few metres of cable leading to the LED tape is the
-likely culprit. The figure below is the schematic used for the simulations.
-
-.. raw:: html
-
-
-
- The schematic of the simulation in LTSpice
-
-
-As tested, the driver does not include any per-output smoothing so the ~.5A transient on each BCM cycle hits the cable
-in full. Combined with the cable inductance, this works out to a considerable lag of the rising edge of the LED
-current, and bad ringing on its falling edge. Below is the voltage on the LED output from an LTSpice simulation of our
-driver.
-
-.. raw:: html
-
-
-
- The result of our LTSpice simulation. This simulation assumes 1µH of wiring inductance and 50Ω of
- output impedance on the part of the shift register. The ringing at the gate visible in the gate voltage graph is
- due to feed-through of the ringing at the output through the MOSFET's parasitic Cgd.
-
-
-We were able to reduce the rining and limit the effect somewhat by putting a 220Ω series resistor in between the shift
-register output and the MOSFET gate. This resistor forms an RC circuit with the MOSFET's nanofarad or two of gate
-capacitance. The result of this is that the LED current passing the wire's ESL rises slightly more slowly and thus the
-series inductance gets excited slightly less, and the overshoot decreases. Below is a picture of the waveform with the
-damping resistor in place and a picture of our measurement for comparison. The resistor values don't agree perfectly
-since the estimated ESL and stray capacitance of the wiring is probably way off.
-
-.. raw:: html
-
-
-
- Adding a resistor in front of the MOSFET gate to slow the transition damped the ringing somewhat,
- but ultimately it cannot be eliminated entirely. Note how you can actually see the miller plateau on the
- trailing edge of this signal.
-
-
-
-.. raw:: html
-
-
-
- The LTSpice simulation result with the same parameters as above but with an extra 100Ω between the
- shfit register's output and the MOSFET's gate.
-
-
-A side effect of this fix is that now the effective on-time of the LED tape is much longer than the duty cycle at the
-shift register's output at very small duty cycles (1µs or less). This is caused by the MOSFET's `miller
-plateau`_. For illustration, below is a graph of both the excitation waveform (the boxy line) and the resulting LED
-current (the other ones) both without damping (top) and with 220Ω damping (bottom). As you can see the effective duty
-cycle of the LED current is not at all equal to the 50% duty cycle of the excitation square wave.
-
-.. raw:: html
-
-
-
- Simulated LED duty cycle with and without damping. The damping resistance used in this simulation
- was 220Ω.
-
-
-.. raw:: html
-
-
-
- The MOSFET gate voltage from the simulation in the figure above. You can clearly see how the miller
- plateau (the horizontal part of the trace at about 1V) is getting much wider with added damped, and how the
- resulting gate charge/discharge curve is not at all that of a capacitor anymore.
-
-
-
-
-In conclusion, we have three major causes for our calculated LED brightness not matching reality:
-
-* Ringing of the equivalent series inductance of the wiring leading up to the LED tape
-* Miller plateau lag
-* The damping resistor and the MOSFET gate forming an RC filter that helps with wire ESL ringing but worsens the miller
- plateau issue and deforms the LED current edges.
-
-Added up, these three effects yield a picture that agrees well with our simulations and measurements. The overall effect
-is neglegible at long period durations (>10µs), but gets really bad at short period durations (<1µs). The effect is
-non-linear, so correcting for it is not as simple as adding an offset.
-
-.. _LTSpice: http://www.analog.com/en/design-center/design-tools-and-calculators/ltspice-simulator.html
-.. _`miller plateau`: https://www.vishay.com/docs/68214/turnonprocess.pdf
-
-Measuring LED tape brightness
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In order to correct for the nonlinearities mentioned above, we decided to implement a lookup table mapping BCM period to
-actual timer setting. That is, each row of the table contains the actual period length we need to set the
-microcontroller's timer to in order to get our intended brightness steps.
-
-To calibrate our driver, we needed a setup for reproducible measurement of the relative brightness of our LED tape at
-different settings. Absolute brightness is not of interest to us as the eye can't perceive it. To perform the
-calibration, the LED driver is set to enable each single BCM period in turn, i.e. brightness values 1, 2, 4, 8, 16 etc.
-
-The setup we used to measure the LED tape's brightness consists of a bunch of LED tape stuck into a tin can for
-shielding against both stray light and electromagnetic interference and a photodiode looking at the LED tape. We used
-the venerable BPW34_ photodiode in our setup as I had a bunch leftover from another project and because they are quite
-sensitive owing to their physically large die area.
-
-.. raw:: html
-
-
-
- The LED brighness measurement setup. The big tin can contains a bunch of LED tape and the
- photodiode. The breadboard on the right is used for the photodiode preamplifier and for jumpering around the LED
- tape's channels. The red board next to it is the buspirate used as ADC. The board on the bottom left is a
- TTL-to-RS485 converter and the board in the middle is the unit under test.
-
-
-The photodiode's photocurrent is converted into a voltage using a very simple transimpedance amplifier based around a
-MCP6002_ opamp that was damped into oblivion with a couple nanofarads of capacitance in its feedback loop. The MCP6002_
-is a fine choice here since I had a bunch and because it is a CMOS opamp, meaning it has low bias current that would
-mess up our measurements. For many applications, opamp bias current is not a big issue but when using the opamp to
-directly measure very small currents at its input it quickly swamps out the signal for most BJT-input types.
-
-The transimpedance amplifier's output is read from the computer using the ADC input of a buspirate USB thinggamajob. In
-general I would not recommend the buspirate as a tool for this job since it's ADC is not particularly good and it's
-programming interface is positively atrocious, but it was what I had and it beat first wiring up one of the dedicated
-ADC chips I had in my parts bin.
-
-The computer runs a small python script cycling the LED tape through all its BCM period settings and taking a brightness
-measurement at each step. Later on, these measurements can be plotted to visualize the resulting slope's linearity, and
-we can even do a simulation of the resulting brightness for all possible control values by just adding the measured
-photocurrents for a certain BCM setpoint just as our retinas would do.
-
-.. raw:: html
-
-
-
-
- A plot of the measured brightness of our LED tape for each BCM period. The brightness values are normalized
- to the value measured at the LSB setpoint (brightness=1/65535). Ideally, this plot would show a straight
- line with slope 1. Obviously, it doesn't. The bend in the curve is caused by the above-mentioned duty cycle
- offset adding an offset to all brightness values. Shown is both the raw data (light), which has essentially zero
- measurement error and a linear fit (dark).
-
- The plot is in log-log to approximate how the human eye would perceive brightness, i.e. highly sensitive at
- low values but not very sensitive at all at large values.
-
-
-
-While it would be possible to fully automate the optimization of BCM driver lookup tables, we needed only one and in the
-end I just sat down and manually tweaked the ideal values we initially calculated until I liked the result. You can see
-the resulting brightness curve below.
-
-.. raw:: html
-
-
-
-.. _BPW34: http://www.vishay.com/docs/81521/bpw34.pdf
-.. _MCP6002: http://ww1.microchip.com/downloads/en/DeviceDoc/21733j.pdf
-
-Controlling the driver
-----------------------
-
-Now that our driver was behaving linear enough that you couldn't see it actually wasn't we needed a nice way to control
-it from a computer of our choice. In the ultimate application (our staircase) we'll use a raspberry pi for this. Since
-we already settled on an RS485_ bus for its robustness and simplicity, we had to device a protocol to control the driver
-over this bus. Here, we settled on a simple, COBS_-based protocol for the reasons I wrote about in `How to talk to your
-microcontroller over serial `_.
-
-To address our driver nodes, we modified the Makefile to build a random 32-bit MAC into each firmware image. The
-protocol has only five message types:
-
-1. A 0-byte *ping* packet, to which each node would reply with its own address in the
- first 100ms after boot. This can be used to initially discover the addresses of all nodes connected to the bus. You'd
- spam the bus with *ping* packets, and then hit reset on each node in turn. The control computer would then receive
- each device's MAC address as you hit reset.
-2. A 4-byte *address* packet that says which device that the following packet is for. This way of us using the packet
- length instead of a packet type field is not particularly elegant, but our system is simple enough and it was easy to
- implement.
-3. A 64-byte *frame buffer* packet that contains 16 bits of left-aligned brightness data for every channel
-4. A one-byte *get status* packet that tells the device to respond with...
-5. ...a 27-byte status packet containing a brief description of the firmware (version number, channel count, bit depth
- etc.) as well as the device's current life stats (VCC, temperature, uptime, UART frame errors etc.).
-
-Wrapped up in a nice python interface we can now easily enumerate any drivers we connect to a bus, query their status
-and control their outputs.
-
-.. _COBS: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
-
-Conclusion
-----------
-
-.. raw:: html
-
-
-
-Putting some thought into the control circuitry and software, you can easily control large numbers of channels of LEDs
-using extremely inexpensive driving hardware without any compromises on dynamic range. The design we settled on can
-drive 32 channels of LED tape with a dynamic range of 14bit at a BOM cost of below 10€. All it really takes is a couple
-of shift registers and a mildly bored STM32 microcontroller.
-
-Get a PDF file of the schematic and PCB layout `here `__ or download the CAD files
-and the firmware sources `from github `_. You can view the Jupyter notebook used to
-analyze the brightness measurement data `here `__.
-
diff --git a/content/blog/paper-sampling-mesh-monitor/index.rst b/content/blog/paper-sampling-mesh-monitor/index.rst
deleted file mode 100644
index 9fd1c7f..0000000
--- a/content/blog/paper-sampling-mesh-monitor/index.rst
+++ /dev/null
@@ -1,104 +0,0 @@
----
-title: "New paper: Monitoring Tamper-Sensing Meshes Using Low-Cost, Embedded Time-Domain Reflectometry"
-date: 2025-10-20T23:42:00+01:00
-summary: >
- I've got a new paper accepted at CHES, to be published in TCHES 2026/1 around beginning of December and out `on
- eprint now `__. The topic of the paper is a way of monitoring a tamper-sensing
- mesh through time-domain reflectometry using very cheap components. The end result is a circuit that costs about
- 10 € in parts that is able to measure TDR responses with a few hundred picoseconds of resolution.
----
-
-.. raw:: html
-
-
-
-
- The final setup. On the right is the measurement board, and on the left is the mesh test specimen plugged
- in. In a real application, you would integrate both into your target circuit.
-
-
-
-I've got a new paper accepted at CHES, to be published in TCHES 2026/1 around beginning of December and out
-`on eprint now `__. The topic of the paper is a way of monitoring a tamper-sensing
-mesh through time-domain reflectometry using very cheap components. The end result is a circuit that costs about 10 € in
-parts that is able to measure TDR responses with a few hundred picoseconds of resolution.
-
-Tamper-Sensing meshes are squiggly circuit traces that are used to tamper-proof high-security devices like hardware
-security modules, ATM pin pads and countertop card payment terminals. Any area where you would like to prevent an
-attacker from drilling or sawing through in a physical attack, you completely cover with one or more such circuit traces
-in a meandering pattern. I've written up some work on a KiCad plugin for creating these meshes `in another post
-<{{< ref "blog/kicad-mesh-plugin" >}}>`__.
-
-Up to now, the state of the art in monitoring these security meshes has mostly been finding ways to precisely monitor
-their ohmic resistance in the analog domain. This has the disadvantage of both being fairly complex in circuitry and of
-presenting a steep trade-off between sensitivity and false-positive rate since all you get out of the whole mesh is a
-single analog measurement containing maybe 12 to 16 bits of entropy. There have been a few papers on using more advanced
-RF techniques, but they all either required really expensive circuitry and/or highly customized meshes that for instance
-couldn't easily be fitted into arbitrary shapes.
-
-.. raw:: html
-
-
-
-
- The sampling edges as measured by the board itself. As you can see, using a cheap microcontroller and some
- cheap display signal redriver ICs along with commodity RF schottkies you can get pretty spicy edges on a
- budget. Link to full resolution.
-
-
-
-In this paper, I wrote up a method using the high-resolution timer of an inexpensive `STM32G4-series microcontroller
-`__ together with a DisplayPort/HDMI "redriver" chips meant for
-amplifying high-speed display signals to create fast pulse edges. I characterized several chips, with the best
-performers being TI's `TDP0604 `__ and Diodes' `PI3HDX12211
-`__, coming in at 2 to 5 € depending on where and how much you buy. The
-fast edges generated by these drivers are then fed to a set of four-diode sampling gates using cheap RF schottky diodes
-to create a really cheap but fast time-domain reflectometer. Using this TDRD circuit, a security mesh can be monitored
-much more precisely than before, since the circuit creates a sort of fingerprint of the mesh's trace along its length.
-
-.. raw:: html
-
-
-
-One of the fun highlights of this project to me was micro-soldering test boards using different redriver ICs. Above, you
-can see the result of that soldering work. I was really happy with my cheap aliexpress microscope and with my fancy
-titanium tweezers!
-
-Have a look into the paper, where I wrote up details on the circuitry as well as a whole bunch of (>1000!) measurements
-characterizing the system. As it turns out, it's really sensitive to attacks while being reasonably robust to
-environmental disturbances. In fact, it's so sensitive that the circuit can distinguish multiple identical (!) copies of
-the same mesh produces by JLCPCB from their manufacturing tolerances such as FR-4 fiber weave alignment.
-
-You can find a preprint of the paper `on eprint `__, and I'll update this post with a
-link to the published version of the paper when it becomes available. The eprint is identical to the published version
-as of now.
-
-The source code of the project is available at `https://git.jaseg.de/sampling-mesh-monitor.git `__.
diff --git a/content/blog/private-contact-discovery/index.rst b/content/blog/private-contact-discovery/index.rst
deleted file mode 100644
index 386bd6e..0000000
--- a/content/blog/private-contact-discovery/index.rst
+++ /dev/null
@@ -1,37 +0,0 @@
----
-title: "Private Contact Discovery"
-date: 2019-06-22T10:30:00+08:00
-summary: >
- I gave a short introduction into Private Contact Discovery protocols at our university workgroup.
----
-
-Private Contact Discovery (PCD) is the formal name for the problem modern smartphone messenger applications have on
-installation: Given a user's address book, find out which of their contacts also use the same messenger without the
-messenger's servers learning anything about the user's address book. The widespread non-private way to do this is to
-simply upload the user's address book to the app's operator's servers and do an SQL JOIN keyed on the phone number field
-against the database of registered users. People have tried sprinkling some hashes over these phone numbers in an
-attempt to improve privacy, but obviously running a brute-force preimage attack given a domain of maybe a few billion
-valid inputs is not cryptographically hard.
-
-Private Contact Discovery can be phrased in terms of Private Set Intersection (PSI), the cryptographic problem of having
-two parties holding one set each find the intersection of their sets without disclosing any other information. PSI has
-been an active field of research for a while and already yielded useful results for some use cases. Alas, none of those
-results were truly practical yet for usage in PCD in a typical messenger application. They would require too much CPU
-time or too much data to be transferred.
-
-At USENIX Security 2019, Researchers from technical universities Graz and Darmstadt published a paper titled *Private
-Contact Discovery at Scale*
-(`eprint `__ | `PDF `__).
-In this paper, they basically optimize the hell out of existing cryptographic solutions to private contact discovery,
-jumping from a still-impractical state of the art right to practicality. Their scheme allows a client with 1k contacts
-to run PCD against a server with 1B contacts in about 3s on a phone. The main disadvantage of their scheme is that it
-requires the client to in advance download a compressed database of all users, that clocks in at about 1GB for 1B users.
-
-I found this paper very interesting for its immediate practical applicability. As an excuse to dig into the topic some
-more, I gave a short presentation at my university lab's research seminar on this paper
-(slides: `PDF `__ | `ODP `__).
-
-Even if you're not working on secure communication systems on a day-to-day basis this paper might interest you. If
-you're working with social account information of any kind I can highly recommend giving it a look. Not only might your
-users benefit from improved privacy, but your company might be able to avoid a bunch of data protection and
-accountability issues by simply not producing as much sensitive data in the first place.
diff --git a/content/blog/serial-protocols/index.rst b/content/blog/serial-protocols/index.rst
deleted file mode 100644
index 2f9bb2d..0000000
--- a/content/blog/serial-protocols/index.rst
+++ /dev/null
@@ -1,249 +0,0 @@
----
-title: "How to talk to your microcontroller over serial"
-date: 2018-05-19T08:09:46+02:00
----
-
-Scroll to the end for the `TL;DR `_.
-
-In this article I will give an overview on the protocols spoken on serial ports, highlighting common pitfalls. I will
-summarize some points on how to design a serial protocol that is simple to implement and works reliably even under error
-conditions.
-
-If you have done low-level microcontroller firmware you will regularly have had to stuff some data up a serial port to
-another microcontroller or to a computer. In the age of USB, an old-school serial port is still the simplest and
-quickest way to get communication to a control computer up and running. Integrating a ten thousand-line USB stack into
-your firmware and writing the necessary low-level drivers on the host side might take days. Poking a few registers to
-set up your UART to talk to an external hardware USB to serial converter is a matter of minutes.
-
-This simplicity is treacherous, though. Oftentimes, you start writing your serial protocol as needs arise. Things might
-start harmless with something like ``SET_LED ON\n``, but as the code grows it is easy to end up in a hot mess of command
-modes, protocol states that breaks under stress. The ways in which serial protocols break are manifold. The simplest one
-is that at some point a character is mangled, leading to both ends of the conversation ending up in misaligned protocol
-states. With a fragile protocol, you might end up in a state that is hard to recover from. In extreme cases, this leads
-to code such as `this gem`_ performing some sort of arcane ritual to get back to some known state, and all just because
-someone did not do their homework. Below we'll embark on a journey through the lands of protocol design, exploring the
-facets of this deceptively simple problem.
-
-.. _`this gem`: https://github.com/juhasch/pyBusPirateLite/blob/dece35f6e421d4f6a007d1db98d148e2f2126ebb/pyBusPirateLite/base.py#L113
-
-Text-based serial protocols
-===========================
-
-The first serial protocol you've likely written is a human-readable, text-based one. Text-based protocols have the big
-advantage that you can just print them on a terminal and you can immediately see what's happening. In most cases you can
-even type out the protocol with your bare hands, meaning that you don't really need a debugging tool beyond a serial
-console.
-
-However, text-based protocols also have a number of disadvantages. Depending on your application, these might not matter
-and in many cases a text-based protocol is the most sensible solution. But then, in some cases they might and it's good
-to know when you hit one of them.
-
-Problems
---------
-
-Low information density
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Generally, you won't be able to stuff much more than four or five bit of information down a serial port using a
-single byte of a human-readable protocol. In many cases you will get much less. If you have 10 commands that are only
-issued a couple times a second nobody cares that you spend maybe ten bytes per command on nice, verbose strings such as
-``SET LED``. But if you're trying to squeeze a half-kilobyte framebuffer down the line you might start to notice the
-difference between hex and base-64 encoding, and a binary protocol might really be more up to the job.
-
-Complex parsing code
-~~~~~~~~~~~~~~~~~~~~
-
-On the computer side of thing, with the whole phalanx of an operating system, the standard library of your programming
-language of choice and for all intents and purposes unlimted CPU and memory resources to spare you can easily parse
-anything spoken on a serial port in real time, even at a blazing fast full Megabaud. The microcontroller side however is
-an entirely different beast. On a small microcontroller, printf_ alone will eat about half your flash. On most small
-microcontrollers, you just won't get a regex library even though it would make parsing textual commands *so much
-simpler*. Lacking these resources, you might end up hand-knitting a lot of low-level C code to do something seemingly
-simple such as parsing ``set_channel (13, 1.1333)\n``. These issues have to be taken into account in the protocol design
-from the beginning. For example, you don't really need matching parentheses, don't use them.
-
-Fragile protocol state
-~~~~~~~~~~~~~~~~~~~~~~
-
-Say you have a ``SET_DISPLAY`` command. Now say your display can display four lines of text. The obvious approach to this
-is probably the SMTP_ or HTTP_ way of sending ``SET_DISPLAY\nThis is line 1\nThis is line 2\n\n``. This would certainly
-work, but it is very fragile. With this protocol, you're in trouble if at any point the terminating second newline
-character gets mangled (say, someone unplugs the cable, or the control computer reboots, or a cosmic ray hits something
-and ``0x10 '\n'`` turns into ``0x50 'P'``).
-
-.. _SMTP: https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol
-.. _HTTP: https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
-
-Timeouts don't work
-~~~~~~~~~~~~~~~~~~~
-
-You might try to solve the problem of your protocol state machine tangling up with a timeout. "If I don't get a valid
-command for more than 200ms I go back to default state." But consider the above example. Say, your control computer
-sends a ``SET_DISPLAY`` command every 100ms. If in one of them the state machine tangles up, the parser hangs since the
-timeout is never hit, because a new line of text is arriving every 100ms.
-
-Framing is hard
-~~~~~~~~~~~~~~~
-
-You might also try to drop the second newline and using a convention such as ``SET_DISPLAY`` is followed by two lines of
-text, then commands resume.". This works as long as your display contents never look like commands. If you are only ever
-displaying the same three messages on a character LCD that might work, but if you're displaying binary framebuffer
-data you've lost.
-
-Solutions
----------
-
-Keep the state machine simple
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In a text-based protocol, always use a single line of text to represent a single command. Don't do protocol states or
-modes where you can toggle between different interpretations for a line. If you have to send human-readable text as part
-of a command (such as ``SET_DISPLAY``), escape it so it doesn't contain any newlines.
-
-This way, you keep your protocol state machine simple. If at any time your serial trips and flips a bit or looses a byte
-your protocol will recover on the next newline character, returning to its base state.
-
-Encode numbers in hex when possible
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Printing a number in hexadecimal is a very tidy operation, even on the smalest 8-bit microcontrollers. In contrast,
-printing decimal requires both division and remainder in a loop which might get annoyingly code- and time-intensive on
-large numbers (say a 32-bit int) and small microcontrollers.
-
-If you have to send fractional values, consider their precision. Instead of sending a 12 bit ADC result as a 32-bit
-float formatted like ``0.176513671875`` sending ``0x2d3`` and dividing by 4096 on the host might be more sensible. If you
-really have to communicate big floats and you can't take the overhead of including both printf_ and scanf_ you can
-use hexadecimal floating point, which is basically ``hex((int)foo) + "." + hex((int)(65536*(foo - (int)foo)))`` for four
-digits. You can also just hex-encode the binary IEEE-754_ representation of the float, sending ``hex(*(int *)&float)``.
-Most programming languages will have a `simple, built-in means to parse this sort of thing
-`__.
-
-.. _printf: http://git.musl-libc.org/cgit/musl/tree/src/stdio/vfprintf.c
-.. _scanf: http://git.musl-libc.org/cgit/musl/tree/src/stdio/vfscanf.c
-.. _IEEE-754: https://en.wikipedia.org/wiki/IEEE_754
-
-Escape multiline strings
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-If you have to send arbitrary strings, escape special characters. This not only has the advantage of yielding a robust
-protocol: It also ensures you can actually see everything that's going on when debugging. The string ``"\r\n"`` is easy to
-distinguish from ``"\n"`` while your terminal emulator might not care.
-
-The simplest encoding to use is the C-style backslash encoding. Host-side, most languages will have a `built-in means of
-escaping a string like that `__.
-
-Encoding binary data
---------------------
-
-For binary data, hex and base-64 are the most common encodings. Since hex is simpler to implement I'd go with it unless
-I really need the 30% bandwidth improvement base-64 brings.
-
-Binary serial protocols
-=======================
-
-In contrast to anything human-readable, binary protocols are generally more bandwidth-efficient and are easier to format
-and parse. However, binary protocols come with their own version of the caveats we discussed for text-based protocols.
-
-The framing problem in binary protocols
----------------------------------------
-
-The most basic problems with binary protocols as with text-based ones is framing, i.e. splitting up the continuous
-serial data stream into discrete packets. The issue is that it is that you have to somehow mark boundaries between
-frames. The simplest way would be to use some special character to delimit frames, but then any 8-bit character you
-could choose could also occur within a frame.
-
-SLIP/PPP-like special character framing
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Some protocols solve this problem much like we have solved it above for strings in line-based protocols, by escaping any
-occurence of the special delimiter character within frames. That is, if you want to use ``0x00`` as a delimiter, you would
-encode a packet containing ``0xde 0xad 0x00 0xbe 0xef`` as something like ``0xde 0xad 0x01 0x02 0xbe 0xef``, replacing the
-null byte with a magic sequence. This framing works, but is has one critical disadvantage: The length of the resulting
-escaped data is dependent on the raw data, and in the worst case twice as long. In a raw packet consisting entirely of
-null bytes, every byte must be escaped with two escape bytes. This means that in this case the packet length doubles,
-and in this particular case we're even less efficient than base-64.
-
-Highly variable packet length is also bad since it makes it very hard to make any timing guarantees for our protocol.
-
-9-bit framing
-~~~~~~~~~~~~~
-
-A framing mode sometimes used is to configure the UARTs to transmit 9-bit characters and to use the 9th bit to designate
-control characters. This works really well, and gives plenty of control characters to work with. The main problem with
-this is that a 9-bit serial interface is highly nonstandard and you need UARTs on both ends that actually support this
-mode. Another issue is that though more efficient than both delmitier-based and purely text-based protocols, it still
-incurs an extra about 10% of bandwidth overhead. This is not a lot if all you're sending is a little command every now
-and then, but if you're trying to push large amounts of data through your serial it's still bad.
-
-COBS
-~~~~
-
-Given the limitations of the two above-mentioned framing formats, we really want something better. The `Serial Line
-Internet Protocol (SLIP)`_ as well as the `Point to Point Protocol (PPP)`_, standardized in 1988 and 1994 respectively,
-both use escape sequences. This might come as a surprise, but humanity has actually still made significant technological
-progress on protocols for 8-bit serial interfaces until the turn of the millennium. In 1999, `Consistent Overhead Byte
-Stuffing (COBS)`_ (`wiki `__) was published by a few
-researchers from Apple Computer and Stanford University. As a reaction on the bandwidth doubling problem present in
-PPP_, COBS *always* has an overhead of a single byte, no matter what or how long a packet's content is.
-
-COBS uses the null byte as a delimiter interleaves all the raw packet data and a `run-length encoding`_ of the non-zero
-portions of the raw packet. That is, it prepends the number of bytes until the first zero byte to the packet, plus one.
-Then it takes all the leading non-zero bytes of the packet, unmodified. Then, it again encodes the distance from the
-first zero to the second zero, plus one. And then it takes the second non-zero run of bytes unmodified. And so on. At
-the end, the packet is terminated with a zero byte.
-
-The result of this scheme is that the encoded packet does not contain any zero bytes, as every zero byte has been
-replaced with the number of bytes until the next zero byte, plus one, and that can't be zero. Both formatter and parser
-each have to keep a counter running to keep track of the distances between zero bytes. The first byte of the packet
-initializes that counter and is dropped by the parser. After that, every encoded byte received results in one raw byte
-parsed.
-
-While this might sound more complicated than the escaping explained above, the gains in predictability and efficiency
-are worth it. An implementation of encoder and decoder should each be about ten lines of C or two lines of Python. A
-minor asymmetry of the protocol is that while decoding can be done in-place, encoding either needs two passes or you
-need to scan forward for the next null byte.
-
-.. _`Point to Point Protocol (PPP)`: https://en.wikipedia.org/wiki/Point-to-Point_Protocol
-.. _PPP: https://en.wikipedia.org/wiki/Point-to-Point_Protocol
-.. _`Serial Line Internet Protocol (SLIP)`: https://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol
-.. _`Consistent Overhead Byte Stuffing (COBS)`: http://www.stuartcheshire.org/papers/COBSforToN.pdf
-.. _`Point-to-Point Protocol (PPP)`: https://en.wikipedia.org/wiki/Point-to-Point_Protocol
-.. _`run-length encoding`: https://en.wikipedia.org/wiki/Run-length_encoding
-
-State machines and error recovery
----------------------------------
-
-In binary protocols even more than in textual ones it is tempting to build complex state machines triggering actions on
-a sequence of protocol packets. Please resist that temptation. As with textual protocols keeping the protocol state to
-the minimum possible allows for a self-synchronizing protocol. A serial protocol should be designed such that if due to
-a dropped packet or two both ends will naturally re-synchronize within another packet or two. A simple way of doing that
-is to always transmit one semantic command per packet and to design these commands in the most idempotent_ way possible.
-For example, when filling a framebuffer piece by piece, include the offset in each piece instead of keeping track of it
-on the receiving side.
-
-.. _idempotent: https://en.wikipedia.org/wiki/Idempotence#Computer_science_meaning
-
-Conclusion
-==========
-
-Here's your five-step guide to serial bliss:
-
-1. Unless you have super-special requirements, always use the slowest you can get away with from 9600Bd, 115200Bd or
- 1MBd. 8N1 framing if you're talking to anything but another microcontroller on the same board. Using common values
- like these makes it easier when you'll inevitably have to guess these at some point in the future ;)
-2. If you're doing something simple and speed is not a particular concern, use a human-readable text-based protocol. Use
- one command/reply per line, begin each line with some sort of command word and format numbers in hexadecimal. Bonus
- points for the device replying to unknown commands with a human-readable status message and printing a brief protocol
- overview on boot.
-3. If you're doing something even slightly nontrivial or need moderate throughput (>1k commands per second or >20 byte of
- data per command) use a COBS-based protocol. A good starting point is a ``[target MAC][command ID][command
- arguments]`` packet format for multidrop busses. For single-drop you may decide to drop the MAC address.
-4. Always include some sort of "status" command that prints life stats such as VCC, temperature, serial framing errors
- and uptime. You'll need some sort of ping command anyway and that command might as well do something useful.
-5. If at all possible, keep your protocol context-free across packets/lines. That is, a certain command should always be
- self-contained, and no command should change the meaning of the next packet/line/command that is sent. This is really
- important to allow for self-synchronization. If you really need to break up something into multiple commands, say you
- want to set a large framebuffer in pieces, do it in a idempotent_ way: Instead of sending something like ``FRAMEBUFFER
- INCOMING:\n[byte 0-16]\n[byte 17-32]\n[...]\nEND OF FRAME`` rather send ``FRAMEBUFFER DATA FOR OFFSET 0: [byte
- 0-16]\nFRAMEBUFFER DATA FOR OFFSET 17: [byte 17-32]\n[...]\nSWAP BUFFERS\n``.
-
diff --git a/content/blog/sybil-resistance-identity/images/succulents.jpg b/content/blog/sybil-resistance-identity/images/succulents.jpg
deleted file mode 100755
index 938bffd..0000000
Binary files a/content/blog/sybil-resistance-identity/images/succulents.jpg and /dev/null differ
diff --git a/content/blog/sybil-resistance-identity/index-old.rst b/content/blog/sybil-resistance-identity/index-old.rst
deleted file mode 100644
index 734cc5a..0000000
--- a/content/blog/sybil-resistance-identity/index-old.rst
+++ /dev/null
@@ -1,246 +0,0 @@
----
-title: "Theia Attack Resistance and Digital Identity"
-date: 2020-09-09T15:00:00+02:00
-draft: true
----
-
-.. raw:: html
-
-
-
-
-Theia in Cyberspace
-===================
-
-In informatics, the term *distributed system* is used to describe the aggregate behavior of a complex network made up of
-individual computers. For decades, computer scientists to some success have been trying to figure out how exactly the
-individual computers that make up such a distributed system need to be programmed for the resulting amalgamation to
-behave in a predictable, maybe even a desirable way. Though seemingly simple on its surface, this problem has a
-surprising depth to it that has yielded research questions for a whole field for several decades now. One particular
-as-of-yet unsolved problem is resistance against *theia attacks* (or "sybil" attacks in older terminology).
-
- Named after the 1973 book by Flora Rheta Schreiber on dissociative identity disorder, a sybil attack is an
- attack where one computer in a distributed system pretends to be multiple computers to gain an advantage. From your
- author's standpoint, naming a type of computer security attack after a medical condition was an unfortunate choice.
- For this reason this post uses the term *Theia attack* to refer to the same concept. Theia is a greek godess of light
- and glitter and the name alludes to the attacker performing something alike an optical illusion, causing the attacked
- to perceive multiple distinct images that in the end are all only reflections of the same attacker.
-
-The core insight of computer science research on theia attacks is that there cannot be any technological way of
-preventing such an attack, and any practical countermeasure must be grounded in some authority or ground truth that is
-external to the systems—bridging from technology to its social or political context.
-
-Looking around, we can see a parallel between this question ("which computer is a real computer?") and a social issue
-that recently has been growing in importance: Just like computers can pretend to be other computers, they can also
-pretend to be humans. As can humans. Be it within the context of election manipulation or down-to-earth astroturfing_
-the recurring issue is that in today's online communities, it is hard for an individual to tell who of their online
-acquaintances are who they seem to be. Different platforms attempt different solutions to this problem, and all fail in
-some way or another. Facebook employs good old snitching, turning people against each other and asking them "Do you know
-this person?". Twitter is more laid-back and avoids this Stasi_ methodology in favor of requiring a working mobile phone
-number from its subjects, essentially short-circuiting identity verification to the phone company's check of their
-subscriber's national passport.
-
-.. the preceding is a simplified representation of these platform's practices. In particular facebook uses several
- methods depending on the case. I think this abbreviated discussion should be ok for the sake of the argument. I am
- not 100% certain on the accuracy on the accuracy of the statement though. Does fb still do the snitching thing? Is
- twitter usually content with a phone number?
-
-Trusting Crypto-Anarchist Authorities
-=====================================
-
-Beyond these centralistic solutions to the problem, crypto-anarchists and anarcho-capitalists have been brewing on some
-interesting novel approaches to online identity based on *blockchain* distributed ledger technology. Distributed
-ledgers are a distributed systems design pattern that yields a system that works like an append-only logbook.
-Participants can create new entries in this logbook, but no one—neither the original author, nor other participants—can
-retroactively change a logbook entry once it has been written. In the blockchain model, past entries are essentially
-written into stone. This near-perfect immutability is what opens them for a number of use cases from cryptographic
-pseudo-currencies [#cryptocurrency]_.
-
-An overview over a variety of these unconventional blockchain identity verification approaches can be found in `this
-unpublished 2020 survey by Siddarth, Ivliev, Siri and Berman `_.
-They walk their readers through a number of different projects that try to solve the question "Is this human who they
-pretend to be?" using joint socio-technological approaches. In the following few sections, you may find a short outline
-of a small selection of them. The conlusion of this post will be a commentary on these approaches, and on the underlying
-problem of identity in a digital world.
-
-.. BrightID
-
-In one scheme, identity is determined by "notary" computers that aggregate large amounts of information on a user's
-social contacts. These computers then run an algorithm derived from the SybilGuard_, SybilLimit_ and SybilInfer_ lineage
-of random-walk based algorithms. These algorithms assume that authentic social graphs are small world graphs: Everyone
-knows everyone else through a friend's friend's friend. They also assume that there is an upper bound on how many
-connections with authentic users an attacker can forge: Anyone who is not embedded into the graph well enough is cut
-out. Like this, they put an upper limit on the number of theia identites an attacker can assume given a certian number
-of connections to real people.
-
-Disregarding the catastrophic privacy issues of storing large amounts of data on social relationships on someone else's
-computer, this second assumption is where this model unfortunately breaks down. Applying common sense, it is completely
-realistic for an attacker to forge a large number of social connections: This is precisely what most of social media
-marketing is about! A more malicious angle on this would be to consider how in meatspace [#meatspacefn]_ multi-level
-marketing schemes are successful in coaxing people to abuse their social graphs to disastrous consequences to the
-well-being of themselves and others. Similar schemes would certainly be possible in cyberspace as well. An additional
-point to consider is that the upper limit SybilGuard_ and others place on the number of fake identities one can have is
-simply not that strict at all. An attacker could still get away with a reasonable number of false identities before
-getting caught by any such algorithm.
-
-.. Duniter
-
-In another scheme, identity is awarded to anyone who can convince several people already in the network to vouch for
-them, and who is at most a few degrees removed from one of several pre-determined celebrities. Apart from again being
-vulnerable to conmen and other scammers, this system has the glaring flaw of roundly refusing to recognize any person
-who is not willing or able to engage with multiple of its members. Along with the system's informal requirement for
-members to only vouch for people they have physically met this leads to a nonstarter in a cyberspace that grown
-specifically *because* it transcends national borders and physical distance—two most serious obstacles to in-person
-communication.
-
-.. Idena Network
-
-The last scheme I will outline in this post is based around a set of `Turing tests`_; that is, quizzes that are designed
-to tell apart man and machine. In this system, all participants have to simultaneously undergo a Turing test once in a
-fortnight. The idea is that this limits the number of theia identities an attacker can assume since they can only solve
-that many Turing tests at the same time. The system uses a particular type of picture classification-based Turing test
-and does not seem to be designed with the blind or mentally disabled in mind with accessibility concerns nowhere to be
-found in the so-called "manifesto" published by its creators. But even ignoring that, the system obviously fails at an
-even more basic level: The idea that everyone takes a Turing test at the same time only works in a world without time
-zones. Or jobs for that matter. Also, it assumes that an attacker cannot simply hire a small army of people someplace
-else to fool the system.
-
-.. _SybilLimit: https://www.comp.nus.edu.sg/~yuhf/yuh-sybillimit.pdf
-.. _SybilGuard: http://www.math.cmu.edu/~adf/research/SybilGuard.pdf
-.. _SybilInfer: https://www.princeton.edu/~pmittal/publications/sybilinfer-ndss09.pdf
-.. _`Turing Tests`: https://en.wikipedia.org/wiki/Turing_test
-
-Identity between Cyberspace and Meatspace
-=========================================
-
-A common thread in these solutions, from the Facebook'esque Stasi_ methods to the crypto-anarchist challenge-response
-utopias, is that they all approach digital identity as a question of Objective Truth™ that can unanimously be decided at
-a system level—or that can be externalized to the next larger system such as the state. Alas, the important question
-remains unasked:
-
- What *is* identity?
-
-The answer to this question certainly depends on the system being examined. For example, an important reason the
-capitalist corporations mentioned above require knowledge about their users' identity is to generate plausible
-statistics for the advertisers that form their customer base, similar to how a farmer will keep statics on yield and
-quality for the buyers of his crop. With this background, a full decoupling of platform accounts from a notion of legal
-identity seems at odds with the platform's business model—and we will have to adjust our expectations for reform
-accordingly.
-
-A common thread among all systems mentioned above is that they all have a social component to them. For this common use
-case of social systems, I want to make a suggestion on how we can approach digital identity in a more practical, less
-discriminatory [#discriminatory]_ manner than any of the methods we discussed above. I think both using people's social
-connections and proxying the decisions of external authorities such as the state are bad systems to decide who is a
-person and who is not. I will now illustrate this point a bit. Let us think about how many digital identities a human
-beign might have. First, consider the case of n=0, someone who simply wants no business with the system at all. For
-simplicity, let us assume that we have solved this issue of consent, i.e. every person who is identified by the system
-consents to this practice. For n=1, the approaches outlined above all provide some approximate solution. States may not
-grant every human sufficient ID (e.g. children, the mentally disabled or prisoners might be left out), and the social
-systems might fail to catch people who simply do not have any friends, but otherwise their approximations hold. Maybe.
-But what about n=2, n=3, ...? None of these systems adequately consider cases where a human being might legitimately
-wish to hold multiple digital identities, non-maliciously.
-
-Consider a hypothetical lesbian, conservative politician. An active social media presence is a core component of a
-modern politician's carreer. At the same time, "conservative homophobe" is still well within the realm of tautology and
-it would be legitimate for this politician to wish to not disclose a large fraction of their private life to the world
-at large. They might have a separate online identity for matters related to it. For this politician, the social
-relationship-based systems referenced above would either incorporate outing as a design feature, or they would force
-the politician to choose either of their two identities: To choose between private life and carreer. When deferring to
-the state as the decider over personhood, at least the platform's operator would know about the outrageously sensitive
-link between the politician's online identities. Clearly, no such solution can be considered socially just.
-
-Let us try not to be caught up on saving the world at this point. The issue of conservative homophobia is out of the
-scope of our consideration, and it is not one that anyone can solve in the near future. Magical realism aside, least of
-all can some technological thing beckon this change. There is a case for legitimate uses of multiple, separate digital
-identities, and we do not have a technical or political answer to it. All hope is not lost yet, though. We can easily
-undo this gordian knot by acknowledging an unspoken assumption that underlies any social relationships between real
-people, past the procrustean bed of computer systems or organizational structures these relationships are cast into.
-
- As a function of social interaction, digital identities conform to roles_ in sociological terminology, and are not
- at all the same as personhood_. Roles are subjective and arise from a relationship between people, and a single
- person might legitimately perform different roles depending on context.
-
-When computer scientists or programmers are creating new systems, there always is an (often implicit) modelling stage.
-Formally, during this stage a domain expert and a modeller with a computer science background come together, each
-contributing their knowledge to form a model that is both appropriate for real-world use and practical from an
-engineering point of view. In practice, these two roles are often necessarily fulfilled by the same person, who is often
-also the programmer of the thing. This leads to many computer systems using poor models. A typical example of this issue
-are systems requiring a person's name that use three input fields labelled "First Name", "Middle Initial" and "Last
-Name". These systems are often created by US-American programmers, who are used to this naming schema from their lived
-experience. Unfortunately, this schema breaks down for those few billion people who use their last name first, who have
-more than one middle name, or who have multiple given names and do not normally use the first one of those.
-
-Once a system creator's implicit assumptions have been encoded into the system like this, it is often very hard to get
-out of that situation. A pattern to use during careful modelling is to keep the model flexible to account for unforeseen
-corner cases. For example, when modelling a system requiring a person's name, one would have to ask what the name is
-used for. It may be the most sensible decision to simply ask the user for their name twice: Once in first name/last name
-format for e.g. tax purposes, and once with a free-form text field for e.g. displaying on their account page.
-
-While for names, many systems already use some form of flexible model by e.g. having a *handle* or *nickname* separate
-from the *display name*, "social" systems still often are stuck with an identity model based around a concept of a
-single, rigid identity. In practice, people perform different roles_ in different circumstances. When asking for a
-person's identity, one would get wildly different answers from different people. A person's identity as perceived by
-others is coupled to their relationship more than to some underlying, biological or administrative truth. Thinking back
-to the straw man politician above, this is evident in subtle ways in almost all our everyday relationships: Some people
-may know me by my legal name, some by my online nickname. To some I may be a computer scientist, to some a flatmate.
-None of my friends and acquaintances have ever wanted to see my passport, or asked to take my DNA to ascertain that I am
-a distinct human being from the other humans they know. Likewise, identifying me by my social connections is impractical
-as it would require an exceedingly weird amount of what can only be described as snooping. Yet, this concept of a
-single, consistent, global, true identity is exactly what up to now all technological solutions to the identity problem
-are trying to achieve.
-
-Building Bridges
-================
-
-I think I can offer you one main take-aways from the discussion above.
-
- During modelling social systems, focus on relationships—not identity.
-
-Rephrased into more actionable points, as someone designing a social digital system, do the following:
-
-0. Early in the design stages, take the time to consider fundamental modelling issues like this one. If you don't, you
- will likely get stuck with a sub-optimal model that will be hard to get rid of.
-1. Where possible, be flexible. Allow people to chose their own identifier. Don't require them to use their real names,
- they may not wish to disclose those or they may not be in a format that is useful to you (they may be too long, too
- short, too ubiquituous, in foreign characters etc.). A free-form text field with a reasonable length limit is a good
- approach here.
-2. Do not use credit cards or phone numbers to identify people. There are many people who do not have either, and
- scammers can simply buy this data in bulk on the darknet.
-3. Allow people to create multiple identites [#accountswitchopsec]_, and acknowledge the role of social relationships in
- your interaction features. People have very legitimate reasons to separate areas of their lifes, and it is not for
- you or your computer to decide who is who to whom. If your thing requires a global search function, re-consider the
- data protection aspects of your system. If you want to encourage social functions in the face of bots and trolls,
- make it easy for people to share their identities out-of-band, such as through a QR code or a copy-and-pasteable
- short link. If you require someone's legal name or address for billing purposes, unify these identities behind the
- scenes if at all and allow them to act as if fully independent in public.
-
-While change of perspective comes with its share of user experience challenges, but also with a promise for a more
-human, more dignified online experience. Perhaps we can find a way to adapt cyberspace to humans, instead of continuing
-trying it the other way around.
-
-.. _astroturfing: https://en.wikipedia.org/wiki/Astroturfing
-.. _Stasi: https://en.wikipedia.org/wiki/Stasi
-
-.. [#cryptocurrency] Pseudo-currencies in that, while they provide some aspects of a regular currency such as ownership
- and transactions, they lack most others. Traditional currencies are backed by states, regulated by central banks
- tasked with maintaining their stability and ultimately provide accountability through law enforcement, courts
- and political elections.
-
-.. [#discriminatory] Discriminatory as in discriminating against minorities, but also as in deciding what is and what is
- not.
-
-.. [#accountswitchopsec] This does mean that you should not actively prevent people from creating multiple accounts. It
- does not necessarily entail building a proper user interface around this practice. If you do the latter, e.g. by
- offering a "switch identity" button or an identiy drop-down menu on a post submission form, you can easily
- encourage slip-ups that might disclose the connection between two identities, and you make it possible for
- someone hacking a single login to learn about this connection as well.
-
-.. [#meatspacefn] Meatspace_ is where people physically are, as opposed to cyberspace
-
-.. _Meatspace: https://dictionary.cambridge.org/dictionary/english/meatspace
-.. _roles: https://en.wikipedia.org/wiki/Role
-.. _personhood: https://en.wikipedia.org/wiki/Personhood
diff --git a/content/blog/sybil-resistance-identity/index.rst b/content/blog/sybil-resistance-identity/index.rst
deleted file mode 100644
index 3c9e008..0000000
--- a/content/blog/sybil-resistance-identity/index.rst
+++ /dev/null
@@ -1,90 +0,0 @@
----
-title: "Identity between Cyberspace and Meatspace"
-date: 2020-09-09T15:00:00+02:00
-draft: true
----
-
-.. raw:: html
-
-
-
-Identity in Cyberspace
-======================
-
-.. Identity is a frequent problem
-.. Easy solutions abound
-.. Precise modelling is uncommon
-.. True identity is sensitive, hard to handle
-..
-.. Often, conversational features emphasized -> true identity is unnecessary
-.. Social role theory
-.. Call to action
-
-Most computer systems that interface with humans have a concept of user identity. The data structures used for its
-storage vary, but usually one *account* corresponds to one human *user*. In many applications, the system operator tries
-to ensure that one user cannot create multiple accounts. In online social networks, astrotufing_ and trolling are easier
-to fight when limits are imposed on account creation. In online stores, fraud prevention means the store operator needs
-their customers legal identity and the operator must be able to ban offending customers. In mobile messaging systems,
-users have to be able to find each other by some identifier such as name or phone number, and this identifier has to be
-unique and hard to forge.
-
-Today, in systems that allow anyone to create an account have largely converged to require either an email address or a
-mobile phone number. Email addresses are used by systems that are less vulnerable to abuse and that are used on laptop
-or desktop computers. Mobile phone numbers are abundantly used in smartphone apps, as well as in systems more prone to
-abuse such as online social networks or ecommerce. Both are easily verified using a confirmation email or SMS.
-
-When designing or programming an online system, it is uncommon that the precise real-world semantics of accounts are
-modelled. Most computer systems use ad-hoc data models. During their creation, their programmers implicit assumptions
-about the world are encoded into these data models. Most of the time this works fine, but it does lead to significant
-blind spots that can make systems break down for a fraction of their users.
-
-Lives in Meatspace
-==================
-
-A consequence of the proliferation of phone numbers being used to identify people is that most people will not be able
-to create multiple accounts. *"That's the point!"* you might say, but while we want to prevent scammers, spammers and
-boored schoolchildren from messing with our systems, everybody else may have legitimate reasons to have more than one
-account.
-
-We can apply sociology's model of roles_ to understand this issue. In sociology, a role is the comprehensive pattern of
-rules and expectations that govern an individual's behavior corresponding to their social position. A key fact is that
-most people occupy mutliple roles. A parent may also be a company employee or a wife and perform accordingly given the
-circumstances. Systems that tie digital identity to legal personhood through the contracts behind phone numbers impede
-their users' attempts at role separation. Effects of this are e.g. that nowadays employers routinely screen applicants'
-social media accounts for unacceptable content.
-
-While this role conflict merely amounts to a minor inconvenience to most there are many to who it poses an existential
-problem. Consider an LGBT+ person living in a repressive country or a politically conservative person living in a
-very liberal city. Both have legitimate reasons to strictly separate parts of their private lives from others. For both,
-much is at stake. Yet, both will have to practically circumvent most online systems registration barriers to implement
-this separation.
-
-Trusting the User
-=================
-
-While there is no single solution to these issues, there are several possible mitigations. The first and most important
-one is to systematically think about the system's data model when creating it. Which assumptions about the real world
-are inherent in it? Are these assumptions likely to cause issues? Ad-hoc models are easily created, but hard to get rid
-of when they start causing problems.
-
-A general guideline on identity should be that hindering trolls by requiring things like phone numbers or credit card
-numbers is very likely to also be an obstacle to many entirely legitimate uses. Captchas_ or invitation links can help
-to keep out the trolls. Another approach is to limit the damage a troll can cause with things like effective moderation
-systems, reputation systems or by limiting the reach of newly created accounts.
-
-Outside of e-commerce, actually tying a digital account to a real-world identity is very rarely necessary. The value of
-a messenger app is not in the names in its contacts list, but the conversations behind these names. When two people meet
-each other on the street, their interaction is shaped by a myriad of social factors—but *not* by them showing each other
-their photo ID.
-
-Humans with their messy identities do not fit today's cyberspace well. Let's adapt cyberspace to humans, instead of
-trying it the other way around.
-
-.. _astroturfing: https://en.wikipedia.org/wiki/Astroturfing
-.. _roles: https://en.wikipedia.org/wiki/Role
-.. _Captchas: https://link.springer.com/content/pdf/10.1007/3-540-39200-9_18.pdf
-
diff --git a/content/blog/telekom-gpon-sfp/index.rst b/content/blog/telekom-gpon-sfp/index.rst
deleted file mode 100644
index d22894a..0000000
--- a/content/blog/telekom-gpon-sfp/index.rst
+++ /dev/null
@@ -1,219 +0,0 @@
----
-title: "Ubiquiti EdgeRouter on Deutsche Telekom GPON Fiber"
-date: 2022-02-21T20:00:00+01:00
-summary: >
- Short tutorial on getting a Deutsche Telekom GPON internet connection running using a SFP ONU unit in an Ubiquiti
- EdgeRouter.
----
-
-Disclaimer
-==========
-
-I provide this guide as a reference for other knowledgeable users without any warranty. Please feel free to use this as
-a resource but do not hold me responsible if this does not work for you. There is a significant chance that due to an
-error on my side or due to Telekom changing their setup this guide will not work for you, and you may end up having to
-pay for an unsuccessful Telekom technician visit. That is your own risk, and I do not assume any liability.
-
-Tl;dr
-=====
-
-The "Telekom Digitalisierungsbox Glasfasermodem" is a GPON ONT in SFP form factor that works with an Ubiquiti EdgeRouter
-6P's SFP port. You can order it from Telekom or other vendors using the Telekom P/N 40823569 or its EAN 4718937619382.
-It costs about the same as the separate plastic box modem, but saves a lot of space and does not require a separate
-power supply.
-
-To configure, first access the SFP ONT's web interface at ``10.10.1.1`` by configuring your SPF port's IP to static
-``10.10.1.2``. User credentials are either admin/admin or admin/1234. In the web interface, set put PLOAM password into the
-"SLID" setting in ASCII mode, then save & reboot the device. Now, configure PPPoE on the router's SFP port using the
-PPPoE UID ``[anschlusskennung] [zugangsnummer] "#" [mitbenutzernummer] "@t-online.de"`` and your "Persönliches Kennwort" as
-PPPoE password. Set the VLAN to ``7``, and you are good to go.
-
-Background
-==========
-
-I moved into a new apartment that has a fiber internet connection operated by Deutsche Telekom. Having made some poor
-experiences with AVM's FritzBox brand of routers that is commonly used by German carriers, I decided to use my own
-Router instead of the one provided by Deutsche Telekom. Like other German providers, Telekom charges exorbitant amounts
-in monthly fees for their routers, so even though my choice ended up being a high-end piece of commercial equipment I
-will still be cheaper than going with Telekom's much shittier device when added up over a two-year contract period.
-
-The hardware I chose is the Ubiquiti EdgeRouter 6P. This device is from Ubiquiti's commercial lineup and is intended to
-power something like a small branch office of a company. It comes in a small form factor (as opposed to larger rackmount
-units), it does not consume a lot of power, it has five PoE-capable Ethernet ports which I can directly connect up to
-the Ubiquiti Unifi UAP access point that I already have, and it has a powerful configuration interface. It can even
-act as a VPN endpoint!
-
-Telekom's fiber internet offering for residential customers is GPON-based. GPON stands for "Gigabit Passive Optical
-Network" and means that instead of patching through one fiber or pair of fibers to each customer, several customers in
-one building are connected to a single fiber through optical splitters. These optical splitters are passive, i.e. they
-are just fancy pieces of glass and fibers and do not require electrical power. The advantage of GPON is lower initial
-cost for the operator, the disadvantage is that competing providers can only ever hope to get traffic handed through by
-Telekom and will never be able to use their own equipment on the "network" end of the fiber.
-
-Telekom wants you to connect to its fiber network through a small plastic box that they call "modem", and that the rest
-of the world calls "ONT", or Optical Network Terminator. Telekom's ONT has an upstream optical port with an LC
-connector, and a regular RJ45 ethernet port downstream. The "modem" in fact contains an entire linux system that
-terminates the ITU-standard suite of protocols that is used to manage what happens on the fiber, e.g. scheduling of
-transmission slots and adjustment of transmitter laser power.
-
-Looking at Telekom's plastic box ONT and my nice and shiny EdgeRouter, I was not a fan of this solution. Doing some
-research I found out that you can in fact get GPON ONTs in an SFP module form factor. My EdgeRouter has an SFP slot, so
-if I could get one of these that is compatible with Telekom's GPON flavor I could theoretically just plug it into my
-EdgeRouter's SFP slot with no separate power supply needed, saving a lot of space in the process.
-
-Finding a GPON SFP ONT that is compatible with Telekom's network turned out to be the hard part. While there are lots of
-commercial devices that look like they *should be* compatible, I could not be sure and I did not feel like sinking lots
-of money and weeks of trial and error into figuring out which are and which are not. After about half a dozen calls with
-various Telekom customer service departments I found the solution that ultimately ended up working: For their business
-customer fiber internet offering, Telekom uses the same GPON standard, but different ONT equipment. Their router for
-business customers is called "Digitalisierungsbox" and it in fact comes with an SFP GPON ONT. And, as it turns out, you
-can order that SFP GPON ONT separately for about 50 € (the same as the plastic box one) from either Telekom or a number
-of independent online stores. The Telekom part number of the thing is 40823569, the EAN is 4718937619382.
-
-Below is a list of steps that I had to undertake in order to get my EdgeRouter/SFP ONT setup to work.
-
-Hardware Setup
-==============
-
-The hardware setup is really simple. The SFP ONU is plugged into the EdgeRouter's SFP port. The ONU is connected to
-the Telekom Fiber through the LC/APC to SC/APC adapter cable that is included in its package. Telekom's technician will
-install an LC/APC coupler to join both cables. To configure the EdgeRouter, connect yourself through an ethernet cable
-*on port 2*. Ubiquiti's setup wizards assume the WAN interface is either port 1 or the SFP port (port 5), and default to
-use port 2 as their LAN interface even when port 5 is configured as the only WAN port. The default IP for the EdgeRouter
-is ``192.168.1.1``, and the default UID/PW is ubnt/ubnt.
-
-Configuration
-=============
-
-Getting access to the SFP ONU's config interface
-------------------------------------------------
-
-In this section I am assuming you want to configure the SFP ONU while it is plugged into the EdgeRouter from a laptop
-connected to the EdgeRouter's ethernet port 2. To do this, we have to first configure the right IP/subnet on the
-EdgeRouter's SFP interface, then patch connections between the SFP ONU and the laptop through the EdgeRouter.
-
-1. First, inside the EdgeRouter's config interface we need to configure a static IP with accompanying SNAT rule on the
- SFP port to allow us to access the SFP module's web interface through the laptop connected to the EdgeRouter. For
- this, configure the eth5 interface (which is the SFP port) to use the static IP ``10.10.1.2/24``.
-
-.. raw:: html
-
-
-
-
-
- SFP interface configuration to access the SFP ONU from a laptop connected to the EdgeRouter's LAN
- port
-
-
-2. With the SFP port assigned an IP address, we need to add a NAT rule to forward connections from the configuration
- laptop on eth2 to the SFP port. We do this by adding a source NAT rule with masquerading enabled, for the TCP
- protocol, with destination address ``10.10.1.0/24`` (the SFP config interface's private network).
-
-.. raw:: html
-
-
-
-
-
- Source NAT configuration to access the SFP ONU from LAN. eth5, masquerading on, TCP, destination
- 10.10.1.1 (the SFP ONU's IP).
-
-
-3. Finally, make sure that your laptop will actually use the EdgeRouter as its gateway for IPs within ``10.10.1.0/24``.
- On the laptop, disable any VPNs, disconnect your Wifi and make sure that IP r shows a default route pointing at the
- EdgeRouter's ``192.168.1.1``. If that isn't the case, on Linux you can manually add the necessary route by using
- ``sudo ip r a 10.10.1.0/24 via 192.168.1.1 dev enp5s0``
-
-After setting up this temporary route, you should be able to access the SFP ONU's configuration web interface by
-pointing a browser at ``http://10.10.1.1/`` Just make sure you use plain-text HTTP here, not secure HTTP**S**. The
-default login credentials for the device are admin/1234.
-
-.. raw:: html
-
-
-
-
-
- The SFP ONU's web interface.
-
-
-Configuring the PLOAM password / SLID / ONT-Installationskennung
-----------------------------------------------------------------
-
-On the SFP ONU's web interface, we only have to change one single setting: Under "Setup", we have to set what the SFP
-ONU calls "SLID" to the PLOAM password for the interface. Telekom calls this the "ONT-Installationskennung". You get
-this from your Telekom technician. In the config interface, select ASCII mode and enter the number using the format
-``ABCD000000`` with four capital letters followed by six zeros. If necessary, you can read the SFP ONU's serial number
-on this page.
-
-.. raw:: html
-
-
-
-
-
- The SFP ONU's config interface to set SLID/PLOAM PW/ONT-Installationskennung.
-
-
-Press "Save Config" on the top right of the web page, then select "Reset ONU" and click "Apply" under the "Reset ONU"
-link on the left. Make sure to not select the factory reset option instead.
-
-.. raw:: html
-
-
-
-
-
- Rebooting the SFP ONU.
-
-
-With the ONU configured, after the reset the "GPON Information" page from the left menu under "Status" from the top menu
-should show ``GPON Line Status: O5``. You can now remove the SNAT rule and IP address from the SFP interface in the
-EdgeRouter's config. I recommend this since there is no way to change the ONU's default credentials, and leaving the
-SNAT rule in place makes it vulnerable to attacks from your LAN. If you use the EdgeRouter's setup wizard in the next
-step, that wizard will reset all of these settings.
-
-Configuring PPPoE and NAT
--------------------------
-
-Our ONU now has a low-level connection to Telekom's fiber network. The next step is to configure the EdgeRouter to
-authenticate with the ONU through PPPoE. The easiest way to do this is to use the EdgeRouter's "Basic Setup" wizard as
-described in the `EdgeOS User Guide`. In the wizard, select the SFP port (``eth5``) as the internet/WAN port. Select
-``Internet Connection Type`` as ``PPPoE``, then enter the PPPoE credentials you got from your Telekom technician. The
-password is your "Persönliches Kennwort" that you also use to log in to your customer account on Telekom's website. The
-account name is ``[anschlusskennung] [zugangsnummer] "#" [mitbenutzernummer] "@t-online.de"``, so something like
-``002712345678012345678901#0001@t-online.de``. Enable "Internet connection is on VLAN" and enter VLAN ID ``7``. This is
-necessary because of the way Telekom set up their triple play (TV/phone/internet) service. After following through with
-the wizard, your internet should be already working on port 2 of the router. Note that despite selecting the SFP port as
-the router's WAN port, the wizard will still reserve port 1 (``eth0``) for another WAN interface, so you will only be
-able to access the configuration interface through port 2 (``eth1``) after the wizard is done. You can of course change
-this later.
-
-That's it, you're done and your internet should be working!
-
-Having Fun with the SPF GPON ONU
-================================
-
-If you want to dig deeper into the internals of Telekom's GPON implementation, the SFP ONU's firmware is a great
-starting point. Default credentials are all admin/admin or admin/1234 and you can even get a regular busybox shell on
-the device through SSH. The device's firmware is based on OpenWRT, and the source for large parts of the core control
-components can be found under open source licenses as well. While I would strictly advice you to not mess around with
-the actual modem settings because due to GPON you share a medium with your neighbors and might very well disrupt their
-internet if you mess up, inspecting the ONU's firmware is a great way to learn about the inner workings of a modern GPON
-network.
-
-If you are interested in messing around with the SFP ONU, there is a github repository where interesting thins are
-collected `here `__.
-
-.. _`EdgeOS User Guide`: https://dl.ubnt.com/guides/edgemax/EdgeOS_UG.pdf
-
diff --git a/content/blog/thors-hammer/index.rst b/content/blog/thors-hammer/index.rst
deleted file mode 100644
index 650716e..0000000
--- a/content/blog/thors-hammer/index.rst
+++ /dev/null
@@ -1,64 +0,0 @@
----
-title: "Thor's Hammer"
-date: 2018-05-03T11:59:37+02:00
-summary: >
- In case you were having an inferiority complex because your friends' IBM Model M keyboards are so much louder than
- the shitty rubber dome freebie you got with your pc... Here's the solution: Thor's Hammer, a simple typing cadence
- enhancer for PS/2 keyboards.
----
-
-In case you were having an inferiority complex because your friends' IBM Model M keyboards are so much louder than the
-shitty rubber dome freebie you got with your pc... Here's the solution: Thor's Hammer, a simple typing cadence enhancer
-for `PS/2`_ keyboards.
-
-.. raw:: html
-
-
-
- A demonstration of the completed project.
-
- h264 download /
- webm download
-
-
-
-The connects to the keyboard's PS/2 clock line and briefly actuates a large solenoid on each key press. An interesting
-fact about PS/2 is that the clock line is only active as long as either the host computer or the input device actually
-want to send data. In case of a keyboard that's the case when a key is pressed or when the host changes the keyboard's
-LED state, otherwise the clock line is silent. We ignore the LED activity for now as it's generally coupled to key
-presses. By just triggering an NE555 configured as astable flipflop we can stretch each train of clock pulses to a
-pulse a few tens of milliseconds long that is enough to actuate the solenoid.
-
-.. raw:: html
-
-
-
- The schematic of the driver stretching the PS/2 clock pulses to drive the solenoid.
-
-
-
-Since PS/2 sends each key press and key release separately this circuit will pulse twice per keystroke. It would be
-possible to ignore one of them but I figure the added noise just adds to the experience.
-
-Built on a breadboard, the circuit looks like this.
-
-.. raw:: html
-
-
-
- The completed circuit built up on a breadboard and attached to a keyboard.
-
-
-
-Since my solenoid did not have a tensioning spring I used a rubber band and some vinyl tape to make an adjustable
-tensioner. The small orange USB hub serves as an end-stop because I had nothing else of the right shape. The sound and
-resonance of the thing can be adjusted to taste by moving the end stop, adjusting the tensioning rubber and tuning the
-excitation duration using the potentiometer. My particular solenoid was a bit slow so I added some pieces of circuit
-board as shims between the plunger and the case to limit the plunger's travel inside the solenoid core.
-
-.. _`PS/2`: https://en.wikipedia.org/wiki/PS/2_port
-
diff --git a/content/blog/wifi-led-driver/index.rst b/content/blog/wifi-led-driver/index.rst
deleted file mode 100644
index 2f89856..0000000
--- a/content/blog/wifi-led-driver/index.rst
+++ /dev/null
@@ -1,153 +0,0 @@
----
-title: "Wifi Led Driver"
-date: 2018-05-02T11:31:03+02:00
-summary: >
- After the multichannel LED driver was completed, I was just getting used to controlling LEDs at 14-bit resolution.
- I liked the board we designed in this project, but at 32 channels it was a bit large for most use cases. Sometimes I
- just want to pop a piece of LED tape or two somewhere, but I don't need a full 32 channels of control. I ended up
- thinking that a smaller version of the 32-channel driver that didn't require a separate control computer would be
- handy. So I sat down and designed a variant of the design with only 8 channels instead of 32 and an on-board ESP8266
- module instead of the RS485 transceiver for WiFi connectivity.
----
-
-Project motivation
-==================
-
-.. FIXME finished project picture with LED tape
-.. raw:: html
-
-
-
- The completed driver board installed in the 3D-printed case. This device can now be connected to
- 12V and two segments of LED tape that can then be controlled trough Wifi. The ESP8266 module goes on the pin
- header on the left and was removed for this picture.
-
-
-
-After the `multichannel LED driver`_ was completed, I was just getting used to controlling LEDs at 14-bit resolution.
-I liked the board we designed in this project, but at 32 channels it was a bit large for most use cases. Sometimes I
-just want to pop a piece of LED tape or two somewhere, but I don't need a full 32 channels of control. I ended up
-thinking that a smaller version of the 32-channel driver that didn't require a separate control computer would be
-handy. So I sat down and designed a variant of the design with only 8 channels instead of 32 and an on-board ESP8266_
-module instead of the RS485_ transceiver for WiFi connectivity.
-
-The Electronics
-===============
-
-The schematic was mostly copy-pasted from the 32-channel design. The PCB was designed from scratch. This time, I went
-for a 5x7cm form factor to allow for enough room for all connectors and to give the ESP8266_'s WiFi antenna enough
-space. The board has two 5-pin Phoenix-style_ for two RGB-White (RGBW) tapes and one 2-pin Phoenix-style_ connector for
-12V power input. The control circuitry and the serial protocol are unchanged, but the STM32_ now talks to an ESP-01_
-module running custom firmware.
-
-The LEDs are driven using a 74HC595_ shift register controlling a bunch of AO3400_ MOSFETs_, with resistors in front of
-the MOSFETs_' gates to slow down the transitions a bit to reduce brighntess nonlinearities and EMI_ resulting from
-ringing of the LED tape's wiring inductance.
-
-The board has two spots for either `self-resettable fuses (polyfuses) `__ or regular melting-wire fuses_ in
-a small SMD_ package, one for each RGBW output. For low currents the self-resettable fuses should be okay but at higher
-currents their `trip times get long enough that they become unlikely to trip in time to save anything
-`__, so plain old non-resettable fuses would be the way to go there.
-
-.. FIXME finished board photos
-.. FIXME board with test tape picture
-
-.. raw:: html
-
-
-
-.. raw:: html
-
-
-
- The completed PCBs of this project (front) and the `multichannel LED driver`_ project the driver
- circuitry was derived from (back).
-
-
-
-
-The Firmware
-============
-
-The STM32_ firmware only had to be slightly modified to accomodate the reduced channel count since the protocol remains
-unchanged. The ESP firmware is based on esphttpd_ by Spritetm_. The modifications to the webserver firmware are pretty
-basic. First, the UART console has been disabled since I use the UART to talk to the STM32. The few bootloader messages
-popping out the UART on boot are not an issue, since they're unlikely to contain the fixed 32-bit address prefix the
-serial protocol requires for the STM32_ to do anything.
-
-Second, I added LED control by adding drivers for the serial protocol and a bunch of colorspace conversion functions.
-When I first tested the prototype software, I noticed that color reproduction was extremely poor. When I just sent a
-HSV_ rainbow fade from a python command line, the result looked totally wrong. The fade did not seem to go at a constant
-speed and some colors, in particular yellow, orange and greens, were not visible at all. The problem turned out to be a
-stark mismatch of the red, green and blue channels of the LED tape and less-than-optimal color reproduction of the pure
-colors. I decided to properly measure the LED tape's color reproduction so I could compensate for it in software. This
-turned out to be an extremely interesting project, the details of which you can read in my `LED characterization`_
-article.
-
-Third, I updated the built-in websites with some ad-hoc documentation on how to use the thing and a basic interface for
-LED control.
-
-.. FIXME screenshot of firmware website
-
-Making an enclosure
-===================
-
-To be actually useful, the driver needed a robust enclosure. Bare PCBs are nice for prototyping, but for actually
-putting the thing anywhere it needs a case to protect it against random destruction.
-
-The board has four mounting holes with comfortable spacing in its corners to allow easy mounting inside a 3D-printed
-case. The case itself is described in an OpenSCAD_ script. To make it look a little nicer, a little 3D relief is laid
-into the lid. The 3D relief is generated with a bit of blender magic. The source STL_ model is loaded into blender, then
-blender's amazingly flexible rendering system is used to export a depth map of a projection of the model as a PNG_ file.
-This depth map is then imported as a triangle mesh into OpenSCAD_.
-
-For the relief to look good, I chose a rather high resolution for the depth map. This unfortunately leads to extreme
-memory use and processing time on the part of OpenSCAD_, but since I have access to a sufficiently fast machine that is
-not a problem. Just be careful if you try opening the OpenSCAD_ file on your machine, OpenSCAD_ will probably crash
-unless you're on a beefy machine or interrupt it when it starts auto-rendering the file.
-
-The board is mounted into the enclosure using knurled insert nuts that are pressed into a 3D-printed hole using a bit of
-violence.
-
-.. _`multichannel LED driver`: {{[}}
-.. _`LED characterization`: {{][}}
-.. _ESP8266: https://en.wikipedia.org/wiki/ESP8266
-.. _RS485: https://en.wikipedia.org/wiki/RS-485
-.. _Phoenix-style: https://www.phoenixcontact.com/online/portal/de?uri=pxc-oc-itemdetail:pid=1757019&library=dede&tab=1
-.. _STM32: http://www.st.com/resource/en/datasheet/stm32f030f4.pdf
-.. _ESP-01: http://www.watterott.com/de/ESP8266-WiFi-Serial-Transceiver-Modul
-.. _74HC595: http://www.ti.com/lit/ds/symlink/sn74hc595.pdf
-.. _AO3400: http://aosmd.com/pdfs/datasheet/AO3400.pdf
-.. _MOSFETs: https://en.wikipedia.org/wiki/MOSFET
-.. _EMI: https://en.wikipedia.org/wiki/Electromagnetic_interference
-.. _polyfuse: https://en.wikipedia.org/wiki/Resettable_fuse
-.. _SMD: https://en.wikipedia.org/wiki/Surface-mount_technology
-.. _fuses: https://en.wikipedia.org/wiki/Fuse_(electrical)
-.. _littlefuse-16r-datasheet: http://m.littelfuse.com/~/media/electronics/datasheets/resettable_ptcs/littelfuse_ptc_16r_datasheet.pdf.pdf
-.. _OpenSCAD: http://www.openscad.org/
-.. _STL: https://en.wikipedia.org/wiki/STL_(file_format)
-.. _PNG: https://en.wikipedia.org/wiki/Portable_Network_Graphics
-.. _esphttpd: https://github.com/Spritetm/esphttpd
-.. _Spritetm: http://spritesmods.com/
-.. _`HSV`: https://en.wikipedia.org/wiki/HSL_and_HSV
-
diff --git a/content/blog/wsdiff-static-html-diffs/index.rst b/content/blog/wsdiff-static-html-diffs/index.rst
deleted file mode 100644
index 51f9175..0000000
--- a/content/blog/wsdiff-static-html-diffs/index.rst
+++ /dev/null
@@ -1,127 +0,0 @@
----
-title: "wsdiff: Responsive diffs in plain HTML"
-date: 2025-07-25T23:42:00+01:00
-summary: >
- There's many tools that render diffs on the web, but almost none that work well on small screens such as phones. I
- fixed this by publishing wsdiff, a diffing tool written in Python that produces diffs as beautiful, responsive,
- static, self-contained HTML pages. wsdiffs wrap text to fit the window, and dynamically switch between unified and
- split diffs based on screen size using only CSS.
----
-
-Demo
-----
-
-First off, have a demo. Because of the width of this page, the output will show an unified diff. To try out the split
-diff layout, make sure your browser window is wide enough and open the demo in a separate tab using `this link
-`__.
-
-wsdiff supports dark mode, try it out by toggling dark mode in your operating system!
-
-.. raw:: html
-
-
-
-Core Features
--------------
-
-There's many tools that render diffs on the web, but almost none that work well on small screens such as phones. I fixed
-this by publishing `wsdiff `__, a diffing tool written in Python that produces diffs
-as beautiful, responsive, static, self-contained HTML pages. wsdiffs wrap text to fit the window, and dynamically switch
-between unified and split diffs based on screen size using only CSS.
-
-Responsive Line Wrapping
-........................
-
-The first challenge I solved was wrapping source code lines to match the available screen space. Other tools often just
-show horizontal scroll bars, which is an okay workaround when you're mostly working with hard-wrapped source code on a
-laptop or desktop screen, but which results in catastrophic UX on any phone.
-
-I solved line breaking with a combination of CSS-controlled, web-standard word breaking rules: ``overflow-wrap:
-anywhere`` for source code (`MDN link `__) and
-``white-space: pre-wrap`` to preserve whitespace accurately (`MDN link
-`__). To make both sides of the split diff align, and to
-align line numbers with wrapped source code lines, the diff is laid out using a `CSS grid layout`_. In side-by-side
-view, the layout has four columns: two for line numbers and two for the content. In unified view, the left ("old")
-content column is dropped, and the deleted or modified lines that are highlighted in it in side-by-side view are slotted
-into the remaining right column.
-
-When soft-wrapping source code, text editors will often display a little curved arrow marker to indicate that a line was
-soft-wrapped, and that there is not actually a newline character in the file at that location. wsdiff solves this
-using the same technique I used for the soft-wrapping code blocks in this blog, described `here <{{][}}>`__. It inserts a string of ``"\a↳\a↳\a↳\a↳\a↳..."`` into the line number
-element's ``::after`` pseudo-element. This string evaluates to a sequence of unicode arrows separated by line breaks,
-and starting with an empty line. The ``::after`` pseudo-element is positioned using ``position: absolute``, and the
-parent ```` has ``position: relative`` set. This way, the arrow pseudo-element gets placed on top
-of the lineno span without affecting the layout at all. By setting ``overflow: clip`` on the parent ````, the arrow pseudo-element gets cut off vertically wherever the parent line number element naturally
-ends. Since both the line and the line number element share a grid row, the line number element always matches the
-height of the soft-wrapped line.
-
-Responsive Split/Unified Layout Selection
-.........................................
-
-To dynamically change between unified and side-by-side views, wsdiff uses a web-standard `Media Query`_. By default, the
-page is laid out for side-by-side view. In the HTML source, the diff is listed as it is displayed in side-by-side view,
-with the old and new lines along with their line numbers interleaved.
-
-The magic happens when the media query gets triggered by a narrow screen width. The media query re-adjusts the layout in
-four core steps:
-
- 1. All unchanged lines in the left (old) column are hidden.
- 2. The left content column of the grid layout is hidden, so that now there are three columns: old line number, new line
- number, and unified content.
- 3. All deleted or changed lines from the left (old) column are re-located to the right column. They naturally slot in
- in the right spot because they already appear in the right order in the HTML source.
- 4. By slotting in the old lines in the right column, we have created gaps in the line number columns. Every deleted
- line has an empty cell in the new line number column, and every inserted line has one in the old line number column.
- The CSS adjusts the layout of these empty cells such that the border lines align nicely, and it overrides the
- newline markers so that they only show in the right (new) line number column, not both.
-
-Since this is all CSS, it happens automatically and near-instantly. Since it is using only web standard features, it
-works across browsers and platforms.
-
-Unchanged Line Folding in CSS
-.............................
-
-When showing the diff of a large file, it is usually best to hide large runs of unchanged lines. wsdiff does this
-similar to code folding in text editors. When a long run of unchanged lines is detected, a marker is placed spanning the
-diff. This marker contains a checkbox that can be toggled to hide the unchanged lines. This feature is done completely
-in CSS using a ``:has(input[type="checkbox"]:checked)`` selector.
-
-The actual mechanics are quite simple. To cleanly hide the lines, they must be placed in a container ``]``. That div
-has a CSS subgrid layout using ``display: grid; grid-template-columns: subgrid;``, meaning that its contents align to
-the surrounding diff grid.
-
-Dark Mode
-.........
-
-Integrating a website with the OS-level dark mode is surprisingly easy. All you need is a `Media Query`_ that selects
-for ``@media (prefers-color-scheme: dark)`` and you're good. wsdiff uses named colors using `CSS Custom Properties`_, so
-the actual dark mode media query only needs to override these color properties, and the rest of the CSS will be
-re-computed automatically.
-
-Limitations: Text selection
-...........................
-
-A limitation in having a combined, single HTML source for both side-by-side and unified diffs is that text selection
-only works naturally in either mode. You can't make text selection work in both simultaneously without re-sorting the
-lines in the HTML source, since there is no way to override the text selection order from pure CSS. In wsdiff, I worked
-around this issue by just disabling text selection on the unchanged lines in the left (old) column, so selecting text in
-the right column copies the unified diff as one would expect.
-
-Try it yourself!
-----------------
-
-You can find the demo from above at `this link `__.
-
-You can install wsdiff yourself `from PyPI
`__:
-
-.. code:: sh
-
- $ pip install -U wsdiff
- Successfully installed wsdiff-0.3.1
- $ wsdiff old.py new.py -o diff.html
-
-.. _`CSS grid layout`: https://css-tricks.com/snippets/css/complete-guide-grid/
-.. _`Media Query`: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries
-.. _`CSS Custom Properties`: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascading_variables/Using_CSS_custom_properties
diff --git a/content/imprint/index.rst b/content/imprint/index.rst
deleted file mode 100644
index bbff4e2..0000000
--- a/content/imprint/index.rst
+++ /dev/null
@@ -1,103 +0,0 @@
----
-title: "Impressum"
-noindex: true
----
-
-Impressum
----------
-
-.. raw:: html
-
-
- Sebastian Götte
- c/o Praxis Dr. Götte
- Krankenhausstr. 1a
- 54634 Bitburg
- imprint@jaseg.net
-
-
- Inhaltlich Verantwortlicher gem. § 55 II RStV: Sebastian Götte (Anschrift s.o.)
-
-Lizenz dieser Seite
--------------------
-
-.. raw:: html
-
- Dieses Werk ist lizenziert unter einer
-
- Creative Commons Namensnennung - Nicht-kommerziell - Weitergabe unter gleichen Bedingungen 4.0 International
- Lizenz (CC-BY-NC-SA)
- .
-
-
-
-
-
-Haftungsbeschränkung für Inhalte der Website
---------------------------------------------
-
-Gemäß § 7 Abs. 1 TMG ist der Verantwortliche der Website i. S. v. § 5 TMG für eigene Informationen, die er zur Nutzung
-bereithält, nach den allgemeinen Gesetzen verantwortlich.
-
-Der Verantwortliche der vorbezeichneten Website übernimmt keine Haftung auf Aktualität, Richtigkeit und Vollständigkeit
-der auf dieser Website zur Verfügung gestellten Inhalte. Dies gilt nicht, wenn dem Verantwortlichen vorsätzliches oder
-grob fahrlässiges Verhalten vorzuwerfen ist. Die Inhalte wurden mit der größtmöglichen Sorgfalt erstellt. Dennoch kann
-die inhaltliche Richtigkeit insbesondere bei komplexen Themen nicht gewährleistet werden, so dass der Verantwortliche
-den Nutzern empfiehlt, bei wichtigen Informationen bei den zuständigen Stellen anzufragen oder rechtliche Beratung in
-Anspruch zu nehmen. Sofern kostenpflichtige Inhalte oder Dienste auf der Website zur Verfügung gestellt werden, handelt
-es sich dabei um unverbindliche Invitatio ad offerendum, welche lediglich zur Abgabe eines Angebots durch den Nutzer
-aufrufen und selbst kein verbindliches Angebot darstellen.
-
-Gemäß §§ 8 ff. TMG ist der Website-Betreiber für fremde Inhalte, die er für einen Nutzer veröffentlicht, nicht
-verantwortlich, sofern
-
-* er keine Kenntnis von der rechtswidrigen Handlung oder der Information hat und ihm im Falle von
- Schadensersatzansprüchen auch keine Tatsachen oder Umstände bekannt sind, aus denen die rechtswidrige Handlung oder
- die Information offensichtlich wird, oder
-
-* er unverzüglich tätig geworden ist, um die Information zu entfernen oder den Zugang zu ihr zu sperren, sobald er
- diese Kenntnis erlangt hat.
-
-Erlangt der Website-Betreiber Kenntnis von solchen rechtswidrigen Inhalten, werden diese unverzüglich entfernt.
-
-Haftung für ausgehende Links
-----------------------------
-
-Der Website-Betreiber verlinkt von seiner Website auf fremde Websites. Durch diese sog. „Hyperlinks“ wird der Nutzer
-direkt auf die fremde Website geleitet. Der Website-Betreiber hat dabei keinerlei Einfluss auf die Informationen der
-fremden Website. Daher kann keine Haftung für die Aktualität, Richtigkeit und Vollständigkeit der Inhalte der fremden
-Website übernommen werden. Der Website-Betreiber versichert jedoch, dass ihm zum Zeitpunkt des Setzens der Verlinkung
-keinerlei rechtliche Verstöße bekannt waren und er die fremde Website im Rahmen des Zumutbaren geprüft hat.
-
-Erhält der Website-Betreiber Kenntnis von der Rechtswidrigkeit der verlinkten Inhalte, wird der entsprechende Link
-unverzüglich entfernt.
-
-Urheberrechte
--------------
-
-Die auf dieser Webseite veröffentlichten Inhalte unterliegen dem deutschen Urheberrecht.
-
-Als Urheber i. S. v. § 7 UrhG stehen dem Website-Betreiber die alleinigen und ausschließlichen Verwertungsrechte gemäß
-§§ 15 ff. UrhG zu. Eine Vervielfältigung oder Verwendung sämtlicher Inhalte der Website in fremden elektronischen oder
-gedruckten Medien ist ohne vorherige Zustimmung des Website-Betreibers untersagt und wird ggf. verfolgt.
-
-Datenschutz
------------
-
-Die Website kann grundsätzlich ohne Eingabe von personenbezogenen Daten wie z. B. Name oder E-Mail-Adresse genutzt
-werden. Sollte die Möglichkeit der Eingabe solcher Daten bestehen, so erfolgt die Mitteilung dieser Daten auf
-freiwilliger Basis durch den Nutzer. Eine Weitergabe dieser Daten an Dritte ist ohne die ausdrückliche Zustimmung durch
-den Nutzer ausgeschlossen.
-
-Ein lückenloser Schutz der übermittelten Daten vor dem Zugriff durch unbefugte Dritte ist jedoch nicht möglich, da es im
-Bereich der Datenübermittlung im Internet zu Sicherheitslücken kommen kann. Der Website-Betreiber versucht jedoch, die
-Gefahr des unbefugten Zugriffs durch geeignete Maßnahmen zu unterbinden und im Falle der Kenntnis von einer
-Sicherheitslücke diese durch geeignete Maßnahmen zu schließen.
-
-Der Nutzung der im Impressum veröffentlichten Kontaktdaten durch Dritte zur Übersendung von nicht ausdrücklich
-gewünschter Werbung, insbesondere Spam-Mails, wird widersprochen und im Falle der Nichtbeachtung ggf. mit rechtlichen
-Schritten verfolgt.
-
-(Quelle: `BUSE HERZ GRUNST Rechtsanwälte
`__)
-
diff --git a/content/projects/8seg/index.rst b/content/projects/8seg/index.rst
deleted file mode 100644
index 43e5c85..0000000
--- a/content/projects/8seg/index.rst
+++ /dev/null
@@ -1,25 +0,0 @@
----
-title: "8seg"
-external_links:
- - name: Sources and hardware design files
- url: "https://git.jaseg.de/8seg.git"
- - name: Enclosure 3D models
- url: "https://www.printables.com/model/695260-parametric-flexible-aluminium-profile-case"
- - name: Technical overview blog post
- url: "/blog/8seg"
-summary: >
- 8seg is an experimental textual display. It is made from a 45m by 1.5m large lacework banner that can be put up in a
- variety of spaces, conforming to the space's size and shape through bending and folding.
----
-
-8seg
-====
-
-8seg is an experimental textual display. It is made from a 45m by 1.5m large lacework banner that can be put up in a
-variety of spaces, conforming to the space's size and shape through bending and folding.
-
-8seg was last set up at 37C3 in the entrance hall, on the second-floor railing opposite Hall X.
-
-By popular request, you can find 3D models for the printable parts of the power supply enclosures `here
-`__
-
diff --git a/content/projects/_index.rst b/content/projects/_index.rst
deleted file mode 100644
index 9f9d49b..0000000
--- a/content/projects/_index.rst
+++ /dev/null
@@ -1,7 +0,0 @@
----
-title: Projects
-hide_date: true
----
-I maintain a number of open-source projects. Most of these I started out of some personal need or interest.
-I strive to keep all of them up to date and maintained, so if you notice an issue with one of them, please
-open an issue on the project's issue tracker.
diff --git a/content/projects/gerbolyze/README.rst b/content/projects/gerbolyze/README.rst
deleted file mode 100644
index 0e8262b..0000000
--- a/content/projects/gerbolyze/README.rst
+++ /dev/null
@@ -1,700 +0,0 @@
-Gerbolyze renders SVG vector and PNG/JPG raster images into existing gerber PCB manufacturing files.
-Vector data from SVG files is rendered losslessly *without* an intermediate rasterization/revectorization step.
-Still, gerbolyze supports (almost) the full SVG 1.1 spec including complex, self-intersecting paths with holes,
-patterns, dashes and transformations.
-
-Raster images can either be vectorized through contour tracing (like gerbolyze v1.0 did) or they can be embedded using
-high-resolution grayscale emulation while (mostly) guaranteeing trace/space design rules.
-
-Try gerbolyze online at https://dyna.kokoroyukuma.de/gerboweb
-
-.. figure:: pics/pcbway_sample_02_small.jpg
- :width: 800px
-
- Drawing by `トーコ Toko `__ converted using Gerbolyze and printed at PCBWay.
-
-
-Tooling for PCB art is quite limited in both open source and closed source ecosystems. Something as simple as putting a
-pretty picture on a PCB can be an extremely tedious task. Depending on the PCB tool used, various arcane incantations
-may be necessary and even modestly complex images will slow down most PCB tools to a crawl.
-
-Gerbolyze solves this problem in a toolchain-agnostic way by directly vectorizing SVG vector and PNG or JPG bitmap files
-onto existing gerber layers. Gerbolyze processes any spec-compliant SVG and "gerbolyzes" SVG vector data into a Gerber
-spec-compliant form. Gerbolyze has been tested against both the leading open-source KiCAD toolchain and the
-industry-standard Altium Designer. Gerbolyze is written with performance in mind and will happily vectorize tens of
-thousands of primitives, generating tens of megabytes of gerber code without crapping itself. With gerbolyze you can
-finally be confident that your PCB fab's toolchain will fall over before yours does if you overdo it with the high-poly
-anime silkscreen.
-
-Gerbolyze is based on gerbonara_.
-
-.. image:: pics/process-overview.png
- :width: 800px
-
-.. contents::
-
-Tl;dr: Produce high-quality artistic PCBs in three easy steps!
---------------------------------------------------------------
-
-Gerbolyze works in three steps.
-
-1. Generate a scale-accurate template of the finished PCB from your CAD tool's gerber output:
-
- .. code::
-
- $ gerbolyze template --top template_top.svg [--bottom template_bottom.svg] my_gerber_dir
-
-2. Load the resulting template image Inkscape_ or another SVG editing program. Put your artwork on the appropriate SVG
- layer. Dark colors become filled gerber primitives, bright colors become unfilled primitives. You can directly put
- raster images (PNG/JPG) into this SVG as well, just position and scale them like everything else. SVG clips work for
- images, too. Masks are not supported.
-
-3. Vectorize the edited SVG template image drectly into the PCB's gerber files:
-
- .. code::
-
- $ gerbolyze paste --top template_top_edited.svg [--bottom ...] my_gerber_dir output_gerber_dir
-
-Quick Start Installation (Any Platform)
----------------------------------------
-
-.. code-block:: shell
-
- python -m pip install --user gerbolyze
-
-To uninstall, run
-
-.. code-block:: shell
-
- python -m pip uninstall gerbolyze gerbonara resvg-wasi svg-flatten-wasi
-
-To update, run
-
-.. code-block:: shell
-
- python -m pip install --user --upgrade --upgrade-strategy eager gerbolyze
-
-Speeding up gerbolyze using natively-built binaries
----------------------------------------------------
-
-This will install gerbolyze's binary dependency resvg and gerbolyze's svg-flatten utility as pre-built cross-platform
-WASM binaries. When you first run gerbolyze, it will take some time (~30s) to link these binaries for your system. The
-output is cached, so any future run is going to be fast.
-
-WASM is slower than natively-built binaries. To speed up gerbolyze, you can natively build its two binary dependencies:
-
-1. Install resvg natively using rust's cargo package manager: ``cargo install resvg``
-2. Install gerbolyze's svg-flatten utility natively. You can get pre-built binaries from gerbolyze's gitlab CI jobs `at
- this link `__ by clicking the three dots on the
- right next to the version you want. These pre-built binaries should work on any x86_64 linux since they are
- statically linked. You can also build svg-flatten yourself by running ``make`` inside the ``svg-flatten`` folder from
- a gerbolyze checkout.
-
-Gerbolyze will pick up these binaries when installed in your ``$PATH``. resvg is also picked up when it is installed by
-cargo in your home's ``~/.cargo``, even if it's not in your ``$PATH``. You can override the resvg, usvg or svg-flatten
-binary that gerbolyze uses by giving it the absoulute path to a binary in the ``$RESVG``, ``$USVG`` and ``$SVG_FLATTEN``
-environment variables.
-
-
-Build from source (any distro)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. code:: sh
-
- git clone --recurse-submodules https://git.jaseg.de/gerbolyze.git
- cd gerbolyze
-
- python3 -m venv
- source venv/bin/activate
- python3 setup.py install
-
-Features
---------
-
-Input on the left, output on the right.
-
-.. image:: pics/test_svg_readme_composited.png
- :width: 800px
-
-* Almost full SVG 1.1 static spec coverage (!)
-
- * Paths with beziers, self-intersections and holes
- * Strokes, even with dashes and markers
- * Pattern fills and strokes
- * Transformations and nested groups
- * Proper text rendering with support for complex text layout (e.g. Arabic)
- * elements via either built-in vectorizer or built-in halftone processor
- * (some) CSS
-
-* Writes Gerber, SVG or KiCAD S-Expression (``.kicad_mod``) formats
-* Can export from top/bottom SVGs to a whole gerber layer stack at once with filename autodetection
-* Can export SVGs to ``.kicad_mod`` files like svg2mod (but with full SVG support)
-* Beziers flattening with configurable tolerance using actual math!
-* Polygon intersection removal
-* Polygon hole removal (!)
-* Optionally vector-compositing of output: convert black/white/transparent image to black/transparent image
-* Renders SVG templates from input gerbers for accurate and easy scaling and positioning of artwork
-* layer masking with offset (e.g. all silk within 1mm of soldermask)
-* Can read gerbers from zip files
-* Limited SVG support for board outline layers (no fill/region support)
-* Dashed lines supported on board outline layers
-
-Gerbolyze is the end-to-end "paste this svg into these gerbers" command that handles all layers on both board sides at
-once. The heavy-duty computer geometry logic of gerbolyze is handled by the svg-flatten utility (``svg-flatten``
-directory). svg-flatten reads an SVG file and renders it into a variety of output formats. svg-flatten can be used like
-a variant of the popular svg2mod that supports all of SVG and handles arbitrary input ```` elements.
-
-Algorithm Overview
-------------------
-
-This is the algorithm gerbolyze uses to process a stack of gerbers.
-
-* Map input files to semantic layers by their filenames
-* For each layer:
-
- * load input gerber
- * Pass mask layers through ``gerbv`` for conversion to SVG
- * Pass mask layers SVG through ``svg-flatten --dilate``
- * Pass input SVG through ``svg-flatten --only-groups [layer]``
- * Overlay input gerber, mask and input svg
- * Write result to output gerber
-
-This is the algorithm svg-flatten uses to process an SVG.
-
-* pass input SVG through usvg_
-* iterate depth-first through resulting SVG.
-
- * for groups: apply transforms and clip and recurse
- * for images: Vectorize using selected vectorizer
- * for paths:
-
- * flatten path using Cairo
- * remove self-intersections using Clipper
- * if stroke is set: process dash, then offset using Clipper
- * apply pattern fills
- * clip to clip-path
- * remove holes using Clipper
-
-* for KiCAD S-Expression export: vector-composite results using CavalierContours: subtract each clear output primitive
- from all previous dark output primitives
-
-Web interface
--------------
-
-You can try gerbolyze online at https://dyna.kokoroyukuma.de/gerboweb
-
-The web interface does not expose all of gerbolyze's bells and whistles, but it allows you to simply paste a single SVG
-file on a board to try out gerbolyze. Upload your design on the web interface, then download the template for either the
-top or bottom side, and put your artwork on the appropriate layer of that template using Inkscape_. Finally, upload the
-modified template and let gerbolyze process your design.
-
-Command-line usage
-------------------
-.. _command_line_usage:
-
-Generate SVG template from Gerber files:
-
-.. code-block:: shell
-
- gerbolyze template [options] [--top|--bottom] input_dir_or.zip output.svg
-
-Render design from an SVG made with the template above into a set of gerber files:
-
-.. code-block:: shell
-
- gerbolyze paste [options] artwork.svg input_dir_or.zip output_dir_or.zip
-
-Use svg-flatten to convert an SVG file into Gerber or flattened SVG:
-
-.. code-block:: shell
-
- svg-flatten [options] --format [gerber|svg] [input_file.svg] [output_file]
-
-Use svg-flatten to convert an SVG file into the given layer of a KiCAD S-Expression (``.kicad_mod``) file:
-
-.. code-block:: shell
-
- svg-flatten [options] --format kicad --sexp-layer F.SilkS --sexp-mod-name My_Module [input_file.svg] [output_file]
-
-Use svg-flatten to convert an SVG file into a ``.kicad_mod`` with SVG layers fed into separate KiCAD layers based on
-their IDs like the popular ``svg2mod`` is doing:
-
-Note:
- Right now, the input SVG's layers must have *ids* that match up KiCAD's s-exp layer names. Note that when you name
- a layer in Inkscape that only sets a ``name`` attribute, but does not change the ID. In order to change the ID in
- Inkscape, you have to use Inkscape's "object properties" context menu function.
-
- Also note that svg-flatten expects the layer names KiCAD uses in their S-Expression format. These are *different* to
- the layer names KiCAD exposes in the UI (even though most of them match up!).
-
- For your convenience, there is an SVG template with all the right layer names and IDs located next to this README.
-
-.. code-block:: shell
-
- svg-flatten [options] --format kicad --sexp-mod-name My_Module [input_file.svg] [output_file]
-
-``gerbolyze template``
-~~~~~~~~~~~~~~~~~~~~~~
-
-Usage: ``gerbolyze template [OPTIONS] INPUT``
-
-Generate SVG template for gerbolyze paste from gerber files.
-
-INPUT may be a gerber file, directory of gerber files or zip file with gerber files. The output file contains a preview
-image of the input gerbers to allow you to position your artwork, as well as prepared Inkscape layers corresponding to
-each gerber layer. Simply place your artwork in this SVG template using Inkscape. Starting in v3.0, gerbolyze
-automatically keeps track of which board side (top or bottom) is contained in an SVG template.
-
-Options:
-********
-``--top | --bottom``
- Output top or bottom side template. This affects both the preview image and the prepared Inkscape layers.
-
-``--vector | --raster``
- Embed preview renders into output file as SVG vector graphics instead of rendering them to PNG bitmaps. The
- resulting preview may slow down your SVG editor.
-
-``--raster-dpi FLOAT``
- DPI for rastering preview
-
-``--bbox TEXT``
- Output file bounding box. Format: "w,h" to force [w] mm by [h] mm output canvas OR "x,y,w,h" to force [w] mm by [h]
- mm output canvas with its bottom left corner at the given input gerber coördinates.
-
-
-``gerbolyze paste``
-~~~~~~~~~~~~~~~~~~~
-(see `below `__)
-
-Usage: ``gerbolyze paste [OPTIONS] INPUT_GERBERS OVERLAY_SVG OUTPUT_GERBERS``
-
-Render vector data and raster images from SVG file into gerbers. The SVG input file can be generated using ``gerbolyze
-template`` and contains the name and board side of each layer. Note that for board outline layers, handling slightly
-differs from other layers as PCB fabs do not support filled Gerber regions on these layers.
-
-Options:
-********
-
-``--bbox TEXT``
- Output file bounding box. Format: "w,h" to force [w] mm by [h] mm output canvas OR "x,y,w,h" to force [w] mm by [h]
- mm output canvas with its bottom left corner at the given input gerber coördinates. This **must match the ``--bbox`` value given to
- template**!
-
-``--subtract TEXT``
- Use user subtraction script from argument (see `below `_)
-
-``--no-subtract``
- Disable subtraction (see `below `_)
-
-``--dilate FLOAT``
- Default dilation for subtraction operations in mm (see `below `_)
-
-``--trace-space FLOAT``
- Passed through to svg-flatten, see `below `__.
-
-``--vectorizer TEXT``
- Passed through to svg-flatten, see `its description below `__. Also have a look at `the examples below `_.
-
-``--vectorizer-map TEXT``
- Passed through to svg-flatten, see `below `__.
-
-``--exclude-groups TEXT``
- Passed through to svg-flatten, see `below `__.
-
-
-.. _outline_layers:
-
-Outline layers
-**************
-
-Outline layers require special handling since PCB fabs do not support filled G36/G37 polygons on these layers. The main
-difference between normal layers and outline layers is how strokes are handled. On outline layers, strokes are
-translated to normal Gerber draw commands (D01, D02 etc.) with an aperture set to the stroke's width instead of tracing
-them to G36/G37 filled regions. This means that on outline layers, SVG end caps and line join types do not work: All
-lines are redered with round joins and end caps.
-
-One exception from this are patterns, which work as expected for both fills and strokes with full support for joins and
-end caps.
-
-Dashed strokes are supported on outline layers and can be used to make easy mouse bites.
-
-.. _subtraction_script:
-
-Subtraction scripts
-*******************
-
-.. image:: pics/subtract_example.png
- :width: 800px
-
-Subtraction scripts tell ``gerbolyze paste`` to remove an area around certain input layers to from an overlay layer.
-When a input layer is given in the subtraction script, gerbolyze will dilate (extend outwards) everything on this input
-layer and remove it from the target overlay layer. By default, Gerbolyze subtracts the mask layer from the silk layer to
-make sure there are no silk primitives that overlap bare copper, and subtracts each input layer from its corresponding
-overlay to make sure the two do not overlap. In the picture above you can see both at work: The overlay contains
-halftone primitives all over the place. The subtraction script has cut out an area around all pads (mask layer) and all
-existing silkscreen. You can turn off this behavior by passing ``--no-subtract`` or pass your own "script".
-
-The syntax of these scripts is:
-
-.. code-block::
-
- {target layer} -= {source layer} {dilation} [; ...]
-
-The target layer must be ``out.{layer name}`` and the source layer ``in.{layer name}``. The layer names are gerbolyze's
-internal layer names, i.e.: ``paste, silk, mask, copper, outline, drill``
-
-The dilation value is optional, but can be a float with a leading ``+`` or ``-``. If given, before subtraction the
-source layer's features will be extended by that many mm. If not given, the dilation defaults to the value given by
-``--dilate`` if given or 0.1 mm otherwise. To disable dilation, simply pass ``+0`` here.
-
-Multiple commands can be separated by semicolons ``;`` or line breaks.
-
-The default subtraction script is:
-
-.. code-block::
-
- out.silk -= in.mask
- out.silk -= in.silk+0.5
- out.mask -= in.mask+0.5
- out.copper -= in.copper+0.5
-
-.. _svg_flatten:
-
-``svg-flatten``
-~~~~~~~~~~~~~~~
-
-Usage: ``svg-flatten [OPTIONS]... [INPUT_FILE] [OUTPUT_FILE]``
-
-Specify ``-`` for stdin/stdout.
-
-Options:
-********
-
-``-h, --help``
- Print help and exit
-
-``-v, --version``
- Print version and exit
-
-``-o, --format``
- Output format. Supported: gerber, gerber-outline (for board outline layers), svg, s-exp (KiCAD S-Expression)
-
-``-p, --precision``
- Number of decimal places use for exported coordinates (gerber: 1-9, SVG: >=0). Note that not all gerber viewers are
- happy with too many digits. 5 or 6 is a reasonable choice.
-
-``--clear-color``
- SVG color to use in SVG output for "clear" areas (default: white)
-
-``--dark-color``
- SVG color to use in SVG output for "dark" areas (default: black)
-
-``-f, --flip-gerber-polarity``
- Flip polarity of all output gerber primitives for --format gerber.
-
-``-d, --trace-space``
- Minimum feature size of elements in vectorized graphics (trace/space) in mm. Default: 0.1mm.
-
-``--no-header``
- Do not export output format header/footer, only export the primitives themselves
-
-``--flatten``
- Flatten output so it only consists of non-overlapping white polygons. This perform composition at the vector level.
- Potentially slow. This defaults to on when using KiCAD S-Exp export because KiCAD does not know polarity or colors.
-
-``--no-flatten``
- Disable automatic flattening for KiCAD S-Exp export
-
-``--dilate``
- Dilate output gerber primitives by this amount in mm. Used for masking out other layers.
-
-``-g, --only-groups``
- Comma-separated list of group IDs to export.
-
-``-b, --vectorizer``
- Vectorizer to use for bitmap images. One of poisson-disc (default), hex-grid, square-grid, binary-contours,
- dev-null. Have a look at `the examples below `_.
-
-``--vectorizer-map``
- Map from image element id to vectorizer. Overrides --vectorizer. Format: id1=vectorizer,id2=vectorizer,...
-
- You can use this to set a certain vectorizer for specific images, e.g. if you want to use both halftone
- vectorization and contour tracing in the same SVG. Note that you can set an ```` element's SVG ID from within
- Inkscape though the context menu's Object Properties tool.
-
-``--force-svg``
- Force SVG input irrespective of file name
-
-``--force-png``
- Force bitmap graphics input irrespective of file name
-
-``-s, --size``
- Bitmap mode only: Physical size of output image in mm. Format: 12.34x56.78
-
-``--sexp-mod-name``
- Module name for KiCAD S-Exp output. This is a mandatory argument if using S-Exp output.
-
-``--sexp-layer``
- Layer for KiCAD S-Exp output. Defaults to auto-detect layers from SVG layer/top-level group IDs. If given, SVG
- groups and layers are completely ignored and everything is simply vectorized into this layer, though you cna still
- use ``-g`` for group selection.
-
-``-a, --preserve-aspect-ratio``
- Bitmap mode only: Preserve aspect ratio of image. Allowed values are meet, slice. Can also parse full SVG
- preserveAspectRatio syntax.
-
-``--no-usvg``
- Do not preprocess input using usvg (do not use unless you know *exactly* what you're doing)
-
-``--usvg-dpi``
- Passed through to usvg's --dpi, in case the input file has different ideas of DPI than usvg has.
-
-``--scale``
- Scale input svg lengths by this factor (-o gerber only).
-
-``-e, --exclude-groups``
- Comma-separated list of group IDs to exclude from export. Takes precedence over --only-groups.
-
-.. _vectorization:
-
-Gerbolyze image vectorization
------------------------------
-
-Gerbolyze has two built-in strategies to translate pixel images into vector images. One is its built-in halftone
-processor that tries to approximate grayscale. The other is its built-in binary vectorizer that traces contours in
-black-and-white images. Below are examples for the four options.
-
-The vectorizers can be used in isolation through ``svg-flatten`` with either an SVG input that contains an image or a
-PNG/JPG input.
-
-The vectorizer can be controlled globally using the ``--vectorizer`` flag in both ``gerbolyze`` and ``svg-flatten``. It
-can also be set on a per-image basis in both using ``--vectorizer-map [image svg id]=[option]["," ...]``.
-
-.. for f in vec_*.png; convert -background white -gravity center $f -resize 500x500 -extent 500x500 (basename -s .png $f)-square.png; end
-.. for vec in hexgrid square poisson contours; convert vec_"$vec"_whole-square.png vec_"$vec"_detail-square.png -background transparent -splice 25x0+0+0 +append -chop 25x0+0+0 vec_"$vec"_composited.png; end
-
-``--vectorizer poisson-disc`` (the default)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. image:: pics/vec_poisson_composited.png
- :width: 800px
-
-``--vectorizer hex-grid``
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. image:: pics/vec_hexgrid_composited.png
- :width: 800px
-
-``--vectorizer square-grid``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. image:: pics/vec_square_composited.png
- :width: 800px
-
-``--vectorizer binary-contours``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. image:: pics/vec_contours_composited.png
- :width: 800px
-
-The binary contours vectorizer requires a black-and-white binary input image. As you can see, like every bitmap tracer
-it will produce some artifacts. For artistic input this is usually not too bad as long as the input data is
-high-resolution. Antialiased edges in the input image are not only OK, they may even help with an accurate
-vectorization.
-
-GIMP halftone preprocessing guide
----------------------------------
-
-Gerbolyze has its own built-in halftone processor, but you can also use the high-quality "newsprint" filter built into
-GIMP_ instead if you like. This section will guide you through this. The PNG you get out of this can then be fed into
-gerbolyze using ``--vectorizer binary-contours``.
-
-1 Import your desired artwork
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Though anime or manga pictures are highly recommended, you can use any image including photographs. Be careful to select
-a picture with comparatively low detail that remains recognizable at very low resolution. While working on a screen this
-is hard to vizualize, but the grain resulting from the low resolution of a PCB's silkscreen is quite coarse.
-
-.. image:: screenshots/02import02.png
- :width: 800px
-
-2 Convert the image to grayscale
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. image:: screenshots/06grayscale.png
- :width: 800px
-
-3 Fine-tune the image's contrast
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To look well on the PCB, contrast is critical. If your source image is in color, you may have lost some contrast during
-grayscale conversion. Now is the time to retouch that using the GIMP's color curve tool.
-
-When using the GIMP's newsprint filter, bright grays close to white and dark grays close to black will cause very small
-dots that might be beyond your PCB manufacturer's maximum resolution. To control this case, add small steps at the ends
-of the grayscale value curve as shown (exaggerated) in the picture below. These steps saturate very bright grays to
-white and very dark grays to black while preserving the values in the middle.
-
-.. image:: screenshots/08curve_cut.png
- :width: 800px
-
-4 Retouch details
-~~~~~~~~~~~~~~~~~
-
-Therer might be small details that don't look right yet, such as the image's background color or small highlights that
-merge into the background now. You can manually change the color of any detail now using the GIMP's flood-fill tool.
-
-If you don't want the image's background to show up on the final PCB at all, just make it black.
-
-Particularly on low-resolution source images it may make sense to apply a blur with a radius similar to the following
-newsprint filter's cell size (10px) to smooth out the dot pattern generated by the newsprint filter.
-
-.. image:: screenshots/09retouch.png
- :width: 800px
-
-In the following example, I retouched the highlights in the hair of the character in the picture to make them completely
-white instead of light-gray, so they still stand out nicely in the finished picture.
-
-.. image:: screenshots/10retouched.png
- :width: 800px
-
-5 Run the newsprint filter
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Now, run the GIMP's newsprint filter, under filters, distorts, newsprint.
-
-The first important settings is the spot size, which should be larger than your PCB's minimum detail size (about 10px
-with ``gerbolyze render`` default settings for good-quality silkscreen). In general the cheap and fast standard option of chinese PCB houses will require a larger detail size, but when you order specialty options like large size, 4-layer or non-green color along with a longer turnaround time you'll get much better-quality silk screen.
-
-The second important setting is oversampling, which should be set to four or slightly higher. This improves the result
-of the edge reconstruction of ``gerbolyze vectorize``.
-
-.. image:: screenshots/11newsprint.png
- :width: 800px
-
-The following are examples on the detail resulting from the newsprint filter.
-
-.. image:: screenshots/12newsprint.png
- :width: 800px
-
-6 Export the image for use with ``gerbolyze vectorize``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Simply export the image as a PNG file. Below are some pictures of the output ``gerbolyze vectorize`` produced for this
-example.
-
-.. image:: screenshots/14result_cut.png
- :width: 800px
-
-.. image:: screenshots/15result_cut.png
- :width: 800px
-
-Manufacturing Considerations
-----------------------------
-
-The main consideration when designing artwork for PCB processes is the processes' trace/space design rule. The two
-things you can do here is one, to be creative with graphical parts of the design and avoid extremely narrow lines,
-wedges or other thin features that will not come out well. Number two is to keep detail in raster images several times
-larger than the manufacturing processes native capability. For example, to target a trace/space design rule of 100 µm,
-the smallest detail in embedded raster graphics should not be much below 1mm.
-
-Gerbolyze's halftone vectorizers have built-in support for trace/space design rules. While they can still produce small
-artifacts that violate these rules, their output should be close enough to satifsy board houses and close enough for the
-result to look good. The way gerbolyze does this is to clip the halftone cell's values to zero whenevery they get too
-small, and to forcefully split or merge two neighboring cells when they get too close. While this process introduces
-slight steps at the top and bottom of grayscale response, for most inputs these are not noticeable.
-
-On the other hand, for SVG vector elements as well as for traced raster images, Gerbolyze cannot help with these design
-rules. There is no heuristic that would allow Gerbolyze to non-destructively "fix" a design here, so all that's on the
-roadmap here is to eventually include a gerber-level design rule checker.
-
-As far as board houses go, I have made good experiences with the popular Chinese board houses. In my experience, JLC
-will just produce whatever you send them with little fucks being given about design rule adherence or validity of the
-input gerbers. This is great if you just want artistic circuit boards without much of a hassle, and you don't care if
-they come out exactly as you imagined. The worst I've had happen was when an older version of gerbolyze generated
-polygons with holes assuming standard fill-rule processing. The in the board house's online gerber viewer things looked
-fine, and neither did they complain during file review. However, the resulting boards looked completely wrong because
-all the dark halftones were missing.
-
-PCBWay on the other hand has a much more rigurous file review process. They will complain when you throw
-illegal garbage gerbers at them, and they will helpfully guide you through your design rule violations. In this way you
-get much more of a professional service from them and for designs that have to be functional their higher level of
-scrutiny definitely is a good thing. For the design you saw in the first picture in this article, I ended up begging
-them to just plot my files if it doesn't physically break their machines and to their credit, while they seemed unhappy
-about it they did it and the result looks absolutely stunning.
-
-PCBWay is a bit more expensive on their lowest-end offering than JLC, but I found that for anything else (large boards,
-multi-layer, gold plating etc.) their prices match. PCBWay offers a much broader range of manufacturing options such as
-flexible circuit boards, multi-layer boards, thick or thin substrates and high-temperature substrates.
-
-When in doubt about how your design is going to come out on the board, do not hesitate to contact your board house. Most
-of the end customer-facing online PCB services have a number of different factories that do a number of different
-fabrication processes for them depending on order parameters. Places like PCBWay have exceptional quality control and
-good customer service, but that is mostly focused on the technical aspects of the PCB. If you rely on visual aspects
-like silkscreen uniformity or solder mask color that is a strong no concern to everyone else in the electronics
-industry, you may find significant variations between manufacturers or even between orders with the same manufacturer
-and you may encounter challenges communicating your requirements.
-
-Limitations
------------
-
-SVG raster features
-~~~~~~~~~~~~~~~~~~~
-
-Currently, SVG masks and filters are not supported. Though SVG is marketed as a "vector graphics format", these two
-features are really raster primitives that all SVG viewers perform at the pixel level after rasterization. Since
-supporting these would likely not end up looking like what you want, it is not a planned feature. If you need masks or
-filters, simply export the relevant parts of the SVG as a PNG then include that in your template.
-
-Gerber pass-through
-~~~~~~~~~~~~~~~~~~~
-
-Since gerbolyze has to composite your input gerbers with its own output, it has to fully parse and re-serialize them.
-gerbolyze gerbonara_ for all its gerber parsing needs. Thus, gerbonara will interpret your gerbers and output will be in
-gerbonara's gerber "dialect". If you find a corner case where this does not work and the output looks wrong, please file
-a bug report with an example file on the gerbonara_ bug tracker. *Always* check the output files for errors before
-submitting them to production.
-
-Gerbolyze is provided without any warranty, but still please open an issue or `send me an email
-`__ if you find any errors or inconsistencies.
-
-Trace/Space design rule adherence
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-While the grayscale halftone vectorizers do a reasonable job adhering to a given trace/space design rule, they can still
-produce small parts of output that violate it. For the contour vectorizer as well as for all SVG primitives, you are
-responsible for adhering to design rules yourself as there is no algorithm that gerboyze could use to "fix" its input.
-
-A design rule checker is planned as a future addition to gerbolyze, but is not yet part of it. If in doubt, talk to your
-fab and consider doing a test run of your design before ordering assembled boards ;)
-
-Gallery
--------
-
-.. image:: pics/sample3.jpg
- :width: 400px
-
-For a demonstration of ``gerbolyze convert``, check out the `Gerbolyze Protoboard Index`_, where you can download gerber
-files for over 7.000 SMD and THT protoboard layouts.
-
-Licensing
----------
-
-This tool is licensed under the rather radical AGPLv3 license. Briefly, this means that you have to provide users of a
-webapp using this tool in the backend with this tool's source.
-
-I get that some people have issues with the AGPL. In case this license prevents you from using this software, please
-send me `an email `__ and I can grant you an exception. I want this software to be useful to as
-many people as possible and I wouldn't want the license to be a hurdle to anyone. OTOH I see a danger of some cheap
-board house just integrating a fork into their webpage without providing their changes back upstream, and I want to
-avoid that so the default license is still AGPL.
-
-.. _usvg: https://github.com/RazrFalcon/resvg
-.. _Inkscape: https://inkscape.org/
-.. _pcb-tools: https://github.com/curtacircuitos/pcb-tools
-.. _pcb-tools-extension: https://github.com/opiopan/pcb-tools-extension
-.. _GIMP: https://gimp.org/
-.. _gerbonara: https://gitlab.com/gerbolyze/gerbonara
-.. _`Gerbolyze Protoboard Index`: https://dyna.kokoroyukuma.de/protos/
-
diff --git a/content/projects/gerbolyze/index.rst b/content/projects/gerbolyze/index.rst
deleted file mode 100644
index 0f0ed47..0000000
--- a/content/projects/gerbolyze/index.rst
+++ /dev/null
@@ -1,17 +0,0 @@
----
-title: "Gerbolyze"
-external_links:
- - name: Sources
- url: "https://git.jaseg.de/gerbolyze.git"
- - name: Issues
- url: "https://github.com/jaseg/gerbolyze/issues"
- - name: Docs
- url: "https://gerbolyze.gitlab.io/gerbolyze"
- - name: PyPI
- url: "https://pypi.org/project/gerbolyze"
-summary: >
- Gerbolyze is a tool that allows the modification of Gerber PCB artwork with a vector graphics editor like Inkscape.
- Gerbolyze directly converts between SVG and Gerber, and accurately reproduces details that other tools can not.
----
-
-.. include:: content/projects/gerbolyze/README.rst
diff --git a/content/projects/gerbonara/index.rst b/content/projects/gerbonara/index.rst
deleted file mode 100644
index 6069351..0000000
--- a/content/projects/gerbonara/index.rst
+++ /dev/null
@@ -1,141 +0,0 @@
----
-title: "Gerbonara"
-external_links:
- - name: Sources
- url: "https://git.jaseg.de/gerbonara.git"
- - name: Issues
- url: "https://gitlab.com/gerbolyze/gerbonara/issues"
- - name: Docs
- url: "https://gerbolyze.gitlab.io/gerbonara"
- - name: PyPI
- url: "https://pypi.org/project/gerbonara"
-summary: >
- Gerbonara is a user-friendly, powerful tool for reading, writing, modification and rendering of Gerber PCB artwork
- from the command line or from Python code. Gerbonara supports the Gerber dialects of all industry-standard EDA
- tools.
----
-
-Gerbonara is a library to read, modify and write PCB manufacturing files such as Gerber, Excellon and IPC-356 through a
-pythonic API. Gerbonara can open a folder of manufacturing files, and parse file names and metadata to figure out which
-file contains what. Gerbonara is tested using an extensive library of real-world example files from CAD tools including
-KiCAD, Altium, Eagle, Allegro, gEDA, Fritzing, Siemens/Mentor Graphics PADS, and Target3001!.
-
-Gerbonara's API is built on two principles:
-
-**Meaningful, object-oriented API**
- Gerbonara abstracts away the details of the underlying file format such as tool indices, coordinate notation and
- graphical state, and presents meaningful "graphical objects" such as a `primitives.Line`,
- `primitives.Arc`, or `Region` through its API. These objects can be easily created,
- manipulated or deleted from code without breaking anything else. You can even copy graphical objects between files,
- and Gerbonara will automatically convert coordinate format, units etc. for you. `GerberFile` and
- `ExcellonFile` use the same types of `graphic objects `, so objects can be directly
- copied between file types without conversion.
-
-**Unit-safety**
- Gerbonara embeds physical `LengthUnit` information in all objects. The high-level API such as
- `LayerStack.merge` or `GerberFile.offset` accepts arguments with an explicitly given unit and
- automatically converts them as needed. Objects can be copied between `GerberFile` instances and unit
- conversion will be handled transparently in the background.
-
-Gerbonara was started as an extensive refactoring of the pcb-tools_ and pcb-tools-extension_ packages. Both of these
-have statement-based APIs, that is, they parse input files into one python object for every line in the file. This means
-that when saving files they can recreate the input file almost byte by byte, but manipulating a file by changing
-statements without breaking things is *hard*.
-
-Gerbonara powers gerbolyze_, a tool for converting SVG_ vector graphics files into Gerber, and embedding SVG_ into
-existing Gerber files exported from a normal PCB tool for artistic purposes.
-
-Features
-========
-
- * File I/O
- * Gerber, Excellon (drill file), IPC-356 (netlist) read and write
- * supports file-level operations: offset, rotate, merge for all file types
- * Modification API (`GraphicObject`)
- * Rendering API (`GraphicPrimitive`)
- * SVG export
- * Full aperture macro support, including transformations (offset, rotation)
-
-Quick Start
-===========
-
-First, install gerbonara from PyPI using pip:
-
-.. code-block:: shell
-
- pip install --user gerbonara
-
-Then, you are ready to read and write gerber files:
-
-.. code-block:: python
-
- from gerbonara import LayerStack
-
- stack = LayerStack.from_directory('output/gerber')
- w, h = stack.outline.size('mm')
- print(f'Board size is {w:.1f} mm x {h:.1f} mm')
-
-Command-Line Interface
-======================
-
-Gerbonara comes with a `built-in command-line interface` that has functions for analyzing, rendering,
-modifying, and merging Gerber files. To access it, use either the ``gerbonara`` command that is part of the python
-package, or run ``python -m gerbonara`` For a list of functions or help on their usage, you can use:
-
-.. code:: console
-
- $ python -m gerbonara --help
- [...]
- $ python -m gerbonara render --help
-
-Development
-===========
-
-Gerbonara is developed on Gitlab under the gerbolyze org:
-
-https://gitlab.com/gerbolyze/gerbonara/
-
-A mirror of the repository can be found at:
-
-https://git.jaseg.de/gerbonara
-
-Our issue tracker is also on Gitlab:
-
-https://gitlab.com/gerbolyze/gerbonara/-/issues
-
-The documentation can be found at gitlab:
-
-https://gerbolyze.gitlab.io/gerbonara/
-
-With Gerbonara, we aim to support as many different format variants as possible. If you have a file that Gerbonara can't
-open, please file an issue on our issue tracker. Even if Gerbonara can open all your files, for regression testing we
-are very interested in example files generated by any CAD or CAM tool that is not already on the list of supported
-tools.
-
-Supported CAD Tools
-===================
-
-Compatibility with the output of these CAD tools is tested as part of our test suite using example files generated by
-these tools. Note that not all of these tools come with default Gerber file naming rules, so YMMV if your Gerbers use
-some non-standard naming convention.
-
- * Allegro
- * Altium
- * Diptrace
- * Eagle
- * EasyEDA
- * Fritzing
- * gEDA
- * KiCAD
- * pcb-rnd
- * Siemens / Mentor Graphics Xpedition
- * Siemens PADS
- * Target 3001!
- * Upverter
- * Zuken CR-8000
-
-.. _pcb-tools: https://github.com/opiopan/pcb-tools-extension
-.. _pcb-tools-extension: https://github.com/curtacircuitos/pcb-tools/issues
-.. _gerbolyze: https://github.com/jaseg/gerbolyze
-.. _SVG: https://en.wikipedia.org/wiki/Scalable_Vector_Graphics
-
diff --git a/content/projects/kicoil/index.rst b/content/projects/kicoil/index.rst
deleted file mode 100644
index 1590449..0000000
--- a/content/projects/kicoil/index.rst
+++ /dev/null
@@ -1,23 +0,0 @@
----
-title: "kicoil"
-date: 2025-12-28T23:42:00+02:00
-external_links:
- - name: Sources
- url: "https://git.jaseg.de/kicoil.git"
- - name: Issue tracker
- url: "https://codeberg.org/jaseg/kicoil/issues/"
-summary: >
- Planar inductor supporting arbitrary shapes of spiral coils, toroidal coils, and hybrids
----
-
-kicoil
-======
-
-.. image:: blahaj-demo.png
- :width: 400px
-
-KiCoil generates planar inductors as footprints. It supports arbitrary shapes with presets such as circles, rectangles,
-circular sectors, and SVG import for arbitrary shapes. It can generate spiral and toroid inductors as well as arbitrary
-intermediates between spiral and toroid inductors. By playing around with this, you can create inductors that have lower
-parasitics and higher self-resonant frequency than standard multilayer spiral inductors.
-
diff --git a/content/projects/kimesh/README.rst b/content/projects/kimesh/README.rst
deleted file mode 100644
index 2c6ac45..0000000
--- a/content/projects/kimesh/README.rst
+++ /dev/null
@@ -1,64 +0,0 @@
-KiCAD Security Mesh Generator
-=============================
-
-.. image:: kicad-mesh-result-large.png
- :width: 800
- :alt: A screenshot of KiCAD showing a PCB security mesh generated by KiMesh.
-
-This repository contains KiMesh, a KiCAD pcbnew plugin that generates security mesh traces on a KiCAD PCB.
-
-Installation
-------------
-
-KiMesh has two parts: The pcbnew plugin that generates the traces, and the magic footprints that you use to tell the
-plugin how many traces of which dimensions to generate where.
-
-To install the plugin, copy the "kimesh" directory into your KiCAD installation's scripting plugin folder. Usually, this
-is `~/.config/kicad/scripting/plugins/` for KiCAD stable installations or
-`~/.config/kicad/[major version].99/scripting/plugins/` for nightly builds. On Windows, these paths can be found in your
-user account's `AppData/Roaming` directory.
-
-To install the footprint libraries, the easiest way is to download the library zip from the project's repo
-`[link] `__, unpack it to your project folder, and
-add the unpacked libraries as project-specific libraries through KiCad's library management thingy.
-
-Usage
------
-
-To work, KiMesh requires four things:
-
-1. An area free of other features such as footprints or traces where to generate the mesh.
-2. One or more "graphic polygons" on a drawing layer that specify the area of the mesh.
-3. A closed board outline on the `Edge.Cuts` layer.
-4. One of the magic footprints from the KiMesh anchor library that defines the mesh's number of wires and their
- dimensions, and tells KiMesh where to start the mesh and in which direction to start it.
-
-You can choose any layer for the outline polygons, such as the pre-defined `User.Eco1` or `User.X` layers, or you can
-define your own. You just have to select that layer later in KiMesh's generator dialog. Note that KiMesh only processes
-graphic polygons on that layer, and ignores other shapes such as lines, rectangles or circles. You can still use other
-shapes, but you have to manually convert them to polygons before running KiMesh. To convert other shapes to
-a polygon, select them, open the context menu with a right click, then choose the "Create from Selection 🞂 Create
-Polygon from Selection" entry. For rectangles or circles, use the "Use Centerlines" option. For lines or arcs, use the
-"Create bounding hull" option.
-
-Place the magic anchor footprint on the outline of the mesh's shape polygons so that you have space to route out the
-traces. The anchor footprint has an arrow on the `F.Fab` layer that indicates in which direction the mesh will be
-generated.
-
-I recommend adding the mesh to the schematic with one of KiCad's built-in `Connector_02xN_Top_Bottom` footprints. For a
-mesh with k wires, choose a symbol with two rows of 2k pins each. For instance, for two mesh wires, choose
-`Connector_02x04_Top_bottom`. Then assign one of the magic footprints to that symbol. To avoid DRC warnings, join the
-two halves of the mesh on the output side of the footprint. That's the right side in default orientation, where the
-higher-numbered pins are.
-
-.. image:: screenshot-mesh-schematic.png
- :width: 800
- :alt: A screenshot of the connector footprint mentioned in the previous paragraph, shown conencted up as described in
- KiCad's schematic editor.
-
-Theory of operation
--------------------
-
-I have published a post_ on my blog on the theory of operation behind KiMesh.
-
-.. _post: https://jaseg.de/blog/kicad-mesh-plugin/
diff --git a/content/projects/kimesh/index.rst b/content/projects/kimesh/index.rst
deleted file mode 100644
index 8613e3c..0000000
--- a/content/projects/kimesh/index.rst
+++ /dev/null
@@ -1,16 +0,0 @@
----
-title: "KiMesh"
-date: 2023-10-04T23:42:00+02:00
-external_links:
- - name: Sources
- url: "https://git.jaseg.de/kimesh.git"
- - name: Issues
- url: "https://github.com/jaseg/kimesh/issues"
- - name: Docs
- url: "https://jaseg.de/projects/kimesh"
-summary: >
- KiMesh is a KiCad plugin that automatically creates security meshes with two or traces covering an
- arbitrarily-shaped outline on the board.
----
-
-.. include:: content/projects/kimesh/README.rst
diff --git a/content/projects/lolcat-c/index.rst b/content/projects/lolcat-c/index.rst
deleted file mode 100644
index 858f6de..0000000
--- a/content/projects/lolcat-c/index.rst
+++ /dev/null
@@ -1,107 +0,0 @@
----
-title: "lolcat-c"
-external_links:
- - name: Sources
- url: "https://git.jaseg.de/lolcat.git"
- - name: Github
- url: "https://github.com/jaseg/lolcat"
- - name: Issues
- url: "https://github.com/jaseg/lolcat/issues"
-summary: >
- lolcat-c is a small, high-performance re-implementation of the
- `lolcat `__
- rainbow cat utility. lolcat-c is meant as a lolcat that you can actually use in production. It is fast, not slowing
- down whatever you pipe through it, and it robustly handles real-world terminal output including escape sequences.
----
-
-What?
-=====
-
-.. image:: LOLCat-Rainbow.jpg
-
-Screenshot
-==========
-
-.. image:: screenshot.png
-
-.. image:: sl.gif
-
-Installation
-============
-
-Archlinux
----------
-
-There's an `AUR package `__:
-
-.. code:: sh
-
- $ git clone https://aur.archlinux.org/packages/c-lolcat
- $ cd c-lolcat
- $ makepkg -csi
-
-Fedora
-------
-
-.. code:: sh
-
- $ dnf install lolcat
-
-Ubuntu (Snap)
--------------
-
-See `this awesome blog post by a kind person from the internet `__:
-
-.. code:: sh
-
- $ snap install lolcat-c
-
-Mac
----
-
-Build loclcat with:
-
-.. code:: sh
-
- $ make lolcat
-
-...and put the resulting binary at a place of your choice.
-
-Others
-------
-
-.. code:: sh
-
- $ make && sudo make install
-
-Why?
-====
-
-This `lolcat` clone is an attempt to reduce the world's carbon dioxide emissions by optimizing inefficient code. It's
->10x as fast and <0.1% as large as the original one.
-
-.. code:: sh
-
- newton~/d/lolcat <3 dmesg>foo
- newton~/d/lolcat <3 time upstream/bin/lolcat foo
- 13.51user 1.34system 0:15.99elapsed 92%CPU (0avgtext+0avgdata 10864maxresident)k
- 0inputs+0outputs (0major+1716minor)pagefaults 0swaps
- newton~/d/lolcat <3 time ./lolcat foo
- 0.02user 0.00system 0:00.09elapsed 34%CPU (0avgtext+0avgdata 1936maxresident)k
- 0inputs+0outputs (0major+117minor)pagefaults 0swaps
-
-Bonus comparison with `python-lolcat `__:
-
-.. code:: sh
-
- newton~/d/lolcat <3 dmesg>foo
- $ time python-lolcat foo
- 12.27user 0.00system 0:12.29elapsed 99%CPU (0avgtext+0avgdata 11484maxresident)k
- 0inputs+0outputs (0major+1627minor)pagefaults 0swaps
- $ time c-lolcat foo
- 0.29user 0.00system 0:00.30elapsed 98%CPU (0avgtext+0avgdata 468maxresident)k
- 0inputs+0outputs (0major+21minor)pagefaults 0swaps
-
-(Read: `c-lolcat << python-lolcat << ruby-lolcat`)
-
-
diff --git a/content/projects/python-mpv/README.rst b/content/projects/python-mpv/README.rst
deleted file mode 100644
index 26815d1..0000000
--- a/content/projects/python-mpv/README.rst
+++ /dev/null
@@ -1,401 +0,0 @@
-.. vim: tw=120 sw=4 et
-
-python-mpv is a ctypes-based python interface to the mpv media player. It gives you more or less full control of all
-features of the player, just as the lua interface does.
-
-Installation
-------------
-
-.. code:: bash
-
- pip install mpv
-
-
-...though you can also realistically just copy `mpv.py`_ into your project as it's all nicely contained in one file.
-
-Requirements
-~~~~~~~~~~~~
-
-libmpv
-......
-``libmpv.so`` either locally (in your current working directory) or somewhere in your system library search path. This
-module is somewhat lenient as far as ``libmpv`` versions are concerned but since ``libmpv`` is changing quite frequently
-you'll only get all the newest features when using an up-to-date version of this module. The unit tests for this module
-do some basic automatic version compatibility checks. If you discover anything missing here, please open an `issue`_ or
-submit a `pull request`_ on github.
-
-On Windows you can place libmpv anywhere in your ``%PATH%`` (e.g. next to ``python.exe``) or next to this module's
-``mpv.py``. Before falling back to looking in the mpv module's directory, python-mpv uses the DLL search order built
-into ctypes, which is different to the one Windows uses internally. Consult `this stackoverflow post
-`__ for details.
-
-Python >= 3.7 (officially)
-..........................
-The ``main`` branch officially only supports recent python releases (3.5 onwards), but there is the somewhat outdated
-but functional `py2compat branch`_ providing Python 2 compatibility.
-
-.. _`py2compat branch`: https://github.com/jaseg/python-mpv/tree/py2compat
-.. _`issue`: https://github.com/jaseg/python-mpv/issues
-.. _`pull request`: https://github.com/jaseg/python-mpv/pulls
-
-Supported Platforms
-...................
-
-**Linux**, **Windows** and **OSX** all seem to work mostly fine. For some notes on the installation on Windows see
-`this comment`__. Shared library handling is quite bad on windows, so expect some pain there. On OSX there seems to be
-some bug int the event logic. See `issue 36`_ and `issue 61`_ for details. Creating a pyQT window and having mpv draw
-into it seems to be a workaround (about 10loc), but in case you want this fixed please weigh in on the issue tracker
-since right now there is not many OSX users.
-
-.. __: https://github.com/jaseg/python-mpv/issues/60#issuecomment-352719773
-.. _`issue 61`: https://github.com/jaseg/python-mpv/issues/61
-.. _`issue 36`: https://github.com/jaseg/python-mpv/issues/36
-
-Usage
------
-
-.. code:: python
-
- import mpv
- player = mpv.MPV(ytdl=True)
- player.play('https://youtu.be/DOmdB7D-pUU')
- player.wait_for_playback()
-
-python-mpv mostly exposes mpv's built-in API to python, adding only some porcelain on top. Most "`input commands `_" are mapped to methods of the MPV class. Check out these methods and their docstrings in `the source `__ for things you can do. Additional controls and status information are exposed through `MPV properties `_. These can be accessed like ``player.metadata``, ``player.fullscreen`` and ``player.loop_playlist``.
-
-Threading
-~~~~~~~~~
-
-The ``mpv`` module starts one thread for event handling, since MPV sends events that must be processed quickly. The
-event queue has a fixed maxmimum size and some operations can cause a large number of events to be sent.
-
-If you want to handle threading yourself, you can pass ``start_event_thread=False`` to the ``MPV`` constructor and
-manually call the ``MPV`` object's ``_loop`` function. If you have some strong need to not use threads and use some
-external event loop (such as asyncio) instead you can do that, too with some work. The API of the backend C ``libmpv``
-has a function for producing a sort of event file descriptor for a handle. You can use that to produce a file descriptor
-that can be passed to an event loop to tell it to wake up the python-mpv event handler on every incoming event.
-
-All API functions are thread-safe. If one is not, please file an issue on github.
-
-Advanced Usage
-~~~~~~~~~~~~~~
-
-Logging, Properties, Python Key Bindings, Screenshots and youtube-dl
-....................................................................
-
-.. code:: python
-
- #!/usr/bin/env python3
- import mpv
-
- def my_log(loglevel, component, message):
- print('[{}] {}: {}'.format(loglevel, component, message))
-
- player = mpv.MPV(log_handler=my_log, ytdl=True, input_default_bindings=True, input_vo_keyboard=True)
-
- # Property access, these can be changed at runtime
- @player.property_observer('time-pos')
- def time_observer(_name, value):
- # Here, _value is either None if nothing is playing or a float containing
- # fractional seconds since the beginning of the file.
- print('Now playing at {:.2f}s'.format(value))
-
- player.fullscreen = True
- player.loop_playlist = 'inf'
- # Option access, in general these require the core to reinitialize
- player['vo'] = 'gpu'
-
- @player.on_key_press('q')
- def my_q_binding():
- print('THERE IS NO ESCAPE')
-
- @player.on_key_press('s')
- def my_s_binding():
- pillow_img = player.screenshot_raw()
- pillow_img.save('screenshot.png')
-
- player.play('https://youtu.be/DLzxrzFCyOs')
- player.wait_for_playback()
-
- del player
-
-Skipping silence using libav filters
-....................................
-
-The following code uses the libav silencedetect filter to skip silence at the beginning of a file. It works by loading
-the filter, then parsing its output from mpv's log. Thanks to Sean DeNigris on github (#202) for the original code!
-
-.. code:: python
-
- #!/usr/bin/env python3
- import sys
- import mpv
-
- p = mpv.MPV()
- p.play(sys.argv[1])
-
- def skip_silence():
- p.set_loglevel('debug')
- p.af = 'lavfi=[silencedetect=n=-20dB:d=1]'
- p.speed = 100
- def check(evt):
- toks = evt['event']['text'].split()
- if 'silence_end:' in toks:
- return float(toks[2])
- p.time_pos = p.wait_for_event('log_message', cond=check)
- p.speed = 1
- p.af = ''
-
- skip_silence()
- p.wait_for_playback()
-
-Video overlays
-..............
-
-.. code:: python
-
- #!/usr/bin/env python3
- import time
- from PIL import Image, ImageDraw, ImageFont
- import mpv
-
- player = mpv.MPV()
-
- player.loop = True
- player.play('test.webm')
- player.wait_until_playing()
-
- font = ImageFont.truetype('DejaVuSans.ttf', 40)
-
- while not player.core_idle:
-
- time.sleep(0.5)
- overlay = player.create_image_overlay()
-
- for pos in range(0, 500, 5):
- ts = player.time_pos
- if ts is None:
- break
-
- img = Image.new('RGBA', (400, 150), (255, 255, 255, 0))
- d = ImageDraw.Draw(img)
- d.text((10, 10), 'Hello World', font=font, fill=(0, 255, 255, 128))
- d.text((10, 60), f't={ts:.3f}', font=font, fill=(255, 0, 255, 255))
-
- overlay.update(img, pos=(2*pos, pos))
- time.sleep(0.05)
-
- overlay.remove()
-
-
-Playlist handling
-.................
-
-.. code:: python
-
- #!/usr/bin/env python3
- import mpv
-
- player = mpv.MPV(ytdl=True, input_default_bindings=True, input_vo_keyboard=True)
-
- player.playlist_append('https://youtu.be/PHIGke6Yzh8')
- player.playlist_append('https://youtu.be/Ji9qSuQapFY')
- player.playlist_append('https://youtu.be/6f78_Tf4Tdk')
-
- player.playlist_pos = 0
-
- while True:
- # To modify the playlist, use player.playlist_{append,clear,move,remove}. player.playlist is read-only
- print(player.playlist)
- player.wait_for_playback()
-
-Directly feeding mpv data from python
-.....................................
-
-.. code:: python
-
- #!/usr/bin/env python3
- import mpv
-
- player = mpv.MPV()
- @player.python_stream('foo')
- def reader():
- with open('test.webm', 'rb') as f:
- while True:
- yield f.read(1024*1024)
-
- player.play('python://foo')
- player.wait_for_playback()
-
-Using external subtitles
-........................
-
-The easiest way to load custom subtitles from a file is to pass the ``--sub-file`` option to the ``loadfile`` call:
-
-.. code:: python
-
- #!/usr/bin/env python3
- import mpv
-
- player = mpv.MPV()
- player.loadfile('test.webm', sub_file='test.srt')
- player.wait_for_playback()
-
-Note that you can also pass many other options to ``loadfile``. See the mpv docs for details.
-
-If you want to add subtitle files or streams at runtime, you can use the ``sub-add`` command. ``sub-add`` can only be
-called once the player is done loading the file and starts playing. An easy way to wait for this is to wait for the
-``core-idle`` property.
-
-.. code:: python
-
- #!/usr/bin/env python3
- import mpv
-
- player = mpv.MPV()
- player.play('test.webm')
- player.wait_until_playing()
- player.sub_add('test.srt')
- player.wait_for_playback()
-
-Using MPV's built-in GUI
-........................
-
-python-mpv is using mpv via libmpv. libmpv is meant for embedding into other applications and by default disables most
-GUI features such as the OSD or keyboard input. To enable the built-in GUI, use the following options when initializing
-the MPV instance. See `Issue 102`_ for more details
-
-.. _`issue 102`: https://github.com/jaseg/python-mpv/issues/61
-
-.. code:: python
-
- # Enable the on-screen controller and keyboard shortcuts
- player = mpv.MPV(input_default_bindings=True, input_vo_keyboard=True, osc=True)
-
- # Alternative version using the old "floating box" style on-screen controller
- player = mpv.MPV(player_operation_mode='pseudo-gui',
- script_opts='osc-layout=box,osc-seekbarstyle=bar,osc-deadzonesize=0,osc-minmousemove=3',
- input_default_bindings=True,
- input_vo_keyboard=True,
- osc=True)
-
-PyQT embedding
-..............
-
-.. code:: python
-
- #!/usr/bin/env python3
- import mpv
- import sys
-
- from PyQt5.QtWidgets import *
- from PyQt5.QtCore import *
-
- class Test(QMainWindow):
- def __init__(self, parent=None):
- super().__init__(parent)
- self.container = QWidget(self)
- self.setCentralWidget(self.container)
- self.container.setAttribute(Qt.WA_DontCreateNativeAncestors)
- self.container.setAttribute(Qt.WA_NativeWindow)
- player = mpv.MPV(wid=str(int(self.container.winId())),
- vo='x11', # You may not need this
- log_handler=print,
- loglevel='debug')
- player.play('test.webm')
-
- app = QApplication(sys.argv)
-
- # This is necessary since PyQT stomps over the locale settings needed by libmpv.
- # This needs to happen after importing PyQT before creating the first mpv.MPV instance.
- import locale
- locale.setlocale(locale.LC_NUMERIC, 'C')
- win = Test()
- win.show()
- sys.exit(app.exec_())
-
-PyGObject embedding
-...................
-
-.. code:: python
-
- #!/usr/bin/env python3
- import gi
-
- import mpv
-
- gi.require_version('Gtk', '3.0')
- from gi.repository import Gtk
-
-
- class MainClass(Gtk.Window):
-
- def __init__(self):
- super(MainClass, self).__init__()
- self.set_default_size(600, 400)
- self.connect("destroy", self.on_destroy)
-
- widget = Gtk.Frame()
- self.add(widget)
- self.show_all()
-
- # Must be created >after< the widget is shown, else property 'window' will be None
- self.mpv = mpv.MPV(wid=str(widget.get_property("window").get_xid()))
- self.mpv.play("test.webm")
-
- def on_destroy(self, widget, data=None):
- self.mpv.terminate()
- Gtk.main_quit()
-
-
- if __name__ == '__main__':
- # This is necessary since like Qt, Gtk stomps over the locale settings needed by libmpv.
- # Like with Qt, this needs to happen after importing Gtk but before creating the first mpv.MPV instance.
- import locale
- locale.setlocale(locale.LC_NUMERIC, 'C')
-
- application = MainClass()
- Gtk.main()
-
-Using OpenGL from PyGObject
-...........................
-
-Just like it is possible to render into a GTK widget through X11 windows, it `also is possible to render into a GTK
-widget using OpenGL `__ through this python API.
-
-Using OpenGL from PyQt5/QML
-...........................
-
-Robozman_ has mangaed to `make mpv render into a PyQt5/QML widget using OpenGL
-`__ through this python API.
-
-Using mpv inside imgui inside OpenGL via GLFW
-.............................................
-
-dfaker_ has written a demo (`link `__) that uses mpv to render video into an `imgui `__ UI running on an OpenGL context inside `GLFW `__. Check out their demo to see how to integrate with imgui/OpenGL and how to access properties and manage the lifecycle of an MPV instance.
-
-Running tests
--------------
-
-Use pytest to run tests.
-
-Coding Conventions
-------------------
-
-The general aim is `PEP 8`_, with liberal application of the "consistency" section. 120 cells line width. Four spaces.
-No tabs. Probably don't bother making pure-formatting PRs except if you think it *really* helps readability or it
-*really* irks you if you don't.
-
-License
--------
-
-python-mpv inherits the underlying libmpv's license, which can be either GPLv2 or later (default) or LGPLv2.1 or later.
-For details, see `the mpv copyright page`_.
-
-.. _`PEP 8`: https://www.python.org/dev/peps/pep-0008/
-.. _`mpv.py`: https://raw.githubusercontent.com/jaseg/python-mpv/main/mpv.py
-.. _cosven: https://github.com/cosven
-.. _Robozman: https://gitlab.com/robozman
-.. _dfaker: https://github.com/dfaker
-.. _`the mpv copyright page`: https://github.com/mpv-player/mpv/blob/master/Copyright
-
diff --git a/content/projects/python-mpv/index.rst b/content/projects/python-mpv/index.rst
deleted file mode 100644
index 12b1c18..0000000
--- a/content/projects/python-mpv/index.rst
+++ /dev/null
@@ -1,18 +0,0 @@
----
-title: "python-mpv"
-external_links:
- - name: Sources
- url: "https://git.jaseg.de/python-mpv.git"
- - name: Issues
- url: "https://github.com/jaseg/python-mpv/issues"
- - name: Docs
- url: "https://neinseg.gitlab.io/python-mpv"
- - name: PyPI
- url: "https://pypi.org/project/mpv"
-summary: >
- python-mpv is a small, ctypes-based Python library wrapping the libmpv media player library. Despite its small size
- and simple API, python-mpv allows advanced control over libmpv and beyond simple remote control of mpv can be used
- to embed mpv in OpenGL, Qt, and GTK-based Python applications.
----
-
-.. include:: content/projects/python-mpv/README.rst
diff --git a/content/projects/svg-flatten/index.rst b/content/projects/svg-flatten/index.rst
deleted file mode 100644
index ae9a8f3..0000000
--- a/content/projects/svg-flatten/index.rst
+++ /dev/null
@@ -1,22 +0,0 @@
----
-title: "svg-flatten"
-external_links:
- - name: Sources
- url: "https://git.jaseg.de/gerbolyze.git/tree/svg-flatten?h=main"
- - name: Issues
- url: "https://github.com/jaseg/gerbolyze/issues"
- - name: Docs
- url: "https://gerbolyze.gitlab.io/svg-flatten"
-summary: >
- svg-flatten is a command-line utility that performs vector occlusion and clipping on SVG files, producing a
- flattened SVG file without overlapping elements, without changing what the file looks like. svg-flatten is used as a
- part of gerbolyze.
----
-
-svg-flatten is a command-line utility that performs vector occlusion and clipping on SVG files, producing a flattened
-SVG file without overlapping elements, without changing what the file looks like. svg-flatten is used as a part of
-gerbolyze.
-
-I developed svg-flatten as part of gerbolyze_, but it can be used independently.
-
-.. _gerbolyze: {{< ref "projects/gerbolyze" >}}
diff --git a/content/projects/wsdiff/index.rst b/content/projects/wsdiff/index.rst
deleted file mode 100644
index 44cd4c3..0000000
--- a/content/projects/wsdiff/index.rst
+++ /dev/null
@@ -1,63 +0,0 @@
----
-title: "wsdiff"
-external_links:
- - name: Sources
- url: "https://git.jaseg.de/wsdiff.git"
- - name: Issues
- url: "https://github.com/jaseg/wsdiff/issues"
- - name: PyPI
- url: "https://pypi.org/project/wsdiff"
-summary: >
- wsdiff is a command-line utility that produces self-contained, syntax-highlighted, HTML-formatted diffs that support
- both unified and side-by-side diffs from a single source file using nothing but CSS magic.
----
-
-wsdiff is a python script that produces a diff of two files or directories as a single, self-contained HTML file. The
-resulting diff works without Javascript and will automatically switch between inline and side-by-side formats depending
-on available screen space.
-
-Installation
-============
-
-.. code:: sh
-
- $ pip install wsdiff
-
-Usage
-=====
-
-::
-
- usage: wsdiff [-h] [-b] [-s SYNTAX_CSS] [-l LEXER] [-L] [-t PAGETITLE]
- [-o OUTPUT] [--header] [--content]
- [old] [new]
-
- Given two source files or directories this application creates an html page
- that highlights the differences between the two.
-
- positional arguments:
- old source file or directory to compare ("before" file)
- new source file or directory to compare ("after" file)
-
- options:
- -h, --help show this help message and exit
- -b, --open Open output file in a browser
- -s SYNTAX_CSS, --syntax-css SYNTAX_CSS
- Path to custom Pygments CSS file for code syntax
- highlighting
- -l LEXER, --lexer LEXER
- Manually select pygments lexer (default: guess from
- filename, use -L to list available lexers.)
- -L, --list-lexers List available lexers for -l/--lexer
- -t PAGETITLE, --pagetitle PAGETITLE
- Override page title of output HTML file
- -o OUTPUT, --output OUTPUT
- Name of output file (default: stdout)
- --header Only output HTML header with stylesheets and stuff,
- and no diff
- --content Only output HTML content, without header
-
-Example Output
-==============
-
-.. image:: latest.png
diff --git a/deploy.py b/deploy.py
deleted file mode 100644
index f0cab5c..0000000
--- a/deploy.py
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env python3
-
-import os
-import tempfile
-import subprocess
-from pathlib import Path
-
-if __name__ == '__main__':
- with tempfile.TemporaryDirectory() as tmpdir:
- pagefind = Path('~/.cargo/bin/pagefind').expanduser()
- pagefind = str(pagefind) if pagefind.is_file() else 'pagefind'
-
- current_branch = subprocess.run(['git', 'symbolic-ref', '-q', 'HEAD'], check=False, capture_output=True).stdout.strip()
- if current_branch == 'refs/heads/deploy':
- raise SystemError('This script cannot be run from the deploy branch (run from main instead)')
-
- subprocess.run(['git', 'worktree', 'add', '--detach', tmpdir], check=True)
- try:
- env = dict(os.environ)
- env['PATH'] = f'{Path("hack").absolute()}:{env["PATH"]}'
- hugo = os.environ.get('HUGO', 'hugo')
- subprocess.run([hugo], cwd=tmpdir, check=True, env=env)
- subprocess.run([pagefind, '--site', 'public'], cwd=tmpdir, check=True, env=env)
- subprocess.run(['git', 'add', '--force', 'public'], cwd=tmpdir, check=True)
- write_tree = subprocess.run(['git', 'write-tree', '--prefix=public/'], cwd=tmpdir, check=True, capture_output=True)
- tree = write_tree.stdout.strip()
-
- commit = subprocess.run(['git', 'commit-tree', '-p', 'HEAD', '-p', 'refs/heads/deploy', '-m', 'deploy.py auto-commit', tree], cwd=tmpdir, check=True, capture_output=True).stdout.strip()
- subprocess.run(['git', 'update-ref', '-m', f'deploy.sh update deploy branch to {commit}', 'refs/heads/deploy', commit], cwd=tmpdir, check=True)
-
- subprocess.run(['git', 'push', 'git@git.jaseg.de:blog.git', 'deploy'], cwd=tmpdir, check=True)
- finally:
- subprocess.run(['git', 'worktree', 'remove', '-f', tmpdir], check=True)
diff --git a/docutils.conf b/docutils.conf
deleted file mode 100644
index 308c0f7..0000000
--- a/docutils.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-[restructuredtext parser]
-syntax_highlight: short
-
diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Medium.ttf b/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Medium.ttf
similarity index 100%
rename from themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Medium.ttf
rename to fonts/bodoni_moda/static/BodoniModa_18pt/BodoniModa_18pt-Medium.ttf
diff --git a/themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-MediumItalic.ttf b/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-MediumItalic.ttf
similarity index 100%
rename from themes/conspiracy/assets/fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-MediumItalic.ttf
rename to fonts/bodoni_moda/static/BodoniModa_9pt/BodoniModa_9pt-MediumItalic.ttf
diff --git a/themes/conspiracy/assets/fonts/fira_code/FiraCode-VariableFont_wght.ttf b/fonts/fira_code/FiraCode-VariableFont_wght.ttf
similarity index 100%
rename from themes/conspiracy/assets/fonts/fira_code/FiraCode-VariableFont_wght.ttf
rename to fonts/fira_code/FiraCode-VariableFont_wght.ttf
diff --git a/themes/conspiracy/assets/fonts/manuskript_gothisch/Manuskript Gothisch UNZ1A.ttf b/fonts/manuskript_gothisch/Manuskript Gothisch UNZ1A.ttf
similarity index 100%
rename from themes/conspiracy/assets/fonts/manuskript_gothisch/Manuskript Gothisch UNZ1A.ttf
rename to fonts/manuskript_gothisch/Manuskript Gothisch UNZ1A.ttf
diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2
similarity index 100%
rename from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2
rename to fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2
diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2
similarity index 100%
rename from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2
rename to fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2
diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Dark.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Dark.woff2
similarity index 100%
rename from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Dark.woff2
rename to fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Dark.woff2
diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-DarkItalic.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-DarkItalic.woff2
similarity index 100%
rename from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-DarkItalic.woff2
rename to fonts/nyght-serif-main/fonts/WEB/NyghtSerif-DarkItalic.woff2
diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Light.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Light.woff2
similarity index 100%
rename from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Light.woff2
rename to fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Light.woff2
diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-LightItalic.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-LightItalic.woff2
similarity index 100%
rename from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-LightItalic.woff2
rename to fonts/nyght-serif-main/fonts/WEB/NyghtSerif-LightItalic.woff2
diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Medium.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Medium.woff2
similarity index 100%
rename from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Medium.woff2
rename to fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Medium.woff2
diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-MediumItalic.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-MediumItalic.woff2
similarity index 100%
rename from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-MediumItalic.woff2
rename to fonts/nyght-serif-main/fonts/WEB/NyghtSerif-MediumItalic.woff2
diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2
similarity index 100%
rename from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2
rename to fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2
diff --git a/themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-RegularItalic.woff2 b/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-RegularItalic.woff2
similarity index 100%
rename from themes/conspiracy/assets/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-RegularItalic.woff2
rename to fonts/nyght-serif-main/fonts/WEB/NyghtSerif-RegularItalic.woff2
diff --git a/themes/conspiracy/assets/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf b/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf
similarity index 100%
rename from themes/conspiracy/assets/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf
rename to fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf
diff --git a/hack/rst2html b/hack/rst2html
deleted file mode 100755
index f9c9146..0000000
--- a/hack/rst2html
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/usr/bin/env python3
-# Based on https://gist.github.com/mastbaum/2655700 for the basic plugin scaffolding
-
-import sys
-import re
-
-import docutils.core
-from docutils.transforms import Transform
-from docutils.nodes import TextElement, Inline, Text
-from docutils.parsers.rst import Directive, directives
-from docutils.writers.html4css1 import Writer, HTMLTranslator
-
-
-class UnfuckedHTMLTranslator(HTMLTranslator):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.in_literal_block = False
-
- def visit_literal_block(self, node):
- # Insert an empty "lineno" span before each line. We insert the line numbers using pure CSS in a ::before
- # pseudo-element. This has the added advantage that the line numbers don't get included in text selection.
- # These line number spans are also used to show line continuation markers when a line is wrapped.
- self.in_literal_block = True
- self.body.append(self.starttag(node, 'pre', CLASS='literal-block'))
- self.body.append('')
-
- def depart_literal_block(self, node):
- self.in_literal_block = False
- self.body.append('\n\n')
-
- def visit_Text(self, node):
- if self.in_literal_block:
- for match in re.finditer('([^\n]*)(\n|$)', node.astext()):
- text, end = match.groups()
-
- if text:
- super().visit_Text(Text(text))
-
- if end == '\n':
- if isinstance(node.parent, Inline):
- self.depart_inline(node.parent)
- self.body.append(f'\n')
- if isinstance(node.parent, Inline):
- self.visit_inline(node.parent)
-
- else:
- super().visit_Text(node)
-
-
-html_writer = Writer()
-html_writer.translator_class = UnfuckedHTMLTranslator
-docutils.core.publish_cmdline(writer=html_writer)
-
diff --git a/themes/conspiracy/assets/images/fabrizio-conti-TUmjK7ZJgbI-unsplash.jpg b/images/fabrizio-conti-TUmjK7ZJgbI-unsplash.jpg
similarity index 100%
rename from themes/conspiracy/assets/images/fabrizio-conti-TUmjK7ZJgbI-unsplash.jpg
rename to images/fabrizio-conti-TUmjK7ZJgbI-unsplash.jpg
diff --git a/imprint/index.html b/imprint/index.html
new file mode 100644
index 0000000..096a92f
--- /dev/null
+++ b/imprint/index.html
@@ -0,0 +1,152 @@
+
+
+
+ Impressum | Home
+
+
+
+
+
+
+
+
+
+
+
+
+