From 1bec99de86207ee8a9c4b3331ba2d3a56d6cd44d Mon Sep 17 00:00:00 2001 From: osmarks Date: Thu, 9 Apr 2026 17:26:03 +0100 Subject: [PATCH] dot new files --- dotfiles/config.fish | 34 ++++++-- dotfiles/link_dots.py | 9 +- dotfiles/project_jail.py | 175 +++++++++++++++++++++++++++++++++++++++ dotfiles/sway_config | 1 + dotfiles/userChrome.css | 6 +- dotfiles/wezterm.lua | 4 + dotfiles/zed.json | 3 + 7 files changed, 224 insertions(+), 8 deletions(-) create mode 100755 dotfiles/project_jail.py diff --git a/dotfiles/config.fish b/dotfiles/config.fish index 31492c4..a795bf8 100644 --- a/dotfiles/config.fish +++ b/dotfiles/config.fish @@ -4,10 +4,32 @@ if status is-interactive end if status is-login and status is-interactive - # To add a key, set -Ua SSH_KEYS_TO_AUTOLOAD keypath - # To remove a key, set -U --erase SSH_KEYS_TO_AUTOLOAD[index_of_key] -# keychain --eval $SSH_KEYS_TO_AUTOLOAD 2> /dev/null | source + # disabled for performance reasons + #keychain --eval $SSH_KEYS_TO_AUTOLOAD 2> /dev/null | source +end + +function __project_jail_chpwd --on-variable PWD + if set -q NORECURSE + set -e NORECURSE + return + end + if set -q IN_PROJECT_JAIL + return + end + + if test -x ~/.local/bin/project-jail + if ~/.local/bin/project-jail + set -l back "$dirprev[1]" + test -n "$back"; or set back "$HOME" + set -g NORECURSE + cd "$back" + end + end +end + +# Also check once at shell startup. +if not set -q IN_PROJECT_JAIL + if test -x ~/.local/bin/project-jail + ~/.local/bin/project-jail + end end -#if status is-interactive -# -#end diff --git a/dotfiles/link_dots.py b/dotfiles/link_dots.py index f00e974..2655d21 100644 --- a/dotfiles/link_dots.py +++ b/dotfiles/link_dots.py @@ -38,7 +38,14 @@ packages = [ "bemenu-wayland", "pavucontrol", "swaylock", - "jq" + "jq", + "fish", + "zoxide", + "atuin", + "arc-icon-theme", + "breeze-icons", + "arc-gtk-theme", + "magic-wormhole" ] subprocess.run(["sudo", "pacman", "-S", *packages]) diff --git a/dotfiles/project_jail.py b/dotfiles/project_jail.py new file mode 100755 index 0000000..29746c6 --- /dev/null +++ b/dotfiles/project_jail.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 + +# Automatically put shell sessions in bwrap when navigating to a project folder. +# Supply chain attack mitigation. +# Written by GPT-5.4 and adapted slightly by hand. + +from __future__ import annotations + +import json +import os +import shutil +import subprocess +import sys +from pathlib import Path + + +CONFIG_PATH = Path.home() / ".config" / "jails.json" +MARKER_ENV = "IN_PROJECT_JAIL" + +PROFILES = { + "rust": [ + ("rw", "~/.cargo/bin"), + ("rw", "~/.cargo/git"), + ("ro", "~/.gitconfig") + ], + "node": [ + ("rw", "~/.npm"), + ("rw", "~/.cache/node-gyp"), + ("ro", "~/.gitconfig") + ], + "python": [] +} + +def load_config() -> dict[str, dict]: + with CONFIG_PATH.open("r", encoding="utf-8") as f: + res = json.load(f) + for path, entry in res.items(): + entry["path"] = path + return res + +def resolve_path(p: str) -> Path: + return Path(os.path.expandvars(os.path.expanduser(p))).resolve() + +def find_matching_entry(cwd: Path, config: dict[str, dict]) -> dict | None: + best: tuple[int, dict] | None = None + + for path, entry in config.items(): + try: + root = resolve_path(str(path)) + cwd.relative_to(root) + except Exception: + continue + + score = len(root.parts) + if best is None or score > best[0]: + best = (score, entry) + + return best[1] if best else None + +def ensure_dir(path: Path) -> None: + path.mkdir(parents=True, exist_ok=True) + +def build_bwrap_command(entry: dict, cwd: Path) -> list[str]: + bwrap = shutil.which("bwrap") + if not bwrap: + print("project-jail: bwrap not found in PATH", file=sys.stderr) + sys.exit(1) + + shell = os.environ.get("SHELL", "/usr/bin/fish") + home = Path.home() + project_root = resolve_path(str(entry["path"])) + + state_dir = Path(os.environ.get("XDG_STATE_HOME", home / ".local" / "state")) / "project-jails" + sandbox_name = entry.get("name") or project_root.name + sandbox_home = state_dir / sandbox_name / "home" + sandbox_tmp = state_dir / sandbox_name / "tmp" + + ensure_dir(sandbox_home) + ensure_dir(sandbox_tmp) + + ro_binds = [ + "/usr", + "/bin", + "/sbin", + "/lib", + "/lib64", + "/opt", + "/etc", + ] + dev_binds = [ + "/dev/dri", # GPU + "/dev/shm", # shared memory + ] + + cmd = [ + bwrap, + "--unshare-all", + "--share-net", + "--die-with-parent", + "--proc", "/proc", + "--dev", "/dev", + "--tmpfs", "/tmp", + "--dir", str(sandbox_home), + "--setenv", "HOME", str(sandbox_home), + "--setenv", "USER", os.environ.get("USER", ""), + "--setenv", "LOGNAME", os.environ.get("LOGNAME", ""), + "--setenv", MARKER_ENV, "1", + "--setenv", "PROJECT_ROOT", str(project_root), + "--chdir", str(cwd), + ] + + rw_binds = [] + + profile = PROFILES[entry["profile"]] + for type, path in profile: + path = str(resolve_path(path)) + if type == "rw": + rw_binds.append(path) + elif type == "ro": + ro_binds.append(path) + else: + assert False + + for path in ro_binds: + if Path(path).exists(): + cmd += ["--ro-bind", path, path] + + for path in rw_binds: + if Path(path).exists(): + cmd += ["--bind", path, path] + + for path in dev_binds: + if Path(path).exists(): + cmd += ["--dev-bind", path, path] + + runtime_dir = os.environ.get("XDG_RUNTIME_DIR") + if runtime_dir and Path(runtime_dir).exists(): + cmd += ["--bind", runtime_dir, runtime_dir] + cmd += ["--setenv", "XDG_RUNTIME_DIR", runtime_dir] + + cmd += ["--setenv", "fish_color_cwd", "red"] + + envs = ["WAYLAND_DISPLAY", "DISPLAY", "DBUS_SESSION_BUS_ADDRESS", "PULSE_SERVER"] + + for env in envs: + if res := os.environ.get(env): + cmd += ["--setenv", env, res] + + # Mount the selected project tree at the same absolute path. + cmd += ["--bind", str(project_root), str(project_root)] + + # Minimal env cleanup. + cmd += [ + shell, + "-i", + ] + print(f"-> sandbox profile {entry['profile']} for {entry['name']}") + return cmd + +def main() -> int: + if os.environ.get(MARKER_ENV) == "1": + return 0 + + cwd = Path.cwd().resolve() + config = load_config() + entry = find_matching_entry(cwd, config) + if not entry: + return 2 + + cmd = build_bwrap_command(entry, cwd) + os.execvp(cmd[0], cmd) + return 1 + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/dotfiles/sway_config b/dotfiles/sway_config index c30b2ae..ff15fda 100644 --- a/dotfiles/sway_config +++ b/dotfiles/sway_config @@ -11,6 +11,7 @@ set $term wezterm set $menu bemenu-run --no-exec | xargs swaymsg exec -- output * adaptive_sync on +output "DP-3" render_bit_depth 10 output * bg #000000 solid_color diff --git a/dotfiles/userChrome.css b/dotfiles/userChrome.css index 3453ae7..0c51e2b 100644 --- a/dotfiles/userChrome.css +++ b/dotfiles/userChrome.css @@ -1,5 +1,9 @@ +* { + border-radius: 0 !important; +} + .tabbrowser-tab:not([selected="true"]) > .tab-stack > .tab-background { - border: 1px solid rgba(130, 130, 130, 0.5) !important; + border: 1px solid rgba(130, 130, 130, 0.5) !important; } .tabbrowser-tab { diff --git a/dotfiles/wezterm.lua b/dotfiles/wezterm.lua index 4d10ff2..5eaefe7 100644 --- a/dotfiles/wezterm.lua +++ b/dotfiles/wezterm.lua @@ -23,4 +23,8 @@ config.font_rules = { } } +config.keys = { + {key="Enter", mods="SHIFT", action=wezterm.action{SendString="\x1b\r"}}, +} + return config diff --git a/dotfiles/zed.json b/dotfiles/zed.json index 0740f84..adf00a4 100644 --- a/dotfiles/zed.json +++ b/dotfiles/zed.json @@ -7,6 +7,9 @@ // custom settings, run `zed: open default settings` from the // command palette (cmd-shift-p / ctrl-shift-p) { + "project_panel": { + "hide_hidden": true + }, "agent": { "default_model": { "provider": "zed.dev",