Issues or comments? Email me!
+{% include openring.html %}
+
diff --git a/_layouts/post.html b/_layouts/post.html
index 6ed4107..b77e537 100644
--- a/_layouts/post.html
+++ b/_layouts/post.html
@@ -1,9 +1,9 @@
---
layout: default
---
-~ / {{page.category}} / {{page.title}}
-
- {{ page.date | date: "%Y-%m-%d" }}
+~ / {{page.category}} / {{page.title}}
+
+ {{ page.date | date: "%Y-%m-%d" }} |
{{ content | reading_time | pluralize: "minute" }}
diff --git a/_posts/blog/2024-02-28-microzig-display-driver.md b/_posts/blog/2024-02-28-microzig-display-driver.md
index 40a8311..31c50da 100644
--- a/_posts/blog/2024-02-28-microzig-display-driver.md
+++ b/_posts/blog/2024-02-28-microzig-display-driver.md
@@ -35,7 +35,7 @@ But where is the fun in that?! Something about this project wouldn't let me just
I started out by trying to just send commands to the screen. I knew the default address of the device from the datasheet (0x3C), and started firing commands over I2C to try and provoke any sort of reaction. The MicroZig driver allowed me to this super easily. Just setup the I2C device, and then send data using the `write_blocking` function. I put the sending code inside a little function to make things easier to parse:
-```
+```zig
const i2c0 = i2c.num(0);
_ = i2c0.apply(.{
.clock_config = rp2040.clock_config,
@@ -44,7 +44,7 @@ _ = i2c0.apply(.{
.baud_rate = 400000,
});
```
-```
+```zig
pub fn send(bytes: []const u8) !void {
const a: i2c.Address = @enumFromInt(0x3C);
_ = i2c0.write_blocking(a, bytes) catch {
@@ -57,7 +57,7 @@ The SSD1306, stood firm, resolutely denying me even a single pixel. There is no
It was at this point that I became really impressed with Zig's compilation, and in particular the caching. As part of this process, I was messing around a lot with my code, and then building and loading onto the Pico with the following command:
-```
+```bash
zig build; picotool load -x zig-out/firmware/pico_i2c.uf2
```
@@ -83,7 +83,7 @@ Surely I was almost there? Well, almost yes, but not quite. Matiasus's commands
The thing that made the breakthrough for me was [this holy grail of a blog post](https://nnarain.github.io/2020/12/01/SSD1306-OLED-Display-Driver-using-I2C.html). Just exactly what I needed at exactly the right time. It calmly and thoroughly explains the process of communicating with the display, as well as the initialisation. I was mostly right in what I interpreted from the datasheet, but it is always nice to have things confirmed by someone who clearly understands this better than you. This led me to the following initialisation commands:
-```
+```zig
const INIT = [_]u8{
CONTROL_COMMAND, 0xAE,
CONTROL_COMMAND, 0xA8, 0x1F,
diff --git a/_posts/blog/2025-05-17-pizza-for-privacy.md b/_posts/blog/2025-05-17-pizza-for-privacy.md
new file mode 100644
index 0000000..5813790
--- /dev/null
+++ b/_posts/blog/2025-05-17-pizza-for-privacy.md
@@ -0,0 +1,262 @@
+---
+title: Pizza for privacy
+category: blog
+layout: post
+---
+
+We recently received a suspicious letter though our door from something called 'The Origin Panel'. It described the details of an exclusive new research project, of which our household had been selected as special members! Wow, how exciting!
+
+The letter immediately set off alarm bells for me and my partner. The project is being run by the ISBA (Incorporated Society of British Advertisers), and its stated aim is "to make advertising more relevant to you". This indicates that they want to collect data to target ads, and indeed this is the basis of the project, to allow us to "anonymously share \[our\] media habits". The letter is also filled with language designed to make the project sound legitimate and incentivize signups. "exciting new research project", "scientific sample", "invitation only". There is also a stated £100 available if you complete signup and installation (!) in four weeks.
+
+This sounds terrible and awful and various other synonyms. Obviously we weren't going to sign up, but it begged to be looked into more. This post is the results of my investigation.
+
+# # Who is behind the curtain?
+
+The Origin Panel, henceforth referred to as Big Brother, provided a helpful link for us to get started: [www.theoriginpanel.uk](www.theoriginpanel.uk). The first question I had was "who is behind this?". Navigating to the About Us section of their website, we can find out that this operation is being run by a company called [Kantar](https://www.kantar.com/). They are a branding and advertising consultancy of the usual ilk, running this on behalf of the ISBA, as mentioned in their letter. This doesn't tell us much about who is actually behind it all, but I think you can probably already guess at the culprits. They provide this helpful description of their stated goals:
+
+> \[Big Brother\] is ISBA’s advertiser backed programme, designed to create a blueprint for cross media measurement. It is part of a global initiative to capture the value of advertising in a constantly changing consumer environment.
+>
+> \[Big Brother\] is designed to address the needs of advertisers, who need to understand and plan campaigns across digital and broadcast platforms. They point out the lack of either a standardised approach to video and display measurement or a common measurement approach across media, particularly across digital platforms and between digital platforms and broadcaster TV.
+>
+> ISBA has been working with the WFA (World Federation of Advertisers) to convene advertisers, online platforms, and broadcasters to discuss a common global approach.
+
+Sorting through the corporate waffle, this seems to part of a scheme to standardise tracking across both internet and traditional (broadcast) mediums to create a unified approach. Why anyone would want to help in this endeavour is beyond me.
+
+After more poking around the website, I found what I was looking for. [This page](https://www.theoriginpanel.uk/OnlinePublishers) reveals the entities who get the collected data. Get your last guesses in now! And ... drumroll please ... it is ... Meta/Google/Amazon/TikTok. Keyser Soze and all that.
+
+This page also contains "\[the\] approved list of websites, platforms or applications that our meters collect “Internet Data” from". So let's get into that.
+
+# # What are you signing up for?
+
+There have been various mentions so far of "installations" and "meters". So what would I actually have to do to get that sweet £100 bonus?
+
+Watching through [the User Guides](https://www.theoriginpanel.uk/UserGuidePublic), there are two different types of meters that will be installed in your home when you sign up, one for each TV in your home, and one that plugs directly into the back of your router (!) that looks suspiciously like a Raspberry Pi in a case.
+
+## ## The TV meter
+
+The TV meters are a small touchscreen display that goes below the TV (and corresponding remote control).
+
+
+
+Not disturbing at all!
+
+
+Every time you watch TV, you have to indicate your presence. They also emphasise that you have to do it even if you just walk into a room where the TV is on.
+
+
+
+Normal human greeting
+
+
+But don't worry, everyone gets a little avatar on the screen. You can even set up extra ones for guests! But only if they are strictly male or female. I guess non-binary people can't participate. Lucky for some!
+
+
+
+A handy button for when you banish all women from your house
+
+
+The device is constantly recording sound to identify which channel is being watched. I guess this works by detecting certain frequencies, but when Google and Amazon are involved, who knows.
+
+## ## The router meter
+
+This is definitely not a Raspberry Pi that you plug into the router.
+
+
+
+Definitely not
+
+
+It monitors your internet usage of a bunch of different sites, including but not limited to:
+
+- Amazon Music
+- Amazon Prime
+- AppleTVPlus
+- Audible
+- BBC
+- BBC Sounds
+- DAZN
+- Discovery
+- Disney+
+- Facebook/Instagram
+- Google
+- Hayu
+- Lionsgate+
+- Netflix
+- NowTV
+- Paramount+
+- SkyGo
+- SkyMedia
+- SkyNews
+- Sound Cloud
+- Spotify
+- STV
+- TikTok
+- Twitch
+- UKTV
+- Viaplay
+- YouTube Music
+- ...
+- Full list here
+
+They really try to emphasise that it will only monitor traffic from the sites they are interested in, but it must be processing all of your outgoing traffic to achieve this. Also, does it really matter if they are tracking everything that you Google search anyway?
+
+There is a strange, almost urgent note in the [online meter PDF](https://www.theoriginpanel.uk/uploads/documents/UserGuides/Online%20meter%20guide%20v5.pdf):
+
+> It’s essential that we preserve your privacy and anonymity so please don’t share your participation on the [Big Brother] with anyone you don’t know. You should also avoid mentioning your participation on the panel with anyone on social media, in or outside your circle of family and close friends
+
+Surely, _surely_, if my data was being properly encrypted and anonymised, there would be no need for me to keep my participation a secret? There would be no way for the data to be connected back to me. Right guys? Right??
+
+They also show their full router name and WiFi password in the video, while they talk about all the precautions they have taken with your data.
+
+## ## Mobile app and software
+
+There is also an app to download, which seems like it will pop-up every time you open your phone to ask who is using it. Looks like there is also some intrusive software to download directly onto you PC/laptop too, just so they don't miss anything!
+
+Here is some information from their [Privacy Policy](https://www.theoriginpanel.uk/uploads/documents/OriginPrivacyPolicy.pdf)[^1] on the information that they collect:
+
+> Kantar will collect the following types of information from the approved list of websites, platforms or applications you visit:
+>
+> - Information about the website, platform or applications you use such as:
+> - How long you visit the for;
+> - How often you visit;
+> - How you navigate through the website, platform or application;
+> - The advertisements you see.
+> - Information about the device you use such as:
+> - The device model and name;
+> - Operating system;
+> - MAC address;
+> - IP address;
+> - Other unique online identifiers such as Ad ID (Advertising Identifier).
+
+Even more worryingly, the privacy policy indicates that your "pseudonymised" information will be directly shared with the companies involved:
+
+> For some websites and apps, it is not possible to identify the
+> advertisements that you have seen through the Focal Meter and so we rely
+> on data from the online publishers themselves
+>
+> ...
+>
+> Kantar will share encrypted unique identifiers with the online publishers
+> (such as your email address or social media handle) or your device (such
+> as a mobile advertising ID or other unique device identifier associated
+> with the device you use) in a matching process. The online publisher will
+> use this identifier to provide us data that includes your online activity and
+> advertisements that have been shown to you on your device(s) (including
+> time stamps of when such content was shown to you) and share this
+> information with Kantar. In some instances we may also receive back
+> demographic information such as age bracket, sex and approximate
+> location (such as region or city) which is used for verification purposes.
+
+
+
+Oh, and don't forget to notify us if you have a newborn baby!
+
+
+Now, it is obviously a terrible idea to sign up for this. You make it so easy for these companies to build up an advertising profile on you, and to develop newer, more intrusive technologies to get to the rest of us. You might be thinking "doesn't my ISP have all this information any way?". And you are (partially) right, your ISP can see all the sites you visit and the requests you make. But this process is evidently much more intrusive and insidious, with multiple devices and pieces of software collecting all the data they can. If they could get all this from your ISP, why would they need to install these devices?
+
+You would be giving up all of your online privacy, for nothing. Well, not nothing, but we'll get into that next.
+
+# # What do you get in return?
+
+So, what is the incentive here? Apart from the £100 signup bonus, what do they give you in return for your privacy?
+
+There is a page on their site that details the ["Reward Scheme"](https://www.theoriginpanel.uk/RewardSchemePublic) (uh oh).
+
+Basically, in return for hoovering up all of your data you will collect points, which can then be redeemed at a specific list of shops. I couldn't find information on exactly where the points can be redeemed, nor any information about how many points you get each month. The [Terms & Conditions](https://www.theoriginpanel.uk/uploads/documents/Origin-TCs_v2-Legal-Entity-Update.pdf) say that 1000 points = £1, so it will likely take a while to get anything meaningful. One example they give in their promotional video is a pizza.
+
+
+
+Tastes like freedom
+
+
+They also say that there are monthly and quarterly prize draws, of up to £1000.
+
+
+
+"Spend" your points
+
+
+So what do you get in return? Not a whole lot. Just some lousy vouchers that can only be spent at specific retailers under strict conditions, and entry into a lottery.
+
+Don't worry though, you can be a good citizen and sacrifice your precious points to a "Kantar-approved charity"!
+
+
+
+Not sure what the local food bank is going to do with Kantar points
+
+
+# # Does anyone actually sign up?
+
+So surely nobody actually signs up for this? You would think, but humans never cease to amaze.
+
+Looking at the [Big Brother's TrustPilot page](https://uk.trustpilot.com/review/theoriginpanel.uk), we can see a promising 2.2 average.
+
+The reviews can be sorted into three categories:
+
+1. People like me, complaining after having received the letter and done a little bit of research. These account for most of the 1 stars.
+2. Fake 5 star reviews. There is a lot of similar language being used throughout the 5 star reviews here. I have not done a proper analysis, but it definitely smells when all the 5 star reviews sound so similar. Looking forward to earning some rewards now!
+3. Real experiences. There does seem to be people who actually signed up, with both "good" and bad experiences.
+
+
+
+
+
+I am so glad "customer" likes their Pizza Hut salad
+
+
+
+
+
+
+Hey, that's the name of this post!
+
+
+There are many reviews of people complaining about terrible customer service, and not being able to access their rewards or remove the software. For those who do seem to be enjoying it are content with \~£10 a month in vouchers. More than I expected, but still about 25 orders of magnitude away from the price of your privacy.
+
+Either that, or pure financial exploitation:
+
+
+
+Struggling financially
+
+
+# # Bloody hell
+
+The last thing I did was take a look at their [FAQs page](https://www.theoriginpanel.uk/FAQPublic), and things somehow got even worse.
+
+Q: **What if we go on holiday?**
+> It is highly recommended that you inform our support team in advance if you are planning to go away. You can do this by calling or emailing us with your holiday dates. In order to check that the meters we send to panellists are functioning properly, we monitor TV viewing and Internet usage levels on a daily basis. If we are given notice that you will be away from the home, we will take your absence into account when monitoring usage.
+
+A: **Please tell us every time your house is going to be empty for a while.**
+
+Q: **Do you keep my children's information?**
+> In compliance with the Terms and Conditions of your panel membership, we do not release any TV or Web usage data connected to household members who are under the age of 16 for any analysis. No internet data is collected from such members, and the TV data we collect from those under the age of 16 is only used for quality control purposes within Kantar.
+
+A: **Yes, yes we do.**
+
+Q: **How long does the program last?**
+> The [Big Brother] program is an on-going project with no specific end date.
+
+A: **FOREVER.**
+
+Q: **Do you sell my data to advertisers?**
+> No, we do not pass on any details to advertisers or marketers. We may use third party service providers to assist in our administration of [Big Brother].
+
+A: **No, we give it to them for free.**
+
+Q: **Are you reading my children's messages?**
+> Our software is designed to only register when it detects audio and to communicate that back to our servers along with the source of that sound. We absolutely cannot read instant messages, emails or any other personal communications.
+
+A: **No! We are just listening to their conversations!**
+
+# # Conclusion
+It is frankly staggering that this exists. It baffles me that anyone would come up with such a scheme, let alone actually implement it! Advertising is a leech on our society, and the erosion of personal privacies and rights is something that we need to fight at every turn. Schemes like this should heavily regulated, if not outright illegal.
+
+I cannot really blame the people getting £10 a month in vouchers, they are not the problem here. Inflation is high and money is hard to come by. The problem is the whole system, trying to squeeze out as much money as possible by monitoring everything we ever do or say, so they can sell it back to us.
+
+(If there is anyone out there who has received one of these letters and is smarter (and braver) than me, I would love to see a proper teardown of these devices and a deep-dive into what is actually being tracked. Please, please, please write a big blog post about it.)
+
+In conclusion, DO NOT SIGN UP FOR THIS. It is a massive invasion of your personal privacy, for little to nothing in return. If the product is free, YOU are the product. There is no such thing as free pizza.
+
+
+[^1]: Surprised they have one!
diff --git a/_posts/til/2023-03-01-nextcloud-php-errors.md b/_posts/til/2023-03-01-nextcloud-php-errors.md
index 2461de9..a4c3a6b 100644
--- a/_posts/til/2023-03-01-nextcloud-php-errors.md
+++ b/_posts/til/2023-03-01-nextcloud-php-errors.md
@@ -1,7 +1,7 @@
---
title: Fixing php errors in a Nextcloud docker-compose configuration
-categories:
-- til
+category: til
+layout: post
tags:
- php
- nextcloud
@@ -15,7 +15,7 @@ I was trying to rescan the files in my Nextcloud server (running on Raspberry Pi
I was trying using the following syntax to call `occ` and scan the files:
-```
+```bash
sudo -u www-data php /path/to/nextcloud/occ files:scan --all
```
@@ -32,7 +32,7 @@ It took me a decent amount of time to diagnose the exact issue, but eventually I
Running `php -m` will print out the list of currently installed PHP modules. I noticed I was missing quite a few of the required modules, but the one that was causing my issue was the missing `pdo_mysql` module.
This can be installed by running:
-```
+```bash
sudo apt-get install php7.4-mysql
```
**Note: This command will change based on your OS, PHP version and database type**
@@ -49,7 +49,7 @@ From first glance, this looks like something wrong in the DNS name resolution. T
Eventually however, after a long and perilous journey over the high seas of Nextcloud forums and StackOverflow, I found [this example](https://techoverflow.net/2020/07/17/how-to-run-nextcloud-php-occ-in-a-docker-compose-configuration/) of running `php occ` in a docker-compose configuration.
This led me to running this command:
-```
+```bash
docker-compose exec -u www-data nextcloud-app php occ files:scan --all
```
**Note: replace nextcloud-app with the name of your Nextcloud container. Also, this command must be run from the directory of your Nextcloud docker-compose.yml**
diff --git a/_posts/til/2023-04-19-running-python-in-cron-in-docker.md b/_posts/til/2023-04-19-running-python-in-cron-in-docker.md
index 0e8ef95..4e25893 100644
--- a/_posts/til/2023-04-19-running-python-in-cron-in-docker.md
+++ b/_posts/til/2023-04-19-running-python-in-cron-in-docker.md
@@ -1,6 +1,7 @@
---
title: Running a Python script periodically in a Docker container using cron
category: til
+layout: post
---
Recently, my partner gave a great idea for utilising my old Kindle: generate a "newspaper" each morning from a bunch of RSS feeds, and email it to the Kindle using "Send-to-Kindle" feature (a blog post about this project is in the works).
@@ -10,7 +11,7 @@ I loved this idea, and thought it would be no problem to get a Python script up
## 1. Double check the user
A lot of problems with `cron` come down to user privileges. Each user has their own `crontab`, and then there is the system-wide *root* `crontab`. The first issue I ran into with creating a `cron` job inside a container was that Docker created the crontab as a non-root user. This issue presented itself to me when I tried to run the following command, to list the current cronjobs in the Docker container:
-```
+```bash
docker-compose exec container-name crontab -l
```
This returned the following output:
@@ -18,24 +19,24 @@ This returned the following output:
no crontab for root
```
Now, it is not necessarily a problem to have non-root `cron` jobs, but just make absolutely certain that you are creating the jobs with the user you expect. For me, I wanted to run as `root`, so I added to following line to my docker-compose.yml:
-```
+```yaml
user: root
```
Now, the `root` user will be used when building your Docker image and the created `crontab` will be where you expect.
## 2. Missing dependencies
When `cron` calls your Python script, you may run into issues with `ModuleNotFoundError` or `ImportError`, where Python cannot find your installed packages. This is because `cron` does not have access to your system environment variables, including the Python path. You can resolve most of these errors with imports by adding the `PYTHONPATH` environment variable to your `crontab`. This should be the path to your `site-packages` folder, something like this:
-```
+```bash
PYTHONPATH=/usr/bin/local/python3
```
You may also need to add a shebang (`#!`) to your Python script to direct `cron` to the correct version. You can find the Python location with one of the following commands:
-```
+```bash
which python
which py
which python3
```
*NOTE*: These commands must be performed in your Docker container when it is up and running. In `docker-compose` syntax this would be the following (with the name of your container instead of `container-name`):
-```
+```bash
docker-compose exec container-name which python3
```
You can then add this to the top of your Python script, as follows:
@@ -44,11 +45,11 @@ You can then add this to the top of your Python script, as follows:
```
## 3. Still missing dependencies
Some modules will still run into errors even when the PYTHONPATH variable has been set. In particular, I ran into problems with `reportlab` and `Pillow/PIL`:
-```
+```python
ImportError: cannot import name '_imaging' from 'PIL'
```
This was solved by adding the system PATH to the `crontab` as well. The system path is included in the default `crontab` that is created when you first run `crontab -e`:
-```
+```bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
```
Therefore, it is a good idea to include it if you are making a new `crontab` to make sure `cron` can find everything it needs to.
@@ -72,7 +73,7 @@ I was able to resolve these by adding `python3-dev`, `wheel` and `Cmake` to my `
I hope this helped you resolve some errors! I've included my Dockerfile, docker-compose.yml and crontab below if you want to set up a similar project or adjust your own files. The full repo is also available [here](https://github.com/andrwcnln/watchman).
Dockerfile:
-```
+```docker
FROM python:3
COPY . .
@@ -90,7 +91,7 @@ RUN crontab crontab
CMD cron -f
```
docker-compose.yml:
-```
+```yaml
version: "2.4"
services:
@@ -105,9 +106,9 @@ services:
dockerfile: Dockerfile
```
crontab:
-```
+```cron
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PYTHONPATH=/usr/bin/local/python3
15 7 * * * python3 /main.py >> /var/log/cron.log 2>&1
-```
\ No newline at end of file
+```
diff --git a/_posts/til/2024-01-10-using-rvm-with-alacritty.md b/_posts/til/2024-01-10-using-rvm-with-alacritty.md
index ce969c0..68b743b 100644
--- a/_posts/til/2024-01-10-using-rvm-with-alacritty.md
+++ b/_posts/til/2024-01-10-using-rvm-with-alacritty.md
@@ -25,11 +25,11 @@ Please visit https://rvm.io/integration/gnome-terminal/ for an example.
So, we need to be in a login shell. Unfortunately, Alacritty does not have a settings GUI [like gnome-terminal](https://rvm.io/integration/gnome-terminal) and similar, and after thorough investigation I couldn't find a way to set this up through Alacritty. We need a different solution.
Luckily, this is possible through the shell itself. You can open a login shell by running the following command (press Ctrl+D to return to the interactive shell):
-```
+```bash
bash --login
```
This also works with `zsh`, or Z-shell:
-```
+```bash
zsh --login
```
Unfortunately for all you fishers out there, I couldn't find a way to to this with `fish`. If you know how, [send me an email!](mailto:andrew@andrewconl.in)
diff --git a/assets/css/default.css b/assets/css/default.css
index fa9a7a4..aaadddf 100644
--- a/assets/css/default.css
+++ b/assets/css/default.css
@@ -1,3 +1,5 @@
+@import "gruvbox.css";
+
@font-face {
font-family: 'Geist';
src: url('/assets/fonts/Geist-Regular.otf') format('opentype');
@@ -5,6 +7,13 @@
font-style: normal;
}
+@font-face {
+ font-family: 'Geist Black';
+ src: url('/assets/fonts/Geist-Black.otf') format('opentype');
+ font-weight: normal;
+ font-style: normal;
+}
+
@font-face {
font-family: 'Geist Mono';
src: url('/assets/fonts/GeistMonoNerdFont-Regular.otf') format('opentype');
@@ -22,7 +31,7 @@ code {
div.container {
margin: 0 auto;
- zoom: 1.5;
+ zoom: 1.4;
}
div.content {
@@ -41,7 +50,7 @@ div.content {
}
div.container {
margin: 0 auto;
- zoom: 1.4
+ zoom: 1.3
}
}
@@ -67,7 +76,7 @@ div.content {
}
div.container {
margin: 0 auto;
- zoom: 1.0
+ zoom: 1.1
}
}
@@ -98,6 +107,10 @@ a:hover {
text-decoration: none;
border-bottom: 2px solid #FABD2F;
border-radius: 1px;
+
+ img.feed {
+ filter: brightness(0) saturate(100%);
+ }
}
img {
@@ -132,10 +145,42 @@ span.growshrink {
}
}
-p {
+body {
font-size: 14px
}
h1 {
font-size: 24px
}
+
+hr {
+ color: #32302F;
+ border-color: #32302F
+}
+
+blockquote {
+ /* background-color:#32302F; */
+ color: #A89984;
+ font-style: italic;
+ margin: 20px 0 30px;
+ padding-left: 20px;
+ border-left: 3px solid #A89984;
+ max-width:90%;
+ max-height:100%;
+ margin-left: auto;
+ margin-right: auto;
+ display: block;
+}
+
+figcaption {
+ color: #A89984;
+ text-align: center;
+ max-width:70%;
+ max-height:100%;
+ margin-left: auto;
+ margin-right: auto;
+ display: block;
+ font-size: 12px;
+ font-style: italic;
+ padding-top: 10px
+}
diff --git a/assets/css/gruvbox.css b/assets/css/gruvbox.css
new file mode 100644
index 0000000..c0af0cb
--- /dev/null
+++ b/assets/css/gruvbox.css
@@ -0,0 +1,73 @@
+.highlight .hll { background-color: #ffffcc }
+.highlight { background: #282828; color: #ebdbb2; background-color: #282828 }
+.highlight .c { color: #928374; font-style: italic; background-color: #282828 } /* Comment */
+.highlight .err { color: #ebdbb2; background-color: #282828 } /* Error */
+.highlight .esc { color: #ebdbb2; background-color: #282828 } /* Escape */
+.highlight .g { color: #ebdbb2; background-color: #282828 } /* Generic */
+.highlight .k { color: #fe8019; background-color: #282828 } /* Keyword */
+.highlight .l { color: #ebdbb2; background-color: #282828 } /* Literal */
+.highlight .n { color: #ebdbb2; background-color: #282828 } /* Name */
+.highlight .o { color: #fe8019; background-color: #282828 } /* Operator */
+.highlight .x { color: #ebdbb2; background-color: #282828 } /* Other */
+.highlight .p { color: #ebdbb2; background-color: #282828 } /* Punctuation */
+.highlight .ch { color: #928374; font-style: italic; background-color: #282828 } /* Comment.Hashbang */
+.highlight .cm { color: #928374; font-style: italic; background-color: #282828 } /* Comment.Multiline */
+.highlight .cp { color: #8ec07c; background-color: #282828 } /* Comment.Preproc */
+.highlight .c1 { color: #928374; font-style: italic; background-color: #282828 } /* Comment.Single */
+.highlight .cs { color: #928374; font-style: italic; background-color: #282828 } /* Comment.Special */
+.highlight .gd { color: #282828; background-color: #fb4934 } /* Generic.Deleted */
+.highlight .ge { color: #83a598; text-decoration: underline; background-color: #282828 } /* Generic.Emph */
+.highlight .gr { color: #ebdbb2; font-weight: bold; background-color: #fb4934 } /* Generic.Error */
+.highlight .gh { color: #b8bb26; font-weight: bold; background-color: #282828 } /* Generic.Heading */
+.highlight .gi { color: #282828; background-color: #b8bb26 } /* Generic.Inserted */
+.highlight .go { color: #504945; background-color: #282828 } /* Generic.Output */
+.highlight .gp { color: #ebdbb2; background-color: #282828 } /* Generic.Prompt */
+.highlight .gs { color: #ebdbb2; background-color: #282828 } /* Generic.Strong */
+.highlight .gu { color: #b8bb26; font-weight: bold; background-color: #282828 } /* Generic.Subheading */
+.highlight .gt { color: #ebdbb2; font-weight: bold; background-color: #fb4934 } /* Generic.Traceback */
+.highlight .kc { color: #fe8019; background-color: #282828 } /* Keyword.Constant */
+.highlight .kd { color: #fe8019; background-color: #282828 } /* Keyword.Declaration */
+.highlight .kn { color: #fe8019; background-color: #282828 } /* Keyword.Namespace */
+.highlight .kp { color: #fe8019; background-color: #282828 } /* Keyword.Pseudo */
+.highlight .kr { color: #fe8019; background-color: #282828 } /* Keyword.Reserved */
+.highlight .kt { color: #fabd2f; background-color: #282828 } /* Keyword.Type */
+.highlight .ld { color: #ebdbb2; background-color: #282828 } /* Literal.Date */
+.highlight .m { color: #d3869b; background-color: #282828 } /* Literal.Number */
+.highlight .s { color: #b8bb26; background-color: #282828 } /* Literal.String */
+.highlight .na { color: #b8bb26; font-weight: bold; background-color: #282828 } /* Name.Attribute */
+.highlight .nb { color: #fabd2f; background-color: #282828 } /* Name.Builtin */
+.highlight .nc { color: #ebdbb2; background-color: #282828 } /* Name.Class */
+.highlight .no { color: #d3869b; background-color: #282828 } /* Name.Constant */
+.highlight .nd { color: #ebdbb2; background-color: #282828 } /* Name.Decorator */
+.highlight .ni { color: #fabd2f; background-color: #282828 } /* Name.Entity */
+.highlight .ne { color: #fb4934; background-color: #282828 } /* Name.Exception */
+.highlight .nf { color: #fabd2f; background-color: #282828 } /* Name.Function */
+.highlight .nl { color: #fb4934; background-color: #282828 } /* Name.Label */
+.highlight .nn { color: #ebdbb2; background-color: #282828 } /* Name.Namespace */
+.highlight .nx { color: #ebdbb2; background-color: #282828 } /* Name.Other */
+.highlight .py { color: #ebdbb2; background-color: #282828 } /* Name.Property */
+.highlight .nt { color: #fb4934; background-color: #282828 } /* Name.Tag */
+.highlight .nv { color: #ebdbb2; background-color: #282828 } /* Name.Variable */
+.highlight .ow { color: #fe8019; background-color: #282828 } /* Operator.Word */
+.highlight .w { color: #ebdbb2; background-color: #282828 } /* Text.Whitespace */
+.highlight .mb { color: #d3869b; background-color: #282828 } /* Literal.Number.Bin */
+.highlight .mf { color: #d3869b; background-color: #282828 } /* Literal.Number.Float */
+.highlight .mh { color: #d3869b; background-color: #282828 } /* Literal.Number.Hex */
+.highlight .mi { color: #d3869b; background-color: #282828 } /* Literal.Number.Integer */
+.highlight .mo { color: #d3869b; background-color: #282828 } /* Literal.Number.Oct */
+.highlight .sb { color: #b8bb26; background-color: #282828 } /* Literal.String.Backtick */
+.highlight .sc { color: #b8bb26; background-color: #282828 } /* Literal.String.Char */
+.highlight .sd { color: #b8bb26; background-color: #282828 } /* Literal.String.Doc */
+.highlight .s2 { color: #b8bb26; background-color: #282828 } /* Literal.String.Double */
+.highlight .se { color: #b8bb26; background-color: #282828 } /* Literal.String.Escape */
+.highlight .sh { color: #b8bb26; background-color: #282828 } /* Literal.String.Heredoc */
+.highlight .si { color: #b8bb26; background-color: #282828 } /* Literal.String.Interpol */
+.highlight .sx { color: #b8bb26; background-color: #282828 } /* Literal.String.Other */
+.highlight .sr { color: #b8bb26; background-color: #282828 } /* Literal.String.Regex */
+.highlight .s1 { color: #b8bb26; background-color: #282828 } /* Literal.String.Single */
+.highlight .ss { color: #83a598; background-color: #282828 } /* Literal.String.Symbol */
+.highlight .bp { color: #fabd2f; background-color: #282828 } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #ebdbb2; background-color: #282828 } /* Name.Variable.Class */
+.highlight .vg { color: #ebdbb2; background-color: #282828 } /* Name.Variable.Global */
+.highlight .vi { color: #ebdbb2; background-color: #282828 } /* Name.Variable.Instance */
+.highlight .il { color: #d3869b; background-color: #282828 } /* Literal.Number.Integer.Long */
diff --git a/assets/fonts/Geist-Black.otf b/assets/fonts/Geist-Black.otf
new file mode 100644
index 0000000..c1489de
Binary files /dev/null and b/assets/fonts/Geist-Black.otf differ
diff --git a/assets/images/icons/rss.svg b/assets/images/icons/rss.svg
new file mode 100644
index 0000000..54759f9
--- /dev/null
+++ b/assets/images/icons/rss.svg
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/assets/images/origin-panel/andy.png b/assets/images/origin-panel/andy.png
new file mode 100644
index 0000000..a8a75b9
Binary files /dev/null and b/assets/images/origin-panel/andy.png differ
diff --git a/assets/images/origin-panel/baby.png b/assets/images/origin-panel/baby.png
new file mode 100644
index 0000000..e794263
Binary files /dev/null and b/assets/images/origin-panel/baby.png differ
diff --git a/assets/images/origin-panel/charity.png b/assets/images/origin-panel/charity.png
new file mode 100644
index 0000000..e740ce8
Binary files /dev/null and b/assets/images/origin-panel/charity.png differ
diff --git a/assets/images/origin-panel/customer.png b/assets/images/origin-panel/customer.png
new file mode 100644
index 0000000..9bf1136
Binary files /dev/null and b/assets/images/origin-panel/customer.png differ
diff --git a/assets/images/origin-panel/helloSARAH.png b/assets/images/origin-panel/helloSARAH.png
new file mode 100644
index 0000000..23c75af
Binary files /dev/null and b/assets/images/origin-panel/helloSARAH.png differ
diff --git a/assets/images/origin-panel/lisa.png b/assets/images/origin-panel/lisa.png
new file mode 100644
index 0000000..05b432d
Binary files /dev/null and b/assets/images/origin-panel/lisa.png differ
diff --git a/assets/images/origin-panel/pizza.png b/assets/images/origin-panel/pizza.png
new file mode 100644
index 0000000..dd55196
Binary files /dev/null and b/assets/images/origin-panel/pizza.png differ
diff --git a/assets/images/origin-panel/pizzaforprivacy.png b/assets/images/origin-panel/pizzaforprivacy.png
new file mode 100644
index 0000000..6f43b6b
Binary files /dev/null and b/assets/images/origin-panel/pizzaforprivacy.png differ
diff --git a/assets/images/origin-panel/removeallfemaleguests.png b/assets/images/origin-panel/removeallfemaleguests.png
new file mode 100644
index 0000000..040aea5
Binary files /dev/null and b/assets/images/origin-panel/removeallfemaleguests.png differ
diff --git a/assets/images/origin-panel/rpi.png b/assets/images/origin-panel/rpi.png
new file mode 100644
index 0000000..9d4d6fe
Binary files /dev/null and b/assets/images/origin-panel/rpi.png differ
diff --git a/assets/images/origin-panel/spend.png b/assets/images/origin-panel/spend.png
new file mode 100644
index 0000000..6178f59
Binary files /dev/null and b/assets/images/origin-panel/spend.png differ
diff --git a/assets/images/origin-panel/struggle.png b/assets/images/origin-panel/struggle.png
new file mode 100644
index 0000000..e265b26
Binary files /dev/null and b/assets/images/origin-panel/struggle.png differ
diff --git a/assets/images/origin-panel/whoispresent.png b/assets/images/origin-panel/whoispresent.png
new file mode 100644
index 0000000..15c943c
Binary files /dev/null and b/assets/images/origin-panel/whoispresent.png differ
diff --git a/deploy.sh b/deploy.sh
new file mode 100644
index 0000000..5d67182
--- /dev/null
+++ b/deploy.sh
@@ -0,0 +1,13 @@
+openring \
+ -s https://andyconlin.com/feed/ \
+ -s https://digitalflapjack.com/blog/index.xml \
+ -s https://gpanders.com/index.xml \
+ -s https://luke.hsiao.dev/atom.xml \
+ -s https://wickstrom.tech/feed.xml \
+ -s https://paulcapewell.com/feed/ \
+ -s https://www.splitbrain.org/feed/blog \
+ -s https://lilysthings.org/blog/rss.xml \
+ < _includes/openringTemplate.html \
+ > _includes/openring.html
+bundle exec jekyll build
+rsync -r _site andrew@itchy:/home/andrew/site
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000..d41fe3a
Binary files /dev/null and b/favicon.ico differ
diff --git a/projects.html b/projects.html
index 41e33f6..093f611 100644
--- a/projects.html
+++ b/projects.html
@@ -4,7 +4,7 @@ title: projects
---
{% include pageTitle.html %}
-
+
tictoc / Fast, simple and accurate Python timing. Written in Rust.
year-progress / A fun side project which gives the progress through the year, relative to other things.