diff --git a/README.md b/README.md index 9019a1d..2973a14 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,34 @@ Nix configurations for iscg infra hosts. -Hosts and naming scheme: see [garden.md](./garden.md). +## hosts -## rebuild +- **poppy** - MacBook Air M1 (nix-darwin) +- **muscari** - NixOS server with k3s +- **rosemary** - backup VM (planned) + +See `docs/` for detailed documentation. + +## quick rebuild ```bash # poppy (macOS) darwin-rebuild switch --flake .#poppy -# muscari, rosemary (NixOS) -sudo nixos-rebuild switch --flake .# -``` \ No newline at end of file +# muscari (NixOS) +sudo nixos-rebuild switch --flake .#muscari +``` + +## documentation + +- [docs/structure.md](docs/structure.md) - how the config is organized +- [docs/poppy.md](docs/poppy.md) - MacBook specific +- [docs/muscari.md](docs/muscari.md) - server + k3s +- [docs/shell.md](docs/shell.md) - zsh, prompt, aliases +- [docs/terminal.md](docs/terminal.md) - kitty + themes +- [docs/cheatsheet.md](docs/cheatsheet.md) - quick commands + +See also [garden.md](garden.md) for host naming scheme. +test +test macchiato +test macchiato diff --git a/docs/bootstrap.md b/docs/bootstrap.md new file mode 100644 index 0000000..f9726c7 --- /dev/null +++ b/docs/bootstrap.md @@ -0,0 +1,135 @@ +# bootstrap + +как поднять систему с нуля, если nix-config уже есть, но машина новая/сброшена. + +## что есть в `home/` + +- `pass` с `pass-otp` (зашифрованный password store) +- `gpg-agent` с кешированием на 1 час +- `git` с `delta` (красивый diff) +- `zsh` с `pure` prompt, autosuggestions, syntax highlighting +- `kitty` (через import в poppy) +- `duti` для default apps + +## step 1: установить nix + +```bash +# Determinate Nix (рекомендуется) +curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/installer | sh +``` + +## step 2: склонировать nix-config + +```bash +git clone https://git.iscg.dev/mikl/nix-config.git ~/repos/nix-config +cd ~/repos/nix-config +``` + +## step 3: импортировать GPG ключ + +**критичный шаг.** без GPG ключа `pass` не работает. + +```bash +# если есть приватная репа с зашифрованным gpg ключом +git clone https://git.iscg.dev/mikl/gpg-secrets.git /tmp/gpg-setup +gpg --import /tmp/gpg-setup/gpg-private.asc +rm -rf /tmp/gpg-setup + +# проверить +gpg --list-secret-keys mikl@iscg.dev +``` + +если репы нет, генерируем новый: + +```bash +gpg --full-gen-key +# email: mikl@iscg.dev +# имя: mikl +# passphrase: +``` + +## step 4: первый rebuild + +```bash +# darwin (mac) +sudo darwin-rebuild switch --flake .#poppy + +# nixos (linux) +sudo nixos-rebuild switch --flake .#muscari +``` + +первый rebuild может долгим — скачает все пакеты. + +## step 5: настроить pass (ручной шаг) + +```bash +# если есть существующий password store +git clone https://git.iscg.dev/mikl/password-store.git ~/.password-store +cd ~/.password-store && git remote remove origin && git remote add origin https://git.iscg.dev/mikl/password-store.git + +# если создаёшь с нуля +pass init # id от gpg --list-secret-keys +pass git init +pass git remote add origin https://git.iscg.dev/mikl/password-store.git +``` + +после этого работают алиасы: +```bash +pass-sync # = pass git push +pass-pull # = pass git pull +pass-status # = pass git status +``` + +## step 6: настроить default apps + +`duti` уже настроен через nix. но **для системных UTI (url, shell-script, pdf, txt)** на macOS Sonoma+ нужно вручную: + +1. правый клик на файл → **Get Info** +2. **Open with:** → выбрать нужное +3. **Change All...** → подтвердить + +для каких файлов: +- `.url`, `.xhtml` → Chrome +- `.sh` → kitty +- `.pdf` → Skim +- `.txt` → VSCodium + +## step 7: настроить ~/.gitconfig (вручную) + +nix генерирует `~/.config/git/config`, но если есть `~/.gitconfig` — он **перебивает**. + +```bash +# если хочешь глобальные настройки +nano ~/.gitconfig + +# пример: +[filter "lfs"] + clean = git-lfs clean -- %f + smudge = git-lfs smudge -- %f +[credential] + helper = osxkeychain +``` + +или удали `~/.gitconfig` чтобы nix-only. + +## post-bootstrap проверка + +```bash +# все пакеты установлены? +which pass gpg zsh delta duti + +# kitty на poppy +ls /Users/michaotic/Applications/Home\ Manager\ Apps/ | grep kitty + +# git config +git config --get user.name # mikl +git config --get user.email # mikl@iscg.dev +``` + +## что не восстанавливается автоматически + +- **Obsidian vault** (если заметки локально) — нужна синхронизация через Obsidian Sync или git +- **SSH ключи** — нужны в `~/.ssh/`, копируй со старой машины или генерируй новые +- **iCloud** — не в Nix +- **App Store apps** (Pages, Keynote, Numbers) — `mas` CLI может восстановить +- **.DS_Store preferences** — настраиваются заново diff --git a/docs/cheatsheet.md b/docs/cheatsheet.md new file mode 100644 index 0000000..3cd336d --- /dev/null +++ b/docs/cheatsheet.md @@ -0,0 +1,100 @@ +# cheatsheet + +quick commands for daily use. + +## rebuild + +```bash +# poppy (macbook) +darwin-rebuild switch --flake .#poppy + +# muscari (server) +sudo nixos-rebuild switch --flake .#muscari +``` + +## update + +```bash +# update flakes then rebuild +nix flake update && darwin-rebuild switch --flake .#poppy +``` + +## check + +```bash +# validate before applying +nix flake check + +# dry run (see what would change) +darwin-rebuild switch --flake .#poppy --dry-run +``` + +## cleanup + +```bash +# remove old generations +nix-collect-garbage -d + +# or with sudo on nixos +sudo nix-collect-garbage -d +``` + +## shell + +```bash +ll # detailed list with icons +cd # actually zoxide - remembers frequent paths +ff # fastfetch system info +``` + +## navigation + +```bash +cd # zoxide - jump to frequent dir +cd .. # parent directory +ctrl+r # fzf history search +ctrl+t # fzf file finder +``` + +## save websites + +```bash +# одна страница в single html (с CSS+JS) +monolith https://example.com -o page.html + +# рекурсивно целый сайт +wget -r -p -k --no-clobber example.com + +# GUI для рекурсивного скачивания +httrack "https://example.com" -O ./out "+*.example.com/*" +``` + +примеры: +- nixos options: `monolith https://search.nixos.org/options -o options.html` +- сохранить доки: `wget -r -p -k nix-darwin.github.io/nix-darwin/manual/` +- monolith и httrack ставятся через `brew install monolith httrack` + +## default apps (duti) + +настраиваются через `~/.config/duti/config` (управляется в `home/duti.nix`): +- `com.google.Chrome` — html +- `MarkEdit` — markdown +- `com.apple.Mail` — email + +проверить текущие defaults: +```bash +duti -x html +duti -x md +duti -x eml +``` + +⚠️ **на macOS Sonoma+** Apple блокирует изменение через CLI для системных UTI: +- `public.url`, `public.xhtml` → Safari +- `public.shell-script` → Terminal/VSCodium +- `com.adobe.pdf` → Preview +- `public.text`, `public.plain-text` → TextEdit + +для этих нужно вручную через Finder: +1. правый клик на файл → **Get Info** +2. **Open with:** → выбрать нужное приложение +3. **Change All...** → подтвердить diff --git a/docs/muscari.md b/docs/muscari.md new file mode 100644 index 0000000..fd0215b --- /dev/null +++ b/docs/muscari.md @@ -0,0 +1,39 @@ +# muscari + +NixOS server on Yandex Cloud. k3s single-node cluster. + +## specs + +- VM: 2 vCPU / 8 GB RAM / 64 GB SSD +- IP: 158.160.166.30 +- Network: Legacy BIOS (no EFI) + +## quick rebuild + +```bash +sudo nixos-rebuild switch --flake .#muscari +``` + +## imports + +- `disk-config.nix` - disko partitioning +- `k3s.nix` - kubernetes cluster setup +- `hosts/common/` - shared nixos settings + +## networking + +open ports: +- 22 - ssh +- 80 - http (traefik ingress) +- 443 - https (traefik ingress) + +6443 is closed - kubectl via ssh tunnel only. + +## ssh keys + +fetched from `https://git.iscg.dev/mikl.keys` with sha256 pinning. + +## TODO + +- [ ] k8s aliases (k, kx, kl, kd, etc) +- [ ] add nix-specific aliases (switch, update, cleanup with sudo) diff --git a/docs/poppy.md b/docs/poppy.md new file mode 100644 index 0000000..bf3ca82 --- /dev/null +++ b/docs/poppy.md @@ -0,0 +1,34 @@ +# poppy + +MacBook Air M1 - daily driver. + +## quick rebuild + +```bash +darwin-rebuild switch --flake .#poppy +``` + +## what's specific here + +### darwin system + +- `nixpkgs.config.allowUnfree = true` - needed for some macos tools +- `services.nix-daemon.enable = true` - multi-user install +- home path: `/Users/mikl` (not `/home/mikl`) + +### home-manager imports + +- `home/kitty.nix` - terminal with catppuccin theme + +### aliases + +| alias | command | +|-------|---------| +| `switch` | `darwin-rebuild switch --flake .#poppy` | +| `update` | `nix flake update && darwin-rebuild switch --flake .#poppy` | +| `cleanup` | `nix-collect-garbage -d` | + +## TODO + +- [ ] telegram mcp aliases (tg-login, tg-reset) +- [ ] apple music tools (gamdl) diff --git a/docs/shell.md b/docs/shell.md new file mode 100644 index 0000000..a2e0f74 --- /dev/null +++ b/docs/shell.md @@ -0,0 +1,41 @@ +# shell + +zsh configuration across all hosts. + +## prompt + +**pure** - minimal, fast, shows git status. +- green `>` on success +- red `>` on error +- right prompt: current time in gray + +## plugins + +| plugin | source | purpose | +|--------|--------|---------| +| pure | github (v1.23.0) | prompt theme | +| zsh-syntax-highlighting | nixpkgs | command coloring | +| zsh-autosuggestions | nixpkgs | gray command hints | + +## history + +- size: 10000 +- shared between sessions +- ignores duplicates +- stored in `~/.local/share/zsh/history` (xdg) + +## universal aliases + +| alias | what | +|-------|------| +| `ll` | `eza -l --icons --git` | +| `la` | `eza -la --icons --git` | +| `ff` | `fastfetch` | + +## startup + +fastfetch runs automatically when opening new shell. + +## host-specific + +see `docs/poppy.md` and `docs/muscari.md` for per-host aliases (nix rebuild commands differ). diff --git a/docs/ssh.md b/docs/ssh.md new file mode 100644 index 0000000..1536522 --- /dev/null +++ b/docs/ssh.md @@ -0,0 +1,185 @@ +# ssh + +SSH keys, agent configuration, and remote access for poppy. + +## как работает ssh-agent + +на macOS ssh-agent запускается через launchd (`com.openssh.ssh-agent`). +- сокет: `/private/tmp/com.apple.launchd.*/Listeners` (берётся из `launchctl getenv SSH_AUTH_SOCK`) +- `ssh-add ~/.ssh/` — добавляет ключ в agent +- `ssh-add -l` — список загруженных ключей +- `ssh-add -d ` — удалить ключ +- `ssh-add -D` — удалить все + +переменная `SSH_AUTH_SOCK` должна указывать на launchd socket, не на +`/Users/michaotic/.gnupg/S.gpg-agent.ssh` (это от gpg-agent — для pass, не для SSH). + +если что-то сломалось: +```bash +unset SSH_AUTH_SOCK +launchctl start com.openssh.ssh-agent +ssh-add -l +``` + +## ключи на poppy + +| файл | comment | для чего | алгоритм | +|------|---------|----------|----------| +| `~/.ssh/id_ed25519` | poppy-legacy | github (isogonalconjugate) | ed25519 | +| `~/.ssh/id_ed25519_forgejo` | forgejo-iscg | git.iscg.dev (port 2222) | ed25519 | +| `~/.ssh/poppy` | — | git.sol.moe (user: mikl) | ed25519 | +| `~/.ssh/id_ed25519_sk_ledger` | ledger@iscg.dev | **TODO**: FIDO2 (Ledger) | ed25519-sk | + +## конфигурация + +управляется в `home/ssh.nix` через `programs.ssh.matchBlocks`: + +```nix +"git.iscg.dev" = { + identityFile = "~/.ssh/id_ed25519_forgejo"; + port = 2222; + identitiesOnly = true; + addKeysToAgent = "yes"; +}; +"github.com" = { + identityFile = "~/.ssh/id_ed25519"; + identitiesOnly = true; + addKeysToAgent = "yes"; +}; +"git.sol.moe" = { + identityFile = "~/.ssh/poppy"; + user = "mikl"; + identitiesOnly = true; + addKeysToAgent = "yes"; +}; +``` + +**`identitiesOnly = true`** — критично: ssh-agent предлагает серверу **только** указанный +ключ, не пробует все подряд. + +**`addKeysToAgent = "yes"`** — при первом использовании ключ автоматически добавляется в ssh-agent. + +генерируется в `~/.ssh/config` (можно посмотреть: `cat ~/.ssh/config`). + +## создание нового ключа + +```bash +# обычный ed25519 +ssh-keygen -t ed25519 -C "comment" -f ~/.ssh/ + +# с passphrase ОБЯЗАТЕЛЬНО для безопасности +# ssh-agent кэширует passphrase на defaultCacheTtl (1 час) +``` + +после создания: +1. загрузить **публичный** ключ (`.pub`) на forge через web UI: + - github: https://github.com/settings/keys + - forgejo: https://git.iscg.dev/user/settings/keys + - gitea/sol.moe: аналогично +2. добавить в `home/ssh.nix` matchBlocks если новый хост +3. `darwin-rebuild switch` +4. `ssh-add ~/.ssh/` чтобы добавить в agent + +## подключение + +```bash +# тест подключения (ничего не делает, только проверка auth) +ssh -T git@github.com +ssh -T git@git.iscg.dev -p 2222 +ssh -T git@git.sol.moe + +# клонирование +git clone git@github.com:user/repo.git +git clone ssh://git@git.iscg.dev:2222/user/repo.git +git clone git@git.sol.moe:user/repo.git +``` + +## macOS Keychain (опционально) + +чтобы не вводить passphrase каждый раз: +```bash +ssh-add --apple-use-keychain ~/.ssh/ +``` + +или в `~/.ssh/config`: +``` +Host * + UseKeychain yes + AddKeysToAgent yes +``` + +ключи хранятся в **Keychain** (зашифровано, привязано к логину). + +## FIDO2 (Ledger Security Key) + +**Ledger поддерживает FIDO2 через `app-security-key` (открытый исходник — `LedgerHQ/app-security-key`).** + +### создание +```bash +# через nix shell (нужен libfido2) +nix shell nixpkgs#libfido2 nixpkgs#openssh --command \ + ssh-keygen -t ed25519-sk -C "ledger@iscg.dev" -f ~/.ssh/id_ed25519_sk_ledger +``` + +опции: +- `-O resident` — приватный ключ хранится на токене (переносимый) +- `-O verify-required` — требует PIN при подписании +- по умолчанию `touch required` — Ledger требует физическое нажатие + +### использование +```bash +# добавить в agent (нажать кнопку при load) +nix shell nixpkgs#libfido2 nixpkgs#openssh --command \ + ssh-add ~/.ssh/id_ed25519_sk_ledger + +# подписать challenge (нажать кнопку на Ledger) +nix shell nixpkgs#libfido2 nixpkgs#openssh --command \ + ssh-keygen -Y sign -f ~/.ssh/id_ed25519_sk_ledger -n ssh + +# проверить подпись +nix shell nixpkgs#libfido2 nixpkgs#openssh --command \ + ssh-keygen -Y verify -f -n ssh -I -s +``` + +### ограничения + +- **нативный `/usr/bin/ssh-keygen` не умеет FIDO2** — только nix openssh +- **macOS ssh-agent** хранит FIDO2 ключи, но подпись через нативный ssh-keygen не работает +- нужен **nix openssh** для `ssh-keygen -Y sign` и других sign-операций + +## factory reset recovery + +перед reset: +- скопировать `~/.ssh/` (или хотя бы файлы `id_ed25519*`, `poppy`, `id_ed25519_sk_ledger`) на USB/cloud +- записать какие ключи на каких forge (чтобы не забыть) + +после reset: +1. восстановить `~/.ssh/` +2. `chmod 600 ~/.ssh/*` (без этого ssh откажется работать) +3. `chmod 644 ~/.ssh/*.pub` +4. `ssh-add ~/.ssh/` (ввести passphrase) +5. `ssh -T git@` для проверки + +для FIDO2 ключа: см. секцию выше, нужен `nix shell nixpkgs#libfido2 nixpkgs#openssh`. + +## tasks (TODO) + +- [ ] **FIDO2 в nix-config** — добавить `libfido2` и `openssh` в `home/cli.nix` + - сейчас нужно каждый раз `nix shell nixpkgs#libfido2 nixpkgs#openssh --command ...` + - хочется чтобы работало нативно +- [ ] **nix ssh-agent** — заменить macOS ssh-agent на nix версию + - macOS ssh-agent не поддерживает FIDO2 sign через нативный ssh-keygen + - нужно `nix shell nixpkgs#libfido2 nixpkgs#openssh --command ssh-agent` +- [ ] **FIDO2 ключ с `resident`** — пересоздать `id_ed25519_sk_ledger` с `-O resident` + - сейчас без resident — файл обязателен для восстановления + - с resident — можно восстановить через `ssh-keygen -K` после factory reset +- [ ] **FIDO2 ключ на github/forgejo** — добавить `id_ed25519_sk_ledger.pub` на github + - для тестирования и использования + - сейчас не добавлен +- [ ] **backups ключей** — сохранить `id_ed25519_backup.pub` (отдельный, обычный) + - страховка если Ledger сломается + - сейчас нет запасного ключа +- [ ] **`SSH_AUTH_SOCK` fix** — добавить `unset SSH_AUTH_SOCK` в shell init + - если gpg-agent когда-то снова его перехватит + - не критично сейчас +- [ ] **pass-store origins** — настроить remotes для `~/.password-store` (отдельная задача в `garden.md`) diff --git a/docs/structure.md b/docs/structure.md new file mode 100644 index 0000000..679f18c --- /dev/null +++ b/docs/structure.md @@ -0,0 +1,63 @@ +# structure + +how the config is organized. + +## top-level + +``` +nix-config/ +├── flake.nix # entry point - dispatches to hosts +├── home/ # home-manager (user space) +├── hosts/ # system-level configs +├── secrets/ # sops-nix encrypted files +└── docs/ # this folder +``` + +## flake.nix flow + +``` +flake.nix +├── nixosConfigurations +│ ├── muscari → hosts/muscari/ + home/ +│ └── rosemary → hosts/rosemary/ + home/ +└── darwinConfigurations + └── poppy → hosts/poppy/ + home/ +``` + +## home/ vs hosts/ + +| what | where | why | +|------|-------|-----| +| shell config | `home/` | same on all machines | +| terminal emulator | `home/` | gui apps belong here | +| system packages | `hosts//` | os-specific | +| networking | `hosts//` | per-host | +| hostname | `hosts//` | obviously | + +## adding things + +**new package for all hosts:** +→ add to `home/cli.nix` in `home.packages` + +**new package for one host:** +→ add to `hosts//default.nix` in `environment.systemPackages` + +**new alias for all hosts:** +→ add to `home/zsh.nix` in `programs.zsh.shellAliases` + +**new alias for one host:** +→ add to `hosts//default.nix` in `home-manager.users.mikl.programs.zsh.shellAliases` + +## host-specific imports + +poppy imports `home/kitty.nix` because it has a screen. muscari doesn't. + +```nix +# hosts/poppy/default.nix +home-manager.users.mikl = { + imports = [ ../../home/kitty.nix ]; + programs.zsh.shellAliases = { + switch = "darwin-rebuild switch --flake .#poppy"; + }; +}; +``` diff --git a/docs/terminal.md b/docs/terminal.md new file mode 100644 index 0000000..5e570ae --- /dev/null +++ b/docs/terminal.md @@ -0,0 +1,36 @@ +# terminal + +kitty configuration for gui hosts. + +## where + +`home/kitty.nix` - imported only by poppy (macbook). + +## theme + +**Catppuccin-Mocha** - dark, soft colors. + +## font + +JetBrainsMono Nerd Font, size 12. + +## features + +- **tabs**: powerline style, at bottom, slanted separators +- **transparency**: background blur 16 +- **cursor**: beam shape +- **scrollback**: 10000 lines +- **cyrillic shortcuts**: cmd+м = paste, cmd+с = copy + +## cyrillic config + +loads `~/.config/kitty/cyrillic-shortcuts.conf` for additional layout fixes. + +## applying changes + +kitty reloads config automatically. if not: + +```bash +killall kitty +# or ctrl+shift+f5 inside kitty +``` diff --git a/flake.lock b/flake.lock index 1753b05..eff8e51 100644 --- a/flake.lock +++ b/flake.lock @@ -27,11 +27,11 @@ ] }, "locked": { - "lastModified": 1782423922, - "narHash": "sha256-qPNd6lUohHP5gcJhqQ7rLV87RwIx0xYR2A4Frb9Zjc4=", + "lastModified": 1782657028, + "narHash": "sha256-PHTCpYZCMzJYS3phhywqRAZphKVr2zjvlGYa+H20ZZ4=", "owner": "nix-community", "repo": "home-manager", - "rev": "5d320ab301cfaaca7d32514f13815d19d109f5f4", + "rev": "4ad9aaae70c9aaab504127f926c0fa9cfbc2b365", "type": "github" }, "original": { @@ -62,16 +62,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1782335603, - "narHash": "sha256-sZkQH1CkiZtdvcaLx4sGQD9Q9h+A8qB04DRpqQCN530=", + "lastModified": 1782545840, + "narHash": "sha256-PkBPmP5ofNtunCU7/tfn6wi9OLlzFGcHAqqmY+/mwUI=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "03c72920da828594fae523aaef96f33dff10b340", + "rev": "3d46470bb3030020f7e1361f33514854f5bfa86d", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-25.11", + "ref": "nixpkgs-unstable", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index 8289a3c..e11eb5f 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ description = "nix-config — multi-host Nix configurations"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; nix-darwin = { url = "github:LnL7/nix-darwin"; @@ -41,7 +41,8 @@ home-manager = { useGlobalPkgs = true; useUserPackages = true; - users.mikl = import ./home; + backupFileExtension = "backup"; + users.michaotic = import ./home; }; } ]; @@ -57,7 +58,8 @@ home-manager = { useGlobalPkgs = true; useUserPackages = true; - users.mikl = import ./home; + backupFileExtension = "backup"; + users.michaotic = import ./home; }; } ]; @@ -66,7 +68,7 @@ { nixosConfigurations = { muscari = mkNixos "muscari"; - rosemary = mkNixos "rosemary"; + # rosemary = mkNixos "rosemary"; # TODO: add disk config }; darwinConfigurations = { diff --git a/garden.md b/garden.md index 07d9b06..fe3c455 100644 --- a/garden.md +++ b/garden.md @@ -24,9 +24,70 @@ Hosts in this repo are named after plants and flowers. - `hosts//default.nix` — per-host config - `hosts/common/default.nix` — shared NixOS config - `hosts/common-darwin/default.nix` — shared darwin config -- `home/default.nix` — shared home-manager config for user `mikl` +- `home/default.nix` — shared home-manager config for user `michaotic` (plan to migrate to `mikl` later) - `secrets/` — sops-encrypted secrets per host +## documentation + +notes are split by topic under `docs/`: + +- `docs/structure.md` — high-level overview, where things go +- `docs/poppy.md` — MacBook specific (homebrew, system defaults) +- `docs/muscari.md` — server + k3s specifics +- `docs/shell.md` — zsh, pure prompt, plugins +- `docs/terminal.md` — kitty + catppuccin theme +- `docs/cheatsheet.md` — quick commands reference + +when adding a new host: copy `docs/structure.md` template, document hostname, role, packages unique to it. + ## migrations -- `nix-config-legacy/` — previous muscari-only config (preserved for reference) \ No newline at end of file +- `nix-config-legacy/` — previous muscari-only config (preserved for reference) + +## future plans + +### username migration + +currently using `michaotic` as username (historical). plan to migrate to `mikl` across all hosts for consistency. + +**why not now:** +- macOS: changing username requires careful handling of home folder, keychain, permissions +- linux: easier, but better to do in one coordinated change + +**when:** after setting up proper backups and testing migration procedure + +### rosemary setup + +currently `flake.nix` has `rosemary` commented out. need to: +- add disk config (likely similar to muscari) +- add users.users.michaotic.openssh.authorizedKeys +- test `nix flake check` + +### documentation improvements + +- [ ] create `docs/bootstrap.md` — manual setup guide for fresh device without nix (emergency fallback) + - list all CLI tools and how to install via brew/pnpm + - list all system defaults that need manual configuration + - explain which tools are nix-managed vs brew-managed +- [ ] create `docs/adding-host.md` — checklist for adding a new host +- [ ] create `docs/secrets.md` — sops-nix setup when we get there +- [ ] add migration notes to `docs/structure.md` as we learn patterns +- [ ] screenshots/notes for things that aren't obvious (e.g. dock layout) + +### tech debt + +- [ ] **git config** — разобраться с конфликтом `~/.gitconfig` (локальный) vs `~/.config/git/config` (home-manager) + - перенести osxkeychain credential helper, lfs в home-manager + - унифицировать user.name/email (michaotic vs mikl) + - добавить `core.pager = delta` явно + +- [ ] **git origins / remotes** — разобраться как декларативно описывать несколько git origins для произвольного репозитория + - использовать для pass-store (github + git.sol.moe + forgejo) + - сейчас делаем руками + - возможно через `home.file` + `.git/config` в Nix + - решить когда будет актуально + +- [ ] **pass-store sync** — настроить `~/.password-store` с remotes (forgejo primary, github/git.sol.moe backup) + - сначала создать репы на всех 3 хостах + - потом настроить через `pass git remote add` + - алиасы в zsh для каждого origin diff --git a/home/cli.nix b/home/cli.nix new file mode 100644 index 0000000..e69de29 diff --git a/home/default.nix b/home/default.nix index 8dd75fe..33915ee 100644 --- a/home/default.nix +++ b/home/default.nix @@ -1,13 +1,23 @@ { pkgs, ... }: { - home.stateVersion = "25.11"; - - programs.zsh.enable = true; - - programs.git.enable = true; - - home.packages = [ - pkgs.ripgrep - pkgs.fd + imports = [ + ./cli.nix + ./git.nix + ./ssh.nix + ./zsh.nix + ./duti.nix + ./pass.nix ]; -} \ No newline at end of file + + home.stateVersion = "24.11"; + + home.sessionVariables = { + SEARXNG_URL = "https://search.iscg.dev"; + PATH = "/etc/profiles/per-user/michaotic/bin:$HOME/.nix-profile/bin:$HOME/bin:$HOME/.local/bin:$HOME/.npm-global/bin:/Users/michaotic/.opencode/bin:/Users/michaotic/.local/share/pi-node/node-v22.23.1-darwin-arm64/bin"; + # OPENROUTER_API_KEY = ""; # TODO: move to sops-nix secrets + SOPS_AGE_KEY_FILE = "$HOME/.config/sops/age/keys.txt"; + EDITOR = "micro"; + VISUAL = "micro"; + KUBECONFIG = "$HOME/.kube/config-jul11"; + }; +} diff --git a/home/duti.nix b/home/duti.nix new file mode 100644 index 0000000..54568fe --- /dev/null +++ b/home/duti.nix @@ -0,0 +1,19 @@ +{ pkgs, ... }: { + home.packages = [ pkgs.duti ]; + + xdg.configFile."duti/config".text = '' + # browser (html работает, url/xhtml - Apple блокирует) + com.google.Chrome public.html all + + # markdown viewer + MarkEdit net.daringfireball.markdown all + MarkEdit public.markdown all + + # mail + com.apple.mail public.email-message all + + # для .url, .pdf, .sh, .txt - настраивать вручную через Finder + # (Apple блокирует CLI настройку системных UTI на Sonoma+) + ''; + # defaults applied via system.activationScripts.defaultApps in hosts/poppy/default.nix +} diff --git a/home/git.nix b/home/git.nix new file mode 100644 index 0000000..35f2d35 --- /dev/null +++ b/home/git.nix @@ -0,0 +1,66 @@ +{ pkgs, ... }: +{ + programs.git = { + enable = true; + + # Git LFS support + lfs.enable = true; + + settings = { + user = { + name = "mikl"; + email = "mikl@iscg.dev"; + }; + + init.defaultBranch = "main"; + + # Always rebase on pull + pull.rebase = true; + + # GPG signing (used by pass) + gpg = { + format = "openpgp"; + program = "${pkgs.gnupg}/bin/gpg"; + }; + + # Better diff algorithm (longer but faster on large files) + diff.algorithm = "histogram"; + + # Color output + color.ui = "auto"; + color.diff = "auto"; + color.status = "auto"; + color.branch = "auto"; + + # Useful aliases (in addition to shell aliases) + alias.st = "status"; + alias.co = "checkout"; + alias.br = "branch"; + alias.ci = "commit"; + alias.unstage = "reset HEAD --"; + alias.last = "log -1 HEAD"; + alias.visual = "!gitk"; + }; + + ignores = [ + ".DS_Store" + "*.swp" + "*~" + ".idea/" + ".vscode/" + ]; + }; + + # Use git-delta for pretty diffs + programs.delta = { + enable = true; + enableGitIntegration = true; + options = { + navigate = true; # n/N to move between files + light = false; # dark mode + line-numbers = true; + side-by-side = false; + syntax-theme = "Catppuccin Macchiato"; + }; + }; +} diff --git a/home/kitty.nix b/home/kitty.nix new file mode 100644 index 0000000..eadc072 --- /dev/null +++ b/home/kitty.nix @@ -0,0 +1,46 @@ +{ pkgs, ... }: +{ + programs.kitty = { + enable = true; + + font = { + name = "JetBrainsMono Nerd Font"; + size = 12; + }; + + themeFile = "Catppuccin-Mocha"; + + settings = { + background_opacity = "1"; + dynamic_background_opacity = "yes"; + background_blur = 16; + cursor_shape = "beam"; + modify_font = "cell_height 110%"; + bold_font = "auto"; + disable_ligatures = "never"; + adjust_line_height = "0"; + adjust_column_width = "0"; + + tab_bar_min_tabs = 1; + tab_bar_edge = "bottom"; + tab_bar_style = "powerline"; + tab_powerline_style = "slanted"; + + tab_title_template = "{title}{' :{}:'.format(num_windows) if num_windows > 1 else ''}"; + + scrollback_lines = 10000; + + # Copy/paste with non-latin layouts + map_cmd_v = "paste_from_clipboard"; + map_cmd_c = "copy_to_clipboard"; + + # Russian layout aliases (Cmd) + "map cmd+м" = "paste_from_clipboard"; + "map cmd+с" = "copy_to_clipboard"; + }; + + extraConfig = '' + include ~/.config/kitty/cyrillic-shortcuts.conf + ''; + }; +} diff --git a/home/pass.nix b/home/pass.nix new file mode 100644 index 0000000..22c5837 --- /dev/null +++ b/home/pass.nix @@ -0,0 +1,21 @@ +{ config, pkgs, ... }: +{ + # Password store (gpg-backed) with pass-otp extension + programs.password-store = { + enable = true; + package = pkgs.pass.withExtensions (exts: [ exts.pass-otp ]); + settings = { + PASSWORD_STORE_DIR = "$HOME/.password-store"; + PASSWORD_STORE_GIT = "ssh://git@git.iscg.dev:2222/mikl/password-store.git"; + PASSWORD_STORE_X_SELECTION_TIMEOUT = "5"; + PASSWORD_STORE_CLIP_TIME = "45"; + PASSWORD_STORE_GENERATED_LENGTH = "25"; + }; + }; + + # GPG agent settings (only for GPG/pass, NOT for SSH) + services.gpg-agent = { + enable = true; + defaultCacheTtl = 3600; + }; +} diff --git a/home/ssh.nix b/home/ssh.nix new file mode 100644 index 0000000..36e719b --- /dev/null +++ b/home/ssh.nix @@ -0,0 +1,32 @@ +{ ... }: +{ + # SSH client configuration with host-specific keys + programs.ssh = { + enable = true; + + matchBlocks = { + # Forgejo (personal) — port 2222 + "git.iscg.dev" = { + identityFile = "~/.ssh/id_ed25519_forgejo"; + port = 2222; + identitiesOnly = true; + addKeysToAgent = "yes"; + }; + + # GitHub + "github.com" = { + identityFile = "~/.ssh/id_ed25519"; + identitiesOnly = true; + addKeysToAgent = "yes"; + }; + + # git.sol.moe + "git.sol.moe" = { + identityFile = "~/.ssh/poppy"; + user = "mikl"; + identitiesOnly = true; + addKeysToAgent = "yes"; + }; + }; + }; +} diff --git a/home/zsh.nix b/home/zsh.nix new file mode 100644 index 0000000..9f00444 --- /dev/null +++ b/home/zsh.nix @@ -0,0 +1,101 @@ +{ pkgs, lib, ... }: +{ + programs.zsh = { + enable = true; + enableCompletion = true; + + autosuggestion.enable = true; + syntaxHighlighting.enable = true; + + # History + history = { + size = 10000; + save = 10000; + ignoreDups = true; + share = true; + extended = true; + }; + + # Pure prompt + plugins = [ + { + name = "pure"; + src = pkgs.fetchFromGitHub { + owner = "sindresorhus"; + repo = "pure"; + rev = "v1.23.0"; + sha256 = "sha256-BmQO4xqd/3QnpLUitD2obVxL0UulpboT8jGNEh4ri8k="; + }; + } + { + name = "zsh-syntax-highlighting"; + file = "share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"; + src = pkgs.zsh-syntax-highlighting; + } + { + name = "zsh-autosuggestions"; + file = "share/zsh-autosuggestions/zsh-autosuggestions.zsh"; + src = pkgs.zsh-autosuggestions; + } + ]; + + # Base aliases (universal) + shellAliases = { + ll = "eza -l --icons --git"; + la = "eza -la --icons --git"; + ff = "fastfetch"; + + # gita (manage multiple git repos) + gll = "gita ll"; + gllg = "gita ll -g"; + gadd = "gita add"; + gstatus = "gita status"; + gfetch = "gita fetch"; + gremote = "gita remote"; + ginfo = "gita info"; + gll-all = "gita ll -g"; + + # pass sync (password store) + pass-sync = "pass git push"; + pass-pull = "pass git pull"; + pass-status = "pass git status"; + pass-sync-forgejo = "pass git push origin"; + pass-sync-github = "pass git push github"; + pass-sync-solmoe = "pass git push git.sol.moe"; + pass-pull-all = "pass git pull --all"; + }; + + # Environment setup (zshenv) + envExtra = '' + if [ -x "/opt/homebrew/bin/brew" ]; then + eval "$(/opt/homebrew/bin/brew shellenv)" + fi + ''; + + # Init content - uses lib.mkBefore to run before home-manager generated code + initContent = lib.mkMerge [ + (lib.mkBefore '' + export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin:$HOME/.nix-profile/bin:/etc/profiles/per-user/michaotic/bin:$PATH" + '') + '' + # Show fastfetch on shell start + echo + fastfetch + echo + + # Load pure prompt + autoload -U promptinit; promptinit + prompt pure + + # Right prompt - time in gray + RPROMPT='%F{240}%D{%H:%M:%S}%f' + + # Pure prompt customization + zstyle ':prompt:pure:prompt:success' color green + zstyle ':prompt:pure:prompt:success' symbol ">" + zstyle ':prompt:pure:prompt:error' color red + zstyle ':prompt:pure:prompt:error' symbol ">" + '' + ]; + }; +} diff --git a/hosts/common/default.nix b/hosts/common/default.nix index d3862f2..2e570d2 100644 --- a/hosts/common/default.nix +++ b/hosts/common/default.nix @@ -6,10 +6,13 @@ "flakes" ]; - users.users.mikl = { + # Required when using zsh as user shell + programs.zsh.enable = true; + + users.users.michaotic = { isNormalUser = true; shell = pkgs.zsh; - home = "/home/mikl"; + home = "/home/michaotic"; }; environment.systemPackages = [ diff --git a/hosts/muscari/default.nix b/hosts/muscari/default.nix index 342905b..56cb3b0 100644 --- a/hosts/muscari/default.nix +++ b/hosts/muscari/default.nix @@ -31,9 +31,9 @@ boot.loader.systemd-boot.enable = lib.mkForce false; boot.loader.efi.canTouchEfiVariables = lib.mkForce false; - # SSH ключи для mikl: forgejo .keys endpoint с sha256-pinning + # SSH ключи для michaotic: forgejo .keys endpoint с sha256-pinning # + hardcoded fallback ключ для восстановления - users.users.mikl.openssh.authorizedKeys.keyFiles = [ + users.users.michaotic.openssh.authorizedKeys.keyFiles = [ (pkgs.fetchurl { url = "https://git.iscg.dev/mikl.keys"; sha256 = "sha256-4n6QLUPozdhHOmsRCuFn7HR9BU3nF0/GR2E7s5Lpz+Q="; diff --git a/hosts/poppy/default.nix b/hosts/poppy/default.nix index 0bc6467..f601478 100644 --- a/hosts/poppy/default.nix +++ b/hosts/poppy/default.nix @@ -1,27 +1,33 @@ -{ pkgs, ... }: -{ +{ pkgs, ... }: { + imports = [ ./homebrew.nix ./system.nix ]; + networking.hostName = "poppy"; + system.primaryUser = "michaotic"; + system.stateVersion = 7; - # Allow unfree packages (some macOS-ported tools need it) nixpkgs.config.allowUnfree = true; + nix.enable = false; - system.primaryUser = "mikl"; - - # nix settings - nix.settings.experimental-features = [ - "nix-command" - "flakes" - ]; - - # nix daemon (multi-user install) - services.nix-daemon.enable = true; - - users.users.mikl = { - home = "/Users/mikl"; + users.users.michaotic = { + home = "/Users/michaotic"; shell = pkgs.zsh; }; - environment.systemPackages = [ - pkgs.vim - ]; -} \ No newline at end of file + environment.systemPackages = [ pkgs.vim ]; + + # Apply default apps via duti on activation + system.activationScripts.defaultApps.text = '' + if [ -f "/Users/michaotic/.config/duti/config" ]; then + ${pkgs.duti}/bin/duti -d /Users/michaotic/.config/duti/config || true + fi + ''; + + home-manager.users.michaotic = { + imports = [ ../../home/kitty.nix ]; + programs.zsh.shellAliases = { + switch = "darwin-rebuild switch --flake .#poppy"; + update = "nix flake update && darwin-rebuild switch --flake .#poppy"; + cleanup = "nix-collect-garbage -d"; + }; + }; +} diff --git a/hosts/poppy/homebrew.nix b/hosts/poppy/homebrew.nix new file mode 100644 index 0000000..74b7b94 --- /dev/null +++ b/hosts/poppy/homebrew.nix @@ -0,0 +1,33 @@ +{ + homebrew.enable = true; + homebrew.onActivation.cleanup = "zap"; + + homebrew.taps = []; + + homebrew.brews = [ + "kew" + ]; + + homebrew.casks = [ + "google-chrome" + "google-drive" + "marta" + "vscodium" + "github" + "docker-desktop" + "ollama-app" + "microsoft-word" + "microsoft-excel" + "microsoft-powerpoint" + "obsidian" + "anki" + "calibre" + "telegram" + "maccy" + "mos" + "caffeine" + "lunacy" + "skim" + "markedit" + ]; +} diff --git a/hosts/poppy/system.nix b/hosts/poppy/system.nix new file mode 100644 index 0000000..5962723 --- /dev/null +++ b/hosts/poppy/system.nix @@ -0,0 +1,148 @@ +{ pkgs, ... }: { + system.defaults = { + NSGlobalDomain = { + AppleInterfaceStyle = "Dark"; + NSAutomaticCapitalizationEnabled = true; + NSAutomaticPeriodSubstitutionEnabled = true; + NSAutomaticQuoteSubstitutionEnabled = true; + NSAutomaticDashSubstitutionEnabled = true; + }; + + CustomUserPreferences = { + "com.apple.HIToolbox" = { + AppleEnabledInputSources = [ + { + InputSourceKind = "Keyboard Layout"; + "KeyboardLayout ID" = 0; + "KeyboardLayout Name" = "U.S."; + } + { + "Bundle ID" = "com.apple.CharacterPaletteIM"; + InputSourceKind = "Non Keyboard Input Method"; + } + { + InputSourceKind = "Keyboard Layout"; + "KeyboardLayout ID" = 19456; + "KeyboardLayout Name" = "Russian"; + } + ]; + AppleSelectedInputSources = [ + { + InputSourceKind = "Keyboard Layout"; + "KeyboardLayout ID" = 0; + "KeyboardLayout Name" = "U.S."; + } + ]; + }; + + "com.apple.symbolichotkeys".AppleSymbolicHotKeys = { + "28" = { + enabled = 1; + value = { + type = "standard"; + parameters = [ 51 20 1441792 ]; + }; + }; + "29" = { + enabled = 1; + value = { + type = "standard"; + parameters = [ 51 20 1179648 ]; + }; + }; + "30" = { + enabled = 1; + value = { + type = "standard"; + parameters = [ 52 21 1441792 ]; + }; + }; + "31" = { + enabled = 1; + value = { + type = "standard"; + parameters = [ 52 21 1179648 ]; + }; + }; + }; + + "NSGlobalDomain" = { + "AppleMenuBarVisibleInFullscreen" = true; + }; + }; + + controlcenter = { + BatteryShowPercentage = true; + }; + + trackpad = { + # Clicking + Clicking = true; + TrackpadRightClick = true; + TrackpadThreeFingerDrag = false; + DragLock = false; + Dragging = false; + + # Scroll + TrackpadMomentumScroll = true; + + # Gestures + TrackpadPinch = true; + TrackpadRotate = true; + TrackpadThreeFingerTapGesture = 0; + TrackpadTwoFingerFromRightEdgeSwipeGesture = 3; + TrackpadThreeFingerVertSwipeGesture = 2; + TrackpadFourFingerHorizSwipeGesture = 2; + TrackpadFourFingerVertSwipeGesture = 0; + TrackpadFourFingerPinchGesture = 2; + }; + + finder = { + AppleShowAllExtensions = true; + ShowPathbar = true; + ShowStatusBar = true; + FXPreferredViewStyle = "clmv"; + NewWindowTarget = "Home"; + ShowExternalHardDrivesOnDesktop = false; + ShowHardDrivesOnDesktop = false; + FXEnableExtensionChangeWarning = false; + _FXShowPosixPathInTitle = true; + }; + + dock = { + tilesize = 48; + largesize = 64; + magnification = true; + orientation = "bottom"; + mineffect = "scale"; + autohide = true; + autohide-time-modifier = 0.8; + show-recents = false; + show-process-indicators = true; + static-only = false; + wvous-tl-corner = 1; + wvous-tr-corner = 4; + wvous-bl-corner = 1; + wvous-br-corner = 1; + persistent-apps = [ + "/System/Applications/System Settings.app" + "/Applications/Google Chrome.app" + "/System/Applications/Mail.app" + "/Applications/Telegram.app" + "/System/Applications/Calendar.app" + "/Applications/Obsidian.app" + "/Applications/VSCodium.app" + "/Users/michaotic/Applications/Home Manager Apps/kitty.app" + "/System/Applications/Books.app" + "/System/Applications/QuickTime Player.app" + ]; + }; + }; + + system.activationScripts.postActivation.text = '' + echo "Restarting Dock..." + sudo -u michaotic /usr/bin/killall Dock + ''; + + security.pam.services.sudo_local.touchIdAuth = true; +} diff --git a/temp.txt b/temp.txt new file mode 100644 index 0000000..329face --- /dev/null +++ b/temp.txt @@ -0,0 +1 @@ +testing this thing