Compare commits

15 Commits

Author SHA1 Message Date
aner fd5793e2bd azos-core bump: notify hook keeps permission rules working, harper org filter fix
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 23:30:11 +03:00
aner a7d90c3b8d Bumped azos-core, added sudo up-front to build 2026-10-18 06:33:58 +03:00
aner 63f9e5c1d7 azos-core bump: pcap-mode filter completion, field hex view, stale-buffer fix
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 11:06:50 +03:00
aner b5c7d7d589 Sync Nextcloud task lists via DAV; azos-core bump: dav module, org-roam to base
features/dav declares the Nextcloud CalDAV account (password from pass,
gated on the YubiKey like mail) for azos-core's new azos.dav module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 09:36:17 +03:00
aner d0c839494c nix flake update; azos-core bump: build evil-hl-line against our own Emacs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 17:36:10 +03:00
aner 2cfaddec4b azos-core bump: fix agent-shell memory resync, use tool-call-update not file-write 2026-10-13 13:18:52 +03:00
aner e36767b625 azos-core bump: fix over-eager AZOS_AGENT_SHELL_SESSION guard on SessionStart hook 2026-10-13 10:55:53 +03:00
aner 2585a0e9d3 Rename claude backend toggles to the new agent-memory/agent-skills shape
Mirrors the azos-core change: azos.claude.enable now sets
azos.agent-memory.backends.claude.enable and
azos.agent-skills.backends.claude.enable instead of the old
azos.claude-memory.enable/azos.claude-skills.enable. The new agent-shell
memory backend needs no wiring here -- it's bundled directly in
azos-core's editor/emacs module, alongside wherever agent-shell itself
gets installed.
2026-10-13 10:51:22 +03:00
aner ac6c15f931 Set real mail accounts and tie presence check to the yubikey toggle
Mirrors the azos-core change: features/mail/default.nix now just sets
accounts.email.accounts (the three real addresses, with the existing
default_account_params/default_gmail_params convenience merging kept
here since it's personal boilerplate, not mechanism) and
azos.mail.presenceCheckCommand, wired to azos-card-present only when
azos.encryption.yubikey.enable is true.
2026-10-13 10:03:48 +03:00
aner b880a194ba Set real values for genericized git-config/encryption/send-alert/printing
Mirrors the azos-core change: drops the now-fully-migrated
features/{git-config,encryption,printing}, rewrites features/homey-alert
down to just the azos-homey-ntfy-alert overlay package (the ntfy-specific
remote-notification command plugged into azos.send-alert.remoteCommand),
and sets every real personal value (name/email/signing key, GPG key id,
YubiKey/browserpass/gpg-ssh toggles and keygrip/pubkey, printing username)
directly in home-manager/home.nix and nixos/configuration.nix, same place
the other personal values (azos.name, azos.headphones.devices) already
live.
2026-10-13 09:50:44 +03:00
aner 8b633f598c Move hfsprogs into azos-core's desktop suite as a NixOS module
Mirrors the azos-core change -- nixos.desktop already imported in
nixos/configuration.nix, so no new import line needed, just drops the
old homeManager.hfsprogs import.
2026-10-13 09:29:02 +03:00
aner da5df102cd Move audio/binfmt/bluetooth/headphones/chess/kubernetes/virtualization into azos-core suites
Mirrors the azos-core change: removes the now-relocated standalone
features from azos/features, and updates both import lists
(home-manager/home.nix, nixos/configuration.nix) to pull the nixos.desktop
and nixos.dev suite modules instead of the old per-feature ones.
2026-10-13 09:19:31 +03:00
aner 5ec9f2c528 azos-core bump: restore exwm's azos-emacs-desktop dependency 2026-10-13 09:05:25 +03:00
aner 80420198e8 Rename azos-core station suite to desktop; move discord out to azos/features
Mirrors the azos-core change: station is renamed desktop (its apps are
generically useful on any machine, work included), discord moves to its
own standalone azos/features/discord (personal-only, so it lives in the
azos project rather than azos-core), and azos-emacs-station becomes
azos-emacs-desktop in lauretta's emacs config to match.
2026-10-13 08:54:39 +03:00
aner 53976751cd Rename azos-core features/ to suites/; fold claude-memory/claude-skills into base
Bumps the azos-core submodule and updates docs/imports to match: AGENTS.md
now documents the suite-grouping convention (no bare top-level modules),
and claude-memory/claude-skills no longer need their own entries in
home-manager/home.nix since they're folded into the base suite.
2026-10-13 08:42:49 +03:00
34 changed files with 208 additions and 847 deletions
+39 -17
View File
@@ -83,7 +83,7 @@ azos/
├── shells/ # Dev shells ├── shells/ # Dev shells
└── azos-core/ # Shared feature library (git submodule) └── azos-core/ # Shared feature library (git submodule)
├── flake.nix ├── flake.nix
├── features/ # Shared/reusable features (auto-discovered) ├── suites/ # Suites, each grouping related sub-features (auto-discovered)
├── overlays/ # Core overlays ├── overlays/ # Core overlays
└── _lib/ # Module schema helpers └── _lib/ # Module schema helpers
``` ```
@@ -93,7 +93,10 @@ Features are auto-discovered by `import-tree` — no central imports file. Each
then explicitly imported in `home-manager/home.nix` via `suiteModules.homeManager.<name>`. then explicitly imported in `home-manager/home.nix` via `suiteModules.homeManager.<name>`.
**Where to put a new feature:** **Where to put a new feature:**
- Shared / reusable across machines → `azos-core/features/<name>/default.nix` - Shared / reusable across machines → inside an existing or new suite directory,
`azos-core/suites/<suite>/<name>/default.nix` (registering
`config.flake.modules.homeManager.<suite>`) — never a bare file directly under
`azos-core/suites/`; every azos-core feature belongs to a suite.
- Machine-specific → `azos/features/<name>/default.nix` - Machine-specific → `azos/features/<name>/default.nix`
## Code Style Guidelines ## Code Style Guidelines
@@ -125,7 +128,7 @@ All features follow this flake-parts registration pattern:
### Naming Conventions ### Naming Conventions
- Option paths: `azos.<module-name>.<option>` - Option paths: `azos.<module-name>.<option>`
- Module names: kebab-case (`git-config`, `claude-memory`) - Module names: kebab-case (`git-config`, `desktop`)
- Feature directories: kebab-case, one `default.nix` per feature - Feature directories: kebab-case, one `default.nix` per feature
- Variables: kebab-case - Variables: kebab-case
@@ -171,8 +174,13 @@ Top-level suites group related features (defined in `azos-core/`):
- `azos.suites.base` — base packages and config - `azos.suites.base` — base packages and config
- `azos.suites.editor` — editor tools - `azos.suites.editor` — editor tools
- `azos.suites.dev` — development tools - `azos.suites.dev` — development tools
- `azos.suites.station` — desktop applications - `azos.suites.desktop` — desktop session plumbing (flameshot, dunst, keyboard layout) plus generally-useful apps (browser, libreoffice, zoom, spotify, gimp, whatsapp). Shared, lives in azos-core — wanted on any GUI machine, work included.
- `azos.suites.exwm` — EXWM window manager - `azos.suites.exwm` — EXWM window manager, depends on `desktop`
Machine-specific, personal-only apps (steam, discord) are **not** part of any azos-core
suite — they live as standalone features directly in `azos/features/`, imported only on
machines that want them (e.g. `azos/features/discord`, `azos/features/steam`), since any
such machine already uses the `azos` project rather than a different downstream repo.
The machine suite (`azos.suites.lauretta`) enables the relevant suites for this machine. The machine suite (`azos.suites.lauretta`) enables the relevant suites for this machine.
@@ -193,26 +201,38 @@ specialisation = {
## Adding New Modules ## Adding New Modules
1. Create `features/<name>/default.nix` (machine-specific) or `azos-core/features/<name>/default.nix` (shared) 1. Create `features/<name>/default.nix` (machine-specific) or, for shared features,
`azos-core/suites/<suite>/<name>/default.nix` inside an existing or new suite
(registering `config.flake.modules.homeManager.<suite>`, not a standalone module name)
2. Use the module template above 2. Use the module template above
3. Add `suiteModules.homeManager.<name>` to the imports list in `home-manager/home.nix` 3. Machine-specific: add `suiteModules.homeManager.<name>` to the imports list in
`home-manager/home.nix`. Shared: no change needed if the suite is already imported.
4. Run `nix fmt` 4. Run `nix fmt`
> **Submodule gotcha**: New files in `azos-core/` must be `git add`-ed inside the submodule > **Submodule gotcha**: New files in `azos-core/` must be `git add`-ed inside the submodule
> before `nix build` will see them — Nix flakes only evaluate git-tracked files. > before `nix build` will see them — Nix flakes only evaluate git-tracked files.
> ```bash > ```bash
> cd azos-core && git add features/<name>/ > cd azos-core && git add suites/<suite>/<name>/
> ``` > ```
## Claude Integration ## Agent Memory/Skills Integration
Claude Code is integrated with this environment via several azos-core features: Memory (org-roam) and skills (slash commands/CLAUDE.md) are backend-pluggable, not
Claude-specific, even though Claude is currently the only skills backend and one of two
memory backends:
| Feature | Option | What it does | | Feature | Option | What it does |
|---------|--------|--------------| |---------|--------|--------------|
| `azos-core/features/claude-memory` | `azos.claude-memory.enable` | Registers org-roam-mcp as a global MCP server in `~/.claude.json` | | `azos-core/suites/base/agent-memory` | `azos.agent-memory.backends.claude.enable` | Registers org-roam-mcp as a global MCP server in `~/.claude.json`; hooks resync org-roam's index on edit and inject project context at session start |
| `azos-core/features/claude-skills` | `azos.claude-skills.enable` | Deploys skills to `~/.claude/commands/` and content to `~/.claude/CLAUDE.md` | | `azos-core/suites/base/agent-memory` | `azos.agent-memory.backends.agent-shell.enable` | Registers org-roam-mcp via `agent-shell-mcp-servers` and resyncs on `file-write` events — applies regardless of which ACP backend (Claude, Gemini CLI, Codex, ...) a given agent-shell session runs. Also marks Claude sessions agent-shell launches (`AZOS_AGENT_SHELL_SESSION=1`) so the claude backend's own hooks no-op for those sessions instead of double-acting |
| `azos/features/claude` | `azos.claude.enable` | Installs claude-code, auto-enables the above two | | `azos-core/suites/base/agent-skills` | `azos.agent-skills.backends.claude.enable` | Deploys skills to `~/.claude/commands/` and content to `~/.claude/CLAUDE.md` |
| `azos/features/claude` | `azos.claude.enable` | Installs claude-code, auto-enables the claude backend for both of the above |
| `azos-core/suites/editor/emacs` | (bundled, not a separate option) | Installs agent-shell, auto-enables `azos.agent-memory.backends.agent-shell.enable` |
Both agent-memory and agent-skills register `config.flake.modules.homeManager.base`
(they live inside the `base` suite, since that's where agent-shell is introduced) — not
standalone module names, so no separate `suiteModules.homeManager.*` import is needed for
them in `home-manager/home.nix`.
### Adding Claude skills or standing instructions ### Adding Claude skills or standing instructions
From any module — no need to touch azos-core: From any module — no need to touch azos-core:
@@ -223,7 +243,9 @@ azos.claude.globalMdSections.my-rule = ''
Always do X when Y. Always do X when Y.
''; '';
``` ```
See `azos-core/features/claude-skills/README.md` for full documentation. `globalSkills`/`globalMdSections` stay Claude-specific (`~/.claude/commands/`,
`~/.claude/CLAUDE.md`) deliberately — no other backend has a uniform equivalent yet. See
`azos-core/suites/base/agent-skills/README.md` for full documentation.
## Key Files ## Key Files
@@ -236,10 +258,10 @@ See `azos-core/features/claude-skills/README.md` for full documentation.
| `nixos/configuration-vm.nix` | Test VM config | | `nixos/configuration-vm.nix` | Test VM config |
| `overlays/` | Package overlays | | `overlays/` | Package overlays |
| `azos-core/` | Shared feature library (git submodule) | | `azos-core/` | Shared feature library (git submodule) |
| `azos-core/features/` | Shared features (auto-discovered by import-tree) | | `azos-core/suites/` | Suites, each grouping related shared sub-features (auto-discovered by import-tree) |
| `features/` | Machine-specific features (auto-discovered by import-tree) | | `features/` | Machine-specific features (auto-discovered by import-tree) |
| `azos-core/features/editor/emacs/config.org` | Literate Emacs config | | `azos-core/suites/editor/emacs/config.org` | Literate Emacs config |
| `azos-core/features/claude-skills/README.md` | Claude skills extensibility docs | | `azos-core/suites/base/claude-skills/README.md` | Claude skills extensibility docs |
## Common Tasks ## Common Tasks
-41
View File
@@ -1,41 +0,0 @@
{...}: {
config.flake.modules.homeManager.audio = {
lib,
config,
...
}: {
options.azos.home-audio.enable = lib.mkOption {
default = true;
type = lib.types.bool;
};
config = lib.mkIf config.azos.home-audio.enable {};
};
config.flake.modules.nixos.audio = {
lib,
config,
pkgs,
...
}: {
options.azos.hardware-audio.enable = lib.mkOption {
default = true;
type = lib.types.bool;
};
config = lib.mkIf config.azos.hardware-audio.enable {
services = {
pipewire = {
enable = true;
audio.enable = true;
pulse.enable = true;
alsa = {
enable = true;
support32Bit = true;
};
jack.enable = true;
};
};
environment.systemPackages = with pkgs; [pavucontrol];
musnix.enable = true;
};
};
}
-15
View File
@@ -1,15 +0,0 @@
{...}: {
config.flake.modules.nixos.binfmt = {
lib,
config,
...
}: {
options.azos.binfmt.enable = lib.mkOption {
default = true;
type = lib.types.bool;
};
config = lib.mkIf config.azos.binfmt.enable {
boot.binfmt.emulatedSystems = ["aarch64-linux"];
};
};
}
-22
View File
@@ -1,22 +0,0 @@
{...}: {
config.flake.modules.nixos.bluetooth = {
lib,
config,
...
}: {
options.azos.bluetooth.enable = lib.mkOption {
default = true;
type = lib.types.bool;
};
config = lib.mkIf config.azos.bluetooth.enable {
services.blueman.enable = true;
hardware.bluetooth.enable = true;
environment.etc."bluetooth/input.conf" = lib.mkForce {
text = ''
[General]
ClassicBondedOnly=false
'';
};
};
};
}
-39
View File
@@ -1,39 +0,0 @@
{...}: {
config.flake.overlayPkgs.emacs-lichess = pkgs: let
epkgs = pkgs.emacs.pkgs;
in
epkgs.trivialBuild {
pname = "lichess";
version = "1.0";
src = pkgs.fetchFromGitHub {
owner = "tmythicator";
repo = "lichess.el";
rev = "572a6a353e2113c8a95564b9bea1f2896e6d3dab";
sha256 = "1d87qjmdpizggkrzq39i9vqgvbpjf5mv97vm7zy28ksbqxmfasqa";
};
# trivialBuild's installPhase only installs *.el/*.elc; the piece SVGs
# under assets/ must be copied alongside so the default
# lichess-board-gui-asset-path (relative to the installed .el file) resolves.
postInstall = ''
cp -r assets $out/share/emacs/site-lisp/
'';
};
config.flake.modules.homeManager.chess = {
lib,
config,
pkgs,
...
}: {
options.azos.chess.enable = lib.mkOption {
default = false;
example = true;
type = lib.types.bool;
};
config = lib.mkIf config.azos.chess.enable {
azos.emacs.pkgs = [pkgs.azos-emacs-chess];
azos.emacs.enabledSuites = ["azos-emacs-chess"];
};
};
}
-14
View File
@@ -1,14 +0,0 @@
{
orgTrivialBuild,
epkgs,
pkgs,
}:
orgTrivialBuild {
pname = "azos-emacs-chess";
version = "0.1.0";
src = ./config.org;
packageRequires = with epkgs; [
pkgs.azos-emacs-base
pkgs.emacs-lichess
];
}
-30
View File
@@ -1,30 +0,0 @@
#+title: Aner's Emacs Chess Configuration
#+property: header-args :results silent
* Base setup
** Require
#+begin_src emacs-lisp
(require 'azos-emacs-base)
#+end_src
* Lichess
#+begin_src emacs-lisp
(use-package lichess
:demand t
:init
(defun azos/chess/open ()
"Open lichess in its dedicated \"chess\" tab."
(interactive)
(azos/tab-bar/open-singleton-tab "chess" #'lichess))
:bind
(:map azos/global-minor-mode/open-keymap
("k" . azos/chess/open)))
#+end_src
* Provide
#+begin_src emacs-lisp
(provide 'azos-emacs-chess)
#+end_src
-3
View File
@@ -1,3 +0,0 @@
{...}: {
config.flake.overlayPkgs.azos-emacs-chess = pkgs: pkgs.localEmacsPkg ./_pkg.nix;
}
+2 -2
View File
@@ -11,8 +11,8 @@
}; };
config = lib.mkIf config.azos.claude.enable { config = lib.mkIf config.azos.claude.enable {
home.packages = with pkgs; [claude-code claude-agent-acp]; home.packages = with pkgs; [claude-code claude-agent-acp];
azos.claude-memory.enable = lib.mkDefault true; azos.agent-memory.backends.claude.enable = lib.mkDefault true;
azos.claude-skills.enable = lib.mkDefault true; azos.agent-skills.backends.claude.enable = lib.mkDefault true;
}; };
}; };
} }
+31
View File
@@ -0,0 +1,31 @@
{...}: {
config.flake.modules.homeManager.dav = {
lib,
config,
pkgs,
...
}: {
config = {
azos.dav.enable = true;
# Same smartcard gate as mail: the password comes from `pass`.
azos.dav.presenceCheckCommand = lib.mkIf config.azos.encryption.yubikey.enable "${pkgs.azos-card-present}/bin/azos-card-present";
accounts.calendar.accounts.nextcloud = {
remote = {
type = "caldav";
# Principal discovery from the DAV root resolves the account's
# user ID, which differs from the login name on this server.
url = "https://nextcloud.zakobar.com/remote.php/dav/";
userName = "aner";
passwordCommand = ["sh" "-c" "pass zakobar.com | head -1"];
};
vdirsyncer = {
enable = true;
collections = ["from a"];
itemTypes = ["VTODO"];
};
};
};
};
}
+16
View File
@@ -0,0 +1,16 @@
{...}: {
config.flake.modules.homeManager.discord = {
lib,
config,
pkgs,
...
}: {
options.azos.discord.enable = lib.mkOption {
default = true;
type = lib.types.bool;
};
config = lib.mkIf config.azos.discord.enable {
home.packages = with pkgs; [discord];
};
};
}
-80
View File
@@ -1,80 +0,0 @@
{...}: {
config.flake.modules.homeManager.encryption = {
lib,
config,
pkgs,
...
}: {
options.azos.encryption.enable = lib.mkOption {
default = true;
type = lib.types.bool;
};
config = lib.mkIf config.azos.encryption.enable {
programs.password-store = {
enable = true;
settings = {
PASSWORD_STORE_KEY = "26B851F81F8F7C55";
};
};
home.packages = with pkgs; [
yubikey-personalization
];
programs.gpg = {
enable = true;
};
programs.browserpass = {
enable = true;
browsers = ["chromium"];
};
services.gpg-agent = {
enable = true;
enableSshSupport = true;
grabKeyboardAndMouse = false;
pinentry.package = pkgs.pinentry-emacs;
extraConfig = ''
allow-emacs-pinentry
allow-loopback-pinentry
'';
};
# pinentry-emacs needs INSIDE_EMACS set, but NOT on gpg-agent itself:
# gpg-agent calls gnupg_unsetenv("INSIDE_EMACS") on its own process at
# startup (agent/gpg-agent.c, both supervised and daemon mode) -- by
# design, it expects this to arrive per-connection from the CLIENT,
# not statically from its own daemon environment. Each gpg/git
# invocation reads INSIDE_EMACS from its own env and forwards it to
# gpg-agent via "OPTION putenv=INSIDE_EMACS=..." (common/asshelp.c:
# send_pinentry_environment); gpg-agent stores that per-session and
# copies it into the forked pinentry's actual env for that one
# request (agent/call-pinentry.c:atfork_core). So the var has to be
# set on whatever shell/process runs git or gpg -- a prior attempt
# at this set it on gpg-agent.service's systemd Environment instead,
# which gpg-agent silently discards before it can ever be read.
#
# Only safe to set globally when Emacs is structurally guaranteed to
# be up and responsive -- i.e. under EXWM, where Emacs *is* the
# window manager. Elsewhere Emacs may not be running (fine: the
# loopback socket connect fails instantly, same fast fallback as
# today) or may be running but transiently busy (not fine: pinentry
# then blocks on the Assuan handshake for up to its 60s timeout
# before falling back, which is strictly worse than today's instant
# failure).
home.sessionVariables = lib.mkIf config.azos.suites.exwm.enable {
INSIDE_EMACS = "t";
};
home.file.".ssh/config".source = ./ssh-config;
home.file.".ssh/gpg-as-ssh.pub".source = ./gpg-as-ssh.pub;
home.file.".gnupg/sshcontrol".source = ./sshcontrol;
# home-manager creates ~/.gnupg (via the files above) with the default
# umask, which can leave it group/other-readable. gpg refuses to trust
# a homedir like that and prints an "unsafe permissions" warning on
# every invocation, which pollutes anything that captures gpg/pass
# output (e.g. shell-command-to-string). Enforce 700 on every switch.
home.activation.gnupgPerms = lib.hm.dag.entryAfter ["writeBoundary"] ''
$DRY_RUN_CMD chmod 700 "$HOME/.gnupg"
'';
};
};
}
-1
View File
@@ -1 +0,0 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDMdozJewuuqG4VuwGhI5Uw2xDIUrkQwyPGQ2q3tvnPq/VoV+zfdnou42I6XYEfOGd6xfLR0byOGCk1Ia1F/picBsxADEaJFBnTny5MWNA5MxwJIYUPW5+kFN1TmxdNGmj0WJ8qZeKkS3ffixdHlHTVu4Rzrlu8qDdw8TmgcvD7sHpNaBB+hwG9B/s5BAozfXvyXtW51lLLrEZdmUXJxAP51ubVgysZ985PA89o4G7LUE20IKAj3rgylLwq+Lz2PoGmVCptPTknLLzGlxTy2mU/tESJtS4iUvshSYiKak73BSen88xLWL/eZGFzOLW4TNem/qT0ykqK1s1BaGWCcQiDWN+Lpa3XqTbFN054MfhLdEMp+NjrdsCbZORzp9p2ZPDqi5jI1Hy7IN/8VzzB+T13MX6YhW7CBBqUZaivGhVQGsNjrFusPGhCaZrzNJ1l4qssLAlOh9qdaYzqDe+BIHsty55vw4Ph+Axou82babpLoiQzkCol2TWjPr7JSfHFkm6CmoTQ7mK8m7jRCodmxF+BEe4Usr+HrTKDc9fr5P4mdfL0O+ZPrlkPuG1zBTMal9MPxeCk4emzOZf7vBMNzb0aKHbOtSdVk/IcjatlDd2EIo34e5A6anLRMAdxb7901mnCAcjvCn6+ArNV8nr+NBk1+UvumZ+Q59LTrAkgRt3rBw== openpgp:0x13E0C25D
-1
View File
@@ -1 +0,0 @@
IdentityAgent ${XDG_RUNTIME_DIR}/gnupg/S.gpg-agent.ssh
-1
View File
@@ -1 +0,0 @@
37710BAB5F6DC9878A450CDA8A4661D932C04ACB
-32
View File
@@ -1,32 +0,0 @@
{...}: {
config.flake.modules.homeManager.git-config = {
lib,
config,
pkgs,
...
}: {
options.azos.git-config.enable = lib.mkOption {
default = true;
type = lib.types.bool;
};
config = lib.mkIf config.azos.git-config.enable {
home.packages = with pkgs; [pass-git-helper unzip];
programs.git = {
enable = true;
signing = {
key = "BB141234A3DD953D";
signByDefault = true;
};
settings = {
credential.helper = "!pass-git-helper $@";
user = {
name = "Aner Zakobar";
email = "aner@zakobar.com";
};
};
};
home.file.".config/pass-git-helper/git-pass-mapping.ini".source =
./pass-git-mapping.ini;
};
};
}
-2
View File
@@ -1,2 +0,0 @@
[*git.zakobar.com*]
target=zakobar.com
-56
View File
@@ -1,56 +0,0 @@
{...}: {
config.flake.modules.homeManager.headphones = {
lib,
config,
pkgs,
...
}: {
options.azos.headphones = {
enable = lib.mkOption {
default = false;
example = true;
type = lib.types.bool;
};
devices = lib.mkOption {
default = [];
type = lib.types.listOf (lib.types.submodule {
options = {
mac = lib.mkOption {type = lib.types.str;};
name = lib.mkOption {type = lib.types.str;};
};
});
description = "Headphone devices; first entry is the default connect target.";
};
};
config = lib.mkIf config.azos.headphones.enable {
home.packages = let
deviceEntries =
lib.concatMapStrings
(d: " ${lib.escapeShellArg "${d.mac} ${d.name}"}\n")
config.azos.headphones.devices;
defaultMac =
if config.azos.headphones.devices == []
then ""
else (lib.head config.azos.headphones.devices).mac;
in [
(pkgs.writeShellScriptBin "azos-connect-headphones" ''
DEVICES=(
${deviceEntries})
case "$1" in
--list)
printf '%s\n' "''${DEVICES[@]}"
;;
--mac)
echo "connect $2" | bluetoothctl
;;
*)
echo "connect ${defaultMac}" | bluetoothctl
;;
esac
'')
];
};
};
}
-16
View File
@@ -1,16 +0,0 @@
{...}: {
config.flake.modules.homeManager.hfsprogs = {
lib,
config,
pkgs,
...
}: {
options.azos.hfsprogs.enable = lib.mkOption {
default = true;
type = lib.types.bool;
};
config = lib.mkIf config.azos.hfsprogs.enable {
home.packages = with pkgs; [hfsprogs];
};
};
}
+12 -175
View File
@@ -1,191 +1,28 @@
{...}: { {...}: {
config.flake.modules.homeManager.homey-alert = { config.flake.overlayPkgs.azos-homey-ntfy-alert = pkgs:
lib, pkgs.writeShellApplication {
config, name = "azos-homey-ntfy-alert";
pkgs, runtimeInputs = with pkgs; [curl pass];
...
}: {
options.azos.homey-alert.enable = lib.mkOption {
default = false;
example = true;
type = lib.types.bool;
};
options.azos.homey-alert.longCommandThresholdSeconds = lib.mkOption {
default = 300;
example = 600;
type = lib.types.int;
description = ''
Installs a global PreToolUse hook (matcher "Bash") in
~/.claude/settings.json that transparently wraps every Bash command
Claude Code runs: it times the command, and if it ran at or past
this threshold, fires homey-alert with the elapsed time and exit
code once it finishes. Commands that finish faster are unaffected.
This exists so long-running commands (big rclone transfers, builds)
notify automatically even when Claude never thinks to chain
homey-alert itself, and even if the harness moves the command to
the background partway through.
The hook only adds timing + notification around the command as
given -- it does not change permissions, so normal confirmation
prompts for risky commands still happen exactly as before.
'';
};
config = lib.mkIf config.azos.homey-alert.enable (let
threshold = toString config.azos.homey-alert.longCommandThresholdSeconds;
# Rewrites every Bash tool command to time itself and fire
# homey-alert if it ran at or past the threshold. The original
# command is spliced in verbatim (not eval'd from an encoded blob)
# so it stays human-readable in any permission prompt, and it's run
# in a subshell so the wrapper's own timing/notify code still runs
# even if the original command uses `set -e` or calls `exit`.
# permissionDecision is deliberately omitted from the hook's JSON
# output: supplying it (even as "allow") would bypass the normal
# confirmation flow, which must stay untouched here.
bashNotifyHook = pkgs.writeShellApplication {
name = "azos-bash-notify-hook";
runtimeInputs = [pkgs.jq];
text = '' text = ''
input=$(cat)
command=$(printf '%s' "$input" | jq -r '.tool_input.command // empty' 2>/dev/null) || command=""
if [ -z "$command" ]; then
echo '{}'
exit 0
fi
wrapped="__azos_notify_start=\$(date +%s)
(
$command
)
__azos_notify_rc=\$?
__azos_notify_dur=\$(( \$(date +%s) - __azos_notify_start ))
if [ \"\$__azos_notify_dur\" -ge ${threshold} ]; then
__azos_notify_tag=\$([ \"\$__azos_notify_rc\" -eq 0 ] && echo white_check_mark || echo x)
homey-alert -t \"Long command\" -T \"\$__azos_notify_tag\" \"Finished in \$__azos_notify_dur s (exit \$__azos_notify_rc)\" >/dev/null 2>&1 || true
fi
exit \"\$__azos_notify_rc\""
jq -n --arg cmd "$wrapped" \
'{hookSpecificOutput: {hookEventName: "PreToolUse", updatedInput: {command: $cmd}}}' 2>/dev/null || echo '{}'
'';
};
in {
azos.claude.globalSkills.notify = ./notify-skill.md;
home.activation.configureBashNotifyHook = lib.hm.dag.entryAfter ["writeBoundary"] ''
SETTINGS_JSON="$HOME/.claude/settings.json"
HOOK_ENTRY=$(${pkgs.jq}/bin/jq -n \
--arg cmd "${bashNotifyHook}/bin/azos-bash-notify-hook" \
'{
matcher: "Bash",
hooks: [{type: "command", command: $cmd}]
}')
if [ -f "$SETTINGS_JSON" ]; then
tmp=$(mktemp)
${pkgs.jq}/bin/jq --argjson entry "$HOOK_ENTRY" \
'.hooks.PreToolUse = ((.hooks.PreToolUse // []) | map(select(.matcher != $entry.matcher))) + [$entry]' \
"$SETTINGS_JSON" > "$tmp" && mv "$tmp" "$SETTINGS_JSON"
else
${pkgs.jq}/bin/jq -n --argjson entry "$HOOK_ENTRY" \
'{hooks: {PreToolUse: [$entry]}}' > "$SETTINGS_JSON"
fi
'';
home.packages = [
bashNotifyHook
(pkgs.writeShellApplication {
name = "homey-alert";
runtimeInputs = with pkgs; [curl pass libnotify];
text = ''
usage() {
cat <<EOF
Usage: homey-alert [OPTIONS] [MESSAGE]
Send a notification to the homey ntfy server (ntfy.zakobar.com/alerts),
and (when run locally, not with --print-remote) also via notify-send --
the ntfy server isn't always reachable, so the local desktop notification
always fires too, independent of whether the ntfy push succeeds.
MESSAGE can be given as a positional argument or piped via stdin.
Options:
-t, --title TITLE notification title
-p, --priority PRIO priority: min|low|default|high|urgent (default: default)
-T, --tags TAGS comma-separated emoji shortcodes (e.g. warning,rotating_light)
-r, --print-remote print the curl command instead of running it
(for pasting into SSH commands to remote machines)
-h, --help show this help
Examples:
# Local
long_cmd && homey-alert -t "Training" -p high -T white_check_mark "done"
# Remote — expands locally, runs on the remote machine
ssh user@host "python train.py && \$(homey-alert -r -t 'Training' 'done')"
EOF
}
TITLE=""
PRIORITY="default"
TAGS=""
PRINT_REMOTE=false
while [[ $# -gt 0 ]]; do
case "$1" in
-t|--title) TITLE="$2"; shift 2 ;;
-p|--priority) PRIORITY="$2"; shift 2 ;;
-T|--tags) TAGS="$2"; shift 2 ;;
-r|--print-remote) PRINT_REMOTE=true; shift ;;
-h|--help) usage; exit 0 ;;
--) shift; break ;;
-*) echo "Unknown option: $1" >&2; usage >&2; exit 1 ;;
*) break ;;
esac
done
if [[ $# -gt 0 ]]; then
MESSAGE="$*"
elif [[ ! -t 0 ]]; then
MESSAGE="$(cat)"
else
echo "homey-alert: no message provided" >&2
usage >&2
exit 1
fi
TOKEN="$(pass zakobar-homecenter/ntfy-token)" TOKEN="$(pass zakobar-homecenter/ntfy-token)"
# Build the curl invocation as an array — single source of truth for both modes
CMD=( CMD=(
curl -sf curl -sf
-H "Authorization: Bearer $TOKEN" -H "Authorization: Bearer $TOKEN"
-H "Priority: $PRIORITY" -H "Priority: ''${PRIORITY:-default}"
) )
[[ -n "$TITLE" ]] && CMD+=(-H "Title: $TITLE") [[ -n "''${TITLE:-}" ]] && CMD+=(-H "Title: $TITLE")
[[ -n "$TAGS" ]] && CMD+=(-H "Tags: $TAGS") [[ -n "''${TAGS:-}" ]] && CMD+=(-H "Tags: $TAGS")
CMD+=(-d "$MESSAGE" https://ntfy.zakobar.com/alerts) CMD+=(-d "''${MESSAGE:-}" https://ntfy.zakobar.com/alerts)
if $PRINT_REMOTE; then if [[ -n "''${AZOS_SEND_ALERT_PRINT_REMOTE:-}" ]]; then
# Shell-quote each element so the output is safe to embed in SSH commands # Shell-quote each element so the output is safe to embed in SSH commands --
# the token is already resolved above, so the remote host only needs curl.
printf '%q ' "''${CMD[@]}" printf '%q ' "''${CMD[@]}"
printf '\n' printf '\n'
else else
"''${CMD[@]}" || true "''${CMD[@]}"
# Local desktop notification, independent of the ntfy push above --
# the server isn't always up, so this always fires as a fallback.
URGENCY="normal"
case "$PRIORITY" in
min|low) URGENCY="low" ;;
high|urgent) URGENCY="critical" ;;
esac
notify-send -u "$URGENCY" "''${TITLE:-homey-alert}" "$MESSAGE" || true
fi fi
''; '';
})
];
});
}; };
} }
-65
View File
@@ -1,65 +0,0 @@
# Homey Alert — sending ntfy notifications when tasks finish
Use `homey-alert` to push notifications to Aner's homelab ntfy server when long tasks complete.
## When to use proactively
When running (or suggesting) a command that will take more than ~30 seconds — training runs,
builds, deploys, downloads, test suites — append a notification automatically. Don't wait to
be asked.
## Background / async tasks (Claude Code specifically)
If you (the agent) are running a command via Bash that could take longer than
~10 minutes — large rclone/rsync transfers, builds, training runs — and it
might run in the background (either because you set `run_in_background`, or
because the harness auto-backgrounds it after its timeout), chain the
`homey-alert` call **into that same command string from the start**. Don't
wait until you notice it's taking a while and then think about notifying —
by then the command is already running without it, and the harness's own
"task finished" signal only reaches this session, not Aner's phone. Chaining
upfront is the only way to get a real ntfy push the moment it actually
finishes:
```bash
rclone copy ... ; homey-alert -t "Backup" -p default -T "$([ $? -eq 0 ] && echo white_check_mark || echo x)" "rclone copy finished (exit $?)"
```
## Local commands
Append after the command so it fires on success only (`&&`) or always (`;`):
```bash
# Notify on success only
long_cmd && homey-alert "Task done"
# Always notify (even on failure) — prefer this for deploys/training
long_cmd; homey-alert -t "Training" -p high -T "$([ $? -eq 0 ] && echo white_check_mark || echo x)" "Exited $?"
```
## Remote commands (SSH / training machines)
Remote machines don't have `homey-alert`. Use `-r`/`--print-remote` to get a self-contained
`curl` command with the token already embedded. The `$(...)` expands **locally**; the resulting
command string is what the remote machine executes.
```bash
ssh user@host "python train.py && $(homey-alert -r -t 'Training' -p high -T white_check_mark 'Model finished')"
```
The remote machine only needs `curl` (universally available).
## Flags
| Flag | Short | Values / notes |
|------|-------|----------------|
| `--title TITLE` | `-t` | Notification title |
| `--priority PRIO` | `-p` | `min` / `low` / `default` / `high` / `urgent` |
| `--tags TAGS` | `-T` | Comma-separated emoji shortcodes: `white_check_mark`, `x`, `warning`, `rotating_light`, `tada` |
| `--print-remote` | `-r` | Print curl command instead of running (embed token locally, run remotely) |
## Choosing priority and tags
- Routine completion (backup, build): `default` priority, `white_check_mark`
- Training / long job: `high` priority, `white_check_mark` on success / `x` on failure
- Something broke: `urgent` priority, `rotating_light`
-16
View File
@@ -1,16 +0,0 @@
{...}: {
config.flake.modules.homeManager.kubernetes = {
lib,
config,
pkgs,
...
}: {
options.azos.kubectl.enable = lib.mkOption {
default = false;
type = lib.types.bool;
};
config = lib.mkIf config.azos.kubectl.enable {
home.packages = with pkgs; [kubectl kubernetes-helm velero];
};
};
}
+1 -1
View File
@@ -12,7 +12,7 @@ orgTrivialBuild {
pkgs.azos-emacs-dev pkgs.azos-emacs-dev
pkgs.azos-emacs-editor pkgs.azos-emacs-editor
pkgs.azos-emacs-exwm pkgs.azos-emacs-exwm
pkgs.azos-emacs-station pkgs.azos-emacs-desktop
agent-shell agent-shell
elfeed-protocol elfeed-protocol
+1 -1
View File
@@ -9,7 +9,7 @@
(require 'azos-emacs-dev) (require 'azos-emacs-dev)
(require 'azos-emacs-editor) (require 'azos-emacs-editor)
(require 'azos-emacs-exwm) (require 'azos-emacs-exwm)
(require 'azos-emacs-station) (require 'azos-emacs-desktop)
#+end_src #+end_src
* Lauretta specific * Lauretta specific
+8 -62
View File
@@ -27,54 +27,15 @@
certificatesFile = "/etc/ssl/certs/ca-certificates.crt"; certificatesFile = "/etc/ssl/certs/ca-certificates.crt";
}; };
}; };
# mbsync's PassCmd shells out to `pass`, which needs the GPG smartcard to
# decrypt. On a timer, a missing card would otherwise surface as a
# pinentry "please insert card" prompt every run. azos-card-present is a
# silent presence check (see overlays/default.nix): skip the sync
# entirely when the card isn't there, instead of letting mbsync hit that
# prompt.
#
# New mail is detected by counting files under every account's Maildir
# "new" subdirectory before and after the sync -- that's where messages
# the server hasn't marked seen land, across all three accounts at once,
# with no need to touch mu's index. A rise in that count means mail
# actually arrived, so only then does it post a desktop notification via
# notify-send -- intentionally separate from mu4e's own "mbsync -a" (its
# mu4e-get-mail-command), which doesn't go through this script at all.
mbsyncSafe = pkgs.writeShellScript "mbsync-safe" ''
if ! ${pkgs.azos-card-present}/bin/azos-card-present; then
exit 0
fi
before=$(find "$HOME/Maildir" -path '*/new/*' -type f 2>/dev/null | wc -l)
${pkgs.isync}/bin/mbsync -a
after=$(find "$HOME/Maildir" -path '*/new/*' -type f 2>/dev/null | wc -l)
new=$((after - before))
if [ "$new" -gt 0 ]; then
label="new message"
[ "$new" -ne 1 ] && label="new messages"
${pkgs.libnotify}/bin/notify-send "Mail" "$new $label"
fi
'';
in { in {
options.azos.mail.enable = lib.mkOption { config = {
default = true; azos.mail.enable = true;
type = lib.types.bool;
}; # mbsync's PassCmd shells out to `pass`, which needs the GPG smartcard to
config = lib.mkIf config.azos.mail.enable { # decrypt -- only meaningful to gate on when the smartcard/YubiKey setup
programs = { # is actually in play.
mu.enable = true; azos.mail.presenceCheckCommand = lib.mkIf config.azos.encryption.yubikey.enable "${pkgs.azos-card-present}/bin/azos-card-present";
mbsync.enable = true;
msmtp.enable = true;
};
home.activation.muInit = lib.hm.dag.entryAfter ["writeBoundary"] ''
if [ ! -d "$HOME/.cache/mu" ]; then
$DRY_RUN_CMD ${pkgs.mu}/bin/mu init \
--maildir="$HOME/Maildir" \
--my-address=aner@zakobar.com \
--my-address=anerisgreat@gmail.com \
--my-address=anerz@post.bgu.ac.il
fi
'';
accounts.email.accounts = { accounts.email.accounts = {
gmail = gmail =
default_gmail_params default_gmail_params
@@ -106,21 +67,6 @@
passwordCommand = "pass privateemail.com | head -1"; passwordCommand = "pass privateemail.com | head -1";
}; };
}; };
systemd.user.services.mbsync-timer = {
Unit.Description = "Sync mail via mbsync (skips silently if smartcard is absent)";
Service = {
Type = "oneshot";
ExecStart = "${mbsyncSafe}";
};
};
systemd.user.timers.mbsync-timer = {
Unit.Description = "Periodic mbsync mail sync";
Timer = {
OnStartupSec = "2m";
OnUnitActiveSec = "5m";
};
Install.WantedBy = ["timers.target"];
};
}; };
}; };
} }
+5 -5
View File
@@ -23,12 +23,12 @@
nextcloudMountSafe = pkgs.writeShellScript "nextcloud-mount-safe" '' nextcloudMountSafe = pkgs.writeShellScript "nextcloud-mount-safe" ''
set -eu set -eu
mountpoint="$HOME/${mountDir}" mountpoint="$HOME/${mountDir}"
mkdir -p "$mountpoint" ${pkgs.coreutils}/bin/mkdir -p "$mountpoint"
while ! ${pkgs.azos-card-present}/bin/azos-card-present; do while ! ${pkgs.azos-card-present}/bin/azos-card-present; do
${pkgs.coreutils}/bin/sleep 10 ${pkgs.coreutils}/bin/sleep 10
done done
password=$(${pkgs.pass}/bin/pass zakobar.com | head -1) password=$(${pkgs.pass}/bin/pass zakobar.com | ${pkgs.coreutils}/bin/head -1)
uid=$(${pkgs.pass}/bin/pass nextcloud-username/aner | head -1) uid=$(${pkgs.pass}/bin/pass nextcloud-username/aner | ${pkgs.coreutils}/bin/head -1)
export RCLONE_CONFIG_${remoteNameEnv}_TYPE=webdav export RCLONE_CONFIG_${remoteNameEnv}_TYPE=webdav
export RCLONE_CONFIG_${remoteNameEnv}_URL="https://nextcloud.zakobar.com/remote.php/dav/files/$uid/" export RCLONE_CONFIG_${remoteNameEnv}_URL="https://nextcloud.zakobar.com/remote.php/dav/files/$uid/"
export RCLONE_CONFIG_${remoteNameEnv}_VENDOR=nextcloud export RCLONE_CONFIG_${remoteNameEnv}_VENDOR=nextcloud
@@ -63,9 +63,9 @@
Unit.Description = "Mount Nextcloud ${remoteSubfolder} via rclone (waits silently for YubiKey)"; Unit.Description = "Mount Nextcloud ${remoteSubfolder} via rclone (waits silently for YubiKey)";
Service = { Service = {
Type = "simple"; Type = "simple";
Environment = "PATH=${pkgs.fuse3}/bin:${pkgs.fuse}/bin"; Environment = "PATH=/run/wrappers/bin:${pkgs.fuse3}/bin:${pkgs.fuse}/bin";
ExecStart = "${nextcloudMountSafe}"; ExecStart = "${nextcloudMountSafe}";
ExecStop = "${pkgs.fuse3}/bin/fusermount3 -u %h/${mountDir}"; ExecStop = "/run/wrappers/bin/fusermount3 -u %h/${mountDir}";
Restart = "always"; Restart = "always";
RestartSec = "10s"; RestartSec = "10s";
}; };
-55
View File
@@ -1,55 +0,0 @@
{...}: {
config.flake.modules.nixos.printing = {
lib,
config,
pkgs,
...
}: let
webpFilter = pkgs.writeShellScript "webptopdf" ''
if [ $# -ge 6 ]; then
INPUT="$6"
TMPFILE=""
else
TMPFILE=$(mktemp /tmp/cups-webp-XXXXXX.webp)
cat > "$TMPFILE"
INPUT="$TMPFILE"
fi
TMPOUT=$(mktemp /tmp/cups-webp-XXXXXX.pdf)
${pkgs.imagemagick}/bin/magick "$INPUT" "$TMPOUT"
cat "$TMPOUT"
rm -f "$TMPOUT"
[ -n "$TMPFILE" ] && rm -f "$TMPFILE"
'';
webpCupsMime = pkgs.runCommand "webp-cups-mime" {} ''
mkdir -p $out/lib/cups/filter $out/share/cups/mime
cp ${webpFilter} $out/lib/cups/filter/webptopdf
chmod +x $out/lib/cups/filter/webptopdf
echo 'image/webp webp string(0,"RIFF") string(8,"WEBP")' > $out/share/cups/mime/webp.types
echo 'image/webp application/pdf 0 webptopdf' > $out/share/cups/mime/webp.convs
'';
in {
options.azos.printing.enable = lib.mkOption {
default = false;
example = true;
type = lib.types.bool;
};
config = lib.mkIf config.azos.printing.enable {
services.printing = {
enable = true;
drivers = [pkgs.hplip webpCupsMime];
};
environment.systemPackages = [pkgs.hplip];
services.avahi = {
enable = true;
nssmdns4 = true;
openFirewall = true;
};
users.users.aner.extraGroups = ["lp"];
};
};
}
+8
View File
@@ -10,6 +10,14 @@
}; };
config = lib.mkIf config.azos.steam.enable { config = lib.mkIf config.azos.steam.enable {
programs.steam.enable = true; programs.steam.enable = true;
# HORI STICK A fight stick (0f0d:0040) is missing from Steam's bundled
# 60-steam-input.rules, so Steam can't open its hidraw node for Steam
# Input and falls back to a generic joystick mapping that drops BTN_C
# (Circle). Grant the same uaccess the other HORI devices get.
services.udev.extraRules = ''
KERNEL=="hidraw*", ATTRS{idVendor}=="0f0d", ATTRS{idProduct}=="0040", MODE="0660", TAG+="uaccess"
'';
}; };
}; };
} }
-30
View File
@@ -1,30 +0,0 @@
{...}: {
config.flake.modules.nixos.virtualization = {
lib,
config,
pkgs,
...
}: {
options.azos.virtualization.enable = lib.mkOption {
default = true;
type = lib.types.bool;
};
config = lib.mkIf config.azos.virtualization.enable {
virtualisation.libvirtd = {
enable = true;
qemu = {
swtpm.enable = true;
};
};
environment.systemPackages = with pkgs; [
qemu_kvm
libvirt
virt-manager
virt-viewer
];
services.spice-vdagentd.enable = true;
networking.firewall.allowedTCPPorts = [5900 5901];
networking.firewall.allowedUDPPorts = [5900 5901];
};
};
}
Generated
+37 -37
View File
@@ -43,11 +43,11 @@
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_2"
}, },
"locked": { "locked": {
"lastModified": 1778617640, "lastModified": 1791902047,
"narHash": "sha256-bG1hjyLIOiRB+M1rXm8ch0ftHPvV43lRtzQ8qWcus5s=", "narHash": "sha256-Mk/M/b2REkIj418YaOdXWWcoKK+5a1VHS08GnLAP7rE=",
"owner": "anerisgreat", "owner": "anerisgreat",
"repo": "evil-hl-line", "repo": "evil-hl-line",
"rev": "7cf5a11bdca2abd9516f0ce572e1e03bb541507f", "rev": "8489f19d6262c6f86e26d8635fa89016d23203c3",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -95,11 +95,11 @@
"nixpkgs-lib": "nixpkgs-lib_2" "nixpkgs-lib": "nixpkgs-lib_2"
}, },
"locked": { "locked": {
"lastModified": 1788450739, "lastModified": 1790898470,
"narHash": "sha256-glZLQlzIn1fXH6PazR2iUmTo7kzzyYSshrWhLS9TqCU=", "narHash": "sha256-zTwuwezD0w3hpga6a6P8emidzAZFyWqlNeRBlpWOOl8=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "31729ca8cbdb4fa927b34e5f4353e6a83f39e993", "rev": "024633cd702b10285db5cb19b40ad48d2399ba60",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -155,11 +155,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1788651960, "lastModified": 1791642191,
"narHash": "sha256-v9wJd32eZ2bvhBzVOd7TIjLQd011P7nwOhjKtWlci5I=", "narHash": "sha256-0bK0A59rVUn1SRZzo4cNi4VSqAe7vR1gL2/MZl53Frw=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "2c0350c759688177331b8f5242311fae8877bdb3", "rev": "c58f54c733a7894d84fa2fdff26cc12088c4b78c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -204,11 +204,11 @@
"nixpkgs": "nixpkgs_3" "nixpkgs": "nixpkgs_3"
}, },
"locked": { "locked": {
"lastModified": 1777848538, "lastModified": 1791575145,
"narHash": "sha256-Dj51C0NWsglqRrCpdmMr2nFiYacFOid1Tor4H9yG2HY=", "narHash": "sha256-A4dB6Yv1Q3ygHgYWefX2DLdbELN//mB+PbCxRMdjTeg=",
"owner": "musnix", "owner": "musnix",
"repo": "musnix", "repo": "musnix",
"rev": "8548782f0d1d0928daa3fffde8a008f72219a3f3", "rev": "6e86730c235db14e8cac6605dfc33f300ce9105e",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -222,11 +222,11 @@
"nixpkgs": "nixpkgs_4" "nixpkgs": "nixpkgs_4"
}, },
"locked": { "locked": {
"lastModified": 1788335262, "lastModified": 1791721963,
"narHash": "sha256-3jBEq8avfzlrsY4UpW4d5TOmm7ocseZfnitEJhqauyc=", "narHash": "sha256-BYhx89XJltnRyQeuDKHVL5eSqbbfAcOKuDts7QRsrxQ=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixos-hardware", "repo": "nixos-hardware",
"rev": "44d95795ee2d475b3d687325e26dcf4ca9104557", "rev": "60a624466c08787ee8303393b7d4752d1826f14f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -244,11 +244,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1784642409, "lastModified": 1789164534,
"narHash": "sha256-hcbDqFuySAJawljt5r0sKBCJKYnbtGD0T/ZIozH1Dq0=", "narHash": "sha256-DoYGPM6QpnYBLWj9gGw6ZwAzIX+HrAVov1BoT+8Jixo=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nixos-wsl", "repo": "nixos-wsl",
"rev": "eaeb18da90024448a60eb1ec7132eafa4003404e", "rev": "72c92b11bb8289e6651c7fef29cc0a885fd6a255",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -288,11 +288,11 @@
}, },
"nixpkgs-lib_2": { "nixpkgs-lib_2": {
"locked": { "locked": {
"lastModified": 1788057806, "lastModified": 1790477367,
"narHash": "sha256-DTQSMxzDWmT0zhguthvegnVkn7CFqGCv4IHCzk5ZUpM=", "narHash": "sha256-oujbxmVKDhci2E4EM3KyAbRUgp5/IsCoiA/LfEF7OOk=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nixpkgs.lib", "repo": "nixpkgs.lib",
"rev": "596e2e3940e09b2abbeb03f75fa1828c57fcd72c", "rev": "f7cd230690d9fc982d06129f9189b101fddcfa3b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -303,11 +303,11 @@
}, },
"nixpkgs-unstable": { "nixpkgs-unstable": {
"locked": { "locked": {
"lastModified": 1788614874, "lastModified": 1791734076,
"narHash": "sha256-7QYjT2vHLuX9Z1pdxHXDKCbh1CR3D/2rywB9Tx0MPRg=", "narHash": "sha256-i5qk1kLiit98rRbh1AXmWBNT9QXC/y4l5Wk/u5EdpDQ=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "c043004d1c6985732bcc1cbc5a9c9aecbbb4e0f0", "rev": "7fd4004d0511991fff3a0524856a9be09e28c5f0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -319,11 +319,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1779508470, "lastModified": 1791734076,
"narHash": "sha256-Ap9KJX+5xHIn3bPIpfNgT6MEXdAECECwo4/rmlQD74M=", "narHash": "sha256-i5qk1kLiit98rRbh1AXmWBNT9QXC/y4l5Wk/u5EdpDQ=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "29916453413845e54a65b8a1cf996842300cd299", "rev": "7fd4004d0511991fff3a0524856a9be09e28c5f0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -335,11 +335,11 @@
}, },
"nixpkgs_3": { "nixpkgs_3": {
"locked": { "locked": {
"lastModified": 1777268161, "lastModified": 1791456040,
"narHash": "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE=", "narHash": "sha256-H83yz/do+hjOO0uujHyH8RgtIQP2v7Mw+cLwcZK/xKc=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76", "rev": "e7439b6b14ad3cc35d05608ebca9bce01a25f5f8",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -351,11 +351,11 @@
}, },
"nixpkgs_4": { "nixpkgs_4": {
"locked": { "locked": {
"lastModified": 1767892417, "lastModified": 1789546076,
"narHash": "sha256-8bW3q88CEg2u4hSP66Vf4lpbLonHz7hqDNBMcCY7E9U=", "narHash": "sha256-vWkSk5bbfTqdtMoSgD9FshACO8JCvXTFi+3cqEp0mH0=",
"rev": "3497aa5c9457a9d88d71fa93a4a8368816fbeeba", "rev": "b1b875982b17dabde9b4a37f3e229e74913e6db3",
"type": "tarball", "type": "tarball",
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre924538.3497aa5c9457/nixexprs.tar.xz" "url": "https://releases.nixos.org/nixos/unstable/nixos-26.11pre1074753.b1b875982b17/nixexprs.tar.xz"
}, },
"original": { "original": {
"type": "tarball", "type": "tarball",
@@ -364,11 +364,11 @@
}, },
"nixpkgs_5": { "nixpkgs_5": {
"locked": { "locked": {
"lastModified": 1788549839, "lastModified": 1791487292,
"narHash": "sha256-kOrCcSIA6w9J1hX5DqHy2k9pDTJymExTsbV74U9UtCA=", "narHash": "sha256-tqM7xDEsaeK1p6VvRygeZ9LkQRpUUkbRT02BHv6hnTM=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "17de0b976395537756f30a3e78f2f06e5cec89ed", "rev": "8edc0c72e3a38faf5434e40d1f19431125fe4b30",
"type": "github" "type": "github"
}, },
"original": { "original": {
+23 -12
View File
@@ -11,19 +11,13 @@
suiteModules.homeManager.base suiteModules.homeManager.base
suiteModules.homeManager.dev suiteModules.homeManager.dev
suiteModules.homeManager.editor suiteModules.homeManager.editor
suiteModules.homeManager.station suiteModules.homeManager.desktop
suiteModules.homeManager.discord
suiteModules.homeManager.exwm suiteModules.homeManager.exwm
suiteModules.homeManager.lauretta suiteModules.homeManager.lauretta
suiteModules.homeManager.audio
suiteModules.homeManager.claude suiteModules.homeManager.claude
suiteModules.homeManager.claude-memory
suiteModules.homeManager.claude-skills
suiteModules.homeManager.encryption
suiteModules.homeManager.git-config
suiteModules.homeManager.hfsprogs
suiteModules.homeManager.headphones
suiteModules.homeManager.hyprland suiteModules.homeManager.hyprland
suiteModules.homeManager.kubernetes suiteModules.homeManager.dav
suiteModules.homeManager.mail suiteModules.homeManager.mail
suiteModules.homeManager.mpris-proxy suiteModules.homeManager.mpris-proxy
suiteModules.homeManager.nextcloud suiteModules.homeManager.nextcloud
@@ -33,8 +27,6 @@
suiteModules.homeManager.snx-rs suiteModules.homeManager.snx-rs
suiteModules.homeManager.roam-backup suiteModules.homeManager.roam-backup
suiteModules.homeManager.ytdl suiteModules.homeManager.ytdl
suiteModules.homeManager.chess
suiteModules.homeManager.homey-alert
suiteModules.homeManager.leetcode suiteModules.homeManager.leetcode
]; ];
@@ -61,7 +53,26 @@
} }
]; ];
azos.roam-backup.enable = true; azos.roam-backup.enable = true;
azos.homey-alert.enable = true;
azos.send-alert.enable = true;
azos.send-alert.remoteCommand = "${pkgs.azos-homey-ntfy-alert}/bin/azos-homey-ntfy-alert";
azos.git-config.userName = "Aner Zakobar";
azos.git-config.userEmail = "aner@zakobar.com";
azos.git-config.signing.enable = true;
azos.git-config.signing.key = "BB141234A3DD953D";
azos.git-config.passCredentialHelper.enable = true;
azos.git-config.passCredentialHelper.mapping = ''
[*git.zakobar.com*]
target=zakobar.com
'';
azos.encryption.passwordStore.gpgKeyId = "26B851F81F8F7C55";
azos.encryption.yubikey.enable = true;
azos.encryption.browserpass.enable = true;
azos.encryption.gpgSsh.enable = true;
azos.encryption.gpgSsh.keygrip = "37710BAB5F6DC9878A450CDA8A4661D932C04ACB";
azos.encryption.gpgSsh.publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDMdozJewuuqG4VuwGhI5Uw2xDIUrkQwyPGQ2q3tvnPq/VoV+zfdnou42I6XYEfOGd6xfLR0byOGCk1Ia1F/picBsxADEaJFBnTny5MWNA5MxwJIYUPW5+kFN1TmxdNGmj0WJ8qZeKkS3ffixdHlHTVu4Rzrlu8qDdw8TmgcvD7sHpNaBB+hwG9B/s5BAozfXvyXtW51lLLrEZdmUXJxAP51ubVgysZ985PA89o4G7LUE20IKAj3rgylLwq+Lz2PoGmVCptPTknLLzGlxTy2mU/tESJtS4iUvshSYiKak73BSen88xLWL/eZGFzOLW4TNem/qT0ykqK1s1BaGWCcQiDWN+Lpa3XqTbFN054MfhLdEMp+NjrdsCbZORzp9p2ZPDqi5jI1Hy7IN/8VzzB+T13MX6YhW7CBBqUZaivGhVQGsNjrFusPGhCaZrzNJ1l4qssLAlOh9qdaYzqDe+BIHsty55vw4Ph+Axou82babpLoiQzkCol2TWjPr7JSfHFkm6CmoTQ7mK8m7jRCodmxF+BEe4Usr+HrTKDc9fr5P4mdfL0O+ZPrlkPuG1zBTMal9MPxeCk4emzOZf7vBMNzb0aKHbOtSdVk/IcjatlDd2EIo34e5A6anLRMAdxb7901mnCAcjvCn6+ArNV8nr+NBk1+UvumZ+Q59LTrAkgRt3rBw== openpgp:0x13E0C25D";
home = { home = {
username = "aner"; username = "aner";
+3 -5
View File
@@ -14,13 +14,10 @@
inputs.home-manager.nixosModules.home-manager inputs.home-manager.nixosModules.home-manager
suiteModules.nixos.base suiteModules.nixos.base
suiteModules.nixos.exwm suiteModules.nixos.exwm
suiteModules.nixos.audio suiteModules.nixos.desktop
suiteModules.nixos.bluetooth suiteModules.nixos.dev
suiteModules.nixos.steam suiteModules.nixos.steam
suiteModules.nixos.virtualization
suiteModules.nixos.binfmt
suiteModules.nixos.attic suiteModules.nixos.attic
suiteModules.nixos.printing
suiteModules.nixos.hardware-auth suiteModules.nixos.hardware-auth
]; ];
@@ -96,6 +93,7 @@
azos.suites.exwm.enable = true; azos.suites.exwm.enable = true;
azos.attic.enable = true; azos.attic.enable = true;
azos.printing.enable = true; azos.printing.enable = true;
azos.printing.username = "aner";
azos.hardware-auth.enable = true; azos.hardware-auth.enable = true;
# Needed for unprivileged FUSE mounts (e.g. rclone mount) -- without this, # Needed for unprivileged FUSE mounts (e.g. rclone mount) -- without this,
+12 -1
View File
@@ -5,7 +5,18 @@ pkgs.mkShell {
nh nh
(pkgs.writeShellScriptBin (pkgs.writeShellScriptBin
"azos-lauretta-update" "azos-lauretta-update"
"nh os switch '.?submodules=1#lauretta'") ''
# Authenticate up front and keep the sudo timestamp fresh during the
# build, so nh's activation step doesn't prompt (and time out) at the end.
sudo -v || exit 1
while true; do
sleep 60
sudo -n -v 2>/dev/null || exit
done &
keepalive=$!
trap 'kill $keepalive 2>/dev/null' EXIT
nh os switch '.?submodules=1#lauretta'
'')
(pkgs.writeShellScriptBin (pkgs.writeShellScriptBin
"azos-update-azos-core" "azos-update-azos-core"
"nix flake update --flake '.?submodules=1' azos-core") "nix flake update --flake '.?submodules=1' azos-core")