Compare commits
No commits in common. "521af2d17abadfc2118c1d374eb6f95452afd82b" and "056ffa2d818b9aa87886e7195574f7ef36d18723" have entirely different histories.
521af2d17a
...
056ffa2d81
|
@ -1,5 +1,4 @@
|
||||||
---
|
---
|
||||||
title: Beets and Soulseek Or, How I Learned to Give Up and Love The Hoard
|
title: Beets and Soulseek Or, How I Learned to Give Up and Love The Hoard
|
||||||
date: 2025-02-03
|
date: 2025-02-03
|
||||||
draft: true
|
|
||||||
---
|
---
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
---
|
---
|
||||||
title: Reversing an image format
|
title: Reversing an image format
|
||||||
date: 2025-01-15
|
date: 2025-01-15
|
||||||
tags: [reverse-engineering, hacking-hikmicro]
|
tags: reverse-engineering, hacking-hikmicro
|
||||||
drafts: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
This post is the first in a series on reverse engineering binary file formats using an array of tools,
|
This post is the first in a series on reverse engineering binary file formats using an array of tools,
|
||||||
mainly to serve as insight into how to approach these kinds of challenges as they can be daunting.
|
mainly to serve as insight into how to approach these kinds of challenges as they can be daunting.
|
||||||
Part one focuses on the image format.
|
Part one focuses on the image format.
|
||||||
|
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
*[ITAR]: International Traffic in Arms Regulations
|
*[ITAR]: International Traffic in Arms Regulations
|
||||||
|
@ -19,19 +19,16 @@ to a host of other companies (TOPDON, Vevor, HTI, UNI-T) to relabel and sell und
|
||||||
form factors: Phone add-on and gun-grip unit. Existing work has documented and reverse-engineered the phone communication protocols,
|
form factors: Phone add-on and gun-grip unit. Existing work has documented and reverse-engineered the phone communication protocols,
|
||||||
but
|
but
|
||||||
|
|
||||||
I got burned once by the phone-dongle style before with Seek Thermal. Simply put, the pace of smartphones is longer than
|
I've been burned once by the phone-dongle style before with Seek Thermal. Simply put, the pace of smartphones is longer than
|
||||||
the target lifespan of these products. This time, I wanted one that was standalone. The PC
|
the target lifespan of these products. Therefore I wanted one that could be used in a dedicated fashion. The PC
|
||||||
connectivity was still important to me, since there are certain operations that can only be done with access to raw
|
connectivity was still important to me, since there are certain operations that can only be done with access to raw
|
||||||
data, like Lock-In Thermography.
|
data, like Lock-In Thermography.
|
||||||
|
|
||||||
For that reason, the Hikmicro line of portable imagers interested me. It's more
|
For that reason, the Hikmicro line of portable imagers interested me. It's more expensive than the whitelabel Infiray
|
||||||
expensive than the whitelabel Infiray products, but offers some pretty nice
|
products, but offers some pretty nice looking PC software and uses a Radiometric JPEG image format for data.
|
||||||
looking PC software and uses a Radiometric JPEG image format for data.They
|
Live USB is advertized and it supposedly also delivers radiometry over the USB (!). This is on
|
||||||
advertise Live USB video and it supposedly also delivers radiometry over the
|
top of being a standalone unit so you don't need to use an odd phone app. However this choice seems less popular on forums
|
||||||
USB (!). This is on top of being a standalone unit so you don't need to use an
|
and there is no reverse engineering project that exists already, so we'll have to start from scratch.
|
||||||
odd phone app. However this choice seems less popular on forums and there is no
|
|
||||||
reverse engineering project that exists already, so we'll have to start from
|
|
||||||
scratch.
|
|
||||||
|
|
||||||
|
|
||||||
# Getting the picture
|
# Getting the picture
|
||||||
|
|
|
@ -4,16 +4,13 @@ date: 2025-04-17
|
||||||
tags: NixOS
|
tags: NixOS
|
||||||
---
|
---
|
||||||
|
|
||||||
{% callout %}
|
Note: this post assumes familiarity with the Nix Ecosystem.
|
||||||
This post assumes familiarity with the Nix ecosystem.
|
|
||||||
{% endcallout %}
|
|
||||||
|
|
||||||
Nix and NixOS make managing servers much easier than bespoke scripts or
|
Nix and NixOS make managing servers much easier than bespoke scripts or
|
||||||
complex Ansible playbooks. While I don't think NixOS is ready for
|
complex Ansible playbooks. While I don't think NixOS is ready for
|
||||||
personal computers, it absolutely makes sense on less dynamic devices
|
personal computers, it absolutely makes sense on less dynamic devices
|
||||||
like servers or embedded-ish machines.
|
like servers or embedded-ish machines.
|
||||||
|
|
||||||
|
|
||||||
We can use NixOS and nixpkgs to derive a fully defined operating system and services.
|
We can use NixOS and nixpkgs to derive a fully defined operating system and services.
|
||||||
Then, we can:
|
Then, we can:
|
||||||
|
|
||||||
|
@ -22,7 +19,7 @@ Then, we can:
|
||||||
- push the new configuration to a remote server using [deploy-rs]()
|
- push the new configuration to a remote server using [deploy-rs]()
|
||||||
|
|
||||||
The latter is interesting. With a bit of setup, we can do GitOps with rollback
|
The latter is interesting. With a bit of setup, we can do GitOps with rollback
|
||||||
and push-deployments. `deploy-rs` is like `nix profile` with helper scripts
|
and push-deployments. `deploy-rs` is a glorified `nix copy` with some extra magic
|
||||||
to "activate" the copied closure. Activation typically means running `home-manager switch`
|
to "activate" the copied closure. Activation typically means running `home-manager switch`
|
||||||
or switching the NixOS profile.
|
or switching the NixOS profile.
|
||||||
|
|
||||||
|
@ -40,60 +37,46 @@ And then have `nginx` use `/home/blog/_site/` as the site root. This works, but
|
||||||
Other users have tied their blog to the NixOS system-level configuration, like [this user](https://jeancharles.quillet.org/posts/2023-08-01-Deploying-a-static-website-with-nix.html). While I think this
|
Other users have tied their blog to the NixOS system-level configuration, like [this user](https://jeancharles.quillet.org/posts/2023-08-01-Deploying-a-static-website-with-nix.html). While I think this
|
||||||
is a good solution, it has the issue of relying on root access for updating the site.
|
is a good solution, it has the issue of relying on root access for updating the site.
|
||||||
To me, this feels excessive. I should be able to deploy my site from a lesser-privileged user.
|
To me, this feels excessive. I should be able to deploy my site from a lesser-privileged user.
|
||||||
This method also ties sysadmin-tasks to our blog, which isn't keeping our concerns separate.
|
|
||||||
|
|
||||||
# Scaffolding the Solution
|
# Scaffolding the Solution
|
||||||
|
|
||||||
Let's recap:
|
Let's recap:
|
||||||
|
|
||||||
1. Build the site using a nix derivation
|
1. Build the site using a nix derivation
|
||||||
2. Configure `nginx` to serve from a well-known path (`/var/lib/site/public`)
|
2. Copy the derivation to a server using a locked down non-root account.
|
||||||
3. Copy the derivation to a server using a locked down non-root account.
|
3. Create well-known path that points to the latest version of the site in the nix store.
|
||||||
4. Symlink `/var/lib/site/public` to our static site in the nix store.
|
4. Configure `nginx` to serve from this well known path.
|
||||||
|
|
||||||
I'll do it manually to get an idea of how it should work.
|
I'll do it manually to get an idea of how it should work.
|
||||||
|
|
||||||
## nix build blog
|
## nix build blog
|
||||||
|
|
||||||
I build my blog using [11ty](https://www.11ty.dev), and I use `npm run build`
|
I build my blog using [11ty](https://www.11ty.dev), and I use `npm run build` to generate
|
||||||
to generate the static sources. Nix supports building npm-like packages with
|
the static sources. Nix supports building npm-like packages with `buildNpmPackage`, which
|
||||||
`buildNpmPackage`. This uses a [Fixed-output
|
uses a [Fixed-output derivation](https://nix.dev/manual/nix/2.28/store/derivation/outputs/content-address#fixed)
|
||||||
derivation](https://nix.dev/manual/nix/2.28/store/derivation/outputs/content-address#fixed)
|
to store the dependencies of the project. Then, for the `installPhase`, we just copy the built contents
|
||||||
to store the dependencies of the project if it is stored elsewhere, but I found
|
to `$out`. I need to add `vips` and `pkg-config` as well because 11ty processes my images.
|
||||||
a better method that imports the `package.lock` directly. Then, for the
|
|
||||||
`installPhase`, we just copy the built contents to `$out`. I need to add `vips`
|
|
||||||
and `pkg-config` as well because 11ty processes my images. You should add any
|
|
||||||
other tools you'd need to build your site.
|
|
||||||
|
|
||||||
The end result looks like this:
|
The end result looks like this:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
{
|
default = pkgs.buildNpmPackage {
|
||||||
nodejs,
|
|
||||||
pkg-config,
|
|
||||||
buildNpmPackage,
|
|
||||||
importNpmLock,
|
|
||||||
vips,
|
|
||||||
}: buildNpmPackage {
|
|
||||||
name = "myblog";
|
name = "myblog";
|
||||||
version = "unstable";
|
version = "unstable";
|
||||||
buildInputs = [
|
buildInputs = with pkgs; [
|
||||||
nodejs
|
nodejs
|
||||||
vips
|
vips
|
||||||
];
|
];
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = with pkgs; [
|
||||||
pkg-config
|
pkg-config
|
||||||
];
|
];
|
||||||
npmDeps = importNpmLock {
|
npmDepsHash = "sha256-Q7rhCjAPPn44DyUZ/uoD+7o4XH33IATfL+v1azEhuW0=";
|
||||||
npmRoot = ./.;
|
|
||||||
};
|
|
||||||
npmConfigHook = importNpmLock.npmConfigHook;
|
|
||||||
src = ./.;
|
src = ./.;
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
mkdir -p $out/public
|
mkdir -p $out/public
|
||||||
cp -ar _site/* $out/public/
|
cp -ar _site/ $out/public
|
||||||
'';
|
'';
|
||||||
}
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, running `nix build` I get my site files in `result/public`, which is what we want.
|
Then, running `nix build` I get my site files in `result/public`, which is what we want.
|
||||||
|
@ -126,14 +109,9 @@ module that will set this up:
|
||||||
# TODO: write this
|
# TODO: write this
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Now we have a scoped user, with an ssh key authorized. It needs a shell so we can actually log
|
|
||||||
in remotely.
|
|
||||||
The last step is creating that symlink. This is where the concept of "activation" comes into play.
|
The last step is creating that symlink. This is where the concept of "activation" comes into play.
|
||||||
|
For NixOS, `deploy-rs` activation just calls `switch-to-configuration` to make the system change the profile.
|
||||||
|
We can effectively do whatever we want here.
|
||||||
For NixOS, `deploy-rs` activation just calls `switch-to-configuration` to make
|
|
||||||
the system change the profile. We can effectively do whatever we want here.
|
|
||||||
|
|
||||||
Reading the [custom activator](https://github.com/serokell/deploy-rs/blob/aa07eb05537d4cd025e2310397a6adcedfe72c76/flake.nix#L58C13-L96C17) source:
|
Reading the [custom activator](https://github.com/serokell/deploy-rs/blob/aa07eb05537d4cd025e2310397a6adcedfe72c76/flake.nix#L58C13-L96C17) source:
|
||||||
```nix
|
```nix
|
||||||
|
@ -194,9 +172,8 @@ ln -sn $PROFILE/public /var/lib/static-site/public
|
||||||
|
|
||||||
Then when deploying with `deploy-rs`:
|
Then when deploying with `deploy-rs`:
|
||||||
|
|
||||||
```bash
|
```
|
||||||
error: cannot add path '/nix/store/2sad737aglfzmil72phv0j8s34zzmvzi-myblog'
|
error: cannot add path '/nix/store/2sad737aglfzmil72phv0j8s34zzmvzi-myblog' because it lacks a signature by a trusted key
|
||||||
because it lacks a signature by a trusted key
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Drat. This makes sense though, since it would be a bit dangerous to allow any old user write access to
|
Drat. This makes sense though, since it would be a bit dangerous to allow any old user write access to
|
||||||
|
@ -205,14 +182,17 @@ the nix store. We have two options:
|
||||||
1. Make `static-site` a trusted user
|
1. Make `static-site` a trusted user
|
||||||
2. Create a trusted keypair to sign our closure when it's built.
|
2. Create a trusted keypair to sign our closure when it's built.
|
||||||
|
|
||||||
Pick your poison - the keypair mechanism is more secure, as it means that
|
Pick your poison - the keypair mechanism is slightly more secure.
|
||||||
the `static-site` user can't upload arbitrary packages.
|
|
||||||
|
|
||||||
I just want to get this working, so I made `static-site` a trusted user.
|
I just want to get this working, so I made `static-site` a trusted user.
|
||||||
Note that if you wanted to use the keypair instead, `deploy-rs` has a
|
Note that if you wanted to use the keypair instead, `deploy-rs` has a
|
||||||
[secret environment variable](https://github.com/serokell/deploy-rs/blob/aa07eb05537d4cd025e2310397a6adcedfe72c76/src/push.rs#L131)
|
[secret environment variable](https://github.com/serokell/deploy-rs/blob/aa07eb05537d4cd025e2310397a6adcedfe72c76/src/push.rs#L131)
|
||||||
called `LOCAL_KEY` which is a file that contains the signing key.
|
called `LOCAL_KEY` which is a file that contains the signing key.
|
||||||
|
|
||||||
Regardless of the outcome you choose, when you re-deploy, it should work properly.
|
|
||||||
|
|
||||||
Let's follow the symlink.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
title: Zig is Dark Souls
|
title: Zig is Dark Souls
|
||||||
description: This is both good and bad.
|
description: This is both good and bad.
|
||||||
|
|
|
@ -145,14 +145,6 @@ export default async function (eleventyConfig) {
|
||||||
//eleventyConfig.addPairedShortcode("section", async (content, transition = "none") => {
|
//eleventyConfig.addPairedShortcode("section", async (content, transition = "none") => {
|
||||||
// return `<section data-transition=${transition}> ${mdIt.renderInline(content)} </section>`;
|
// return `<section data-transition=${transition}> ${mdIt.renderInline(content)} </section>`;
|
||||||
//})
|
//})
|
||||||
//
|
|
||||||
// Paired shortcode for callout
|
|
||||||
eleventyConfig.addPairedShortcode("callout", function(content) {
|
|
||||||
// The 'content' variable holds the text/HTML placed between
|
|
||||||
// {% callout %} and {% endcallout %}
|
|
||||||
// We wrap it with our div structure.
|
|
||||||
return `<div class="callout">${content}</div>`;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Features to make your build faster (when you need them)
|
// Features to make your build faster (when you need them)
|
||||||
|
|
||||||
|
|
36
flake.nix
36
flake.nix
|
@ -16,21 +16,29 @@
|
||||||
));
|
));
|
||||||
|
|
||||||
in {
|
in {
|
||||||
apps = forAllSystems (pkgs: {
|
packages = forAllSystems (pkgs: rec {
|
||||||
"deploy" = {
|
default = pkgs.buildNpmPackage {
|
||||||
type = "app";
|
name = "myblog";
|
||||||
program = let
|
version = "unstable";
|
||||||
ci = (pkgs.writeShellApplication {
|
buildInputs = with pkgs; [
|
||||||
name = "ci.sh";
|
nodejs
|
||||||
text = ''
|
vips
|
||||||
nix build
|
];
|
||||||
'';
|
nativeBuildInputs = with pkgs; [
|
||||||
});
|
pkg-config
|
||||||
in "${ci}/ci.sh";
|
];
|
||||||
|
# npmDepsHash = "sha256-rMU1q2BPvPihovOyjbCezd1UyEODSrtCLr9TJedOVl0=";
|
||||||
|
npmDeps = pkgs.importNpmLock {
|
||||||
|
npmRoot = ./.;
|
||||||
|
|
||||||
|
};
|
||||||
|
npmConfigHook = pkgs.importNpmLock.npmConfigHook;
|
||||||
|
src = ./.;
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out/public
|
||||||
|
cp -ar _site/* $out/public/
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
});
|
|
||||||
packages = forAllSystems (pkgs: {
|
|
||||||
default = pkgs.callPackage ./package.nix {};
|
|
||||||
});
|
});
|
||||||
deploy.nodes.myblog = {
|
deploy.nodes.myblog = {
|
||||||
hostname = "saji.dev";
|
hostname = "saji.dev";
|
||||||
|
|
32
package.nix
32
package.nix
|
@ -1,32 +0,0 @@
|
||||||
{
|
|
||||||
nodejs,
|
|
||||||
pkg-config,
|
|
||||||
buildNpmPackage,
|
|
||||||
importNpmLock,
|
|
||||||
vips,
|
|
||||||
lib,
|
|
||||||
}: buildNpmPackage {
|
|
||||||
name = "myblog";
|
|
||||||
version = "unstable";
|
|
||||||
buildInputs = [
|
|
||||||
];
|
|
||||||
nativeBuildInputs = [
|
|
||||||
pkg-config
|
|
||||||
nodejs
|
|
||||||
vips
|
|
||||||
];
|
|
||||||
npmDeps = importNpmLock {
|
|
||||||
npmRoot = ./.;
|
|
||||||
};
|
|
||||||
npmConfigHook = importNpmLock.npmConfigHook;
|
|
||||||
src = ./.;
|
|
||||||
installPhase = ''
|
|
||||||
mkdir -p $out/public
|
|
||||||
cp -ar _site/* $out/public/
|
|
||||||
'';
|
|
||||||
meta = {
|
|
||||||
description = "My blog, yay";
|
|
||||||
homepage = "https://saji.dev";
|
|
||||||
platforms = lib.platforms.all;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -7,41 +7,16 @@
|
||||||
|
|
||||||
/* Theme colors */
|
/* Theme colors */
|
||||||
:root {
|
:root {
|
||||||
|
|
||||||
--color-black: #000000;
|
|
||||||
--color-black-bright: #5a4e51;
|
|
||||||
|
|
||||||
--color-red: #cf5080;
|
|
||||||
--color-red-bright: #e26b95;
|
|
||||||
|
|
||||||
--color-green: #7b8d39;
|
|
||||||
--color-green-bright: #8fa445;
|
|
||||||
|
|
||||||
--color-yellow: #b57235;
|
|
||||||
--color-yellow-bright: #cf8544;
|
|
||||||
|
|
||||||
--color-blue: #3c8cbf;
|
|
||||||
--color-blue-bright: #4fa1d7;
|
|
||||||
|
|
||||||
--color-magenta: #9468d8;
|
|
||||||
--color-magenta-bright: #a782e5;
|
|
||||||
|
|
||||||
--color-cyan: #a782e5;
|
|
||||||
--color-cyan-bright: #47ac9b;
|
|
||||||
|
|
||||||
--color-white: #c2bdbd;
|
|
||||||
--color-white-bright: #ffffff;
|
|
||||||
|
|
||||||
--color-gray-20: #e0e0e0;
|
--color-gray-20: #e0e0e0;
|
||||||
--color-gray-50: #C0C0C0;
|
--color-gray-50: #C0C0C0;
|
||||||
--color-gray-90: #333;
|
--color-gray-90: #333;
|
||||||
|
|
||||||
--background-color: var(--color-white);
|
--background-color: #fff;
|
||||||
|
|
||||||
--text-color: var(--color-gray-90);
|
--text-color: var(--color-gray-90);
|
||||||
--text-color-link: var(--color-cyan);
|
--text-color-link: #082840;
|
||||||
--text-color-link-active: var(--color-cyan-bright);
|
--text-color-link-active: #5f2b48;
|
||||||
--text-color-link-visited: var(--color-cyan);
|
--text-color-link-visited: #17050F;
|
||||||
|
|
||||||
--syntax-tab-size: 2;
|
--syntax-tab-size: 2;
|
||||||
}
|
}
|
||||||
|
@ -51,16 +26,13 @@
|
||||||
--color-gray-20: #e0e0e0;
|
--color-gray-20: #e0e0e0;
|
||||||
--color-gray-50: #C0C0C0;
|
--color-gray-50: #C0C0C0;
|
||||||
--color-gray-90: #dad8d8;
|
--color-gray-90: #dad8d8;
|
||||||
--background-color: var(--color-black);
|
|
||||||
|
|
||||||
/* --text-color is assigned to --color-gray-_ above */
|
/* --text-color is assigned to --color-gray-_ above */
|
||||||
/* --text-color-link: #1493fb; */
|
--text-color-link: #1493fb;
|
||||||
/* --text-color-link-active: #6969f7; */
|
--text-color-link-active: #6969f7;
|
||||||
/* --text-color-link-visited: #a6a6f8; */
|
--text-color-link-visited: #a6a6f8;
|
||||||
--text-color-link: var(--color-cyan);
|
|
||||||
--text-color-link-active: var(--color-cyan-bright);
|
|
||||||
--text-color-link-visited: var(--color-cyan);
|
|
||||||
|
|
||||||
|
--background-color: #15202b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,9 +145,6 @@ pre:not([class*="language-"]) {
|
||||||
}
|
}
|
||||||
code {
|
code {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
|
||||||
color: var(--color-black);
|
|
||||||
background-color: var(--color-white);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Header */
|
/* Header */
|
||||||
|
@ -308,45 +277,3 @@ h1, h2, h3 {
|
||||||
font-family: WO3;
|
font-family: WO3;
|
||||||
letter-spacing: .1rem;
|
letter-spacing: .1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Callout styles */
|
|
||||||
|
|
||||||
.callout {
|
|
||||||
padding: 2em; /* Adjust padding as needed */
|
|
||||||
position: relative; /* Needed for positioning pseudo-elements */
|
|
||||||
border: none; /* Ensure no default border */
|
|
||||||
margin: 1em 0; /* Add some margin for spacing */
|
|
||||||
overflow: hidden; /* Prevents pseudo-elements from causing scrollbars if they slightly exceed bounds */
|
|
||||||
}
|
|
||||||
|
|
||||||
.callout p {
|
|
||||||
margin: 0; /* Remove default paragraph margin inside callout */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Top-left corner bracket */
|
|
||||||
.callout::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0.5em; /* Adjust position */
|
|
||||||
left: 0.5em; /* Adjust position */
|
|
||||||
width: 1.5em; /* Adjust size */
|
|
||||||
height: 1.5em; /* Adjust size */
|
|
||||||
border-top: 3px solid #ffffff; /* White border, adjust thickness */
|
|
||||||
border-left: 3px solid #ffffff; /* White border, adjust thickness */
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bottom-right corner bracket */
|
|
||||||
.callout::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0.5em; /* Adjust position */
|
|
||||||
right: 0.5em; /* Adjust position */
|
|
||||||
width: 1.5em; /* Adjust size */
|
|
||||||
height: 1.5em; /* Adjust size */
|
|
||||||
border-bottom: 3px solid #ffffff; /* White border, adjust thickness */
|
|
||||||
border-right: 3px solid #ffffff; /* White border, adjust thickness */
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,3 @@ nixpkgs?
|
||||||
NixOS
|
NixOS
|
||||||
|
|
||||||
keypair
|
keypair
|
||||||
|
|
||||||
whitelabel
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue