···1+/* This is Sprinkles' global.css.
2+ *
3+ * The styles you add here will be added to every page you visit.
4+ *
5+ * To add styles specific to a single domain, create files in this directory, named
6+ * after the (full) domain, eg. "twitter.com.css" or "subdomain.example.com.css".
7+ *
8+ * For example, uncomment the line below to get an extra creamy web experience: */
9+10+/* body { background-color: papayawhip; } */
+14
.config/sprinkles/global.js
···00000000000000
···1+// This is Sprinkles' global.js.
2+//
3+// The JavaScript code you add here will be run on every page you visit.
4+//
5+// To add scripts specific to a single domain, create files in this directory, name
6+// after the domain, eg. "twitter.com.js" or "subdomain.example.com.js".
7+//
8+// For example, uncomment the lines below to change every image on the web to random new one:
9+10+// for (const elm of document.querySelectorAll("img")) {
11+// elm.src = `//picsum.photos/${elm.width}`
12+// }
13+14+console.log("sprinkles running")
···1+console.log("Optimized Mycecurity sprinkle is running.");
2+("this is pretty cool");
3+4+/**
5+ * Finds and formats ISO date strings within a given DOM node and its children.
6+ * @param {Node} node The root node to search within.
7+ */
8+function formatDatesWithinNode(node) {
9+ // 1. Ensure the node is an element we can query.
10+ // This filters out text nodes, comments, etc.
11+ if (!(node instanceof Element)) {
12+ return;
13+ }
14+15+ // 2. Create a list of candidates to check:
16+ // - The node itself (if it's a potential candidate).
17+ // - All descendant elements that could contain a date.
18+ const candidates = [node, ...node.querySelectorAll("div, span, p, td")];
19+20+ // 3. Filter the candidates to find only the ones with parsable dates.
21+ const dateElements = candidates.filter(
22+ (el) => el.textContent && !isNaN(Date.parse(el.textContent.trim())),
23+ );
24+25+ // 4. Loop through and format the valid date elements.
26+ for (const element of dateElements) {
27+ // Skip elements we have already formatted to prevent errors.
28+ if (element.dataset.formatted) continue;
29+30+ const originalDate = element.textContent.trim();
31+ element.textContent = new Date(originalDate).toLocaleDateString(
32+ "fr-FR",
33+ {
34+ year: "numeric",
35+ month: "numeric",
36+ day: "numeric",
37+ },
38+ );
39+40+ // Mark the element as formatted.
41+ element.dataset.formatted = "true";
42+ }
43+}
44+45+// Create an observer that will run our function on specific added nodes.
46+const observer = new MutationObserver((mutationsList) => {
47+ for (const mutation of mutationsList) {
48+ // We only care about 'childList' mutations (nodes being added/removed).
49+ if (mutation.type === "childList") {
50+ // For every node that was added, run our formatting function on it.
51+ for (const addedNode of mutation.addedNodes) {
52+ formatDatesWithinNode(addedNode);
53+ }
54+ }
55+ }
56+});
57+58+// Start observing the entire body for changes to its descendants.
59+observer.observe(document.body, {
60+ childList: true,
61+ subtree: true,
62+});
63+64+// Finally, run the function once on the entire body for the initial page load.
65+formatDatesWithinNode(document.body);
···1+# Add deno completions to search path
2+if [[ ":$FPATH:" != *":/Users/finxol/.zsh/completions:"* ]]; then export FPATH="/Users/finxol/.zsh/completions:$FPATH"; fi
3+# If you come from bash you might have to change your $PATH.
4+# export PATH=$HOME/bin:/usr/local/bin:$PATH
5+6+# Path to your oh-my-zsh installation.
7+export ZSH="$HOME/.oh-my-zsh"
8+9+# Set name of the theme to load --- if set to "random", it will
10+# load a random theme each time oh-my-zsh is loaded, in which case,
11+# to know which specific one was loaded, run: echo $RANDOM_THEME
12+# See https://github.com/ohmyzsh/ohmyzsh/wiki/Themes
13+ZSH_THEME="gallifrey"
14+15+# Set list of themes to pick from when loading at random
16+# Setting this variable when ZSH_THEME=random will cause zsh to load
17+# a theme from this variable instead of looking in $ZSH/themes/
18+# If set to an empty array, this variable will have no effect.
19+# ZSH_THEME_RANDOM_CANDIDATES=( "robbyrussell" "agnoster" )
20+21+# Uncomment the following line to use case-sensitive completion.
22+# CASE_SENSITIVE="true"
23+24+# Uncomment the following line to use hyphen-insensitive completion.
25+# Case-sensitive completion must be off. _ and - will be interchangeable.
26+# HYPHEN_INSENSITIVE="true"
27+28+# Uncomment one of the following lines to change the auto-update behavior
29+# zstyle ':omz:update' mode disabled # disable automatic updates
30+# zstyle ':omz:update' mode auto # update automatically without asking
31+# zstyle ':omz:update' mode reminder # just remind me to update when it's time
32+33+# Uncomment the following line to change how often to auto-update (in days).
34+# zstyle ':omz:update' frequency 13
35+36+# Uncomment the following line if pasting URLs and other text is messed up.
37+# DISABLE_MAGIC_FUNCTIONS="true"
38+39+# Uncomment the following line to disable colors in ls.
40+# DISABLE_LS_COLORS="true"
41+42+# Uncomment the following line to disable auto-setting terminal title.
43+# DISABLE_AUTO_TITLE="true"
44+45+# Uncomment the following line to enable command auto-correction.
46+# ENABLE_CORRECTION="true"
47+48+# Uncomment the following line to display red dots whilst waiting for completion.
49+# You can also set it to another string to have that shown instead of the default red dots.
50+# e.g. COMPLETION_WAITING_DOTS="%F{yellow}waiting...%f"
51+# Caution: this setting can cause issues with multiline prompts in zsh < 5.7.1 (see #5765)
52+# COMPLETION_WAITING_DOTS="true"
53+54+# Uncomment the following line if you want to disable marking untracked files
55+# under VCS as dirty. This makes repository status check for large repositories
56+# much, much faster.
57+# DISABLE_UNTRACKED_FILES_DIRTY="true"
58+59+# Uncomment the following line if you want to change the command execution time
60+# stamp shown in the history command output.
61+# You can set one of the optional three formats:
62+# "mm/dd/yyyy"|"dd.mm.yyyy"|"yyyy-mm-dd"
63+# or set a custom format using the strftime function format specifications,
64+# see 'man strftime' for details.
65+# HIST_STAMPS="mm/dd/yyyy"
66+67+# Would you like to use another custom folder than $ZSH/custom?
68+# ZSH_CUSTOM=/path/to/new-custom-folder
69+70+# Which plugins would you like to load?
71+# Standard plugins can be found in $ZSH/plugins/
72+# Custom plugins may be added to $ZSH_CUSTOM/plugins/
73+# Example format: plugins=(rails git textmate ruby lighthouse)
74+# Add wisely, as too many plugins slow down shell startup.
75+plugins=(git)
76+77+source $ZSH/oh-my-zsh.sh
78+79+# User configuration
80+81+# export MANPATH="/usr/local/man:$MANPATH"
82+83+# You may need to manually set your language environment
84+# export LANG=en_US.UTF-8
85+86+# Preferred editor for local and remote sessions
87+# if [[ -n $SSH_CONNECTION ]]; then
88+# export EDITOR='vim'
89+# else
90+# export EDITOR='mvim'
91+# fi
92+93+# Compilation flags
94+# export ARCHFLAGS="-arch x86_64"
95+96+# Set personal aliases, overriding those provided by oh-my-zsh libs,
97+# plugins, and themes. Aliases can be placed here, though oh-my-zsh
98+# users are encouraged to define aliases within the ZSH_CUSTOM folder.
99+# For a full list of active aliases, run `alias`.
100+#
101+# Example aliases
102+# alias zshconfig="mate ~/.zshrc"
103+# alias ohmyzsh="mate ~/.oh-my-zsh"
104+105+106+107+108+alias ll="eza -la --icons --group-directories-first --git"
109+110+export PATH="/opt/homebrew/opt/node@20/bin:$PATH"
111+112+113+###############
114+# SSH Aliases #
115+###############
116+117+# Connect to VM
118+alias ssh-vm="ssh 'finxol@fdfa:6c9d:d516:7dda:78b0:39ff:fe3d:e285'"
119+120+# Connect to DigitalOcean droplet
121+alias dopers="ssh finxol@157.230.116.140"
122+123+# Connect to local Raspberry Pi
124+raspi() {
125+ ip=`curl -s https://am.i.mullvad.net/ip`
126+127+ if [ "$ip" = "82.67.127.125" ]; then
128+ ssh finxol@192.168.1.158
129+ else
130+ echo "Not connected to home network"
131+ echo "Please connect to Wireguard VPN"
132+ fi
133+}
134+135+###############
136+137+# bun completions
138+[ -s "/Users/finxol/.bun/_bun" ] && source "/Users/finxol/.bun/_bun"
139+140+# bun
141+export BUN_INSTALL="$HOME/.bun"
142+export PATH="$BUN_INSTALL/bin:$PATH"
143+144+# Deno global packages
145+export PATH="$PATH:$HOME/.deno/bin"
146+147+export EDITOR="vim"
148+149+alias c="clear -x"
150+export PATH="/opt/homebrew/opt/node@22/bin:$PATH"
151+152+# pnpm
153+export PNPM_HOME="/Users/finxol/Library/pnpm"
154+case ":$PATH:" in
155+ *":$PNPM_HOME:"*) ;;
156+ *) export PATH="$PNPM_HOME:$PATH" ;;
157+esac
158+# pnpm end
159+160+# bit
161+case ":$PATH:" in
162+ *":/Users/finxol/bin:"*) ;;
163+ *) export PATH="$PATH:/Users/finxol/bin" ;;
164+esac
165+# bit end
166+167+168+eval "$(zoxide init zsh)"
169+170+# Automatically attach to or create tmux session named 'default'
171+# only when running inside Ghostty and not already in tmux.
172+if [[ -z "$TMUX" && "$TERM_PROGRAM" = "ghostty" ]]; then
173+ exec tmux new -A -s default
174+fi
175+176+# Deploy Jester's stock website
177+deploy-stock() {
178+ dopers "pm2 stop stock-stats && rm -rf /srv/http/stock-stats/*" && rsync -av --exclude=node_modules -e "ssh -i ~/.ssh/id_rsa" ~/Projects/dev/web/stock-stats/* finxol@157.230.116.140:/srv/http/stock-stats && dopers "cd /srv/http/stock-stats && ~/.bun/bin/bun install && ~/.bun/bin/bun run build && NODE_ENVIRONMENT=production pm2 start .output/server/index.mjs --name stock-stats"
179+}
180+181+# Unquarantine app
182+fix-app() {
183+ xattr -d com.apple.quarantine $1
184+}
185+. "/Users/finxol/.deno/env"
186+# Initialize zsh completions (added by deno install script)
187+autoload -Uz compinit
188+compinit
···1+#!/bin/bash
2+3+# This script automates the initial setup and hardening of a new Debian-based server.
4+# It must be run as root.
5+#
6+# The script will:
7+# 1. Create a new sudo user.
8+# 2. Move the root SSH key to the new user.
9+# 3. Harden the SSH server configuration.
10+# 4. Install bat, eza, and Docker.
11+12+# --- Configuration ---
13+readonly USERNAME="finxol"
14+# Define a temporary password. This will be immediately expired.
15+# WARNING: This password will be stored in the script file and potentially
16+# in your shell history. This is an acceptable risk for a brand new server
17+# where the password will be changed upon first login.
18+readonly TEMP_PASS="password"
19+20+# --- Script Execution ---
21+22+# Exit immediately if a command exits with a non-zero status.
23+set -e
24+# Treat unset variables as an error.
25+set -u
26+# Ensure that pipelines return the exit status of the last command to fail.
27+set -o pipefail
28+29+# --- Helper Functions ---
30+log() {
31+ echo
32+ echo "▶ $1"
33+ echo "--------------------------------------------------"
34+}
35+36+# --- Pre-flight Checks ---
37+if [ "$(id -u)" -ne 0 ]; then
38+ echo "This script must be run as root." >&2
39+ exit 1
40+fi
41+42+# --- User and Sudo Setup ---
43+log "Creating user '$USERNAME' and granting sudo privileges"
44+45+# Create a new user without an interactive password prompt.
46+adduser --disabled-password --gecos "" "$USERNAME"
47+48+# Programmatically set the temporary password for the new user.
49+echo "$USERNAME:$TEMP_PASS" | chpasswd
50+echo "Temporary password has been set for '$USERNAME'."
51+52+# Force the user to change their password on the next login.
53+chage -d 0 "$USERNAME"
54+55+# Add the new user to the 'sudo' group to grant administrative privileges.
56+usermod -aG sudo "$USERNAME"
57+echo "User '$USERNAME' created and added to the sudo group."
58+# --- SSH Key Migration ---
59+log "Migrating SSH key from root to '$USERNAME'"
60+# Create the .ssh directory for the new user if it doesn't exist.
61+mkdir -p "/home/$USERNAME/.ssh"
62+63+# Move the root user's authorized_keys file to the new user's .ssh directory.
64+mv /root/.ssh/authorized_keys "/home/$USERNAME/.ssh/authorized_keys"
65+66+# Set the correct ownership and permissions for the .ssh directory and its contents.
67+chown -R "$USERNAME:$USERNAME" "/home/$USERNAME/.ssh"
68+chmod 700 "/home/$USERNAME/.ssh"
69+chmod 600 "/home/$USERNAME/.ssh/authorized_keys"
70+echo "SSH key successfully migrated."
71+72+# --- SSH Server Hardening ---
73+log "Hardening SSH server configuration"
74+readonly SSHD_CONFIG="/etc/ssh/sshd_config"
75+76+# A function to safely set a parameter in sshd_config.
77+# It comments out any existing instance of the key and appends the new setting.
78+set_ssh_config() {
79+ local key="$1"
80+ local value="$2"
81+82+ # Comment out any existing lines with the key to deactivate them.
83+ # The -E flag enables extended regular expressions for the '+' quantifier.
84+ sed -i -E "s/^[[:space:]]*#?[[:space:]]*($key)([[:space:]]+.*)?$/#\1\2/g" "$SSHD_CONFIG"
85+86+ # Append the new, correct setting to the end of the file.
87+ echo "$key $value" >> "$SSHD_CONFIG"
88+}
89+90+# --- Apply Hardening Rules ---
91+# Note: We are now using a function to ensure settings are applied correctly,
92+# preventing issues with duplicate or conflicting rules.
93+94+set_ssh_config "UsePAM" "yes"
95+set_ssh_config "PasswordAuthentication" "no"
96+set_ssh_config "KbdInteractiveAuthentication" "no"
97+98+set_ssh_config "PermitRootLogin" "no"
99+set_ssh_config "PermitEmptyPasswords" "no"
100+set_ssh_config "X11Forwarding" "no"
101+set_ssh_config "AllowAgentForwarding" "no"
102+103+# --- Custom Hardening Settings ---
104+set_ssh_config "ClientAliveInterval" "300"
105+set_ssh_config "ClientAliveCountMax" "2"
106+set_ssh_config "LoginGraceTime" "60"
107+set_ssh_config "MaxAuthTries" "3"
108+set_ssh_config "MaxSessions" "4"
109+110+# Validate the new sshd_config and restart the SSH service to apply changes.
111+sshd -t && systemctl restart sshd
112+echo "SSH server hardened and restarted."
113+114+# --- Package Installation ---
115+log "Updating package lists and installing applications"
116+apt-get update
117+118+# Install bat (a cat clone with syntax highlighting)
119+apt-get install -y bat
120+# On Debian/Ubuntu, the binary can be named 'batcat'. Create a symlink if needed.
121+if ! command -v bat &>/dev/null && command -v batcat &>/dev/null; then
122+ ln -s /usr/bin/batcat /usr/bin/bat
123+fi
124+125+# Install eza (a modern replacement for ls)
126+apt-get install -y gpg
127+mkdir -p /etc/apt/keyrings
128+wget -qO- https://raw.githubusercontent.com/eza-community/eza/main/deb.asc |
129+ gpg --dearmor -o /etc/apt/keyrings/gierens.gpg
130+echo "deb [signed-by=/etc/apt/keyrings/gierens.gpg] http://deb.gierens.de stable main" |
131+ tee /etc/apt/sources.list.d/gierens.list
132+chmod 644 /etc/apt/keyrings/gierens.gpg /etc/apt/sources.list.d/gierens.list
133+apt-get update
134+apt-get install -y eza
135+136+# Install Docker Engine
137+apt-get install -y ca-certificates curl
138+install -m 0755 -d /etc/apt/keyrings
139+curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
140+chmod a+r /etc/apt/keyrings/docker.asc
141+142+echo \
143+ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
144+ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" |
145+ tee /etc/apt/sources.list.d/docker.list >/dev/null
146+apt-get update
147+apt-get install -y docker-ce docker-ce-cli containerd.io \
148+ docker-buildx-plugin docker-compose-plugin
149+150+# Add the new user to the 'docker' group to allow running Docker without sudo.
151+# The '|| true' prevents the script from failing if the group already exists.
152+groupadd docker || true
153+usermod -aG docker "$USERNAME"
154+echo "Docker installed and '$USERNAME' added to the docker group."
155+156+# --- Finalization ---
157+log "Server setup complete!"
158+echo "You can now log out and reconnect as '$USERNAME' using your SSH key."
159+echo "Root login and password authentication have been disabled."