master
Tait Hoyem 3 years ago
commit 7ffad423f0

Binary file not shown.

@ -5,6 +5,8 @@ source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
gem "jekyll"
gem "webrick"
gem "rack", ">= 2.2.3"
gem "kramdown"
gem "kramdown-math-katex"
gem "execjs"
@ -12,13 +14,13 @@ gem "duktape"
gem "addressable", "~> 2.8"
gem "ffi", "~> 1.15"
gem "webrick", "~> 1.7"
gem "ffi"
group :jekyll_plugins do
gem 'jekyll-katex'
gem 'jekyll-sitemap'
gem 'jekyll-feed'
gem 'jekyll-seo-tag'
gem 'jekyll-admin'
gem 'jekyll-minifier'
gem 'jekyll-scholar'
gem 'jekyll-contentblocks'

@ -1,43 +1,51 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.7.0)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
backports (3.18.2)
bibtex-ruby (6.0.0)
latex-decode (~> 0.0)
citeproc (1.0.10)
namae (~> 1.0)
citeproc-ruby (1.1.14)
citeproc (~> 1.0, >= 1.0.9)
csl (~> 1.6)
colorator (1.1.0)
concurrent-ruby (1.1.7)
concurrent-ruby (1.1.9)
csl (1.6.0)
namae (~> 1.0)
rexml
csl-styles (1.0.1.11)
csl (~> 1.0)
cssminify2 (2.0.1)
duktape (2.6.0.0)
em-websocket (0.5.2)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0.6.0)
eventmachine (1.2.7)
execjs (2.7.0)
ffi (1.13.1)
execjs (2.8.1)
ffi (1.15.4)
forwardable-extended (2.6.0)
htmlcompressor (0.4.0)
http_parser.rb (0.6.0)
i18n (1.8.5)
i18n (1.8.10)
concurrent-ruby (~> 1.0)
jekyll (4.1.1)
jekyll (4.2.0)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 1.0)
jekyll-sass-converter (~> 2.0)
jekyll-watch (~> 2.0)
kramdown (~> 2.1)
kramdown (~> 2.3)
kramdown-parser-gfm (~> 1.0)
liquid (~> 4.0)
mercenary (~> 0.4.0)
pathutil (~> 0.9)
rouge (~> 3.0)
safe_yaml (~> 1.0)
terminal-table (~> 1.8)
jekyll-admin (0.10.2)
jekyll (>= 3.7, < 5.0)
sinatra (~> 1.4)
sinatra-contrib (~> 1.4)
jekyll-feed (0.15.0)
terminal-table (~> 2.0)
jekyll-feed (0.15.1)
jekyll (>= 3.7, < 5.0)
jekyll-katex (1.0.0)
execjs (~> 2.7)
@ -50,70 +58,72 @@ GEM
uglifier (~> 4.1)
jekyll-sass-converter (2.1.0)
sassc (> 2.0.1, < 3.0)
jekyll-seo-tag (2.6.1)
jekyll (>= 3.3, < 5.0)
jekyll-scholar (7.0.0)
bibtex-ruby (~> 6.0)
citeproc-ruby (~> 1.0)
csl-styles (~> 1.0)
jekyll (~> 4.0)
jekyll-seo-tag (2.7.1)
jekyll (>= 3.8, < 5.0)
jekyll-sitemap (1.4.0)
jekyll (>= 3.7, < 5.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
json (2.3.1)
json (2.5.1)
json-minify (0.0.3)
json (> 0)
kramdown (2.3.0)
katex (0.8.0)
execjs (~> 2.7)
kramdown (2.3.1)
rexml
kramdown-math-katex (1.0.1)
katex (~> 0.4)
kramdown (~> 2.0)
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
latex-decode (0.3.2)
liquid (4.0.3)
listen (3.2.1)
listen (3.7.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.4.0)
multi_json (1.15.0)
namae (1.1.1)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (4.0.6)
rack (1.6.13)
rack-protection (1.5.5)
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rb-fsevent (0.10.4)
rack (2.2.3)
rb-fsevent (0.11.0)
rb-inotify (0.10.1)
ffi (~> 1.0)
rexml (3.2.4)
rouge (3.23.0)
rexml (3.2.5)
rouge (3.26.0)
safe_yaml (1.0.5)
sassc (2.4.0)
ffi (~> 1.9)
sinatra (1.4.8)
rack (~> 1.5)
rack-protection (~> 1.4)
tilt (>= 1.3, < 3)
sinatra-contrib (1.4.7)
backports (>= 2.0)
multi_json
rack-protection
rack-test
sinatra (~> 1.4.0)
tilt (>= 1.3, < 3)
terminal-table (1.8.0)
terminal-table (2.0.0)
unicode-display_width (~> 1.1, >= 1.1.1)
tilt (2.0.10)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unicode-display_width (1.7.0)
webrick (1.7.0)
PLATFORMS
ruby
aarch64-linux
DEPENDENCIES
duktape
execjs
jekyll
jekyll-admin
jekyll-feed
jekyll-katex
jekyll-minifier
jekyll-scholar
jekyll-seo-tag
jekyll-sitemap
kramdown
kramdown-math-katex
rack (>= 2.2.3)
webrick
BUNDLED WITH
2.1.4
2.2.26

@ -0,0 +1,18 @@
---
---
References
==========
@book{canadian_history_for_dummies,
title = {Canadian History For Dummies, 2nd Edition},
author = {Will Ferguson},
year = {2005},
publisher = {John Wiley & Sons Canada, Ltd.},
isbn = {0-470-83656-3}
}
@book{industrial_society_and_its_future,
title = {Industrial Society and Its Futurte},
author = {Theodore John Kaczynski},
year = {1995},
}

@ -0,0 +1,24 @@
---
title: "Canadian History For Dummies"
layout: post
---
Lookup more info on these isolated languages:
> "The Pacific Northwest was the most densely populated area of Canada.
It has been estimated that almost *half* of Canada's total native population was living in British Columbia at the time of first contact.
More than 30 languages were spoken here, making it one of the most linguistically diverse areas anywhere on earth.
And two of the languages (Haida and Tlingit) are isolates:
unique and unrelated to any others."---
{% cite canadian_history_for_dummies -l25 %}
<hr>
Find info on thie document this is taken from:
> "To seeke out, discover and finde whatsoever liescountreyes, regions or provinces of the hethen and infidells ... whiche before this time have beene unknowen to all Christians"---from the directives given to John Cabot by Henry VII
{% cite canadian_history_for_dummies -l38 %}
<hr>
{% bibliography --cited %}

@ -0,0 +1,6 @@
---
layout: default
title: "Industrial Society and Its Future"
---

@ -21,3 +21,4 @@ kramdown:
scholar:
style: "ieee"
#style: "modern-language-association"

@ -1,7 +1,7 @@
- company: "Zone4 Systems Inc."
position: "Software Developer"
years: "June 2021-present"
description: "Created an automated test environment running in Docker. Mostly fix Javascript bugs."
description: "Software development and QA (testing) for an international race timing company based in Canmore, AB. Testing using unittest and Selenium. Development done in Javascript and Tornado. Zone4 was founded in 2001 by Canadian Olympian Dan Roycroft.."
- company: "Bytetools Technologies Inc."
position: "Founder"
years: "2020-present"
@ -13,7 +13,7 @@
- company: "Independent"
position: "Tutoring/Transcribing"
years: "2019-present"
description: "Working with computer science students explaining introductory to advanced concepts. Sometimes I also transcribe textbooks or other resources for visually impaired students."
description: "Working with computer science students explaining introductory to advanced concepts. Covering languages from C/C++ to Javascript to Python. I am skilled in working with students who have visual impairments: transcribing inaccessible computer code (from images or a screen-share) and presenting complex math equations in an accessible format."
- company: "Total Impact Signs"
position: "Contractor"
years: "2014-2016"

@ -12,6 +12,12 @@
- name: Github
label: github.com/TTWNO
value: https://github.com/TTWNO
- name: RSS Feed
label: feed.xml
value: /feed.xml
- name: Book Notes
label: /book-notes/
value: /book-notes/
- name: Backup/Mirror Site
value: https://beta.tait.tech/
label: beta.tait.tech
@ -23,6 +29,9 @@
- name: Protonmail
value: https://protonmail.com
label: INHERIT
- name: Matrix
value: https://matrix.org
label: INHERIT
- heading: Linux Links
values:
- name: Arch Linux Wiki
@ -67,8 +76,5 @@
- heading: Friends' Websites
values:
- name: Justin Pilon
value: https://justinpilon.ca/
label: INHERIT
- name: Melody Shih
value: https://melly.tech/
value: https://elephantpaw.ca/
label: INHERIT

@ -10,6 +10,9 @@
- name: "epub-with-pinyin"
link: "https://github.com/TTWNO/epub-with-pinyin"
description: "A program to add Pinyin above Chinese characters in .epub files to assist those learning Mandarin Chinese."
- name: "Programming Tutorials For The Visually Impaired"
link: "/emacspeak-tutorials/"
description: "Amateur-level production quality videos with all file buffers, written text, and shell commands read out by <a href=\"http://emacspeak.sourceforge.net/\">Emacspeak</a>, an Emacs extention for the blind."
- name: "lamegames"
link: "https://github.com/TTWNO/lamegames.io"
description: "A little games website I made for some demonstrations of Django and websocket functionality. Very, very lame. Would not recommend."
@ -19,3 +22,9 @@
- name: "Emacspeak C Tutorials"
link: "https://tait.tech/emacspeak-tutorials/"
description: "C Tutorials for the Visually Impaired"
- name: "Simple Markdown Editor"
link: "https://tait.tech/md/"
description: "A simple online (and offline) browser-based markdown editor that supports the automatic creation of MathML."
#- name: "Emacspeak C Tutorials"
# link: "https://tait.tech/emacspeak-tutorials/"
# description: "C Tutorials for the Visually Impaired"

@ -1,4 +1,6 @@
---
language: "C"
library: "Standard"
title: "Arrays in C"
layout: emacspeak_tutorial
yt_link: "https://youtu.be/JdOmV2V4dzs"

@ -1,4 +1,6 @@
---
language: "C"
library: "Standard"
title: "Functions in C"
layout: emacspeak_tutorial
yt_link: "https://youtu.be/UV-xmgS3qhc"

@ -1,4 +1,6 @@
---
language: "C"
library: "Standard"
layout: emacspeak_tutorial
title: "Loops in C"
zip_link: "/assets/emacspeak-tutorials/loops.zip"

@ -1,4 +1,6 @@
---
language: "C"
library: "Standard"
title: "Pointers in C"
layout: emacspeak_tutorial
yt_link: "https://youtu.be/t3BgDa1p7Qc"

@ -1,4 +1,6 @@
---
language: "C"
library: "Standard"
layout: emacspeak_tutorial
title: "Printing in C"
zip_link: "/assets/emacspeak-tutorials/printing.zip"

@ -1,4 +1,6 @@
---
language: "C"
library: "Standard"
title: "Structs in C"
layout: emacspeak_tutorial
yt_link: "https://youtu.be/SaUnC_Ofysw"

@ -1,4 +1,6 @@
---
language: "C"
library: "Standard"
layout: emacspeak_tutorial
title: "User Input and Variables in C"
zip_link: "/assets/emacspeak-tutorials/variables.zip"

@ -1 +1 @@
This page is mirrored on <a href="https://beta.tait.tech{{ page.url }}">beta.tait.tech</a>.
This page will be mirrored on <a href="https://beta.tait.tech{{ page.url }}">beta.tait.tech</a>.

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ page.title }} | tait.tech</title>
<link rel="stylesheet" href="/assets/css/book.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="wrapper">
<article>
<header>
<h1>{{ page.title }}</h1>
<address>
by <area rel="author">{{ page.author }}</area>
</address>
</header>
<main>
{{ content }}
</main>
</article>
</div>
</body>
</html>

@ -3,11 +3,16 @@
<head>
<meta charset="UTF-8">
<title>{{ page.title }} | tait.tech</title>
<link rel="stylesheet" href="/assets/css/style.css">
<link rel="stylesheet" href="/assets/css/style.css" id="main-stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% if page.math %}
<link rel="stylesheet" href="/assets/css/katex.css">
{%- if page.math -%}
<link rel="stylesheet" href="/assets/css/katex.css" id="math-stylesheet">
{% endif %}
{%- if page.code -%}
<!-- TODO: add this to /md page -->
<link rel="stylesheet" href="/assets/css/highlight.css" id="code-stylesheet">
{% endif %}
{% contentblock extrahead %}
</head>
<body>
<main>

@ -83,7 +83,7 @@ The two biggest "implementations" of public-key cryptography vary only in the ma
and how the numbers are ["trapdoored"](https://en.wikipedia.org/wiki/Trapdoor_function) to decrypt if you have the correct key.
I will discuss the differences in approach here.
If you want to skip to the next article where I show you how to encrypt your own documents using RSA, see [this link](/2020/04/06/rsa4.html).
If you want to skip to the next article where I show you how to encrypt your own documents using RSA, see [this link](/2020/04/06/rsa4/).
### RSA

@ -0,0 +1,174 @@
---
title: "Idea For A VPN Service"
layout: post
math: true
---
Recently I've been thinking about starting a VPN service.
This service has some interesting requirements that I have never seen a VPN service do before, so I'd like to put down my thoughts as to what might be sensible for a centralized yet encrypted\* VPN service.
I would license all the code and scripts under the AGPLv3.
This creates an environment where I could allow my company to use this code, and any other company for that matter. However, no company would be allowed to take it into their own hands and use it without contributing back to the project.
## E2EE VPN
I want this service in many ways to be on par with [ProtonMail](https://protonmail.com):
end-to-end encrypted (E2EE), and with a focus in data security for the user of the service.
Full encryption, so that even me, the writer and the deployer of the service, cannot view any information about the user: this is the utmost security.
The bad news is that this is very hard to do in a convenient way.
I've decided for now that the best thing to do is to target the Linux nerd.
Target the user who is familiar with these advanced security practices, then make them available to the general public as the layers on top of the robust security are refined.
## Why?
End-to-end encryption is necessary in a country like Canada, where I may be sent a subpoena to provide customer data.
This is the case especially in the [Five Eyes](https://en.wikipedia.org/wiki/Five_Eyes) anglophone group of countries, who essentially spy on each others' citizens for eachother.
In essence, any data in the hand of one government agency in the "Eyes Countries" may be shared between the Five, Nine, and 14 Eyes countries.
I am not against government surveillance *in principle*.
In theory, the government should be finding bad guys: pedophiles, sex trafficking rings and drug cartels.
In practice, the U.S. government especially, uses its authority to spy on its own citizens who are simply minding their own business. ~~Bulk data collection~~ mass surveillance is not a freedom respecting characteristic of modern western democracies.
I do run the risk of not being able to help much in the case of a genuine warrant against a genuine, evil criminal.
That is the risk of privacy.
That said, let's see what can be built that can do these 2 things:
1. Maximize privacy for the user.
2. Allow for (optional) monetization, depending on the provider. This is in some contradiction to premise 1.
## What We Need
A VPN service needs access to some basic information:
1. Service discontinue time (the amount of time until the customer must renew).
2. Active connections (a number which can not be exceeded by an individual user).
The client needs access to some information from the server as well:
1. A list of VPNs able to be connected to (with filters).
2. For every VPN:
1. IP Address.
2. Maximum bandwidth.
3. Number of connected users or connection saturation percentage.
4. Supported protocols.
Can we do this in a end-to-end encrypted fashion?
I'm honestly not sure. But here are my ideas so far as to how *some* of these functions might work.
## How To Do It
### "Usernames"
There will be one button to create your account: *"Generate username"*
The username, or unique identifier for a user will be generated for them by a random generator.
I plan to generate a username from a list of [Base 64](https://en.wikipedia.org/wiki/Base64) characters; it will be a guaranteed length of 16.
This gives a total of: `79228162514264337593543950336` or {% katex %}7.9 \times 10^{28}{% endkatex %} posibilities.
This is sufficient for a username.
The other option is to use a standard "username" field that uses a modern hash function like [SHA512](https://en.wikipedia.org/wiki/Secure_Hash_Algorithms) to store it in the database.
This is less secure as it is vulnerable to a brute-force attack of finding users,
but this is also a very easy attack to defend against, i.e. IP banning after 10-ish tries of not finding a username.
A *non-unique, universal* [salt](https://en.wikipedia.org/wiki/Salt_(cryptography)) will also be used on each username before storing it in the database to make it more secure.
This decreases the possibility of an advanced attacker being able to find usernames in a leaked database using [rainbow tables](https://en.wikipedia.org/wiki/Rainbow_table).
That said, the fact that it is a fixed salt makes it much more vulnerable to an attack.
Although it would be known only by the server machine, it would still be somewhat of a vulnerability.
The operator may also store the salt in an encrypted password store of their own in case the server is erased, broken into, etc.
It would be fairly easy, if they have access to the active salt, to migrate to a new salt every few days/months, or perhaps every time a server upgrade/maintenance happens.
This does run the possibility of larger issues if the server is shut down or hangs during a migration and needs to be restarted.
Many users may end up with accounts they cannot access without manual cleanup.
In the end, the *application* would need a backup of this salt, otherwise login times would become linear to the number of users as the database checks every user's salt to see if it matches the hash made with the username input.
Note that the *database* does not store the salt, so finding it will be very hard, even in the case of a leaked database.
So, here's the overview:
The username will be generated, then stored *after* being salted and hashed.
The salt will be a fixed or rolling salt across all usernames to avoid linear scaling of searching for a user.
The server will only see the username once, when sending it to the user for them to save for the first time;
there will be no database entry with the original username in it.
This does mean that if the username is lost, the account is lost too. There is no way to recover the account.
Again, this is ok for now, as my target audience is advanced Linux and privacy enthusiasts.
### "Passwords"
There are a few options for passwords/secret keys.
I think the best is to treat it similarly to the username is above, except it will *not* be generated for you.
When a new account is generated, you will be taken to a password reset screen where you will set your password to whatever your want, using your own secure system to handle it.
This is ideal for Linux and tech enthusiasts as they generally already have a password management system setup.
This will also be salted, with its own unique salt, then hashed and stored alongside the username.
### Active Time Remaining
It is easy and ideal to have a field connected to a user with their expiry date for their account.
When a payment is made, this date will be increased by the number of days, hours and minutes proportional to the payment received.
For example: if a "month" (30 days) costs ten dollars, then a payment of fifteen dollars would add 45 days to an account. So essentially 33 cents per day, {% katex %} \frac{10}{30 \times 24}=0.0138{% endkatex %} dollars per hour, or {% katex %}\frac{10}{30 \times 24 \times 60}=0.00023\overline{148}{% endkatex %} dollars per minute.
This is the second biggest threat to the users' data privacy, as this, by definition, cannot be encrypted as my server needs access to this data to decide whether a user should be allowed to: view a list of VPN nodes available to them or connect to a VPN.
The best I can think of in this case is:
1. Use a system similar to the username: use a common salt and hash algorithm to store them in the database.
2. Use full-disk and full-database encryption to keep the data secure to outside attackers.
This is not a fantastic solution, and still has the threat of a service provider snooping in on the database.
The truth is: a service provider has root access to any machine it hosts.
This necessitates that the *physical* infrastructure hosting the central database server must by physically owned and operated by the VPN operator and not any third party.
In addition, it means top security root passwords, tamper resistant cases (in the case of a co-hosting or server room environment), sensors to indicate it has been opened or touched.
If you thought this was bad, wait until the next part.
### Active Connections
In order to stop a user from simply using the entire bandwidth of all the VPN nodes available to them, there must be a way to know how many active connections the user has.
This is *by far* the biggest issue in terms of user privacy.
There are a few options here:
1. Do not have a limit on the number of connections a user may have. This is dangerous from a [DDoS (distributed denial-of-service)](https://en.wikipedia.org/wiki/Denial-of-service_attack) perspective.
This also makes the VPN provider vulnerable to be used as a DDoS distribution method by putting all their traffic through the VPN provider, and them not having any logs---the bad guys could use the distributed nature of VPN nodes to attack whoever they see fit.
This is not a viable option.
2. Have a list of connected users sent to the central server every 15 to 30 seconds. This is fairly efficient, but more privacy invasive.
3. When a user connects, log an explicit "connect" message.
When a user disconnects, send an explicit "disconnect".
Have the VPN server report an *implicit* "disconnect" after an amount of time, say 15 minutes, then send an implicit "connect" message once traffic continues. This is all in RAM under temporary storage and is lost upon restart of the server.
The best method (used currently by [Mullvad VPN](https://mullvad.net)) is number 3.
## Panel
The admin panel will have some broad info about the nodes:
* Active connections
* Server load (held and reported every minute by the nodes themselves. Not sure how to do this yet.)
* Location
* IP Address
* Failed connections in last X amount of time (i.e. invalid credentials)
* Physical server status (i.e. owned by the hoster vs. contracted out to another hosting company in the area)
This panel would also have options to stop, start or soft stop the VPN service on each node for maintenance.
A soft stop will stop new connections and remove it from the list of available servers for the end-user. Users will disconnect whenever they feel like it---eventually winding down to zero connections.
This allows maintenance without service disruption.
I'm not sure how to do this securely.
Best I can think of right now is have an admin login, then have the server have a key in each node machine.
This completely compromises the SSH key system though.
Now every node is secured with nothing but a password. Maybe the console will require connecting to a local instance on a machine through an encrypted connection which will require a key.
Even then, that does make every machine vulnerable to one point of failure (the key to connect to the local instance).
Another way to approach this, security-wise is to make a shell script (or locally running flask app) which reads info about the servers from a sqlite database.
Then, it uses the local computer to connect to the servers---assuming the local machine has all the keys necessary to do so.
This fixes one problem and creates another.
It fixes the single point of failure in the cloud. This *massively* reduces the attack surface to intentionally stealing physical hardware from trusted parties, or software-hacking these same trusted people.
But, if the key is lost by the host... The entire service is kaput. No maintenance may be performed, no checks, bans, addition of servers can be done whatsoever.
This also increases the possibility of sloppy security from trusted parties.
Perhaps a trusted member leaves his laptop unattended for a few minutes and a hacker is able to steal the simple key file. He's in!!!
This is very unlikely, I must say, but it comes down to: should I trust people or machines more to keep the data secure.
Depending on the person, I might trust them more.
## Conclusion
With all of these ideas in mind, I have realized how difficult it really is to make a VPN service.
Boy do they deserve every dollar they get!
If you don't have a VPN, get one.
Doesn't really matter which one, unless you're a nerd---for your average person you can just pick whatever the best deal is at the time and you're off to the races.
Anyway, I think I've rambled on long enough about VPNs and my crazy ideas, so I'm going to leave this one for now.
Happy VPN hacking :D

@ -0,0 +1,206 @@
---
title: "How To Produce Semantically Correct MathML From XaTeX/LaTeX (and other accessibility ideas)"
layout: post
math: true
---
During a recent run-in with the Simon Fraser Fraser University accessibility department,
I learned that they're writers are so well-trained as to write "image" where a simple diagram is shown,
and "print out picture of output" where a piece of code lies.
I figure the geniuses over there could use some help creating files for the visually impaired.
Here's a quick guide!
## Diagrams
Most unexplained diagrams I saw were ones which mirrored classic computer-science imagery;
these diagrams, for the most part, were not complex nor exotic;
they are straight-forward to explain in writing,
or easy to turn into a table.
I'll show two examples here,
one will show a visual aide in relation to stacks and queues,
and the other will show a memory representation of a stack.
Both of these were explained as "image" to the student.
## Stacks
Diagram 1:
<figure>
<img src="/assets/img/access1/stack.png" alt="image...lol! Just kidding, will explain it below w/ table">
<figcaption>Simple diagram explaining the push/pop process. Source: <a href="https://stackoverflow.com/questions/32151392/stacks-queues-and-linked-lists">Stackoverflow</a></figcaption>
</figure>
Ok, so here we have a diagram showing the pushing and popping process of a stack.
Now, "image" is hardly sufficient to explain this, so let's try it with text.
I won't finish it because it gets unwieldy very fast:
> A diagram showing a stack. It starts with the operation "Push A", and now the stack contains the variable "A"; now the stack pushes "B", which displays now "B" on top of "A"...
This is no solution.
It is hard to explain this correctly and accurately without being extremely verbose and frankly, confusing---this defeats the whole purpose of describing the image.
The good news, is that computer science diagrams especially tend to lean towards being tabular data.
Now to be clear, something does not need to look like a table to be tabular data;
this image happens to look almost like a table if you squinted hard enough,
but many data not written down in a table, are still "tabular data".
I will show an example of that next!
For now though, here is the same idea, same data without words:
Operator|Stack Values
---|---
Push A|[A]
Push B|[B, A]
Push C|[C, B, A]
Push D|[D, C, B, A]
Pop D|[C, B, A]
Now this diagram does imply you can pop other items, like "Pop A", which is just not true.
But that's the fault of the diagram, not the representation of it.
Here is the raw text equivalent (in Markdown):
<pre>
Operator|Stack Values
---|---
Push A|[A]
Push B|{B, A]
Push C|[C, B, A]
Push D|[D, C, B, A]
Pop (D)|[C, B, A]
</pre>
## Stacks in Memory
So I couldn't find a good non-copyright image of a stack in memory, but I'll write it down here in plain text, and you should get the idea.
Now again, remember this is still labeled "image" to the student,
they do not have access to a text version of this.
<pre>
( ) ( ( ( ) ) ) ( ) ( ( ) ( ( )
1 0 1 2 3 2 1 0 1 0 1 2 1 2 3 2
</pre>
Now, someone who looks at this can probably see that the number goes up for a left parenthesis, and down for a right parenthesis.
"Image", however, does not handle the detail.
The issue here is a transcriber is likely to want to transcribe this as *text*.
But it's really not.
This is again, tabular data, which is best represented in a table.
Table of this:
Character|Counter
---|---
(|1
)|0
(|1
(|2
(|3
)|2
)|1
)|0
(|1
)|0
(|1
(|2
)|1
(|2
(|3
)|2
Raw text in markdown:
<pre>
Character|Counter
---|---
(|1
)|0
(|1
(|2
(|3
)|2
)|1
)|0
(|1
)|0
(|1
(|2
)|1
(|2
(|3
)|2
</pre>
Insanely simple!
Look for clues of tabular data.
Things which have a one to one correspondence of any kind can usually be represented as a table, even if it's only "aligned" on the slide or note.
## Math Expressions & MathML
Here is a more complex example:
using math within a presentation.
Let's take for example the mathematical expression $$16 = 2^{4}$$. This is a very simple math expression that completely breaks in some cases.
When converting some math expressions to text, it will convert that expression as $$16 = 24$$, erasing the superscript to denote the exponent.
This gets even worse with large mathematical expressions like this:
{% katex display %}
\text{B2U}(X) = \sum_{i=0}^{w-1} x_{i} \times 2^{i}
{% endkatex %}
Here is what I get by extracting the text from the PDF:
<pre>
B2U(X ) =
w-1
Σ xi •2
i=0
i
</pre>
And this is generous, as the sigma sign, bullet point, equal sign and minus sign were for some reason not UTF-8 encoded so it displayed as a chat sign emoji, down arrow, video camera and book sign respectively.
Not sure about you, but I certainly can't get the equation out of that mess.
These can be written in LaTeX, then converted to MathML (an accessible math format) using [KaTeX](https://katex.org).
Here's an example of what to write to product the function above:
<pre>
\text{B2U}(X) = \sum_{i=0}^{w-1} x_{i} \times 2^{i}
</pre>
For someone who is doing transcription as a *job* for visually impaired students,
I would go so far as to say to learn this is a necessity.
1. It's not difficult. You can learn the vast majority of LaTeX math syntax in an afternoon.
2. It's easier for *everyone* to read. Especially with KaTeX. KaTeX is able to convert the formula to both MathML for screenreader users and HTML markup for people who just want to see those fancy math expressions.
Likely, the teacher is already using some LaTeX derivative to create the math in the first place,
they might as well use a program like KaTeX, MathJax or likewise to convert it to MathML.
## Code & Output
How did it even happen that entire programs and outputs were just ignored with the label "picture of output" is beyond me.
Everything should be transcribed.
Whoever transcribed that document should be fired.
## Conclusion
To teachers:
Presenting information in plain text, or at least having alternates forms of images, diagrams and math formulas makes education better for everyone, not just blind students.
It makes it better for people running on cheaper devices which may not handle running heavy software like Microsoft PowerPoint;
it makes it better for people who use operating systems other than MacOS and Windows (this is especially important in the technology sector, where Linux/BSD users make up a sizeable minority of users);
and finally, it makes it easier to search through the content of all notes at once using simple text-manipulation tools.
To accessibility departments:
Running a `pdftotext` program, or simply transcribing handwritten notes is not enough to properly describe slides and notes---handwritten or not.
Every diagram, math equation, annotation, piece of code or output---every single thing must be transcribed to plain text, or some alternate format like MathML.
I find it sad that a student (with their own full-time job) can product better work than someone who has this job exclusively at a major university.
Perhaps I am mistaken and the university has volunteers do this work.
In that case I guess you can't ask for too much, but somehow I feel like this is probably not the case.
Big sad.

@ -0,0 +1,18 @@
@import "vars";
body {
background-color: $background-color;
color: $normal-text-color;
font-family: -apple-system, helvetica, arial, sans-serif;
line-height: $line-height;
padding: .25em;
}
a {
color: $link-color;
}
a:visited {
color: $visited-link-color;
}
h1, h2 {
text-align: center;
}

@ -1,24 +1,4 @@
$normal-text-color: #ffffff;
$background-color: #222222;
$lighter-color: #ffffff;
$nav-link-color: #ffffff;
$nav-link-hover-color: black;
$link-color: #7ad;
$visited-link-color: #ff3492;
$last-p-padd: 1.5em;
$nav-padd: 1em;
$line-under: 1px solid #aaa;
$line-height: 1.5em;
$term-line-height: 1em;
$file-line-height: 1.2em;
$terminal-bg: #000000;
$terminal-fg: #00FF00;
$file-bg: #444444;
$file-fg: #ffffff;
@import "vars";
body {
background-color: $background-color;
@ -49,7 +29,7 @@ body {
padding: 0;
}
.projects a {
.projects .namelink {
font-weight: bold;
}

@ -0,0 +1,8 @@
@import "vars";
textarea {
width: 100%;
height: 300px;
overflow-y: scroll;
}

@ -0,0 +1,17 @@
$normal-text-color: #ffffff;
$background-color: #222222;
$lighter-color: #ffffff;
$nav-link-color: #ffffff;
$nav-link-hover-color: black;
$link-color: #7ad;
$visited-link-color: #ff3492;
$last-p-padd: 1.5em;
$nav-padd: 1em;
$line-under: 1px solid #aaa;
$line-height: 1.5em;
$term-line-height: 1em;
$file-line-height: 1.2em;
$terminal-bg: #000000;
$terminal-fg: #00FF00;
$file-bg: #444444;
$file-fg: #ffffff;

@ -0,0 +1,3 @@
---
---
@import "book";

File diff suppressed because one or more lines are too long

@ -0,0 +1,3 @@
---
---
@import "markdown";

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={771:function(t){t.exports=e}},r={};function n(e){var a=r[e];if(void 0!==a)return a.exports;var i=r[e]={exports:{}};return t[e](i,i.exports,n),i.exports}n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,{a:t}),t},n.d=function(e,t){for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var a={};return function(){n.d(a,{default:function(){return s}});var e=n(771),t=n.n(e),r=function(e,t,r){for(var n=r,a=0,i=e.length;n<t.length;){var o=t[n];if(a<=0&&t.slice(n,n+i)===e)return n;"\\"===o?n++:"{"===o?a++:"}"===o&&a--,n++}return-1},i=/^\\begin{/,o=function(e,t){for(var n,a=[],o=new RegExp("("+t.map((function(e){return e.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&")})).join("|")+")");-1!==(n=e.search(o));){n>0&&(a.push({type:"text",data:e.slice(0,n)}),e=e.slice(n));var l=t.findIndex((function(t){return e.startsWith(t.left)}));if(-1===(n=r(t[l].right,e,t[l].left.length)))break;var d=e.slice(0,n+t[l].right.length),s=i.test(d)?d:e.slice(t[l].left.length,n);a.push({type:"math",data:s,rawData:d,display:t[l].display}),e=e.slice(n+t[l].right.length)}return""!==e&&a.push({type:"text",data:e}),a},l=function(e,r){var n=o(e,r.delimiters);if(1===n.length&&"text"===n[0].type)return null;for(var a=document.createDocumentFragment(),i=0;i<n.length;i++)if("text"===n[i].type)a.appendChild(document.createTextNode(n[i].data));else{var l=document.createElement("span"),d=n[i].data;r.displayMode=n[i].display;try{r.preProcess&&(d=r.preProcess(d)),t().render(d,l,r)}catch(e){if(!(e instanceof t().ParseError))throw e;r.errorCallback("KaTeX auto-render: Failed to parse `"+n[i].data+"` with ",e),a.appendChild(document.createTextNode(n[i].rawData));continue}a.appendChild(l)}return a},d=function e(t,r){for(var n=0;n<t.childNodes.length;n++){var a=t.childNodes[n];if(3===a.nodeType){var i=l(a.textContent,r);i&&(n+=i.childNodes.length-1,t.replaceChild(i,a))}else 1===a.nodeType&&function(){var t=" "+a.className+" ";-1===r.ignoredTags.indexOf(a.nodeName.toLowerCase())&&r.ignoredClasses.every((function(e){return-1===t.indexOf(" "+e+" ")}))&&e(a,r)}()}},s=function(e,t){if(!e)throw new Error("No element provided to render");var r={};for(var n in t)t.hasOwnProperty(n)&&(r[n]=t[n]);r.delimiters=r.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\begin{equation}",right:"\\end{equation}",display:!0},{left:"\\begin{align}",right:"\\end{align}",display:!0},{left:"\\begin{alignat}",right:"\\end{alignat}",display:!0},{left:"\\begin{gather}",right:"\\end{gather}",display:!0},{left:"\\begin{CD}",right:"\\end{CD}",display:!0},{left:"\\[",right:"\\]",display:!0}],r.ignoredTags=r.ignoredTags||["script","noscript","style","textarea","pre","code","option"],r.ignoredClasses=r.ignoredClasses||[],r.errorCallback=r.errorCallback||console.error,r.macros=r.macros||{},d(e,r)}}(),a=a.default}()}));

File diff suppressed because one or more lines are too long

@ -0,0 +1,110 @@
const SAVE_HTML_KEY = "KatexHTMLOutput";
const SAVE_MD_KEY = "MarkdownInput";
const updateRender = async () => {
const htmlDiv = document.getElementById("html-output");
renderMathInElement(htmlDiv, {
delimiters: [
{left: "$$", right: "$$", display: true},
{left: "$", right: "$", display: false}
],
throwError: false,
});
}
const download = async (filename, mime, text) => {
let ele = document.createElement("a");
ele.setAttribute("href", "data:" + mime + ";charset=utf-8," + encodeURIComponent(text));
ele.setAttribute("download", filename);
ele.style.display = 'none';
document.body.appendChild(ele);
ele.click();
document.body.removeChild(ele);
};
const downloadMd = async () => {
const md = document.getElementById("markdown-input");
download("markdown.md", "text/plain", md.value);
};
const getRequiredCss = async () => {
let css = "";
const css1 = document.getElementById("main-stylesheet").sheet;
const css2 = document.getElementById("math-stylesheet").sheet;
css += Array.from(css1.cssRules).map(rule => rule.cssText).join(' ');
css += Array.from(css2.cssRules).map(rule => rule.cssText).join(' ');
return css
};
const downloadHTML = async () => {
const css = await getRequiredCss();
const htmlEle = document.getElementById("html-output");
let html = "<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><style>"
html += css;
html += "</style></head><body>";
html += htmlEle.innerHTML;
html += "</body></html>";
download("output.html", "text/html", html);
};
const setStatus = async (msg) => {
const statusBox = document.getElementById("status");
statusBox.innerText = msg;
};
const saveHTML = async () => {
setStatus("Saving HTML...");
const html = document.getElementById("html-output");
localStorage.setItem(SAVE_HTML_KEY, html.innerHTML);
setStatus("Saved");
};
const saveMd = async () => {
setStatus("Saving markdown...");
const md = document.getElementById("markdown-input");
localStorage.setItem(SAVE_MD_KEY, md.value);
setStatus("Saved");
};
const renderHTML = async () => {
setStatus("Rendering...");
const md = new remarkable.Remarkable({
html: true,
});
const markdownInput = document.getElementById("markdown-input");
const htmlOutput = document.getElementById("html-output");
const newHtml = md.render(markdownInput.value);
htmlOutput.innerHTML = newHtml;
updateRender();
saveMd();
saveHTML();
setStatus("Done");
}
document.addEventListener("DOMContentLoaded", async () => {
console.log("loaded");
// preload save
if (localStorage.getItem(SAVE_HTML_KEY) !== null){
const html = document.getElementById("html-output");
html.innerHTML = localStorage.getItem(SAVE_HTML_KEY);
}
if (localStorage.getItem(SAVE_MD_KEY) !== null){
const md = document.getElementById("markdown-input");
md.value = localStorage.getItem(SAVE_MD_KEY);
}
const updateBtn = document.getElementById("update-html");
updateBtn.addEventListener("click", renderHTML);
const saveMdBtn = document.getElementById("save-markdown");
saveMdBtn.addEventListener("click", saveMd);
const saveHTMLBtn = document.getElementById("save-html");
saveHTMLBtn.addEventListener("click", saveHTML);
const downloadMdBtn = document.getElementById("download-markdown");
downloadMdBtn.addEventListener("click", downloadMd);
const downloadHTMLBtn = document.getElementById("download-html");
downloadHTMLBtn.addEventListener("click", downloadHTML);
});

File diff suppressed because one or more lines are too long

@ -0,0 +1,10 @@
---
layout: default
title: "Book Notes"
---
<ul>
{% for notes in site.book_notes %}
<li><a href="{{ notes.url }}">{{ notes.title }}</a></li>
{% endfor %}
</ul>

@ -3,8 +3,34 @@ layout: default
title: "Emacspeak Tutorial Resources"
---
# Emacspeak Tutorial Resources
This is a list of resources and videos associated with my emacspeak tutorials.
They are grouped by language and library.
Although I try my best to keep each individual video as self-sufficient as possible...
sometimes it's not quite possible. Especially with complex libraries.
The lack of sorting is intentional.
See Luke Smith's video on [why to ramble and not be sequential](https://www.youtube.com/watch?v=ent5g6_gnik).
<!--
TODO: host on at least one alternate and one self-hosted platform.
MUST be done before making new videos.
-->
<ul>
{% for tut in site.emacspeak_tutorials %}
<li><a href="{{ tut.url }}">{{ tut.title }}</a></li>
{% assign languages = site.emacspeak_tutorials | group_by: "language" %}
{% for language in languages %}
<li id="{{ language.name | downcase }}">{{ language.name }}
<ul>
{% assign libraries = language.items | group_by: "library" %}
{% for library in libraries %}
<li id="{{ language.name | downcase }}-{{ library.name | downcase }}">{{ library.name }}
<ul>
{% for tutorial in library.items %}
<li id="{{ language.name | downcase }}-{{ library.name | downcase }}-{{ tutorial.name }}"><a href="{{ tutorial.url }}">{{ tutorial.title }}</a></li>
{% endfor %}
</ul>
{% endfor %}
</li>
{% endfor %}
</ul>

@ -6,6 +6,9 @@ title: "Ideas"
## 1. Accessible BIOS
Update:
See my blog post with the guy who's writing the [new audio driver into EDK2](/2021/06/21/uefi-audio/).
Some server motherboards include serial UART/I<sup>2</sup>C ports which can be used to manage a BIOS via serial.
If this is possible, would it be able to attach to a braille display via an intermediary like a Rockchip/Pi SBC or Arduino compatible chip using [BRLTTY](https://brltty.app) and serial input from the motherboard?
Maybe not as it [appears to require](https://tldp.org/HOWTO/Remote-Serial-Console-HOWTO/rhl-biosserial.html) a full Unicode terminal, which I have the suspicion that BRLTTY will not be able to automatically filter out the formatting characters.
@ -23,6 +26,7 @@ Either I am not setting it up correctly, or I do not have the proper sound setup
This requires more research and investment to understand UEFI, HDA audio, what systems have it and how to work with words and other sounds.
## 2. Terminal-oriented browser
Use selenium to allow a cross-engine compatible terminal-browser with JS support. Yes, sure, it has all the bloat of the modern web as it uses the full code of Chrome/Firefox/Webkit---but at least it can be used in the terminal.
@ -34,7 +38,7 @@ Change backend on-the-fly with a page reload. So if a website doesn't work with
Just an idea.
## Dead Simple Chess App
## 3. Dead Simple Chess App
I want to make a simple chess app which can connect to multiple backends
(i.e. Lichess, Chess.com, etc.) and then users can use one app to play many games.
@ -42,7 +46,7 @@ This should be quite simple given how easy the lichess API is, and the [chess.co
> This is read-only data. You cannot send game-moves or other commands to Chess.com from this system. ***If you wish to send commands, you will be interested in the Interactive API releasing later this year.***
## Open-Source VPN Deployment System
## 4. Open-Source VPN Deployment System
Help my business and others start their own.
@ -60,3 +64,29 @@ Help my business and others start their own.
* A client would be able to use a native wireguard client on linux (i.e. store in `/etc/wireguard` (or its default location).
* A client would allow local options like blocking LAN, kill-switch support, and the ability to change your VPN region based on latest list of servers.
* The list of servers will be updated manually with `bt update`.
## 5. 3d printing of Google Maps/OpenStreetMaps data for the visually impaired.
A larger project, to be sure, but one I think could be of interest.
Imagine being able to download some data from Google or OpenStrretMaps,
then put it into a program and have it generate a 3d map which can be printed.
Unsure what to do, as the braille overlay on top of the streets and important buildings, etc. needs to be of a uniform size (braille cannot be scaled) but the buildings, streets, and parks do need to be scaled in size.
I think for beginning, forget the braille entirely and simply product a map.
This can be done in the STL file format or some intermediary if that is easier.
Roads will have a slight border on the side,
parks will have a diamond texture,
buildings will have slight rectangular borders (slightly wider than the roads),
paths will be a thin line, and the label for the path will need to extend the thin line into a (rounded) rectangle with text on it.
Start with roads.
Get a road, get it to generate the correct shape.
Then add a border around the side.
Then, add 4 more roads and figure out how to intersect them.
If it can be done on a display, it can be done in a file.
Start with that. Wow what a daunting project!
This is being worked on through the [touch-mapper](https://github.com/skarkkai/touch-mapper) project.
They do not, however, have labels yet.

@ -25,7 +25,7 @@ I have all of my code projects hosted on <a href="https://github.com/TTWNO">my G
{% for project in site.data.projects %}
<li>
<p>
<a href="{{ project.link }}">{{ project.name }}</a> &mdash; {{ project.description }}
<a class="namelink" href="{{ project.link }}">{{ project.name }}</a> &mdash; {{ project.description }}
</p>
</li>
{% endfor %}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save