refactor: ponytail audit

This commit is contained in:
kiri committed 2026-09-06 04:03:54 +02:00
1 parent de8b59ec16
commit f080ebe36e
57 files changed
+416 -1987

No files matched your search

+91 -476
View File
@@ -2,499 +2,114 @@
Source: [`../workflow-discovery.md`](../workflow-discovery.md) Source: [`../workflow-discovery.md`](../workflow-discovery.md)
Apply the global and repository-local `AGENTS.md` files and the ponytail skill The declarative workflow implementation is complete. This file is the living
at full intensity throughout. Preserve user work, prefer native capabilities, handoff for operational constraints and external gates; implementation history
and add no abstraction without a demonstrated repeated need. is available in Git.
## Non-negotiable activation boundary ## Activation boundary
Only the user may switch or activate configurations. The agent must never run Only the user may switch or activate configurations. Agents must not run
`nixos-rebuild switch`, a Home Manager activation or switch command, or any `nixos-rebuild switch`, Home Manager activation, or an equivalent command that
equivalent operation that changes the active generation. Building and changes the active generation. Evaluation and builds are allowed; after a
evaluating configurations is allowed; after a successful build, give the user successful build, provide the exact activation command and wait for the user.
the exact activation command and wait for confirmation.
On 2026-09-05 the agent violated this boundary by directly running a built Home This boundary includes changes described as user-level, harmless, temporary, or
Manager activation package. It restarted the Kitty service and closed the necessary for live verification. A direct Home Manager activation previously
user's terminal session. Do not repeat this, including for changes described as restarted Kitty and closed the user's terminal session.
user-level, harmless, temporary, or needed only for live verification.
## Execution discipline ## Execution discipline
Advance every safe independent lane before asking the user to act. Batch - Advance all safe independent work before asking the user to act.
declarative changes behind one user-controlled activation and consolidate - Batch declarative changes behind user-controlled activation.
empirical, credential, hardware, and approval gates into one checklist only - Consolidate empirical, credential, hardware, and approval gates only after no
after no autonomous work remains. A single pilot reaching its user gate does autonomous work remains.
not make it the global next step while another pilot can still progress. - Keep repositories and worktrees outside `~/sync`; synchronize authored
artifacts instead.
Never place repositories or worktrees under `~/sync`; synchronize authored - Keep GitHub and other repository remotes strictly read-only.
artifacts instead. The folder's sole ignore pattern is the local `.zk` state.
## Current checkpoint ## Current checkpoint
- Tranche: autonomous consolidation across repositories, tools, and pilots - State: declarative implementation and autonomous verification complete.
- State: autonomous implementation exhausted; remaining gates are external or - Branch: `chore/workflow-system`.
explicitly deferred - Validation: `nix flake check --no-build --quiet` plus no-link Zenith and Orion
- Branch: `chore/workflow-system` system builds passed on 2026-09-06 after Orion became the sole transcription
- Baseline: `nix flake check --no-build --quiet` and no-link Zenith and Orion owner. The expected dirty-tree and unknown `modules` warnings were emitted.
system builds passed on 2026-09-06 after making Orion the sole transcription - Live state: Walker project and terminal actions, daily notes, stable web apps,
owner; only the expected dirty-tree and unknown `modules` warnings were Evolution D-Bus activation, the Zenith VPN, invocation-scoped GitHub and
emitted. The generated project menu finds 30 live repositories Context7 credentials, Pinta, and Orion transcription passed their completed
without `.archive` or `.worktrees` entries, and the generated daily-note pilots.
configuration passes an isolated same-day idempotence test. The managed Actual
and WhatsApp entries are active in the current user session, Evolution's
source registry activates over D-Bus, and the VPN reconnects successfully.
The user activated the terminal root fix: `xdg-terminal-exec` now selects
Kitty correctly and Yazi
launches from Walker. After restarting the stale Elephant and Walker
processes, repository launch also succeeds and exiting Neovim leaves a shell
in the repository. The daily-note launcher and migrated notebook contents
also pass their live sanity check. The attempted Elephant menu restart trigger
stopped its dependent Walker during activation without bringing Walker back,
so it has been removed in favor of applying rare menu changes at login. After
activating that removal and relogging, Walker opens normally and repository
selection starts Neovim in Oil at the project root. Five fresh local clones
pass their flake checks, expose their tracked command catalogs, and remain
clean after shell entry. The dashboard contract additionally passes its full
check and a bounded two-process development smoke test. The dashboard and
traceability PostgreSQL stacks also ran concurrently while both repositories'
checks passed; all test processes and containers were then stopped. The
retired workstation tools are absent and `rclone` is live. Herdr preserves
its pane across detach and reattach. The data-science contract and a real
read-only PR review pilot pass locally. The broad GitHub credentials are gone;
a mode-0600 fine-grained token now passes repository, issue, pull-request,
Actions, and organization Project reads with no stored fallback credential.
The user activated its loader and confirmed a new shell can list organization
Projects; the skill directory and durable remote boundary are also live. The
activated read-only wrapper leaves `GH_TOKEN` out of cached dev shells while
`gh` identifies the work account and Git reads a private Cargo dependency;
the complete yookr-api contract passes. The replacement Context7 key is
mode 0600, the exposed key is revoked, and a live resolve-and-query round trip
passes through the activated runtime header helper. Pinta passed its real-edit
trial, and Outlook's installed PWA exposed stable app ID
`eoficlgicibekocmfdomjbfnjmehnhcd`; after activation, GIMP is absent and the
managed Outlook launcher is the live store-backed entry. A stopped
10.28-second Fossify M4A
transferred intact into `sync/recordings`, establishing its actual format and
path. The user selected Fossify's native `recordings/inbox` destination, and
the event-driven Orion CPU pipeline passes both an isolated transcription and
a live remote run of that recording without exposing its contents. Orion
waited for the full settlement interval, archived the byte-identical original,
published the expected fallback Markdown, and exited successfully. The user
confirmed its bilingual output, and a guarded rerun with the copied pyannote
model replaced that fallback with a successfully diarized transcript. A pathological
synchronized repository tree was removed from the Pixel. Repositories are now
explicitly outside the Syncthing boundary instead of relying on an expanding
generated-directory ignore list.
- Final audit: the complete branch diff passes `git diff --check`; removed
Helix, Foot, Zed, and stable-Nixpkgs owners have no remaining Nix references,
and the lockfile cleanup contains no replacement inputs. The current desktop,
Walker, Niri, host, and transcription owners were reviewed in full without
finding another autonomous correction to make.
- Next: continue independent external pilots as their real inputs arrive. No
configuration activation is pending.
## Progress ## External or deferred gates
| Batch | State | Evidence or blocker | These gates do not block work on each other.
| --- | --- | --- |
| Bootstrap and repository ownership | Complete | Repository reconciliation, local plaintext credential cleanup, and Context7 rotation pass |
| Declarative workstation foundation | Complete | All-host builds pass with one Nixpkgs pin, `just`, and an explicit Podman Compose provider |
| Repository contracts and workbench | Waiting externally | Five clean-checkout contracts, the full yookr-api check, and read-only Kubernetes pass; API user credentials, database, report, and S3 inputs remain externally gated |
| Desktop navigation and interaction | Waiting externally | Walker project and terminal actions plus stable Actual, WhatsApp, and Outlook launchers work; the two-output check awaits a docked session |
| Agent workflow | Waiting externally | Herdr, read-only PR review, the corrected skill owner, durable remote boundary, invocation-scoped credentials, and organization Projects pass; a real Monday ticket remains external |
| Notes | Complete | The daily launcher and migrated notebook pass live checks; the approved rollback copy is recoverable from Trash |
| PIM | Deferred | Evolution 3.60.2 with Microsoft 365 support remains available beside Thunderbird; the user deferred account setup and uses Thunderbird plus Outlook meanwhile |
| Transcription | Complete | Real Fossify transfer, settlement, bilingual CPU transcription, diarization, fallback, archive, and synchronization pass on Orion; Zenith has no transcription owner |
| Conditional retirement and completion | Active | Superseded tools and GIMP are absent and Pinta passed; Bruno, DBeaver, and PIM clients remain behind separate gates |
## Repository checkpoints - During a docked Zenith session, capture the external display's stable identity
and test the two-output `Super+A` exchange. The current recorded session exposed
| Repository | Branch | Commit | Purpose | only `eDP-1`.
| --- | --- | --- | --- | - Resume Evolution only when the user chooses to configure the personal,
| NixOS configuration | `chore/workflow-system` | `41bc61b` | Discovery, paths, Git identity, and Codex defaults | old-personal, university, work, and Radicale accounts. Keep Thunderbird and
| NixOS configuration | `chore/workflow-system` | `69552b7` | Simplify flake inputs and declare workstation lifecycle tools | Outlook until the daily-use gate passes.
| NixOS configuration | `chore/workflow-system` | `0c121a6` | Declare Garden tooling and the local workbench | - On Android, complete the DAVx5/Radicale and Tasks.org pilots, including default
| NixOS configuration | `chore/workflow-system` | `2fb6061` | Package Garden completion declaratively | accounts, recurrence, reminders, and Microsoft To Do within documented limits.
| NixOS configuration | `chore/workflow-system` | `ad01b9b` | Enable NetworkManager's native OpenVPN plugin | - Supply one real Yookr S3 task, a least-privileged remote, and explicit permission
| NixOS configuration | `chore/workflow-system` | `c9ee688` | Advance VPN, desktop, agent, note, and PIM pilots | for any upload. `rclone` is installed; no remote or wrapper is preconfigured.
| NixOS configuration | `chore/workflow-system` | `cfa5d6d` | Correct PWA ownership and Evolution service activation | - Deploy a GET image exposing `/full-api-docs/openapi.json`, then rerun
| NixOS configuration | `chore/workflow-system` | `c2f48df` | Restore terminal-backed Walker actions | `just openapi-refresh test`.
| NixOS configuration | `chore/workflow-system` | `6631889` | Open Walker projects at their repository root | - Provide current API user credentials before the planned login and harmless
| NixOS configuration | `chore/workflow-system` | `ca65328` | Remove the unsafe Elephant restart trigger | administrator read. The recovered legacy service credentials returned 401 and
| NixOS configuration | `chore/workflow-system` | `d085512` | Advance from Walker to the agent pilot | were not repurposed.
| NixOS configuration | `chore/workflow-system` | `ee64a7b` | Retire superseded tools and prepare the S3 pilot | - Add workbench database connections and a Marimo-to-HTML report only when a real
| NixOS configuration | `chore/workflow-system` | `2ef74d4` | Expose the NixOS skill as a supported directory link | cross-system investigation gives them durable value.
| NixOS configuration | `chore/workflow-system` | `1996411` | Load the machine-local read-only GitHub credential | - Keep Bruno, DBeaver, and PIM clients until their separate pilots decide them.
| NixOS configuration | `chore/workflow-system` | `422a1ea` | Scope GitHub and Context7 credentials to their callers | - Wait for a real Monday ticket before adding anything beyond native GitHub,
| NixOS configuration | `chore/workflow-system` | `ec54ddd` | Own Outlook's stable launcher and select Pinta over GIMP | Herdr, Git, and worktree workflows.
| NixOS configuration | `chore/workflow-system` | `656f144` | Prepare event-driven CPU transcription on Orion |
| NixOS configuration | `chore/workflow-system` | `01c8855` | Make Orion the sole transcription owner after its live pass |
| NixOS configuration | `chore/workflow-system` | `76e9d03` | Declare ADB for the Android sync recovery |
| NixOS configuration | `chore/workflow-system` | `fa17d77` | Exclude generated devenv state from Syncthing |
| NixOS configuration | `chore/workflow-system` | `b38fb9e` | Keep repository trees outside the Syncthing boundary |
| NixOS configuration | `chore/workflow-system` | `5fa85c5` | Complete the bilingual and diarized transcription pilot |
| `yookr-dashboard-new` | `feat/gogv2` | `934605d` | Remove obsolete dashboard converter |
| `yookr-dashboard-new` | `feat/gogv2` | `8e6e4bc` | Establish its project contract and native lifecycle |
| `yookr-dashboard-new` | `feat/gogv2` | `c99f8b6` | Move runtime configuration to its machine-local owner |
| `rules-service` | `chore/workflow-contract` | `555d9cd` | Establish the first common repository contract |
| `yookr-api` | `chore/workflow-contract` | `a76b2b7` | Establish the HTTP and OpenAPI contract |
| `traceability-data-model` | `chore/workflow-contract` | `33cb7ff` | Establish the database and schema contract |
| `traceability-data-model` | `chore/workflow-contract` | `774e710` | Remove Mathesar and simplify the local stack to PostgreSQL |
| `yookr-data-science` | `chore/workflow-contract` | `fa410e1` | Establish its non-mutating project contract |
| `yookr-workbench` | `main` | `badc61c` | Create the workbench and isolate its Garden session |
## Deferred or externally blocked actions
None of these block work on independent pilots. Surface the exact applicable
step to the user only when it blocks the current task.
- During a docked Zenith session, tell the agent the external display is
attached so its stable identity and the two-output `Super+A` pilot can be
completed. The current session exposes only `eDP-1`.
- When the user resumes the deferred Evolution pilot, add accounts without removing any
existing client or changing authority: personal and old-personal through
their existing IMAP/SMTP service, university and work through the
`Microsoft 365` account type, and Radicale through native CalDAV, CardDAV,
and VTODO. Complete tenant consent/sign-in when prompted, then tell the agent
which accounts connected; the full mail/calendar/contact/task gate remains
a daily-use pilot rather than a one-time setup claim.
- On Android, complete the DAVx5/Radicale and Tasks.org pilots described in the
discovery plan, including correct default accounts, recurrence, reminders,
and Microsoft To Do within its documented limits.
- Supply one real Yookr S3 task plus a least-privileged remote and authorize one
bounded upload. `rclone` is prepared, but no remote or wrapper is guessed.
- Deploy a GET image that exposes `/full-api-docs/openapi.json`, then rerun
`just openapi-refresh test`; the current test deployment returns 404.
- Provide current API user credentials for the ignored private HTTP environment,
then perform the planned login and harmless administrator read. The recovered
legacy service UUID/password returned 401 from production and was not copied
into the user-login schema.
## Deferred Polaris replication ## Deferred Polaris replication
Do not access or test Polaris during this implementation. Treat a Zenith pass Do not access or test Polaris unless the user resumes this deferred replication.
as representative, but retain these machine-local steps for later execution on Zenith's result remains representative meanwhile.
Polaris:
1. Activate this NixOS configuration so `networkmanager-openvpn` is installed. 1. Activate this NixOS configuration so `networkmanager-openvpn` is installed.
2. Run `chmod 600 ~/sync/yookr-vpn.ovpn`. 2. Restrict `~/sync/yookr-vpn.ovpn` to mode 0600.
3. Run `nmcli connection import type openvpn file ~/sync/yookr-vpn.ovpn`. 3. Import it with `nmcli connection import type openvpn file ~/sync/yookr-vpn.ovpn`.
4. Rename the imported `yookr-vpn` connection to `Yookr VPN`, disable 4. Rename the connection to `Yookr VPN`, disable autoconnect, and add
autoconnect, and add the required legacy cipher: `data-ciphers=AES-256-CBC`.
`nmcli connection modify yookr-vpn connection.id "Yookr VPN" connection.autoconnect no +vpn.data data-ciphers=AES-256-CBC`.
5. Connect with `nmcli connection up "Yookr VPN"`. 5. Connect with `nmcli connection up "Yookr VPN"`.
6. Create `~/.config/yookr/github-read-only.token` without a trailing newline, 6. Create mode-0600 `~/.config/yookr/github-read-only.token` without a trailing
containing the fine-grained `yookrdev` token, and set its mode to 0600. Open newline, then verify a new shell can run
a new Zsh after activation and verify `gh project list --owner yookrdev`. `gh project list --owner yookrdev`.
7. Create `~/.config/context7/api-key` without a trailing newline, containing a 7. Create mode-0600 `~/.config/context7/api-key` without a trailing newline,
current Context7 key, and set its mode to 0600. Restart Codex after activation restart Codex after activation, and run one Context7 lookup.
and run one Context7 documentation lookup.
## Decisions and discoveries ## Durable implementation decisions
- Use `/home/kiri/work/repos/yookr-dashboard-new` as the reconciliation base; - Active work repositories live under `~/dev/work`, archives under `.archive`,
its `feat/gogv2` head is 17 commits newer than the sync copy and matches its and worktrees under `.worktrees`. Repository trees never enter Syncthing.
remote-tracking branch. - Participating repositories expose non-mutating direnv shells, minimal flake
- The user confirmed the legacy BSON migration is no longer needed and the checks, native commands through `just`, concise `AGENTS.md`, and machine-local
former work copy is authoritative. The sync copy was explicitly deleted; credential inputs.
its removal may propagate through Syncthing. - `yookr-workbench` starts with cross-system work only; isolation and additional
- Active repositories now live under `/home/kiri/dev/work`; inactive material tools wait for observed needs.
is preserved intact under `.archive`, and the registered dashboard sensor - Walker's native Elephant menu scans repository roots and calls one Nix-owned
worktree is under `.worktrees`. `open-project` command. Rare menu changes take effect at login.
- Removed the obsolete dashboard converter app and Python dependency. The - Kitty and Neovim are the sole declared terminal and editor. Retired duplicate
dashboard flake evaluates, while the pre-existing feature work remains applications and global language runtimes stay absent unless a concrete owner
uncommitted and unstaged. appears.
- Pruned the absent `feat/echarts-domain` worktree metadata; its branch and - The fine-grained GitHub token and Context7 key stay in mode-0600 machine-local
commit remain. The registered sensor worktree is valid at its new path. files and are exposed only to their callers. Secrets do not enter Nix or broad
- Shared Codex defaults and MCP endpoints are generated at process environments.
`/etc/codex/config.toml`. Context7's credential is supplied at connection time - `sync/notes` is the single notebook owner. Daily notes, meetings, and raw
by Codex's native HTTP-header helper reading `~/.config/context7/api-key`; no transcripts have separate paths, and agents do not create issues or ingest
secret enters Nix or the process-wide environment. The unused generic transcripts automatically.
`~/.config/mcp/mcp.json` is absent. - Orion is the sole transcription owner. It waits for file settlement, validates
- Removed redundant legacy-path trust entries from the current Codex config; media, archives originals, and publishes an unlabeled transcript when
the existing trusted home scope covers the moved repositories. diarization fails.
- Bruno's transitional OpenAPI reference now follows the archived management - Evolution remains a deferred presentation-layer pilot. Microsoft 365 and
repository until the HTTP pilot retires or retains Bruno. Radicale remain the data authorities; Thunderbird and Outlook remain available.
- Post-move repository IDs, remotes, dirty counts, and the registered sensor - GitHub and every other repository remote stay read-only. Local files, commits,
worktree match the pre-move inventory. The Nix flake still evaluates. fetches, clones, and worktrees remain available.
- No-link system builds for Zenith, Polaris, and Orion pass with the migrated
paths and declarative Codex defaults. ## Next step
- Removed the unused stable Nixpkgs input and made Noctalia follow the root
package-set pin. Added `just` to the workstation tools and selected the Continue an external pilot only when its real input arrives. Otherwise no workflow
store-backed `podman-compose` provider through native `containers.conf`. implementation action remains.
- The resulting flake check and no-link builds for all three hosts pass.
- The verified-empty legacy directories `~/work/.data/mongodb`, `~/work/.data`,
and `~/work/repos` are removed. The stale `~/.config/codex` tree was moved to
desktop Trash after explicit approval; the active `~/.codex` tree is intact.
- `Sync/uni/algorithms` remained on the Pixel after its Zenith and Orion copies
were deleted. Android Files and MTP crashed, while recursive ADB operations
appeared to hang because `.devenv/state/venv` contained at least one thousand
generated files and deeply nested package data behind Android's FUSE layer;
no malformed top-level entry or filesystem failure was found. With the
Syncthing-Fork folder paused and explicit approval, ADB atomically renamed and
then permanently deleted only that exact tree. The live and temporary paths
are absent. A later Files hang was isolated to the remaining stale `Sync`
tree, which included a repository with a very large `node_modules`; that
1.7 GiB quarantined tree was permanently removed with explicit approval, and
both its visible and hidden paths are absent. The durable rule is that
repositories and worktrees never enter `~/sync`; the
shared folder therefore keeps only its intentional `**/.zk` ignore.
- `rules-service` now has a non-mutating direnv shell, minimal Nix toolchain and
formatting check, native Cargo recipes, factual repository instructions,
one `.http` request collection, explicit test/live deployment paths, and a
corrected test-oriented environment example. The full locked Cargo check
passes; Clippy reports 18 pre-existing advisory warnings.
- Private Cargo Git dependencies remain at their upstream HTTPS URLs and use
Git's existing `gh auth git-credential` integration. They are not vendored or
rewritten.
- `yookr-api` now owns its reviewed OpenAPI 3.1 contract, local/test/live HTTP
metadata, curated login/health/admin-read requests, a non-mutating shell, and
one explicit snapshot-and-diff recipe. `oasdiff` 1.31.0 is pinned with source
and vendor hashes because Nixpkgs does not provide it.
- The API flake evaluation, complete locked Rust test suite, and OpenAPI
validation pass. The fine-grained token reads the exact locked
`constant-store` commit and two other private Yookr crates through the
invocation-scoped `gh` credential helper; no dependency URL changed. The
validator retains two pre-existing informational example findings. The test
service is reachable, but its currently deployed GET image returns 404 for
the full OpenAPI route already present in local source and ingress
configuration.
- `traceability-data-model` preserves the existing domain/schema redesign while
replacing automatic schema recreation with explicit `just reset`. Ordinary
`just dev` retains its Compose volumes, and the flake check loads fixtures in
text-ID mode plus a fresh schema in UUID-ID mode. Both the built check and the
pinned `podman-compose` configuration validation pass.
- Mathesar is removed from `traceability-data-model`; the development stack now
owns only PostgreSQL plus the explicit schema-reset job. The active named
volume and its historical local role are preserved to avoid stranding data;
the obsolete `mathesar_django` database and orphaned secrets volume were
explicitly approved and deleted. `just reset`, `just dev`, the PostGIS
fixture query, Compose validation, and the flake check pass.
- `yookr-dashboard-new` now has the same minimal project contract: a
non-mutating flake shell, `just dev` over its existing Process Compose graph,
`just check`, factual `AGENTS.md` and README guidance, a flake formatter and
formatting check, and a complete public environment example. Its full type,
lint, and format checks pass; a bounded smoke test reached ready MongoDB and
Vite processes and stopped cleanly. The commit contains only contract files;
the user's existing dashboard source work remains unstaged and untouched.
- The active ignored `rules-service/.env` is moved unchanged to
`~/.config/yookr/rules-service.env`; its existing `.envrc` now loads that
file successfully. The dashboard environment is likewise moved unchanged to
`~/.config/yookr/yookr-dashboard-new.env`, and its `.envrc`, instructions,
and README now name that owner. Both files are mode 0600, both shells expose
their expected variables without recreating repository `.env` files, and the
dashboard commit contains only those three contract files. The unrelated
dashboard source work remains unstaged.
- The unreferenced `/home/kiri/work/SECRETS` file overlapped an older
rules-service environment but contained different credentials, so it was not
merged or discarded. It is preserved byte-for-byte as the mode-0600
`~/.config/yookr/rules-service-legacy.env`; the original path is absent. Its
production Yookr service UUID/password returned 401 from `/serviceLogin`; no
token was retained and the credentials were not repurposed as a user login.
- Fresh local clones of `rules-service`, `yookr-api`,
`traceability-data-model`, `yookr-dashboard-new`, and `yookr-data-science`
pass their flake and project checks, expose their tracked `just` catalogs,
and remain clean after shell entry. Credential-bearing and externally blocked
checks remain separate gates.
- `yookr-data-science` now has a non-mutating shell and explicit `just setup`,
`check`, `run`, and `fetch-live` boundaries. Locked local checks and the
archived notebook use repository-relative paths; live Yookr access remains
explicit and was not invoked. Stale path-bound caches were replaced through
the explicit setup command, local credentials are mode 0600, and a fresh
clone passes the full contract without shell-created files.
- Gardenctl 2.14.0 and Gardenlogin 0.10.0 are pinned from their official release
assets; the hashes match the existing binaries byte-for-byte. All three host
builds pass, the OIDC kubectl plugin is declarative, and Zsh consumes the
native `gardenctl rc zsh` output with its completion packaged declaratively.
- The local-only `yookr-workbench` has no remote and initially owns only
`just k8s test`. Each invocation creates an isolated Garden session, rejects
every environment except test, and opens K9s read-only. Its flake check and
command catalog pass, and the live rejection path exits before Garden access.
- The user activated and rebooted into the Garden tooling, then confirmed that
`just k8s test` opens the test shoot in read-only K9s. The initial shell error
came from Garden writing completion into an absent Flatpak `$fpath` entry; the
initial target warning came from the fresh session inheriting its parent's
`KUBECONFIG`. Completion is now store-owned, and the child recipe initializes
Garden's session-local `KUBECONFIG` before targeting instead of unsetting it.
The former mutable Garden binaries and generic MCP output are already absent,
so their stale cleanup gate is closed without further user work.
- NetworkManager's native OpenVPN plugin is declared on Zenith and Polaris;
no profile is embedded in Nix or imported automatically.
- Zenith's initial NetworkManager activation never established a tunnel:
OpenVPN 2.6 preserved the profile's legacy `cipher=AES-256-CBC` setting but
excluded it from cipher negotiation. Adding `data-ciphers=AES-256-CBC` to
the imported connection fixes the negotiation; NetworkManager then installs
the pushed tunnel address, default route, private routes, and DNS servers.
Company database access, disconnect/reconnect, suspend, and reboot pass on
Zenith, so OpenVPN 3 is retired.
- For the entire workflow implementation, do not access or test Polaris.
Zenith results are accepted for both workstations; preserve exact deferred
Polaris replication steps instead.
- OpenVPN 3 is removed from the configuration. A full Zenith system build
passes, and the first password-authorized switch removed it from the live
generation. NetworkManager reconnected the VPN successfully afterward.
- Walker's native cached Lua menu scans the four identity roots and refreshes
when their top level changes. Its default action pipes the selected path to
one Nix-owned `open-project` command. Elephant supplies that path without a
terminating newline, so the launcher now accepts a successful partial read
instead of exiting under `errexit`. It opens Neovim through the configured
`xdg-terminal-exec` owner and leaves an interactive shell after Neovim exits.
Elephant reads menu actions only at startup, so rare declarative menu changes
take effect at the next login. The generated Walker configuration extends,
rather than copies or replaces, the pinned default provider list and prefixes.
- The shared terminal-launch failure came from the Kitty package rewrite:
unanchored `sed` changed both `Exec=kitty` and `TryExec=kitty` to include
`-1`. Arguments are invalid in `TryExec`, causing `xdg-terminal-exec` to
reject the configured terminal. The rewrite now changes only `Exec`; live
resolver output selects `kitty.desktop` and retains `kitty -1`, and Yazi
launches successfully from Walker.
- Restarting the pre-change Elephant and Walker processes loaded the corrected
project action. The user confirmed that repository launch works and exiting
Neovim leaves a usable shell in that repository. Passing the project root to
Neovim now opens the configured Oil default directory explorer immediately.
- A custom restart trigger for Elephant proved unsafe: Walker requires Elephant,
so activation stopped Walker with its backend but restarted only Elephant.
The trigger is removed; relogging is the deliberately simple refresh path
unless real usage proves that inconvenient. The user activated the removal,
relogged, and confirmed both Walker and the project-root Oil action work.
- Removed the decided Niri arrow/Page Up/Down/numbered duplicates, direct quit
and monitor-power bindings, extra screenshot modes, first/last and centering
actions, repeated resize families, and their four shortcut-only scripts. The
existing `Super+A` helper remains until a two-output session can validate its
bounded name-based replacement.
- Home Manager owns the existing Vivaldi launcher filenames for Actual and
WhatsApp at the higher-precedence user-data location, so both invoke Vivaldi
8.1.4087.70 with the `Default` profile and stable app ID. Direct activation
replaced both generated files with managed links and retained the originals
as adjacent `.bak` files. The entries use native forced ownership because
Vivaldi may regenerate those exact paths. The installed Outlook PWA identified
itself as `eoficlgicibekocmfdomjbfnjmehnhcd`; its prepared managed launcher
preserves mailto handling and the calendar, compose, and new-event actions.
- The first `nixos-maintainer` skill is instruction-only, Nix-owned, and passes
the skill-creator validator. It encodes repository-specific ownership,
research, formatting, no-switch, and affected-host validation behavior
without duplicating generic agent guidance or adding scripts.
- Removed Zed, the inactive Helix modules and note integration, Helix's cache
settings and complete lock subgraph, and inactive Foot modules. Neovim and
Kitty remain the sole declared editor and terminal. A repository-wide search
found no configured npm caller, so global Node.js and Bun are also removed;
project shells retain language runtimes where needed.
- Postman, HTTPie, Posting, and Resterm are removed after their valuable request
definitions moved to tracked HTTP owners; Bruno remains through its own
pilot. Rainfrog, dblab, Harlequin and its profile, Tabularis, DbGate, pgAdmin,
MongoDB Compass, pgModeler, tbls, global `usql`, and global MariaDB are
removed; Dadbod and wrapper-local clients remain, while DBeaver stays through
its gate. Obsidian, Planify, and Calcure are removed after note and task
authority was established. Pinta replaces GIMP; the Evolution-era PIM clients
remain independently gated.
- `rclone` is installed as the only preparation for the selected S3 pilot. No
remote, wrapper, or interactive browser is added before a real task and
least-privileged credentials exist.
- GitHub remote state is absolutely read-only; local files, commits, fetches,
clones, and worktrees remain available. The generated global agent guidance
records this boundary. The former broad `jellespr` credential and stale,
invalid `kirigar` credential are removed locally. The only working API route
is now a fine-grained `github_pat_` token in the mode-0600 machine-local
`~/.config/yookr/github-read-only.token`. The Nix-owned `gh` package wrapper
reads it only when `GH_TOKEN` is otherwise absent, so `gh` and its Git
credential helper work inside Nix dev shells without copying the secret into
Nix, exporting it process-wide, or adding per-repository configuration.
- The fine-grained token passes identity, repository contents, issues, pull
requests, Actions, and organization Projects reads with no stored fallback.
The organization exposes 219 open issues, five open pull requests, and 60
Projects. Project 52 is a 38-issue high-level overview; Project 8 is an
82-item software backlog with the native ticket fields needed for the planned
workflow. Its only three items assigned to `jellespr` are Done, while the two
open issues assigned across the organization are not present there. Keep the
native `gh` and Git workflow and wait for the first real Monday ticket; add no
dashboard or wrapper before usage exposes a concrete gap.
- The user activated the initial GitHub token loader, whole-directory skill
owner, and durable remote boundary. A fresh shell contained the fine-grained
`GH_TOKEN`, `nixos-maintainer` resolved to a store-backed directory, the
generated global `AGENTS.md` contained the strict remote-state policy, and
the user confirmed `gh project list --owner yookrdev` succeeds. A cached
nix-direnv shell then proved it strips inherited `GH_TOKEN`; this concrete gap
prompted the invocation-scoped wrapper above. After activation, a cached dev
shell contains no `GH_TOKEN`, while `gh api user` identifies `jellespr` and
Git reads the private `constant-store` remote through the wrapper-backed
credential helper.
- The former Context7 declaration put a literal key in
`env_http_headers`, whose values Codex interprets as environment-variable
names; it therefore exposed the secret without supplying the documented
authentication. The static key is absent from the current worktree. Official
Codex configuration and Context7 documentation instead support the built
`http_headers_helper`, which emits the required `Authorization: Bearer` header.
The user replaced and revoked the historical key, the machine-local file is
mode 0600, and a restarted Codex session successfully resolves Nixpkgs and
queries its documentation through Context7.
- The PR gate used `prediction-api#125`: native `gh pr view`, `checks`, and
`diff` reads work, while an isolated exact-base reconstruction passes patch,
whitespace, and Rust formatting checks and leaves no durable checkout. The PR
has no CI, review, linked issue, or concurrency/retry regression test for its
deadlock change; private dependency and MySQL requirements prevented a safe
full test. Native GitHub and Git tooling suffice without another review UI.
- Five authorized ephemeral read-only Codex evaluations respected the no-switch
boundary and generally selected the right owners, but exposed that the live
`nixos-maintainer` file was absent from the model-visible skill catalog. Home
Manager now links the whole skill directory, the shape documented as supported
by Codex, rather than linking only `SKILL.md`. The flake and generated Home
Manager build pass, and an isolated Codex discovery probe returns
`DISCOVERED`; the live store-backed directory is now discoverable.
- Herdr reports the agent lifecycle correctly and preserves its pane and output
across detach and reattach. Its native defaults suffice; no configuration or
wrapper was added.
- In zsh, never use lowercase `path` as a loop or local variable: it is tied to
`PATH`. A diagnostic command temporarily hid normal executables by doing so;
the declarative session environment was correct and needed no patch.
- Pinta passed the bounded real-edit trial and is selected; GIMP is absent from
the active workstation package set.
- `sync/notes` is now the sole declarative notebook owner, with `daily`,
`meetings`, and `transcripts` directories, a native `zk daily` alias, and a
`Super+N` dedicated Kitty/Neovim launcher. The five unique authored work notes
were copied byte-for-byte, and both distinct todo lists were merged without
dropping an item. Syncthing returned to idle with no needed files, pull
errors, or new conflicts. The user confirmed the daily launcher and migrated
contents, then explicitly approved moving the intact `sync/work/notes`
rollback copy to desktop Trash. The pre-merge todo copies in `/tmp` may now
expire normally.
- The Fossify pilot produced a 10.28-second, mono 48 kHz AAC/M4A named
`20260906_004200.m4a`. Zenith's Syncthing log records its completed transfer at
00:44:12, after the recording stopped, and the received file remains stable
and media-readable. Fossify will write future recordings directly into
`recordings/inbox`; this is a native one-time producer setting, not a manual
filing step. Orion now owns the CPU `whisper-cpp` consumer through a systemd
path unit. The scanner waits for five minutes of unchanged size, nanosecond
mtime, and inode, validates media duration, archives without clobbering, and
publishes an unlabeled transcript when diarization is unavailable. A copied
sample passed that complete CPU fallback path in an isolated sync tree. The
user deployed Orion with
`nixos-rebuild switch --target-host orion --flake ~/.config/nixos#orion`,
activated Zenith's desktop changes, and stopped Zenith's old timer before the
shared-inbox test. Moving only the observed sample into Orion's inbox triggered
the path unit; after the full settlement interval, roughly two minutes of CPU
inference produced a 563-byte fallback transcript and archived the original
at its unchanged 126741-byte size with no failure artifact. The results synced
back to Zenith, and both current generations match the simplified
configuration: Orion is the sole declarative transcription owner. The user
confirmed the sample's bilingual output. Zenith's already accepted 33 MB
pyannote model contained no credential files and was copied byte-for-byte to
Orion; a guarded isolated rerun then completed diarization and atomically
replaced the fallback transcript with the user's approval while preserving
the archived audio hash. All five temporary audio/transcript test directories
were removed from Zenith and Orion; the canonical synchronized archive and
transcript remain.
- Evolution 3.60.2 is composed through Nixpkgs with `evolution-ews` 3.60.2,
whose pinned source contains the separate native Microsoft 365 mail,
calendar, contacts, recurrence, and OAuth backends. Live inspection caught
that overriding `evolutionWithPlugins.plugins` replaced its default Evolution
package, and that a Home Manager package alone did not register the data
server on D-Bus. The pilot now uses NixOS's native `programs.evolution`
integration with the EWS plugin beside Thunderbird. The follow-up activation
succeeded, its source-registry service activates through D-Bus, no account is
preconfigured, and no authority is changed. The user deferred account setup;
Thunderbird and the Outlook PWA remain the working clients meanwhile.
- Named database connections and a Marimo-to-HTML report remain workbench
evaluation evidence, not scaffolding requirements. Add them only for a real
cross-system investigation that gives the queries and report durable value.
- Run independent pilots in parallel, but gate every retirement separately.
+158 -785
View File
@@ -1,818 +1,191 @@
# Workflow system blueprint # Workflow system blueprint
Status: authoritative design; discovery complete, implementation tracked in Status: implemented. Current external gates and operational handoff live in
[`plans/workflow-system.md`](plans/workflow-system.md) [`plans/workflow-system.md`](plans/workflow-system.md).
Last revised: 2026-09-05 This document is the durable design for the workstation and surrounding Yookr
workflow. Implementation history belongs in Git.
This document is the source of truth for streamlining the workstation and the
surrounding Yookr workflow. It records the intended system, the changes needed
to reach it, the trials that must precede conditional decisions, and work that
is deliberately deferred. Historical exploration and rejected alternatives are
not retained unless they prevent a likely wrong turn.
Remaining uncertainty is empirical rather than conceptual. Every unsettled
choice below has a bounded pilot, a pass condition, and a fallback; it is not an
invitation to resume general tool discovery.
## Governing intent ## Governing intent
Build a quiet, keyboard-first control plane over development, investigation, Build a quiet, keyboard-first control plane over development, investigation,
operations, agents, and personal knowledge. The goal is a small coherent set of operations, agents, and personal knowledge. Keep the system small and give each
interfaces, not one application pretending to do every job. intention one canonical owner.
The governing rules are: - Standardize vocabulary and entry points without forcing unlike workloads onto
one implementation.
- Give each intention one canonical owner. A native CLI may remain as a - Put frequent actions on direct keys and less frequent actions on their owning
troubleshooting substrate without becoming a second documented workflow. discovery surface.
- Standardize vocabulary, entry points, and lifecycle boundaries. Do not force - Keep stable packages, services, integration, and durable preferences in Nix.
unlike workloads onto the same implementation merely for visual uniformity. Keep credentials, sessions, databases, caches, and other mutable state with
- Put frequent actions on direct keys and make less frequent actions their owning application.
discoverable through the surface that owns them. - Prefer native file formats and supported application layers. Add wrappers only
- Keep stable packages, services, integration, and durable preferences under for demonstrated gaps.
Nix. Keep application databases, caches, sessions, trust, credentials, and
other mutable state with the application that owns them.
- Prefer supported application layers and native file formats. Add adapters or
wrappers only for a demonstrated gap.
- Repositories own reproducible development context, executable project - Repositories own reproducible development context, executable project
documentation, and artifacts specific to their code or schema. Global state documentation, and code-specific artifacts.
is reserved for genuinely cross-project or personal material. - Select `local`, `test`, or `live` per operation; do not depend on an ambient
- Select an environment per operation. Do not rely on a long-lived ambient
production context. production context.
- Keep destructive or production actions more deliberate than navigation and - Make production and destructive actions more deliberate than navigation and
read-only inspection. read-only inspection.
- Promote automation only after the underlying manual workflow has repeated and - Promote automation only after the manual workflow repeats and can be verified.
can be verified. A pilot is not yet a permanent abstraction.
- Prefer maintained free and open-source software. A paid tool is acceptable
only when it has an unusually strong, non-reproducible advantage.
- Optimize conceptual complexity and review burden, not package count or line - Optimize conceptual complexity and review burden, not package count or line
count in isolation. count in isolation.
The principal vocabulary is: The main coordinates are scope (`work`, `own`, `ext`, or `forks`), Yookr domain,
environment (`local`, `test`, or `live`), and intention (develop, inspect, query,
review, operate, or deploy).
- scope: `work`, `own`, `ext`, or `forks`; ## Ownership model
- Yookr domain: a service/product repository, data science,
`traceability-data-model`, or the cross-system `yookr-workbench`;
- environment: exactly `local`, `test`, or `live`;
- intention: develop, inspect, query, review, operate, or deploy;
- status: **selected**, **pilot**, **retire after migration**, or **deferred**.
These coordinates compose. They are not stages in a mandatory linear process.
### Explicit exception
Credentials and `.env` files normally stay outside Git, the Nix store, and
broad synchronization. The existing secret-bearing `yookr-vpn.ovpn` is a
user-accepted exception: it may remain in Syncthing for use on Zenith and
Polaris. Restrict the source file to the user where Syncthing permits, never
print or commit its contents, and import it into each machine's root-owned
NetworkManager connection store. Do not generalize this exception.
## Target ownership model
<!-- markdownlint-disable MD013 --> <!-- markdownlint-disable MD013 -->
| Intention | Canonical surface | Owner and status | | Intention | Canonical owner |
| --- | --- | --- | | --- | --- |
| Find applications, projects, or open windows | Walker/Elephant | **Selected**; applications plus an Elephant Lua project menu | | Find applications, projects, or windows | Walker/Elephant and direct Niri bindings |
| Search personal passwords globally | Walker with `rbw` | **Selected** for personal Vaultwarden; company Bitwarden remains in its browser extension | | Search personal passwords | Walker with `rbw`; company Bitwarden stays in its browser extension |
| Navigate tabs, history, bookmarks, and browser commands | Vivaldi Quick Commands (`F2`) | **Selected**; Vivaldi remains the only bookmark authority | | Navigate browser state | Vivaldi Quick Commands |
| Arrange windows and displays | Niri | **Selected**; dynamic workspaces and no application-specific display roles | | Arrange windows and displays | Niri dynamic workspaces |
| Work in a terminal | Kitty | **Selected**; no routine Kitty tiling or second terminal implementation | | Work in a terminal | Kitty |
| Edit related files and views | Neovim | **Selected**; buffers and temporary splits, without automatic session restoration | | Edit related files and views | Neovim |
| Enter a repository environment | `direnv` with `nix-direnv` | **Selected**; tracked `.envrc` and a default flake development shell | | Enter a repository environment | `direnv` with `nix-direnv` |
| Discover non-obvious project actions | `just --list` | **Selected** for participating repositories; recipes delegate to native tools | | Discover project actions | `just --list` |
| Run actively edited first-party code | Native processes, optionally Process Compose | **Selected** for fast reload and debugger access | | Run edited first-party code | Native processes, optionally Process Compose |
| Run a container-only repository stack | Rootless Podman Compose | **Selected**; foreground, explicit, and scoped to the work session | | Run a container-only project stack | Rootless Podman Compose |
| Run a persistent containerized user service | Rootless Podman Quadlet | **Selected**; user systemd owns lifecycle and logs | | Run a persistent containerized user service | Rootless Podman Quadlet |
| Run a durable host service | NixOS module and systemd | **Selected** when a maintained native module fits | | Run a durable host service | NixOS modules and systemd |
| Exercise APIs | Kulala and `xh` | **Selected**; JetBrains-compatible `.http` files are the durable artifact | | Exercise APIs | Kulala and `xh`, with tracked `.http` files |
| Query databases | Dadbod and native driver CLIs | **Selected**; named machine-local connections and repository-owned queries | | Query databases | Dadbod and native driver CLIs |
| Investigate across Yookr services | `yookr-workbench` | **Selected**, initially local to Zenith | | Investigate across Yookr systems | `yookr-workbench` |
| Connect the Yookr VPN | Noctalia Network control center | **Pilot** NetworkManager/OpenVPN migration; OpenVPN 3 remains the fallback until both hosts pass | | Connect the Yookr VPN | NetworkManager/OpenVPN through Noctalia |
| Inspect test Kubernetes | `just k8s test` to Gardenctl and `k9s` | **Selected**; isolated session and unchanged parent shell | | Inspect test Kubernetes | `just k8s test` using Gardenctl and read-only K9s |
| Inspect or operate RabbitMQ | Official management UI | **Selected**; `rabbitmqadmin` is the terminal fallback | | Inspect RabbitMQ | Official management UI; `rabbitmqadmin` for exact terminal operations |
| Browse S3-compatible storage | `rclone` | **Pilot**; explicit read/list/copy commands before interactive mutation surfaces | | Browse S3-compatible storage | Native `rclone` commands |
| Capture and find notes | `zk` through Neovim | **Selected**; one synchronized Markdown notebook and one daily intake path | | Capture and find notes | `zk` through Neovim in one synchronized Markdown notebook |
| Present work/personal PIM on desktop | Evolution | **Pilot**; server authorities do not change during evaluation | | Present work and personal PIM | Evolution pilot beside Thunderbird and Outlook |
| Present personal PIM on Android | DAVx5, aCalendar+, Google Contacts, Tasks.org | **Pilot**; the server authorities remain unchanged | | Present personal PIM on Android | DAVx5, aCalendar+, Google Contacts, and Tasks.org |
| Give agents durable project context | `AGENTS.md` and project commands | **Selected**; concise facts, invariants, architecture pointers, and checks | | Give agents durable project context | `AGENTS.md` and project commands |
| Run top-level interactive agents | Herdr | **Pilot** for persistence, attention state, notifications, and worktrees | | Run top-level agents | Herdr on demand |
| Maintain conversation state and internal subagents | Each native agent, primarily Codex | **Selected**; Herdr does not mirror internal subagents | | Maintain conversation state and subagents | Each native agent, primarily Codex |
| Review local changes | LazyGit in the task checkout | **Selected**; Git remains branch/worktree truth | | Review local changes | LazyGit in the task checkout |
| Review a pull request | `gh` for metadata/diff, then a review worktree when needed | **Pilot**; do not add another review application yet | | Review pull requests | `gh`, then a review worktree when local checks are useful |
<!-- markdownlint-enable MD013 --> <!-- markdownlint-enable MD013 -->
## Change program ## Repository contract
The phases below are dependency ordered. Each change appears once; trial details Active work repositories live under `~/dev/work`; inactive repositories live
live in the evaluation section rather than being restated throughout the plan. under `~/dev/work/.archive`, and worktrees under `~/dev/work/.worktrees`.
Repositories and worktrees never live under `~/sync`.
### Phase 1: reconcile state and ownership
Participating repositories use the following small contract:
#### Repositories and paths
- A tracked `.envrc` enters the default flake development shell without starting
- Before moving any repository, inspect its dirty state, linked worktrees, services, fetching data, or mutating the checkout.
destination collisions, and authored path consumers. Move registered - `flake.nix` owns reproducible dependencies, checks, packages, and formatting.
worktrees with `git worktree move` where possible and run - `just --list` indexes non-obvious tasks and delegates to native commands.
`git worktree repair` after changing a repository root. Do not apply a broad - `AGENTS.md` contains concise project facts, invariants, architecture pointers,
filesystem move to unchecked repositories. and checks without duplicating global guidance.
- Reconcile the divergent dirty copies of `yookr-dashboard-new` under `~/sync` - Service-owned `.http` and schema-owned `.sql` files stay beside their owner;
and `~/work/repos` before moving or deleting either. Establish the generated views, caches, and scratch output do not become durable sources.
authoritative commit and preserve unique tracked, untracked, ignored, local - Secrets enter at runtime from an approved source or a user-only file outside
data, and secret inputs deliberately. A general archive operation must not checkouts, initially `~/.config/yookr/<repository>.env`.
choose between these copies implicitly. - A new worktree behaves like a fresh clone and requires its own explicit
- Consolidate source repositories under the existing XDG projects root: `direnv allow`.
`~/dev/work/<repository>`. Do not keep the accidental `~/work` root, the - Branch-derived ports, databases, and container names are added only after a
intermediate `repos/` directory, or a permanent compatibility symlink. real collision.
- Keep these work repositories active: `aggregation-api`, `prediction-api`,
`rules-service`, `yookr-api`, `yookr-dashboard`, `yookr-dashboard-new`, Use Process Compose only when multiple foreground processes benefit from one
`yookr-data-science`, `yookr-management-svelte-redo`, and visible lifecycle. Use Podman Compose for wholly containerized project stacks,
`traceability-data-model` (currently `schemas`). Add `yookr-workbench` when Quadlet for persistent user containers, and NixOS modules for durable host
its pilot begins. services. Do not nest lifecycle tools for another status screen.
- Rename `schemas` to `traceability-data-model` during the move. Update authored
path consumers; generated Codex, shell, and Herdr histories may remain `yookr-workbench` owns cross-system API requests, operational queries, the
historical. Garden/Kubernetes entry point, and Marimo investigations. It does not collect
- Move every other repository from the current work inventory intact to service OpenAPI specifications, schema migrations, deployable model code, or
`~/dev/work/.archive`. This includes `yookr-management-new`, copied application state.
`yookr-management-svelte`, `yookr-dashboard-new.old`, `pipelines.old`,
`yookr-dashboard-new.zip`, `rules-service-hex`, ## Interaction and knowledge boundaries
`yookr-data-science-no-kedro`, and tutorial repositories. Do not infer active
status from a product-looking name. - Walker scans the four repository roots, excludes `.archive` and `.worktrees`,
- Archive the small `~/work/rabbitmq` scratch project. Remove the empty legacy and invokes one Nix-owned `open-project` command. The command opens Kitty at
`.data/mongodb` directory only after a final emptiness check; do not move the the repository root, starts Neovim, and leaves a shell after Neovim exits.
global `.data` directory into the source tree. - Vivaldi remains the bookmark and web-app authority. Home Manager owns stable
- Make Git identity rules explicit and non-overlapping: work identity below launchers for Actual, WhatsApp, and Outlook without duplicating browser state.
`~/dev/work`, personal identity below `~/dev/own`, `~/dev/ext`, and - Niri keeps dynamic general-purpose workspaces and a compact navigation and
`~/dev/forks`. Update the old `~/work` conditional include. movement vocabulary. The two-output active-workspace exchange remains a
- Treat work/personal labels as identity and navigation boundaries, not privacy bounded pilot.
boundaries. Non-secret work material may synchronize to the Pixel and Orion; - Codex is primary; Claude Code and Pi are fallbacks. Herdr owns persistence and
source checkouts, credentials, and runtime state do not belong in the broad attention for top-level sessions, while each agent owns its internal state.
`~/sync` tree. - Use one primary writer per checkout. Subagents may perform bounded independent
- Remove stale path-bound Codex trust and obsolete approval entries after the read-heavy work, but overlapping changes are sequenced.
move. Update the transitional Bruno workspace path only if Bruno still needs - Substantial multi-session work may keep one living
it during migration. `docs/plans/<task-slug>.md`; lasting knowledge moves to normal documentation
and transient execution history is removed when finished.
#### Nix and mutable application state - `~/sync/notes` is the Markdown/zk authority. Daily intake uses
`daily/YYYY-MM-DD.md`; planned meetings and raw transcripts use `meetings/`
- Preserve the existing host/profile/capability structure, typed facts, and `transcripts/` respectively.
flake-parts composition, Home Manager integration, and primary-tool - Microsoft 365 remains authoritative for work/university PIM and Radicale for
indirection. No repository-wide architecture migration is justified. personal CalDAV, VTODO, and CardDAV. Clients present those authorities without
- Keep flakes and `nh os switch` as the normal activation path. Keep Neovim's bridging or copying them.
Lua, wrapper, plugins, language servers, and formatters store-backed; accept - Fossify records on Android and Syncthing delivers completed recordings to
the measured rebuild cost rather than adding an out-of-store fast path. Orion. Orion owns event-driven CPU transcription, diarization fallback,
- Do not prototype Hjem/Rum, replace flakes with `npins`, or add a separate Home archival, and Markdown publication.
Manager activation wrapper. These do not address the accepted ownership
model. ## Safety boundaries
- Remove the unused `nixpkgs-stable` input and its dead local binding. Let
Noctalia follow the root Nixpkgs input if the pinned configuration evaluates GitHub and all other remotes are read-only. Local commits, clones, fetches, and
unchanged; describe this as dependency-graph hygiene, not a latency fix. worktrees are allowed; remote mutation requires a separate change to policy.
- Retire or rewrite the stale root `PLAN.md` after checking whether it contains
unfinished Neovim work. It must not continue to claim that Helix is active. Credentials and `.env` files normally stay outside Git, the Nix store, and broad
- Keep Flatpak enabled as an intentionally available installation facility, but synchronization. The accepted `yookr-vpn.ovpn` is a narrow exception for Zenith
add no declarative Flatpak manager, remotes, or applications while it has no and Polaris: keep it user-restricted and import it into NetworkManager without
consumers. printing or committing its contents.
- Continue declaring Noctalia's stable policy through Nix. When behavior differs
from the declaration, inspect its writable XDG state as a second supported Only the user activates a NixOS or Home Manager generation. Agents may evaluate
application layer; keep only genuinely local runtime state there rather than and build configurations, then provide an activation command.
adding another synchronization mechanism.
## Remaining pilots
#### Codex configuration ownership
- Capture Zenith's external display identity during a docked session and test the
- Rotate the exposed Context7/MCP credential and replace plaintext headers with two-output `Super+A` exchange.
an environment-backed header or supported credential mechanism. - Resume Evolution and Android PIM only when the user chooses to run those daily
- Generate stable Codex defaults at `/etc/codex/config.toml`: MCP endpoints, use pilots.
approval defaults, notifications when Codex owns them, and other durable - Add workbench database connections and a Marimo report only for a real
cross-machine preferences. Official precedence makes this a lower-precedence cross-system investigation.
default layer. - Configure an S3 remote only for a real task with least-privileged credentials
- Leave `~/.codex/config.toml` writable and machine-local for project trust and and explicit upload authorization.
unavoidable local settings. Review non-trust keys occasionally: promote a - Rerun the Yookr OpenAPI refresh after the deployed image exposes
deliberate cross-machine preference to Nix, retain generated/machine state, `/full-api-docs/openapi.json`; run authenticated API checks only with current
and remove accidental overrides. Do not add a drift daemon. user credentials.
- Keep global `AGENTS.md`, intentional rules, and user-authored skills under - Keep Bruno, DBeaver, and existing PIM clients until their independent gates are
Nix/Home Manager. Keep authentication, conversations, databases, caches, resolved.
downloaded plugins/skills, and generated Herdr integrations untracked.
- Remove the stale `~/.config/codex` tree; its preservation audit found no unique
authored material worth migrating.
### Phase 2: establish the project contract and Yookr workbench
#### Common repository contract
Participating repositories expose the same concepts without copying an empty
directory taxonomy:
- A tracked `.envrc` enters the default flake development shell and selects only
non-secret local defaults. Activation must not perform `uv sync`, start
services, fetch data, or do other mutating/network work.
- `flake.nix` owns reproducible dependencies, packages, checks, and the
formatter. A flake app remains only when it is an actual runnable or
distributable program.
- `just --list` is the documented index of non-obvious human and agent tasks.
Recipes delegate to native commands and omit trivial one-off operations; the
native CLI remains available for diagnosis without becoming a second catalog.
- Install `just` as a workstation bootstrap tool and include it in participating
development shells so the repository remains portable.
- `AGENTS.md` records architecture pointers, invariants, project commands, and
required checks. It stays factual and concise; it does not duplicate generic
global behavior.
- Service-owned `.http` examples and schema-owned `.sql` files live beside the
code or schema they describe. Generated views, caches, and scratch results are
not durable sources.
- Environment selection is per operation. Kulala selects a buffer environment,
Dadbod selects a visibly named connection, and recipes accept an explicit
argument such as `just k8s test`.
- Secrets enter at runtime from the service's approved source or a user-only
machine file outside all checkouts, initially
`~/.config/yookr/<repository>.env`. A tracked `.envrc` may load that stable
path with `dotenv_if_exists`.
- A new worktree behaves like a fresh clone. Recreate dependencies, caches,
generated files, and build output; do not copy ignored state from a main
checkout. Require an explicit `direnv allow` for each worktree.
- Do not pre-allocate branch-derived ports, databases, Compose projects, or
container names. Add targeted isolation only after a real task exposes a
collision.
First prove this contract in `rules-service`, paired with the API workflow in
`yookr-api` and the database workflow in `traceability-data-model`. Only after
two repositories reveal genuinely repeated files should a small native Nix
flake-template repository be extracted. Language-native initializers continue
to own language source layout.
Repository-specific corrections are part of the pilots:
- In `rules-service`, add a README, flake, `.envrc`, `justfile`, and concise
`AGENTS.md`; document the local run path; correct the obsolete environment
example; use `test`, not `dev`, for the deployed test environment. Do not
encode the transitional rollout as a durable `deploy` recipe.
- In `traceability-data-model`, rename the repository and make destructive
schema recreation an explicitly named reset action rather than the ordinary
`dev` path.
- In `yookr-data-science`, default ordinary fetching away from live, remove
`uv sync` from shell activation, keep the deployable soil-moisture pipeline,
and move only generic cross-system exploration to the workbench.
- In `yookr-dashboard-new`, preserve the useful visible Process Compose
lifecycle. Expose it through `just dev` when the project adopts the common
contract instead of maintaining a workflow-only flake app in parallel.
#### Workload lifecycle
- Run actively edited first-party code natively.
- Use Process Compose only for two or more native or mixed foreground processes
that need one visible lifecycle, dependency graph, and log view.
- Use rootless Podman Compose for a wholly containerized repository stack. Keep
support stacks manual, foreground, and scoped to a work session; preserve
named volumes between runs and make running state visible to Podman tools.
- Use Quadlet for persistent containerized user services and NixOS modules for
durable host services when a good module exists. Do not nest lifecycle tools
merely to obtain another status screen.
- Configure Podman's external Compose provider deterministically. Do not use
Compose dotenv behavior as the secret contract.
#### `yookr-workbench`
Create a minimal `yookr-workbench` on Zenith before adding workbench-owned API,
database, or Kubernetes commands. Keep it without a remote while proving its
contents. It owns only cross-system work:
- curated cross-service `.http` requests;
- operational queries and MongoDB scripts that do not belong to one service;
- explicit local/test investigation commands;
- the Garden/Kubernetes entry point;
- uv-managed Marimo analyses, disposable experiments, and report exports.
It does not own service OpenAPI specifications, schema migrations, deployable
model code, general bookmarks, RabbitMQ URLs, or copied application state.
Scratch work is promoted to its nearest durable owner or deleted. Unrelated
personal Dadbod scratch stays under XDG data.
Use Marimo's Python files as the analysis source. Ignore raw/local data and
scratch output. Share source notebooks with technical readers, self-contained
HTML as the faithful general report, browser-produced PDF when the delivery
channel favors it, and individual SVG/PNG files only when a figure is itself the
deliverable. Do not add a notebook hub or report server.
Production data may be used on a developer machine; this streamline adds no
separate anonymization or local-retention policy. Raw datasets and
secret-bearing output still remain outside Git and broad synchronization.
#### API and database workflow
- Make Kulala and `xh` the editor/CLI pair. Keep hand-curated `.http` files as
durable examples; use `kulala-fmt` OpenAPI conversion only to seed a migration
or produce a disposable view.
- Commit non-secret `local`, `test`, and `live` metadata in
`http-client.env.json`. Keep credentials in ignored
`http-client.private.env.json` or runtime environment variables. Make
`local` the default environment; keep test/live selection explicit and
visible per buffer. Kubernetes being test-only does not remove explicit live
HTTP access.
- Use Yookr API as the first proof. Support the sibling service-owned
`openapi.yaml` and ignored snapshots fetched from test/live
`/full-api-docs/openapi.json`. Authenticate through ordinary login and run a
harmless administrator-authorized read.
- Compare local and network specifications semantically with `oasdiff`. A
network refresh writes and validates an ignored cache; it never overwrites a
checked-in contract automatically. Do not add timestamped archives or polling.
- Add `just openapi` to a service only when that repository has a reproducible
generation command. A service repository remains authoritative for its
specification; the workbench does not collect copied specs.
- Use Dadbod UI's machine-local named connection store, normally
`~/.local/share/db_ui/connections.json`, instead of the current single
`DATABASE_URL` assumption. Name entries
`<environment>-<service-or-domain>-<engine>`, for example
`test-rules-mysql`, `test-prediction-mysql`, and
`local-traceability-postgres`.
- Support PostgreSQL, MySQL/MariaDB, and MongoDB while the migration requires all
three. Keep relational artifacts as `.sql` and MongoDB artifacts in their
native JavaScript format. Durable queries belong in repositories; DBUI saved
queries are personal scratch.
- Prefer read-only credentials for workbench inspection where available.
Dadbod is not itself a safety boundary. Intentional schema or data mutations
belong in an owning repository or an explicitly reviewed operation.
- Populate connection URLs manually. Do not extract Kubernetes Secrets or build
a Bitwarden-to-Dadbod translation layer before manual connection management
demonstrates a real cost.
#### Kubernetes, RabbitMQ, S3, and SharePoint
- Install `networkmanager-openvpn` on Polaris and Zenith, import the accepted
VPN profile separately as a root-owned connection with autoconnect disabled,
give it the same visible name on both machines, and use Noctalia's built-in
Network control center. Retain OpenVPN 3 until the migration gate passes on
both hosts.
- Package official `gardenctl` and `gardenlogin` releases through Nix, use the
nixpkgs OIDC plugin, remove duplicate binaries from `~/.garden` and
`~/.local/bin`, and replace handwritten Zsh initialization with
`gardenctl rc zsh`. Keep Garden configuration and kubeconfigs mutable and
private outside the store.
- Implement `just k8s test` in the workbench. It creates a fresh
`GCTL_SESSION_ID`, targets Garden `leafcloud-test`, project `ryxtdg8ydl`, shoot
`test`, derives `KUBECONFIG`, and opens `k9s` in the foreground. The parent
shell remains unchanged. Use explicit `kubectl --namespace` for exceptional
terminal work; do not guess a namespace.
- Do not expose live Kubernetes or a deploy command. Image construction and GHCR
publication remain in GitHub Actions while the team release process is
unresolved.
- Use the official RabbitMQ management UI for inspection, publishing/getting,
and purge. Put `https://test.rabbitmq.yookr.org` and
`https://rabbitmq.yookr.org` in a Vivaldi operations bookmark folder with
explicit test/live labels rather than duplicating them as workbench recipes or
Walker actions. Keep `rabbitmqadmin` for exact terminal operations; purge
remains visibly interactive.
- Pilot one machine-local `rclone` S3 remote using the least-privileged available
credentials. Begin with `lsd`, `lsjson`, `tree`, and explicit read/copy
operations. Do not expose `rclone ncdu` initially because its interface also
deletes and has no read-only mode.
- Reuse `rclone`'s OneDrive backend for a later SharePoint browse/download pilot
before considering a second SharePoint tool. Do not introduce continuous sync
or a mount until the real access pattern and company consent are known.
#### Pull-request review
- Use `gh pr view` for metadata/checks and `gh pr diff` for a quick read-only
review.
- Create a dedicated review worktree when local navigation, tests, or agent
assistance are needed, then use LazyGit in that checkout.
- Add no PR dashboard or wrapper until repeated reviews expose a specific gap.
### Phase 3: simplify global navigation and desktop interaction
#### Walker and project opening
- Keep Walker/Elephant as the global discovery surface and Niri direct bindings
for the most frequent intentions. Keep `z <project>` as the shell fast path;
it is another invocation surface over the filesystem, not another catalog.
- Use Elephant's native dynamic Lua menu support as `menus:projects`. Scan Git
roots across `~/dev/work`, `~/dev/own`, `~/dev/ext`, and `~/dev/forks`; exclude
`.archive` and every worktree root. Show one flat result set with scope labels.
- Have each result invoke one Nix-owned `open-project` command. It opens an
interactive Kitty shell at the repository root, starts Neovim, and returns to
a usable prompt after Neovim exits. It restores no buffers, layouts, or
services.
- Treat `yookr-workbench` as an ordinary project result. Do not generate a
desktop entry per repository or maintain a separate project registry.
- Retain Elephant's shipped provider defaults/prefixes instead of copying them
into Nix. Keep the current `?` Bitwarden prefix as the local exception.
Clipboard and credentials remain prefixed; quiet navigation providers may
participate in unprefixed search.
- Do not include bookmarks in Walker until a maintained provider can read
Vivaldi live. Elephant's one-time CSV import would create a second authority.
#### Browser ownership
- Keep one neutral Vivaldi profile. Work/personal cookie and history isolation
has no demonstrated value; rename the misleading `Work` profile if visible.
- Use Vivaldi Quick Commands for tabs, history, bookmarks, and browser commands.
Keep vertical tabs. Do not initially add Vivaldi workspaces, pinned tabs, Web
Panels, command chains, or another Chromium browser.
- Keep Actual, WhatsApp, and Outlook as web apps. Vivaldi owns installation,
identity, authentication, permissions, and sync. After capturing each app ID,
Home Manager owns one stable desktop entry invoking current Vivaldi with
`--profile-directory=Default --app-id=<id>`; suppress the duplicate
Vivaldi-generated launcher. Reuse the known Actual ID
`ahoihnifhakgijcmjlhbcgehigihinlo` and WhatsApp ID
`hnpfjngllnobngcgfapefoaidbinmjnm`; capture Outlook's ID after installing it.
This prevents stale Nix-store paths.
- Keep a small Speed Dial beginning with ChatGPT and the Yookr GitHub
organization. Google remains the search engine rather than a bookmark.
- Sync durable bookmarks/Speed Dials, supported settings, extensions, and web
apps between Zenith and Polaris. Keep open tabs and history local. Passwords
remain Bitwarden-owned.
- Use Bitwarden's multi-account browser extension for personal Vaultwarden and
company Bitwarden. Walker's `rbw` provider remains personal-only; do not copy
company secrets, create another browser profile, or build a private adapter.
#### Niri vocabulary
- Design for two general-purpose displays with dynamic workspaces. Do not pin
applications, roles, or content to a monitor. On Zenith, place the external
widescreen to the right and focus it initially once its stable identity has
been captured during a docked session.
- Keep `Super+H/J/K/L` for focus and `Super+Alt+H/J/K/L` for frequent
window/column movement. Keep `Super+U/I` for workspace navigation, the
corresponding deliberate move/reorder operations, useful mouse-wheel
bindings, and touchpad gestures.
- Remove arrow-key duplicates, Page Up/Down aliases, numbered-workspace
bindings, first/last-column jumps, centering, maximize-to-edges, direct
compositor quit, monitor power-off, and the unused hotkey-overlay binding.
- Remove the NixOS-config terminal, NixOS-switch, edit-secrets, and color-picker
shortcuts and their shortcut-only scripts. Underlying terminal commands stay.
- Consolidate screenshots on Niri's interactive `Super+S` UI. Keep
`Super+O` overview, `Super+Q` close, and `Super+Ctrl+L` lock.
- Keep `Super+R` preset width cycling, one incremental resize family, column
maximize, and fullscreen. Retain stacking/consume-expel actions, tabbed
columns, toggle floating, and focus switching between floating and tiling as
secondary capabilities.
- Replace the fragile mutable-index `Super+A` helper only through the bounded
pilot below. Niri 26.04 actions accept a workspace index or name, not stable
IPC workspace IDs, and have no atomic active-workspace swap.
### Phase 4: make the agent workflow coherent
#### Context, skills, and client boundaries
- Keep Codex primary and Claude Code/Pi as experimental fallbacks without fixed
specializations or forced feature parity.
- Share repository `AGENTS.md`, concise global guidance, project commands,
user-authored skills where natively supported, and verified MCP endpoint
facts. Keep client-specific model/UI settings, approvals, authentication,
trust, and runtime state native.
- Centralize MCP endpoint facts in the Nix AI capability only after Codex and
Claude both consume them, then render thin native configurations. Pi has no
native MCP owner and should not receive a wrapper for cosmetic parity. Remove
generic Home Manager `programs.mcp` output if it remains unused.
- Do not create a prompt-template menu or demonstration skills. Promote a
procedure only after it has stable inputs, outputs, and verification.
- Create the instruction-only `nixos-maintainer` skill as the first demonstrated
candidate. It inspects repository instructions and the complete current
owner, prefers native options and existing patterns, researches
version-sensitive choices, keeps mutable state outside the store, makes
focused idiomatic edits, formats them, and performs the smallest meaningful
no-switch validation for affected hosts. Switching a generation requires a
separate explicit request. Add scripts only after a deterministic repeated
operation appears.
#### Top-level agents and worktrees
- Pilot Herdr as owner of top-level Codex, Claude, and Pi processes, cross-agent
attention state, detach/reattach, desktop notifications, and the normal
create/open/list/remove worktree flow.
- Start or attach Herdr on demand. Do not add a supervised login service: the
client already starts a persistent background session. Stop it deliberately
with `herdr server stop`. Keep ordinary terminals, Neovim, dev processes, and
LazyGit outside Herdr.
- Configure Herdr as the sole notification owner during the pilot. If it fails,
use Codex's built-in TUI notifications before an external notify command.
- Use one workspace per active checkout or cross-repository investigation and
one pane per top-level agent. Internal Codex subagents remain in Codex.
- Let Herdr use Git worktrees as its substrate. For the first work-only pilot,
place them under `~/dev/work/.worktrees/<repository>/<branch-slug>` and exclude
that tree from Walker. Use an explicit path for other identity scopes until a
scope-preserving convention is proven.
- Do not install Worktrunk initially. Herdr 0.8.2 covers the proposed basic
lifecycle and leaves branches intact for human review. Revisit Worktrunk only
for demonstrated needs such as richer cross-worktree state, shell switching,
CI/PR views, process cleanup, or integration-aware branch removal. Keep native
`git worktree list`, `repair`, and `prune` as maintenance escape hatches.
- Defer `codex agents`. Codex 0.153.4 exposes it, but local diagnostics show
ephemeral app-server mode and daemon bootstrap that assumes a standalone
managed install rather than the Nix-store binary. Do not add a second mutable
Codex installation or custom daemon workaround.
#### Task lifecycle
- Use one primary writer per checkout. It may delegate bounded independent
read-heavy work; concurrently running internal subagents do not edit the same
checkout. Confirmed read-only investigations may share a checkout and need no
branch or worktree. Sequence tasks expected to overlap in files or behavior.
- Use `feat/<issue>-<slug>`, `fix/<issue>-<slug>`, or
`chore/<issue>-<slug>`, omitting the issue when none exists. Reuse the slug for
the checkout, Herdr label, and optional execution plan.
- Keep setup explicit: create the worktree, review and authorize `.envrc`, then
start the agent there. Do not add hooks, copy ignored files, launch agents from
Walker, or hide these state changes in a wrapper.
- Use Codex `/plan` when design is uncertain, then one `/goal` containing the
outcome, constraints, and verification for substantial execution. A goal is a
persistent target, not a milestone hierarchy.
- For genuinely substantial multi-session work, keep one living
`docs/plans/<task-slug>.md` in the branch and reference it from the goal. Record
scope/non-goals, architectural facts, verifiable milestones, progress,
discoveries, decisions, evidence, and the exact next step. Link to source
instead of copying logs. Move lasting knowledge to normal docs/ADR at the end
and delete a transient plan when it has no continuing value.
- Let the primary agent make local commits at meaningful verified or resumption
boundaries, not mechanically after every milestone. It does not push, merge,
open a pull request, or mutate GitHub without an explicit request.
- End on a checked, locally committed branch with a handoff covering outcome,
verification, limitations, and unresolved decisions. Review with LazyGit in
the task worktree. The user chooses publication, integration, revision, or
abandonment; remove the checkout only afterward.
### Phase 5: consolidate notes, PIM, and transcription
#### Notes and meeting knowledge
- Consolidate both notebooks into `~/sync/notes` on one fully synchronized node
with no concurrent writers. Preserve authored notes/images and merge the two
`todo.md` files deliberately. Stop generating `~/sync/work/notes`.
- Use `daily/YYYY-MM-DD.md` as the unclassified intake path, with no title prompt
or required frontmatter. Use a `zk` group/alias for idempotent daily-note
creation. Keep ordinary titled notes at the notebook root until use justifies
another collection.
- Bind unused `Mod+N` to a small Nix-owned command that opens today's note at the
append position in a dedicated Kitty/Neovim window and closes with Neovim.
- Store planned meetings as dated files under `meetings/` and raw transcripts
separately under `transcripts/`. A person curates decisions/context into the
meeting note. During the Monday development meeting, the team cooperatively
decides and creates issues, which the meeting note then links. Agents do not
create issues or automatically ingest transcripts into daily notes.
- Keep Markdown/zk as notes authority. Personal actionable tasks belong in
Radicale VTODO, private/email-derived work follow-ups in Microsoft To Do, and
shared development work in GitHub issues. Link Markdown context instead of
duplicating it into another task database.
#### PIM authority and presentation
- The PIM foundation must remain free of charge, with open-source components
preferred. Paid services such as Morgen are excluded from this role.
- Keep Microsoft 365 authoritative for work/university mail, calendar, people,
and Microsoft To Do. Keep Radicale authoritative for personal CalDAV
calendars/VTODO and CardDAV contacts. Clients may present both but do not copy
data between authorities or create a bidirectional bridge.
- Pilot Evolution with all four active mail identities: personal, old personal,
university, and work. Use its Microsoft 365 account type for Microsoft
identities, not legacy Exchange/EWS. Connect personal accounts and Radicale
through native IMAP/SMTP, CalDAV, CardDAV, and VTODO.
- Keep Thunderbird and the Outlook Vivaldi app throughout the pilot. Change no
authority until consent and daily behavior pass. If Evolution passes its
gate, make it the canonical desktop PIM and retire Thunderbird. Keep Outlook
as the Microsoft-native fallback, not a second general PIM workflow.
- Keep Radicale rather than adding Baikal or Nextcloud. A larger groupware stack
does not solve Microsoft federation and no personal web UI is required.
- On Android, use DAVx5 as the sole Radicale adapter. aCalendar+ and Google
Contacts consume Android providers; Tasks.org consumes DAVx5/OpenTasks and may
separately connect to Microsoft To Do. Do not configure Radicale twice.
- Use free/F-Droid DAVx5 and Tasks.org builds. Account for Tasks.org's Microsoft
To Do limits--partial recurrence, one reminder, no attachments, and no manual
ordering--and give each collection one notification owner. Test Outlook's
optional calendar/contact export and correct default accounts.
- Before removing the last independent PIM copy, test restoration of Radicale
collections and authentication data. Keep broader Orion backup redesign out
of scope.
#### Meeting transcription
- Use Fossify Voice Recorder on the Pixel and Syncthing directly to Orion. Orion
becomes the always-on transcription owner; this does not reopen general
backup, CI, builder, or agent-offload design.
- First observe a real recording transfer. Fossify writes directly to its final
filename while recording, so a Syncthing/path event alone does not prove the
meeting ended. Prefer an automatic post-record move to a dedicated inbox if
available; otherwise require an unchanged size/mtime settlement period plus
media readability/duration validation.
- Once completion detection is proven, refactor the Zenith/Vulkan module for
Orion's CPU `whisper-cpp` runtime and one inbox/processing/archive/failed tree.
Use an event-triggered path/service pair to wake an idempotent scanner instead
of polling every two minutes.
- Keep the multilingual model and automatic language handling. Run diarization
by default, but publish an unlabeled transcript when Whisper succeeds and
diarization fails; record the partial failure instead of withholding it.
- Write transcripts under `~/sync/notes/transcripts` and preserve originals in
the recordings archive. The transcript is the quiet success signal;
systemd state/logs expose failures. Add no desktop notification.
- Validate with a Dutch/English meeting, preferably with code switching, and
require completion by the next morning. Consider a GPU worker only if Orion
misses that service level. Syncthing replication is not backup.
### Phase 6: retire duplicate applications and dead configuration
Perform removals after the corresponding migration or pilot.
- After valuable definitions are migrated, remove Postman, HTTPie, Posting, and
Resterm. Keep Kulala and `xh`; keep Bruno only through the HTTP pilot and
remove it if no indispensable capability appears.
- Remove Rainfrog, dblab, Harlequin, Tabularis, DbGate, pgAdmin, Beekeeper
Studio, MongoDB Compass, pgModeler, and tbls. Remove the Harlequin profile.
Keep Dadbod and wrapper-local clients; keep DBeaver only through the database
pilot and remove it if no GUI-only need emerges.
- Remove global `usql` and MariaDB. Keep PostgreSQL, MariaDB, and `mongosh` only
in the Neovim wrapper or repository environments that own them.
- Search once for an application/plugin that shells out to npm. If found,
declare Node with that owner; otherwise remove global Node.js and Bun and keep
language runtimes in repository flakes.
- Remove Zed and the inactive Helix module, cache settings, and flake input.
Keep Neovim. Retain Jujutsu as a **deferred** experiment with no assigned
workflow until a concrete use case exists.
- Remove inactive Foot modules and keep Kitty as the only terminal.
- Remove Obsidian, Planify, and Calcure after note/PIM ownership is in place;
none owns data that needs migration.
- If Evolution passes, remove Khal, Todoman, and local `pimsync`. Keep Noctalia
as a read-oriented calendar glance surface. Add no terminal PIM client without
a demonstrated operation.
- Install Pinta beside GIMP for its bounded trial. Remove GIMP only if Pinta
covers the workload; otherwise remove Pinta and retain GIMP.
- Retain Spotify, OnlyOffice, Nautilus, imv, Sioyek, mpv, qBittorrent, and
Discord. Keep Nautilus as graphical file manager/directory default and
`xdg-desktop-portal-gnome` with GTK fallback as file chooser. Do not add
Thunar, Dolphin, Nemo, or another file manager without a concrete gap.
## Evaluation gates
### Project contract and workbench
Pass when a fresh checkout of each pilot repository can enter its environment,
discover relevant commands, run checks, and reach a useful local state using
only tracked configuration plus documented machine-local inputs. Confirm no
shell activation mutates state and a new worktree reads no ignored files from
another checkout. Extract templates only after two pilots share real boilerplate.
Run two representative development servers and test suites concurrently using
their native behavior. Continue deferring branch-derived ports, databases, and
container-project names only if endpoints remain discoverable and no actual
resource collision appears; otherwise add isolation only for the observed
conflict.
For the workbench, prove one authenticated Yookr API read, one named connection
for every needed database engine, isolated `just k8s test`, and one
Marimo-to-HTML report. Publish or expand it only after those artifacts confirm
the boundary.
### Herdr and worktrees
Exercise short and long Codex sessions, an approval-blocked session,
detach/reattach, one Claude or Pi fallback, and two independently writing task
worktrees. Evaluate runtime/attention, notifications, and worktree handling as
separate components, retaining only those that pass. Attention state must be
accurate, exactly one notification must be delivered, paths must be predictable,
dirty removal must be refused, branches must remain for review, and layout
overhead must cost less than the polling/navigation it replaces. If Herdr's
runtime or notification role fails, retain native agent sessions and Codex TUI
notifications. If its worktree role fails, use native
`git worktree add/list/remove`; compare Worktrunk only after that ceremony
becomes repeated friction.
### Agent conventions
Test the instruction-only `nixos-maintainer` skill with representative prompts
that add, change, remove, and diagnose a capability, plus unrelated prompts that
must not trigger it. Pass when it follows repository ownership, makes focused
edits, performs appropriate no-switch validation, and leaves switching to an
explicit request.
Separately, give a fresh Codex session only the repository, its `AGENTS.md`, and
a living execution plan from a substantial paused task. Promote the convention
only if it can identify the exact next step and resume correctly without chat
history or copied logs.
### S3 and SharePoint
On one real Yookr task, configure a machine-local, least-privileged S3 remote and
prove target selection, listing, download, and an explicitly authorized upload
without accidental mutation. Pass when native `rclone` commands are clear and
auditable enough that no interactive browser is needed; otherwise remove the
pilot configuration and package. Keep SharePoint deferred until company consent
and a real browse/download case exist.
### Pull-request review gate
Exercise one `gh pr view`/`gh pr diff` review and one review worktree with local
navigation or tests. Pass when metadata, checks, local verification, LazyGit,
and optional agent assistance are reachable without another review UI. If it
fails, record the exact missing operation before evaluating one maintained
alternative.
### Evolution and Android PIM
Pass Evolution only after all four accounts prove send/receive, folder behavior,
search, offline mail, invitations, recurrence exceptions, contacts/GAL,
Microsoft To Do recurrence/reminders, and acceptable daily UX. Confirm tenant
consent and one notification owner per collection. If it fails, retain
Thunderbird plus Outlook PWA and keep Radicale clients native.
On Android, prove two-way Radicale calendar/contact/task synchronization,
Microsoft To Do within documented limits, clear collection labels, correct
default accounts, recurrence, reminders, and Outlook export where permitted.
If DAVx5 fails, keep personal Radicale data in the existing desktop/native
clients and Outlook as the Microsoft 365 Android client. If only Tasks.org's
Microsoft integration fails, keep Tasks.org for personal VTODO through DAVx5
and use Outlook for Microsoft tasks. Neither failure changes server authority.
### NetworkManager VPN
Install `networkmanager-openvpn`, import the profile separately on Polaris and
Zenith with autoconnect disabled, and use Noctalia's existing Network control
center as the only desktop toggle. Pass after route, DNS, test-service access,
suspend/resume, reconnect, and reboot work on both machines. Remove OpenVPN 3
only after both pass.
### Niri active-workspace exchange
Pilot a two-output-only helper that assigns collision-free temporary names to
both active workspaces, preserves original names, moves each named workspace to
the other output, makes both arrivals active, restores names, and returns focus
to the original physical output. It remains non-atomic and must restore names
on failure where possible. Keep it only if repeated swaps are reliable and the
visible transition is acceptable; otherwise retain the current shortcut
temporarily and track a native Niri action rather than growing the wrapper.
Capture Zenith's external display identity before encoding right-hand placement
and initial focus.
### Pinta
Reproduce recent edits covering clipboard/open, selection, crop/resize/canvas,
text/drawing/shapes, layers/transparency, basic adjustments, and PNG/JPEG export.
If every required operation works, select Pinta and remove GIMP. Otherwise
remove Pinta and keep GIMP; do not retain both indefinitely.
### Transcription
Observe whether Syncthing transfers a growing Fossify recording and prove the
completion protocol before automation. Then validate one real Dutch/English
meeting end to end: recording, completed-file detection, transfer, CPU
transcription, diarization fallback, archive, and Markdown by the next morning.
Remove Zenith's timer only after Orion passes.
### Stable web-app launchers
Capture the Outlook app ID, replace stale generated launchers for Actual and
WhatsApp, and verify every Walker result starts current Vivaldi with the intended
app/profile after a NixOS upgrade and Vivaldi Sync cycle. Expose one launcher per
app.
## Deferred work ## Deferred work
- Design the team release process: intentional releases, immutable - Define the team release process and OpenAPI publication policy before encoding
commit-addressed images, explicit revision promotion, rollout verification, deployment automation.
rollback evidence, and Kubernetes-native base/overlay composition before a - Adopt a company-wide infrastructure secret source only after Yookr selects one.
heavier GitOps platform. Do not encode mutable `:latest` rollout as a - Publish `yookr-workbench` only when sharing becomes useful.
canonical workstation action. - Pilot SharePoint browse/download through `rclone` after Microsoft consent.
- Establish service-owned OpenAPI generation, publication, provenance, and - Revisit Worktrunk, `codex agents`, Walker-to-agent launching, formal agent
consumer-refresh policy. Existing local/network drift calls for review, not evaluation, remote builders, CI runners, and agent offload only after a concrete
automatic overwrite. gap appears.
- Adopt a company-wide infrastructure secret source when Yookr chooses one. - Add report servers, notebook hubs, groupware, terminal PIM, meeting summaries,
Until then, keep manually supplied machine-local inputs and do not assume that concurrency isolation, OCR, dictation, clipboard remapping, or additional
Bitwarden Secrets Manager is available merely because Password Manager is. convenience surfaces only after repeated use establishes an owner.
- Publish `yookr-workbench` privately when sharing becomes useful; do not create
a temporary synchronization path.
- Pilot SharePoint browse/download through `rclone` after Microsoft consent is
known. Continuous sync and mounts require a separate demonstrated need.
- Revisit Walker bookmark search only with a live Vivaldi reader, and company
credential search only with maintained multi-vault support or repeated cost.
- Revisit Worktrunk only after a concrete Herdr worktree gap. Revisit
`codex agents` after Nix-managed daemon startup works without a second mutable
installation.
- Build a formal agent evaluation harness only after representative tasks and
concrete configurations exist. Compare repeated runs on correctness, human
intervention, review burden, conceptual complexity, wall time, and token use;
structured agent output is telemetry, not the quality judgment. Controlled
Codex evaluations consume `codex exec --json`; they never parse the
interactive TUI or mutable internal databases.
- Add Walker-to-agent launching only after the explicit Herdr/worktree sequence
becomes repetitive enough to define a stable contract.
- Keep remote Herdr, builders, CI runners, agent offload, and general Orion
backup redesign out of scope until a concrete bottleneck justifies them.
- Add a report server, notebook hub, Nextcloud groupware/Notes, terminal PIM
client, or meeting-summary skill only after a missing capability is proven.
- Add branch-derived ports, databases, container projects, or other concurrency
isolation only after an observed resource collision.
- Add LocalSend, OCR, dictation, global clipboard remapping, a second hotkey-help
surface, or other distribution-inspired conveniences only after repeated use
gives them a clear owner.
## Reference baseline ## Reference baseline
Implementation must recheck fast-moving interfaces against the pinned version. Recheck fast-moving interfaces against their pinned versions before changing
them.
<!-- markdownlint-disable MD013 --> <!-- markdownlint-disable MD013 -->
- Projects: [Nix flake templates](https://nix.dev/manual/nix/latest/command-ref/new-cli/nix3-flake-init), [nix-direnv](https://github.com/nix-community/nix-direnv), [just manual](https://just.systems/man/en/), [Podman Compose](https://docs.podman.io/en/latest/markdown/podman-compose.1.html), [Quadlet](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html), [Marimo](https://docs.marimo.io/). - Projects: [Nix flake templates](https://nix.dev/manual/nix/latest/command-ref/new-cli/nix3-flake-init), [nix-direnv](https://github.com/nix-community/nix-direnv), [just manual](https://just.systems/man/en/), [Podman Compose](https://docs.podman.io/en/latest/markdown/podman-compose.1.html), [Quadlet](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html), [Marimo](https://docs.marimo.io/).
- APIs/data: [Kulala environments](https://kulala.app/usage/environments), [Kulala](https://github.com/mistweaverco/kulala.nvim), [Dadbod](https://github.com/tpope/vim-dadbod), [Dadbod UI](https://github.com/kristijanhusak/vim-dadbod-ui/blob/master/doc/dadbod-ui.txt), [RabbitMQ management](https://www.rabbitmq.com/docs/management), [rclone S3](https://rclone.org/s3/), [rclone OneDrive/SharePoint](https://rclone.org/onedrive/). - APIs/data: [Kulala](https://kulala.app/), [Dadbod](https://github.com/tpope/vim-dadbod), [RabbitMQ management](https://www.rabbitmq.com/docs/management), [rclone S3](https://rclone.org/s3/), [rclone OneDrive/SharePoint](https://rclone.org/onedrive/).
- Desktop: [Walker providers](https://walkerlauncher.com/docs/providers), [Elephant menus](https://github.com/abenz1267/elephant/tree/master/internal/providers/menus), [Vivaldi web apps](https://help.vivaldi.com/desktop/miscellaneous/progressive-web-apps/), [Vivaldi Sync](https://help.vivaldi.com/desktop/tools/sync/), [Niri IPC](https://niri-wm.github.io/niri/IPC.html), [Niri actions](https://niri-wm.github.io/niri/niri_ipc/enum.Action.html), [Pinta guide](https://www.pinta-project.com/user-guide/). - Desktop: [Walker](https://walkerlauncher.com/docs/providers), [Elephant menus](https://github.com/abenz1267/elephant/tree/master/internal/providers/menus), [Vivaldi web apps](https://help.vivaldi.com/desktop/miscellaneous/progressive-web-apps/), [Niri IPC](https://niri-wm.github.io/niri/IPC.html).
- Agents: [Codex configuration precedence](https://learn.chatgpt.com/docs/config-file/config-basic#configuration-precedence), [Codex long-running work](https://learn.chatgpt.com/docs/long-running-work), [Codex subagents](https://learn.chatgpt.com/docs/agent-configuration/subagents), [Codex notifications](https://learn.chatgpt.com/docs/config-file/config-advanced#notifications), [Herdr configuration](https://herdr.dev/docs/configuration/), [Herdr CLI](https://herdr.dev/docs/cli-reference/), [Git worktrees](https://git-scm.com/docs/git-worktree), [Worktrunk](https://worktrunk.dev/). - Agents: [Codex configuration](https://learn.chatgpt.com/docs/config-file/config-basic), [Codex long-running work](https://learn.chatgpt.com/docs/long-running-work), [Herdr](https://herdr.dev/docs/configuration/), [Git worktrees](https://git-scm.com/docs/git-worktree).
- PIM/knowledge: [Evolution account types](https://help.gnome.org/evolution/intro-account-types.html), [Evolution Microsoft 365 implementation](https://gitlab.gnome.org/GNOME/evolution-ews/-/tree/master/src/Microsoft365), [Radicale](https://radicale.org/v3.html), [DAVx5](https://manual.davx5.com/introduction.html), [Tasks.org with DAVx5](https://tasks.org/docs/sync_davx5/), [Tasks.org Microsoft To Do](https://tasks.org/docs/sync_microsoft/), [`zk` daily journal](https://github.com/zk-org/zk/blob/main/docs/tips/daily-journal.md). - PIM/knowledge: [Evolution account types](https://help.gnome.org/evolution/intro-account-types.html), [Radicale](https://radicale.org/v3.html), [DAVx5](https://manual.davx5.com/introduction.html), [Tasks.org](https://tasks.org/docs/), [`zk` daily journal](https://github.com/zk-org/zk/blob/main/docs/tips/daily-journal.md).
- Infrastructure: [Gardenctl v2](https://gardener.cloud/docs/gardenctl-v2/), [NetworkManager `nmcli`](https://networkmanager.pages.freedesktop.org/NetworkManager/NetworkManager/nmcli.html), [NetworkManager keyfiles](https://networkmanager.dev/docs/api/latest/nm-settings-keyfile.html), [Syncthing synchronization](https://docs.syncthing.net/users/syncing), [Fossify recorder implementation](https://github.com/FossifyOrg/Voice-Recorder/blob/main/app/src/main/kotlin/org/fossify/voicerecorder/services/RecorderService.kt). - Infrastructure: [Gardenctl](https://gardener.cloud/docs/gardenctl-v2/), [NetworkManager](https://networkmanager.pages.freedesktop.org/NetworkManager/NetworkManager/nmcli.html), [Syncthing](https://docs.syncthing.net/users/syncing).
<!-- markdownlint-enable MD013 --> <!-- markdownlint-enable MD013 -->
Generated
+18 -43
View File
@@ -57,9 +57,13 @@
"elephant": { "elephant": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
"walker",
"nixpkgs" "nixpkgs"
], ],
"systems": "systems" "systems": [
"walker",
"systems"
]
}, },
"locked": { "locked": {
"lastModified": 1784365424, "lastModified": 1784365424,
@@ -156,7 +160,7 @@
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
], ],
"systems": "systems_2", "systems": "systems",
"treefmt-nix": "treefmt-nix" "treefmt-nix": "treefmt-nix"
}, },
"locked": { "locked": {
@@ -215,7 +219,9 @@
}, },
"nixos-hardware": { "nixos-hardware": {
"inputs": { "inputs": {
"nixpkgs": "nixpkgs" "nixpkgs": [
"nixpkgs"
]
}, },
"locked": { "locked": {
"lastModified": 1788335262, "lastModified": 1788335262,
@@ -234,15 +240,15 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1767892417, "lastModified": 1788400875,
"narHash": "sha256-8bW3q88CEg2u4hSP66Vf4lpbLonHz7hqDNBMcCY7E9U=", "narHash": "sha256-v4ZEET1Ah1K9+wMaIwPH/JTQK3MwW/gZC/SyjnlBX74=",
"rev": "3497aa5c9457a9d88d71fa93a4a8368816fbeeba", "rev": "5545adfad2e98de106a5544ca7067e03010410bd",
"type": "tarball", "type": "tarball",
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre924538.3497aa5c9457/nixexprs.tar.xz" "url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.11pre1066699.5545adfad2e9/nixexprs.tar.xz"
}, },
"original": { "original": {
"type": "tarball", "type": "tarball",
"url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz" "url": "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"
} }
}, },
"nixpkgs-lib": { "nixpkgs-lib": {
@@ -260,19 +266,6 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs_2": {
"locked": {
"lastModified": 1788400875,
"narHash": "sha256-v4ZEET1Ah1K9+wMaIwPH/JTQK3MwW/gZC/SyjnlBX74=",
"rev": "5545adfad2e98de106a5544ca7067e03010410bd",
"type": "tarball",
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.11pre1066699.5545adfad2e9/nixexprs.tar.xz"
},
"original": {
"type": "tarball",
"url": "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"
}
},
"noctalia": { "noctalia": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@@ -296,7 +289,6 @@
"root": { "root": {
"inputs": { "inputs": {
"disko": "disko", "disko": "disko",
"elephant": "elephant",
"flake-parts": "flake-parts", "flake-parts": "flake-parts",
"home-manager": "home-manager", "home-manager": "home-manager",
"import-tree": "import-tree", "import-tree": "import-tree",
@@ -304,7 +296,7 @@
"nix-index-database": "nix-index-database", "nix-index-database": "nix-index-database",
"nix-wrapper-modules": "nix-wrapper-modules", "nix-wrapper-modules": "nix-wrapper-modules",
"nixos-hardware": "nixos-hardware", "nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs_2", "nixpkgs": "nixpkgs",
"noctalia": "noctalia", "noctalia": "noctalia",
"sops-nix": "sops-nix", "sops-nix": "sops-nix",
"walker": "walker" "walker": "walker"
@@ -331,21 +323,6 @@
} }
}, },
"systems": { "systems": {
"locked": {
"lastModified": 1689347949,
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
"owner": "nix-systems",
"repo": "default-linux",
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default-linux",
"type": "github"
}
},
"systems_2": {
"locked": { "locked": {
"lastModified": 1681028828, "lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
@@ -360,7 +337,7 @@
"type": "github" "type": "github"
} }
}, },
"systems_3": { "systems_2": {
"locked": { "locked": {
"lastModified": 1689347949, "lastModified": 1689347949,
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
@@ -398,13 +375,11 @@
}, },
"walker": { "walker": {
"inputs": { "inputs": {
"elephant": [ "elephant": "elephant",
"elephant"
],
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
], ],
"systems": "systems_3" "systems": "systems_2"
}, },
"locked": { "locked": {
"lastModified": 1784217994, "lastModified": 1784217994,
+4 -6
View File
@@ -20,7 +20,10 @@
url = "github:BirdeeHub/nix-wrapper-modules"; url = "github:BirdeeHub/nix-wrapper-modules";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
nixos-hardware.url = "github:NixOS/nixos-hardware/master"; nixos-hardware = {
url = "github:NixOS/nixos-hardware/master";
inputs.nixpkgs.follows = "nixpkgs";
};
nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"; nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz";
noctalia = { noctalia = {
url = "github:noctalia-dev/noctalia"; url = "github:noctalia-dev/noctalia";
@@ -35,13 +38,8 @@
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
elephant = {
url = "github:abenz1267/elephant";
inputs.nixpkgs.follows = "nixpkgs";
};
walker = { walker = {
url = "github:abenz1267/walker"; url = "github:abenz1267/walker";
inputs.elephant.follows = "elephant";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
}; };
@@ -232,9 +232,7 @@ process_scope() {
local source processing_target recording_status signature index scope_status=0 local source processing_target recording_status signature index scope_status=0
local -a sources signatures local -a sources signatures
if ! mkdir -p "$inbox_dir" "$processing_dir" "$archive_dir" "$failed_dir" "$transcript_dir"; then mkdir -p "$inbox_dir" "$processing_dir" "$archive_dir" "$failed_dir" "$transcript_dir" || return 1
return 1
fi
while IFS= read -r -d '' source; do while IFS= read -r -d '' source; do
if is_recording "$source"; then if is_recording "$source"; then
@@ -291,4 +289,3 @@ mkdir -p "$runtime_dir"
set +o errexit set +o errexit
process_scope "$TRANSCRIBE_SYNC_ROOT/recordings" "$TRANSCRIBE_SYNC_ROOT/notes/transcripts" process_scope "$TRANSCRIBE_SYNC_ROOT/recordings" "$TRANSCRIBE_SYNC_ROOT/notes/transcripts"
exit $?
-1
View File
@@ -6,7 +6,6 @@
alsa.enable = true; alsa.enable = true;
pulse.enable = true; pulse.enable = true;
jack.enable = true; jack.enable = true;
wireplumber.enable = true;
}; };
}; };
} }
+19 -38
View File
@@ -1,4 +1,3 @@
{ ... }:
{ {
flake.modules.homeManager.desktop-commands = flake.modules.homeManager.desktop-commands =
{ lib, ... }: { lib, ... }:
@@ -35,12 +34,20 @@
actualId = "ahoihnifhakgijcmjlhbcgehigihinlo"; actualId = "ahoihnifhakgijcmjlhbcgehigihinlo";
outlookId = "eoficlgicibekocmfdomjbfnjmehnhcd"; outlookId = "eoficlgicibekocmfdomjbfnjmehnhcd";
whatsappId = "hnpfjngllnobngcgfapefoaidbinmjnm"; whatsappId = "hnpfjngllnobngcgfapefoaidbinmjnm";
actualDesktop = pkgs.makeDesktopItem { in
name = "vivaldi-${actualId}-Default"; {
desktopName = "Actual"; desktop.commands.browser = [ command ];
home = {
packages = [ pkgs.vivaldi ];
sessionVariables.BROWSER = command;
};
xdg = {
desktopEntries = {
"vivaldi-${actualId}-Default" = {
name = "Actual";
exec = appCommand actualId; exec = appCommand actualId;
icon = "vivaldi-${actualId}-Default"; icon = "vivaldi-${actualId}-Default";
extraConfig.StartupWMClass = "crx_${actualId}"; settings.StartupWMClass = "crx_${actualId}";
actions = { actions = {
Accounts = { Accounts = {
name = "Accounts"; name = "Accounts";
@@ -56,20 +63,18 @@
}; };
}; };
}; };
whatsappDesktop = pkgs.makeDesktopItem { "vivaldi-${whatsappId}-Default" = {
name = "vivaldi-${whatsappId}-Default"; name = "WhatsApp Web";
desktopName = "WhatsApp Web";
exec = appCommand whatsappId; exec = appCommand whatsappId;
icon = "vivaldi-${whatsappId}-Default"; icon = "vivaldi-${whatsappId}-Default";
extraConfig.StartupWMClass = "crx_${whatsappId}"; settings.StartupWMClass = "crx_${whatsappId}";
}; };
outlookDesktop = pkgs.makeDesktopItem { "vivaldi-${outlookId}-Default" = {
name = "vivaldi-${outlookId}-Default"; name = "Outlook";
desktopName = "Outlook";
exec = "${appCommand outlookId} %U"; exec = "${appCommand outlookId} %U";
icon = "vivaldi-${outlookId}-Default"; icon = "vivaldi-${outlookId}-Default";
mimeTypes = [ "x-scheme-handler/mailto" ]; mimeType = [ "x-scheme-handler/mailto" ];
extraConfig.StartupWMClass = "crx_${outlookId}"; settings.StartupWMClass = "crx_${outlookId}";
actions = { actions = {
Calendar = { Calendar = {
name = "Open calendar"; name = "Open calendar";
@@ -85,27 +90,6 @@
}; };
}; };
}; };
in
{
desktop.commands.browser = [ command ];
home = {
packages = [ pkgs.vivaldi ];
sessionVariables.BROWSER = command;
};
xdg = {
dataFile = {
"applications/vivaldi-${actualId}-Default.desktop" = {
force = true;
source = "${actualDesktop}/share/applications/vivaldi-${actualId}-Default.desktop";
};
"applications/vivaldi-${whatsappId}-Default.desktop" = {
force = true;
source = "${whatsappDesktop}/share/applications/vivaldi-${whatsappId}-Default.desktop";
};
"applications/vivaldi-${outlookId}-Default.desktop" = {
force = true;
source = "${outlookDesktop}/share/applications/vivaldi-${outlookId}-Default.desktop";
};
}; };
mimeApps.defaultApplicationPackages = [ pkgs.vivaldi ]; mimeApps.defaultApplicationPackages = [ pkgs.vivaldi ];
}; };
@@ -168,9 +152,6 @@
enable = true; enable = true;
cacheHome = "${localDir}/cache"; cacheHome = "${localDir}/cache";
configHome = "${homeDir}/.config";
dataHome = "${localDir}/share";
stateHome = "${localDir}/state";
userDirs = { userDirs = {
enable = true; enable = true;
+1 -5
View File
@@ -1,4 +1,3 @@
{ ... }:
{ {
flake.modules.homeManager.dev-tools = flake.modules.homeManager.dev-tools =
{ config, pkgs, ... }: { config, pkgs, ... }:
@@ -44,10 +43,7 @@
gardenlogin gardenlogin
]; ];
programs.lazygit = { programs.lazygit.enable = true;
enable = true;
enableZshIntegration = true;
};
programs.jujutsu.enable = true; programs.jujutsu.enable = true;
programs.jq.enable = true; programs.jq.enable = true;
+1 -3
View File
@@ -1,5 +1,3 @@
{ {
flake.modules.nixos.flatpak = { flake.modules.nixos.flatpak.services.flatpak.enable = true;
services.flatpak.enable = true;
};
} }
+1 -13
View File
@@ -28,21 +28,9 @@
]; ];
fontconfig = { fontconfig = {
enable = true;
antialias = true;
allowBitmaps = false; allowBitmaps = false;
useEmbeddedBitmaps = false; useEmbeddedBitmaps = false;
subpixel.lcdfilter = "none";
hinting = {
enable = true;
autohint = false;
style = "slight";
};
subpixel = {
rgba = "none";
lcdfilter = "none";
};
}; };
}; };
}; };
+1 -3
View File
@@ -1,5 +1,3 @@
{ {
flake.modules.homeManager.keyring = { flake.modules.homeManager.keyring.services.gnome-keyring.enable = true;
services.gnome-keyring.enable = true;
};
} }
+5 -8
View File
@@ -1,4 +1,4 @@
{ config, lib, ... }: { lib, ... }:
let let
palette = (import ./_kanagawa.nix).palette; palette = (import ./_kanagawa.nix).palette;
hex = lib.removePrefix "#"; hex = lib.removePrefix "#";
@@ -9,9 +9,9 @@ in
flake.modules.nixos.limine = flake.modules.nixos.limine =
{ config, lib, ... }: { config, lib, ... }:
let let
displayValues = builtins.attrValues config.facts.machine.displays; primaryDisplay = lib.findFirst (display: display.primary) null (
primaryDisplays = lib.filter (display: display.primary) displayValues; builtins.attrValues config.facts.machine.displays
primaryDisplay = if primaryDisplays == [ ] then null else builtins.head primaryDisplays; );
interfaceResolution = interfaceResolution =
if primaryDisplay != null && primaryDisplay.width != null then if primaryDisplay != null && primaryDisplay.width != null then
"${toString primaryDisplay.width}x${toString primaryDisplay.height}" "${toString primaryDisplay.width}x${toString primaryDisplay.height}"
@@ -24,6 +24,7 @@ in
limine = { limine = {
enable = true; enable = true;
maxGenerations = 10; maxGenerations = 10;
resolution = interfaceResolution;
style = { style = {
backdrop = hex palette.secondaryBackground; backdrop = hex palette.secondaryBackground;
@@ -64,13 +65,9 @@ in
interface = { interface = {
branding = config.networking.hostName; branding = config.networking.hostName;
brandingColor = hex palette.accents.blue; brandingColor = hex palette.accents.blue;
helpHidden = false;
resolution = interfaceResolution; resolution = interfaceResolution;
}; };
}; };
}
// lib.optionalAttrs (interfaceResolution != null) {
resolution = interfaceResolution;
}; };
}; };
}; };
+1 -3
View File
@@ -4,9 +4,7 @@
{ {
programs.mpv = { programs.mpv = {
enable = true; enable = true;
bindings = { bindings.D = "cycle deband";
D = "cycle deband";
};
config = { config = {
profile = "high-quality"; profile = "high-quality";
+1 -4
View File
@@ -1,7 +1,6 @@
{ config, ... }: { config, ... }:
let let
account = config.facts.account; account = config.facts.account;
homeModules = config.flake.modules.homeManager;
in in
{ {
flake.modules.homeManager.neovim = flake.modules.homeManager.neovim =
@@ -214,15 +213,13 @@ in
kulalaCorePath = "${pkgs.kulala-core}/bin/kulala-core"; kulalaCorePath = "${pkgs.kulala-core}/bin/kulala-core";
}; };
binName = "nvim";
}; };
}; };
flake.modules.homeManager.primary-editor-neovim = flake.modules.homeManager.primary-editor-neovim =
{ pkgs, ... }: { pkgs, ... }:
{ {
imports = [ homeModules.neovim ]; imports = [ config.flake.modules.homeManager.neovim ];
home.sessionVariables = { home.sessionVariables = {
EDITOR = "nvim"; EDITOR = "nvim";
@@ -5,8 +5,7 @@ for _, lhs in ipairs({ "grn", "gra", "grx", "grr", "gri", "grt", "gO" }) do
vim.keymap.del("n", lhs) vim.keymap.del("n", lhs)
end end
vim.keymap.del("x", "gra") vim.keymap.del("x", "gra")
vim.keymap.del("i", "<C-S>") vim.keymap.del({ "i", "s" }, "<C-S>")
vim.keymap.del("s", "<C-S>")
vim.keymap.set("n", "<C-h>", "<C-w><C-h>", { desc = "Focus left window" }) vim.keymap.set("n", "<C-h>", "<C-w><C-h>", { desc = "Focus left window" })
vim.keymap.set("n", "<C-j>", "<C-w><C-j>", { desc = "Focus lower window" }) vim.keymap.set("n", "<C-j>", "<C-w><C-j>", { desc = "Focus lower window" })
@@ -15,7 +15,6 @@ require("lz.n").load({
after = function() after = function()
require("blink.cmp").setup({ require("blink.cmp").setup({
keymap = { keymap = {
preset = "default",
["<C-j>"] = { "select_next", "fallback" }, ["<C-j>"] = { "select_next", "fallback" },
["<C-k>"] = { "select_prev", "fallback" }, ["<C-k>"] = { "select_prev", "fallback" },
["<Tab>"] = { "select_and_accept", "fallback" }, ["<Tab>"] = { "select_and_accept", "fallback" },
@@ -44,7 +44,6 @@ require("lz.n").load({
after = function() after = function()
require("nvim-treesitter-textobjects").setup({ require("nvim-treesitter-textobjects").setup({
select = { lookahead = true }, select = { lookahead = true },
move = { set_jumps = true },
}) })
local select = require("nvim-treesitter-textobjects.select") local select = require("nvim-treesitter-textobjects.select")
@@ -34,8 +34,7 @@ require("lz.n").load({
}, },
}, },
after = function() after = function()
local conform = require("conform") require("conform").setup({
conform.setup({
formatters_by_ft = formatters_by_ft, formatters_by_ft = formatters_by_ft,
format_on_save = function(bufnr) format_on_save = function(bufnr)
if formatters_by_ft[vim.bo[bufnr].filetype] then if formatters_by_ft[vim.bo[bufnr].filetype] then
@@ -16,12 +16,10 @@ require("lz.n").load({
"nvim-lspconfig", "nvim-lspconfig",
event = { "BufReadPre", "BufNewFile" }, event = { "BufReadPre", "BufNewFile" },
after = function() after = function()
local attach_group = vim.api.nvim_create_augroup("lux-lsp-attach", { clear = true })
local detach_group = vim.api.nvim_create_augroup("lux-lsp-detach", { clear = true })
local highlight_group = vim.api.nvim_create_augroup("lux-lsp-highlight", { clear = true }) local highlight_group = vim.api.nvim_create_augroup("lux-lsp-highlight", { clear = true })
vim.api.nvim_create_autocmd("LspAttach", { vim.api.nvim_create_autocmd("LspAttach", {
group = attach_group, group = vim.api.nvim_create_augroup("lux-lsp-attach", { clear = true }),
callback = function(args) callback = function(args)
local client = assert(vim.lsp.get_client_by_id(args.data.client_id)) local client = assert(vim.lsp.get_client_by_id(args.data.client_id))
local bufnr = args.buf local bufnr = args.buf
@@ -85,7 +83,7 @@ require("lz.n").load({
}) })
vim.api.nvim_create_autocmd("LspDetach", { vim.api.nvim_create_autocmd("LspDetach", {
group = detach_group, group = vim.api.nvim_create_augroup("lux-lsp-detach", { clear = true }),
callback = function(args) callback = function(args)
local supports_highlights = vim local supports_highlights = vim
.iter(vim.lsp.get_clients({ .iter(vim.lsp.get_clients({
@@ -103,21 +103,15 @@ require("lz.n").load({
actions.smart_send_to_qflist(prompt_bufnr) actions.smart_send_to_qflist(prompt_bufnr)
vim.cmd("Trouble qflist open") vim.cmd("Trouble qflist open")
end end
local picker_mappings = {
["<C-j>"] = actions.move_selection_next,
["<C-k>"] = actions.move_selection_previous,
["<C-q>"] = promote,
}
require("telescope").setup({ require("telescope").setup({
defaults = { defaults = {
mappings = { mappings = { i = picker_mappings, n = picker_mappings },
i = {
["<C-j>"] = actions.move_selection_next,
["<C-k>"] = actions.move_selection_previous,
["<C-q>"] = promote,
},
n = {
["<C-j>"] = actions.move_selection_next,
["<C-k>"] = actions.move_selection_previous,
["<C-q>"] = promote,
},
},
}, },
extensions = { extensions = {
["ui-select"] = require("telescope.themes").get_dropdown(), ["ui-select"] = require("telescope.themes").get_dropdown(),
@@ -60,29 +60,22 @@ require("lz.n").load({
change = { text = "│" }, change = { text = "│" },
delete = { text = "_" }, delete = { text = "_" },
topdelete = { text = "‾" }, topdelete = { text = "‾" },
changedelete = { text = "~" },
untracked = { text = "┆" },
}, },
on_attach = function(bufnr) on_attach = function(bufnr)
local gs = require("gitsigns") local gs = require("gitsigns")
vim.keymap.set("n", "]h", function() local map_hunk = function(lhs, direction, desc)
vim.keymap.set("n", lhs, function()
if vim.wo.diff then if vim.wo.diff then
return "]h" return lhs
end end
vim.schedule(function() vim.schedule(function()
gs.nav_hunk("next") gs.nav_hunk(direction)
end) end)
return "<Ignore>" return "<Ignore>"
end, { buffer = bufnr, desc = "Next git hunk", expr = true }) end, { buffer = bufnr, desc = desc, expr = true })
vim.keymap.set("n", "[h", function()
if vim.wo.diff then
return "[h"
end end
vim.schedule(function() map_hunk("]h", "next", "Next git hunk")
gs.nav_hunk("prev") map_hunk("[h", "prev", "Previous git hunk")
end)
return "<Ignore>"
end, { buffer = bufnr, desc = "Previous git hunk", expr = true })
end, end,
}) })
end, end,
+26 -58
View File
@@ -1,9 +1,19 @@
{ {
browser, browser,
launcher, launcher,
shortcutCommands, swapMonitorWorkspaces,
terminal, terminal,
}: }:
let
spawnLocked = command: {
spawn-sh = command;
_props.allow-when-locked = true;
};
withCooldown = action: {
${action} = { };
_props.cooldown-ms = 150;
};
in
{ {
# Application and utility launchers # Application and utility launchers
"Mod+Return" = { "Mod+Return" = {
@@ -25,46 +35,16 @@
}; };
# Hardware media, volume, and brightness keys # Hardware media, volume, and brightness keys
"XF86AudioPlay" = { "XF86AudioPlay" = spawnLocked "playerctl play-pause";
spawn-sh = "playerctl play-pause"; "XF86AudioStop" = spawnLocked "playerctl stop";
_props.allow-when-locked = true; "XF86AudioPrev" = spawnLocked "playerctl previous";
}; "XF86AudioNext" = spawnLocked "playerctl next";
"XF86AudioStop" = { "XF86AudioRaiseVolume" = spawnLocked "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+";
spawn-sh = "playerctl stop"; "XF86AudioLowerVolume" = spawnLocked "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-";
_props.allow-when-locked = true; "XF86AudioMute" = spawnLocked "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle";
}; "XF86AudioMicMute" = spawnLocked "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle";
"XF86AudioPrev" = { "XF86MonBrightnessUp" = spawnLocked "brightnessctl s 5%+";
spawn-sh = "playerctl previous"; "XF86MonBrightnessDown" = spawnLocked "brightnessctl s 5%-";
_props.allow-when-locked = true;
};
"XF86AudioNext" = {
spawn-sh = "playerctl next";
_props.allow-when-locked = true;
};
"XF86AudioRaiseVolume" = {
spawn-sh = "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+";
_props.allow-when-locked = true;
};
"XF86AudioLowerVolume" = {
spawn-sh = "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-";
_props.allow-when-locked = true;
};
"XF86AudioMute" = {
spawn-sh = "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle";
_props.allow-when-locked = true;
};
"XF86AudioMicMute" = {
spawn-sh = "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle";
_props.allow-when-locked = true;
};
"XF86MonBrightnessUp" = {
spawn-sh = "brightnessctl s 5%+";
_props.allow-when-locked = true;
};
"XF86MonBrightnessDown" = {
spawn-sh = "brightnessctl s 5%-";
_props.allow-when-locked = true;
};
# Screenshots # Screenshots
"Mod+S".screenshot = { }; "Mod+S".screenshot = { };
@@ -105,7 +85,7 @@
"Mod+Tab".focus-monitor-next = { }; "Mod+Tab".focus-monitor-next = { };
"Mod+A" = { "Mod+A" = {
_props.repeat = false; _props.repeat = false;
spawn = shortcutCommands.swapMonitorWorkspaces; spawn = swapMonitorWorkspaces;
_props.hotkey-overlay-title = "Swap Monitor Workspaces"; _props.hotkey-overlay-title = "Swap Monitor Workspaces";
}; };
"Mod+Alt+Tab".move-column-to-monitor-next = { }; "Mod+Alt+Tab".move-column-to-monitor-next = { };
@@ -124,22 +104,10 @@
"Mod+Shift+I".move-workspace-up = { }; "Mod+Shift+I".move-workspace-up = { };
# Mouse workspace navigation # Mouse workspace navigation
"Mod+WheelScrollDown" = { "Mod+WheelScrollDown" = withCooldown "focus-workspace-down";
focus-workspace-down = { }; "Mod+WheelScrollUp" = withCooldown "focus-workspace-up";
_props.cooldown-ms = 150; "Mod+Alt+WheelScrollDown" = withCooldown "move-column-to-workspace-down";
}; "Mod+Alt+WheelScrollUp" = withCooldown "move-column-to-workspace-up";
"Mod+WheelScrollUp" = {
focus-workspace-up = { };
_props.cooldown-ms = 150;
};
"Mod+Alt+WheelScrollDown" = {
move-column-to-workspace-down = { };
_props.cooldown-ms = 150;
};
"Mod+Alt+WheelScrollUp" = {
move-column-to-workspace-up = { };
_props.cooldown-ms = 150;
};
# Mouse column navigation and movement # Mouse column navigation and movement
"Mod+WheelScrollRight".focus-column-or-monitor-right = { }; "Mod+WheelScrollRight".focus-column-or-monitor-right = { };
+2 -11
View File
@@ -1,10 +1,5 @@
{ { pkgs }:
lib, pkgs.writeShellApplication {
pkgs,
}:
rec {
scripts = {
swapMonitorWorkspaces = pkgs.writeShellApplication {
name = "niri-shortcut-swap-monitor-workspaces"; name = "niri-shortcut-swap-monitor-workspaces";
runtimeInputs = [ runtimeInputs = [
pkgs.jq pkgs.jq
@@ -39,8 +34,4 @@ rec {
niri msg action focus-workspace "$target_workspace_idx" niri msg action focus-workspace "$target_workspace_idx"
niri msg action move-workspace-to-monitor "$focused_output" niri msg action move-workspace-to-monitor "$focused_output"
''; '';
};
};
commands = lib.mapAttrs (_: lib.getExe) scripts;
} }
+3 -8
View File
@@ -29,12 +29,7 @@ in
}: }:
let let
palette = kanagawa.palette; palette = kanagawa.palette;
shortcuts = import ./_shortcuts.nix { swapMonitorWorkspaces = import ./_shortcuts.nix { inherit pkgs; };
inherit
lib
pkgs
;
};
machine = config.facts.machine; machine = config.facts.machine;
in in
{ {
@@ -47,7 +42,7 @@ in
brightnessctl brightnessctl
wireplumber wireplumber
] ]
++ lib.attrValues shortcuts.scripts; ++ [ swapMonitorWorkspaces ];
}; };
dconf.settings."org/gnome/desktop/interface".color-scheme = "prefer-dark"; dconf.settings."org/gnome/desktop/interface".color-scheme = "prefer-dark";
@@ -140,7 +135,7 @@ in
launcher launcher
terminal terminal
; ;
shortcutCommands = shortcuts.commands; swapMonitorWorkspaces = lib.getExe swapMonitorWorkspaces;
}; };
}; };
}; };
+1 -4
View File
@@ -3,15 +3,12 @@ let
account = config.facts.account; account = config.facts.account;
in in
{ {
flake.modules.nixos.nix = flake.modules.nixos.nix = {
{ ... }:
{
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;
nix = { nix = {
gc.automatic = true; gc.automatic = true;
optimise.automatic = true; optimise.automatic = true;
registry.nixpkgs.flake = inputs.nixpkgs;
channel.enable = false; channel.enable = false;
settings = { settings = {
+1 -13
View File
@@ -61,17 +61,7 @@
}; };
}; };
desktop_widgets = { desktop_widgets.enabled = false;
enabled = false;
schema_version = 2;
widget_order = [ ];
grid = {
cell_size = 16;
major_interval = 4;
visible = true;
};
widget = { };
};
location.address = "Meterik, The Netherlands"; location.address = "Meterik, The Netherlands";
@@ -98,7 +88,6 @@
font_family = "Lexend"; font_family = "Lexend";
polkit_agent = true; polkit_agent = true;
screen_time_enabled = true; screen_time_enabled = true;
settings_show_advanced = true;
animation.speed = 1.5; animation.speed = 1.5;
screen_corners = { screen_corners = {
enabled = true; enabled = true;
@@ -127,7 +116,6 @@
widget = { widget = {
bluetooth.hide_when_no_connected_device = true; bluetooth.hide_when_no_connected_device = true;
clock.format = "{:%H:%M}";
media = { media = {
art_size = 90.0; art_size = 90.0;
max_length = 800; max_length = 800;
+1 -4
View File
@@ -12,16 +12,13 @@ in
pkgs, pkgs,
... ...
}: }:
let
pinentryPackage = pkgs.pinentry-gnome3;
in
{ {
programs.rbw = { programs.rbw = {
enable = true; enable = true;
settings = { settings = {
base_url = facts.services.vaultwarden.origin; base_url = facts.services.vaultwarden.origin;
email = account.emails.${account.primaryEmail}.address; email = account.emails.${account.primaryEmail}.address;
pinentry = pinentryPackage; pinentry = pkgs.pinentry-gnome3;
}; };
}; };
}; };
+1 -14
View File
@@ -21,7 +21,6 @@ in
{ {
config, config,
lib, lib,
pkgs,
... ...
}: }:
let let
@@ -31,7 +30,6 @@ in
... ...
}: }:
{ {
enable = true;
inherit address; inherit address;
realName = account.realName; realName = account.realName;
userName = address; userName = address;
@@ -50,7 +48,6 @@ in
... ...
}: }:
{ {
enable = true;
inherit address; inherit address;
realName = account.realName; realName = account.realName;
userName = address; userName = address;
@@ -59,13 +56,11 @@ in
authentication = "plain"; authentication = "plain";
host = "taylor.mxrouting.net"; host = "taylor.mxrouting.net";
port = 993; port = 993;
tls.enable = true;
}; };
smtp = { smtp = {
authentication = "plain"; authentication = "plain";
host = "taylor.mxrouting.net"; host = "taylor.mxrouting.net";
port = 465; port = 465;
tls.enable = true;
}; };
}; };
mkEmailAccount = mkEmailAccount =
@@ -103,9 +98,6 @@ in
pkgs, pkgs,
... ...
}: }:
let
calendarsPath = "${config.xdg.dataHome}/calendars";
in
{ {
imports = [ homeModules.sops ]; imports = [ homeModules.sops ];
@@ -136,16 +128,11 @@ in
}; };
accounts.calendar = { accounts.calendar = {
basePath = calendarsPath; basePath = "${config.xdg.dataHome}/calendars";
accounts.radicale = { accounts.radicale = {
primary = true; primary = true;
primaryCollection = "personal"; primaryCollection = "personal";
local = {
type = "filesystem";
fileExt = ".ics";
};
remote = { remote = {
url = "${facts.services.radicale.origin}/"; url = "${facts.services.radicale.origin}/";
type = "caldav"; type = "caldav";
+1 -4
View File
@@ -1,12 +1,9 @@
{ config, ... }: { config, ... }:
let
homeModules = config.flake.modules.homeManager;
in
{ {
flake.modules.nixos.podman = flake.modules.nixos.podman =
{ lib, pkgs, ... }: { lib, pkgs, ... }:
{ {
home-manager.sharedModules = [ homeModules.podman ]; home-manager.sharedModules = [ config.flake.modules.homeManager.podman ];
virtualisation.podman.enable = true; virtualisation.podman.enable = true;
virtualisation.containers.containersConf.settings.engine.compose_providers = [ virtualisation.containers.containersConf.settings.engine.compose_providers = [
+1 -3
View File
@@ -7,9 +7,7 @@
printing = { printing = {
enable = true; enable = true;
browsed.enable = true; browsed.enable = true;
drivers = with pkgs; [ drivers = [ pkgs.cnijfilter2 ];
cnijfilter2
];
}; };
avahi = { avahi = {
+1 -3
View File
@@ -13,9 +13,7 @@
theme = "${ theme = "${
pkgs.sddm-astronaut.override { embeddedTheme = "purple_leaves"; } pkgs.sddm-astronaut.override { embeddedTheme = "purple_leaves"; }
}/share/sddm/themes/sddm-astronaut-theme"; }/share/sddm/themes/sddm-astronaut-theme";
extraPackages = with pkgs; [ extraPackages = [ pkgs.kdePackages.qtmultimedia ];
kdePackages.qtmultimedia
];
settings.Theme = { settings.Theme = {
CursorTheme = "phinger-cursors-light"; CursorTheme = "phinger-cursors-light";
CursorSize = 24; CursorSize = 24;
+3 -7
View File
@@ -1,21 +1,17 @@
{ config, ... }: { config, ... }:
let
service = config.facts.services.actual;
nixosModules = config.flake.modules.nixos;
in
{ {
flake.modules.nixos.actual = { flake.modules.nixos.actual = {
imports = [ nixosModules.caddy ]; imports = [ config.flake.modules.nixos.caddy ];
services.actual = { services.actual = {
enable = true; enable = true;
openFirewall = false;
settings = { settings = {
hostname = "127.0.0.1"; hostname = "127.0.0.1";
port = 3000; port = 3000;
}; };
}; };
services.caddy.virtualHosts.${service.domain}.extraConfig = "reverse_proxy 127.0.0.1:3000"; services.caddy.virtualHosts.${config.facts.services.actual.domain}.extraConfig =
"reverse_proxy 127.0.0.1:3000";
}; };
} }
-3
View File
@@ -19,9 +19,6 @@ in
ROOT_URL = "${service.origin}/"; ROOT_URL = "${service.origin}/";
HTTP_PORT = 3001; HTTP_PORT = 3001;
HTTP_ADDR = "127.0.0.1"; HTTP_ADDR = "127.0.0.1";
START_SSH_SERVER = false;
SSH_PORT = 22;
}; };
service.DISABLE_REGISTRATION = true; service.DISABLE_REGISTRATION = true;
+4 -8
View File
@@ -1,10 +1,8 @@
{ config, lib, ... }: { config, lib, ... }:
let let
account = config.facts.account; account = config.facts.account;
personalPublicKeys = personalPublicKeys = map (machine: machine.sshKeys.personal.publicKey) (
machines: lib.filter (machine: machine.sshKeys ? personal) (builtins.attrValues config.facts.machines)
map (machine: machine.sshKeys.personal.publicKey) (
lib.filter (machine: machine.sshKeys ? personal) (builtins.attrValues machines)
); );
in in
{ {
@@ -15,9 +13,7 @@ in
}; };
}; };
flake.modules.nixos.openssh = flake.modules.nixos.openssh = {
{ ... }:
{
services.openssh = { services.openssh = {
enable = true; enable = true;
settings = { settings = {
@@ -27,6 +23,6 @@ in
}; };
}; };
users.users.${account.name}.openssh.authorizedKeys.keys = personalPublicKeys config.facts.machines; users.users.${account.name}.openssh.authorizedKeys.keys = personalPublicKeys;
}; };
} }
+2 -9
View File
@@ -1,11 +1,7 @@
{ config, ... }: { config, ... }:
let
service = config.facts.services.radicale;
nixosModules = config.flake.modules.nixos;
in
{ {
flake.modules.nixos.radicale = { flake.modules.nixos.radicale = {
imports = [ nixosModules.caddy ]; imports = [ config.flake.modules.nixos.caddy ];
services.radicale = { services.radicale = {
enable = true; enable = true;
@@ -17,15 +13,12 @@ in
htpasswd_filename = "/var/lib/radicale/users"; htpasswd_filename = "/var/lib/radicale/users";
htpasswd_encryption = "bcrypt"; htpasswd_encryption = "bcrypt";
}; };
storage.filesystem_folder = "/var/lib/radicale/collections";
}; };
}; };
services.caddy.virtualHosts.${service.domain}.extraConfig = '' services.caddy.virtualHosts.${config.facts.services.radicale.domain}.extraConfig = ''
reverse_proxy 127.0.0.1:5232 { reverse_proxy 127.0.0.1:5232 {
header_up X-Script-Name / header_up X-Script-Name /
header_up X-Forwarded-For {remote}
header_up X-Remote-User {http.auth.user.id} header_up X-Remote-User {http.auth.user.id}
} }
''; '';
@@ -1,11 +1,10 @@
{ config, ... }: { config, ... }:
let let
service = config.facts.services.vaultwarden; service = config.facts.services.vaultwarden;
nixosModules = config.flake.modules.nixos;
in in
{ {
flake.modules.nixos.vaultwarden = { flake.modules.nixos.vaultwarden = {
imports = [ nixosModules.caddy ]; imports = [ config.flake.modules.nixos.caddy ];
services.vaultwarden = { services.vaultwarden = {
enable = true; enable = true;
@@ -1,222 +0,0 @@
{ ... }:
{
flake.modules.nixos.vps-insights =
{
config,
lib,
pkgs,
...
}:
let
localAddress = "127.0.0.1";
grafanaDataDir = config.services.grafana.dataDir;
lokiDataDir = config.services.loki.dataDir;
lokiUrl = "http://${localAddress}:3100";
prometheusUrl = "http://${localAddress}:9090";
hasCaddy = config.services.caddy.enable;
hasOpenSSH = config.services.openssh.enable;
in
{
environment = {
etc."alloy/config.alloy".text = ''
loki.relabel "journal" {
forward_to = []
rule {
source_labels = ["__journal__systemd_unit"]
target_label = "unit"
}
rule {
source_labels = ["__journal_syslog_identifier"]
target_label = "syslog_identifier"
}
rule {
source_labels = ["__journal_priority_keyword"]
target_label = "level"
}
}
loki.source.journal "system" {
forward_to = [loki.write.local.receiver]
relabel_rules = loki.relabel.journal.rules
max_age = "24h"
labels = {
host = "${config.networking.hostName}",
}
}
loki.write "local" {
endpoint {
url = "${lokiUrl}/loki/api/v1/push"
}
}
'';
systemPackages = with pkgs; [
goaccess
lynis
];
};
services = {
# Keep local system logs available for Loki and manual inspection.
journald.extraConfig = ''
Storage=persistent
SystemMaxUse=1G
MaxRetentionSec=30day
'';
# Detect and block common attacks against SSH and Caddy.
crowdsec = {
enable = true;
hub.collections = [ "crowdsecurity/linux" ] ++ lib.optional hasCaddy "crowdsecurity/caddy";
localConfig.acquisitions =
lib.optional hasOpenSSH {
source = "journalctl";
journalctl_filter = [ "_SYSTEMD_UNIT=sshd.service" ];
labels.type = "syslog";
}
++ lib.optional hasCaddy {
filenames = [ "/var/log/caddy/*.log" ];
labels.type = "caddy";
};
};
crowdsec-firewall-bouncer = {
enable = true;
registerBouncer.bouncerName = "${config.networking.hostName}-firewall-bouncer";
};
# Keep Grafana local and separate from Actual's port.
grafana = {
enable = true;
settings = {
analytics.reporting_enabled = false;
server = {
http_addr = localAddress;
http_port = 3002;
};
security = {
admin_password = "$__file{${grafanaDataDir}/admin-password}";
secret_key = "$__file{${grafanaDataDir}/secret-key}";
};
};
provision.datasources.settings = {
prune = true;
datasources = [
{
name = "Prometheus";
type = "prometheus";
uid = "prometheus";
url = prometheusUrl;
isDefault = true;
}
{
name = "Loki";
type = "loki";
uid = "loki";
url = lokiUrl;
}
];
};
};
# Store local logs in Loki and feed them from journald through Alloy.
loki = {
enable = true;
configuration = {
analytics.reporting_enabled = false;
auth_enabled = false;
server = {
http_listen_address = localAddress;
http_listen_port = 3100;
grpc_listen_address = localAddress;
grpc_listen_port = 9096;
};
common = {
path_prefix = lokiDataDir;
replication_factor = 1;
instance_interface_names = [ "lo" ];
ring = {
instance_addr = localAddress;
kvstore.store = "inmemory";
};
};
schema_config.configs = [
{
from = "2025-01-01";
store = "tsdb";
object_store = "filesystem";
schema = "v13";
index = {
prefix = "index_";
period = "24h";
};
}
];
storage_config.filesystem.directory = "${lokiDataDir}/chunks";
compactor = {
working_directory = "${lokiDataDir}/compactor";
retention_enabled = true;
delete_request_store = "filesystem";
};
limits_config.retention_period = "720h";
};
};
alloy = {
enable = true;
extraFlags = [ "--server.http.listen-addr=${localAddress}:12345" ];
};
# Collect basic VPS health metrics for Grafana.
prometheus = {
enable = true;
listenAddress = localAddress;
retentionTime = "30d";
scrapeConfigs = [
{
job_name = "prometheus";
static_configs = [
{
targets = [ "${localAddress}:9090" ];
}
];
}
{
job_name = "node";
static_configs = [
{
targets = [ "${localAddress}:9100" ];
}
];
}
];
exporters.node = {
enable = true;
listenAddress = localAddress;
};
};
};
systemd.services.grafana.preStart = ''
umask 077
if [ ! -s ${grafanaDataDir}/admin-password ]; then
${pkgs.openssl}/bin/openssl rand -base64 32 > ${grafanaDataDir}/admin-password
fi
if [ ! -s ${grafanaDataDir}/secret-key ]; then
${pkgs.openssl}/bin/openssl rand -hex 32 > ${grafanaDataDir}/secret-key
fi
'';
};
}
+4 -33
View File
@@ -22,7 +22,6 @@
enable = true; enable = true;
dotDir = "${config.xdg.configHome}/zsh"; dotDir = "${config.xdg.configHome}/zsh";
enableCompletion = true;
completionInit = '' completionInit = ''
autoload -U compinit autoload -U compinit
compinit -C compinit -C
@@ -50,16 +49,7 @@
historySubstringSearch.enable = true; historySubstringSearch.enable = true;
history = { history.path = "${config.xdg.dataHome}/zsh_history";
ignoreDups = true;
save = 10000;
size = 10000;
path = "${config.xdg.dataHome}/zsh_history";
};
profileExtra = lib.optionalString (config.home.sessionPath != [ ]) ''
export PATH="$PATH''${PATH:+:}${lib.concatStringsSep ":" config.home.sessionPath}"
'';
initContent = '' initContent = ''
source <(gardenctl rc zsh --no-completion) source <(gardenctl rc zsh --no-completion)
@@ -118,19 +108,8 @@
programs.starship = { programs.starship = {
enable = true; enable = true;
enableZshIntegration = true;
settings = { settings = {
add_newline = true; format = "$nix_shell$hostname$directory$git_branch$git_state$line_break$character";
format = lib.concatStrings [
"$nix_shell"
"$hostname"
"$directory"
"$git_branch"
"$git_state"
"$line_break"
"$character"
];
directory = { directory = {
truncation_length = 99; truncation_length = 99;
@@ -147,8 +126,6 @@
format = "([$state( $progress_current/$progress_total)]($style)) "; format = "([$state( $progress_current/$progress_total)]($style)) ";
style = "bright-black"; style = "bright-black";
}; };
line_break.disabled = false;
}; };
}; };
@@ -157,15 +134,9 @@
extraOptions = [ "--group-directories-first" ]; extraOptions = [ "--group-directories-first" ];
}; };
programs.zoxide = { programs.zoxide.enable = true;
enable = true;
enableZshIntegration = true;
};
programs.fzf = { programs.fzf.enable = true;
enable = true;
enableZshIntegration = true;
};
programs.bottom.enable = true; programs.bottom.enable = true;
+8 -14
View File
@@ -1,11 +1,14 @@
{ lib, config, ... }: { lib, config, ... }:
let let
kanagawa = import ./_kanagawa.nix;
homeModules = config.flake.modules.homeManager; homeModules = config.flake.modules.homeManager;
in
kittySingleInstance = {
pkgs: flake.modules.homeManager.terminal-kitty =
pkgs.symlinkJoin { { pkgs, ... }:
let
theme = import ./_kanagawa.nix;
palette = theme.palette;
kitty = pkgs.symlinkJoin {
name = "kitty-single"; name = "kitty-single";
paths = [ pkgs.kitty ]; paths = [ pkgs.kitty ];
meta = pkgs.kitty.meta; meta = pkgs.kitty.meta;
@@ -16,15 +19,6 @@ let
sed 's/^Exec=kitty/Exec=kitty -1/' ${pkgs.kitty}/share/applications/kitty.desktop > $out/share/applications/kitty.desktop sed 's/^Exec=kitty/Exec=kitty -1/' ${pkgs.kitty}/share/applications/kitty.desktop > $out/share/applications/kitty.desktop
''; '';
}; };
in
{
flake.modules.homeManager.terminal-kitty =
{ pkgs, ... }:
let
theme = kanagawa;
palette = theme.palette;
kitty = kittySingleInstance pkgs;
in in
{ {
programs.kitty = { programs.kitty = {
+4 -15
View File
@@ -1,28 +1,21 @@
{ config, ... }: { config, ... }:
let let
hmModules = config.flake.modules.homeManager;
cursor = { cursor = {
name = "phinger-cursors-light"; name = "phinger-cursors-light";
packageName = "phinger-cursors";
size = 24; size = 24;
}; };
mkCursor = pkgs: cursor // { package = pkgs.${cursor.packageName}; };
in in
{ {
flake.modules.nixos.theme = flake.modules.nixos.theme =
{ pkgs, ... }: { pkgs, ... }:
let
cursorTheme = mkCursor pkgs;
in
{ {
home-manager.sharedModules = [ hmModules.theme ]; home-manager.sharedModules = [ config.flake.modules.homeManager.theme ];
environment.systemPackages = [ cursorTheme.package ]; environment.systemPackages = [ pkgs.phinger-cursors ];
}; };
flake.modules.homeManager.theme = flake.modules.homeManager.theme =
{ pkgs, ... }: { pkgs, ... }:
let let
cursorTheme = mkCursor pkgs;
version = "unstable-2025-10-23"; version = "unstable-2025-10-23";
src = pkgs.fetchFromGitHub { src = pkgs.fetchFromGitHub {
owner = "Fausto-Korpsvart"; owner = "Fausto-Korpsvart";
@@ -52,13 +45,9 @@ in
}; };
in in
{ {
home.pointerCursor = { home.pointerCursor = cursor // {
enable = true; enable = true;
inherit (cursorTheme) package = pkgs.phinger-cursors;
name
package
size
;
dotIcons.enable = false; dotIcons.enable = false;
gtk.enable = true; gtk.enable = true;
}; };
+3 -12
View File
@@ -1,7 +1,4 @@
{ config, ... }: { config, ... }:
let
homeModules = config.flake.modules.homeManager;
in
{ {
flake.modules.homeManager.transcription = flake.modules.homeManager.transcription =
{ {
@@ -111,7 +108,6 @@ in
"TRANSCRIBE_DIARIZATION_MODEL=${diarizationModel}" "TRANSCRIBE_DIARIZATION_MODEL=${diarizationModel}"
"TRANSCRIBE_DIARIZATION_MODEL_NAME=${diarizationModelName}" "TRANSCRIBE_DIARIZATION_MODEL_NAME=${diarizationModelName}"
"TRANSCRIBE_DIARIZATION_MODEL_REVISION=${diarizationModelRevision}" "TRANSCRIBE_DIARIZATION_MODEL_REVISION=${diarizationModelRevision}"
"TRANSCRIBE_SETTLE_SECONDS=300"
"TRANSCRIBE_SYNC_ROOT=${home}/sync" "TRANSCRIBE_SYNC_ROOT=${home}/sync"
]; ];
Restart = "on-failure"; Restart = "on-failure";
@@ -124,18 +120,13 @@ in
systemd.user.paths.transcribe-recordings = { systemd.user.paths.transcribe-recordings = {
Unit.Description = "Watch for synchronized recordings"; Unit.Description = "Watch for synchronized recordings";
Path = { Path.PathChanged = "${home}/sync/recordings/inbox";
PathChanged = "${home}/sync/recordings/inbox";
Unit = "transcribe-recordings.service";
};
Install.WantedBy = [ "paths.target" ]; Install.WantedBy = [ "paths.target" ];
}; };
}; };
flake.modules.nixos.transcription = flake.modules.nixos.transcription = {
{ ... }: home-manager.sharedModules = [ config.flake.modules.homeManager.transcription ];
{
home-manager.sharedModules = [ homeModules.transcription ];
}; };
} }
+1 -4
View File
@@ -1,10 +1,7 @@
{ config, inputs, ... }: { config, inputs, ... }:
let
homeModules = config.flake.modules.homeManager;
in
{ {
flake.modules.nixos.walker = { flake.modules.nixos.walker = {
home-manager.sharedModules = [ homeModules.walker ]; home-manager.sharedModules = [ config.flake.modules.homeManager.walker ];
nix.settings = { nix.settings = {
extra-substituters = [ extra-substituters = [
+1 -3
View File
@@ -1,7 +1,5 @@
{ {
flake.modules.nixos.yookr-dev = flake.modules.nixos.yookr-dev = {
{ config, ... }:
{
security.pki.certificateFiles = [ security.pki.certificateFiles = [
./yookr-dev/zenith-rootCA.pem ./yookr-dev/zenith-rootCA.pem
./yookr-dev/polaris-rootCA.pem ./yookr-dev/polaris-rootCA.pem
+1 -3
View File
@@ -266,9 +266,7 @@ in
machines machines
topology topology
; ;
services = lib.mapAttrs (_: service: { services = lib.mapAttrs (_: service: removeAttrs service [ "origin" ]) osConfig.facts.services;
inherit (service) scheme domain;
}) osConfig.facts.services;
}; };
}; };
}; };
+8 -8
View File
@@ -7,15 +7,10 @@
let let
mkHost = mkHost =
name: machine: name: machine:
{ pkgs, ... }: hostArgs@{ pkgs, ... }:
let let
account = config.facts.account; account = config.facts.account;
machineFacts = machine // { services = lib.mapAttrs (_: service: removeAttrs service [ "origin" ]) config.facts.services;
inherit name;
};
services = lib.mapAttrs (_: service: {
inherit (service) scheme domain;
}) config.facts.services;
in in
{ {
imports = [ imports = [
@@ -30,7 +25,9 @@ let
topology topology
; ;
inherit services; inherit services;
machine = machineFacts; machine = machine // {
inherit name;
};
}; };
networking.hostName = name; networking.hostName = name;
@@ -38,9 +35,12 @@ let
programs.zsh.enable = true; programs.zsh.enable = true;
sops.secrets.hashed-password.neededForUsers = true;
users.users.${account.name} = { users.users.${account.name} = {
isNormalUser = true; isNormalUser = true;
home = account.homeDirectory; home = account.homeDirectory;
hashedPasswordFile = hostArgs.config.sops.secrets.hashed-password.path;
extraGroups = [ extraGroups = [
"wheel" "wheel"
"networkmanager" "networkmanager"
-3
View File
@@ -10,7 +10,6 @@
type = "gpt"; type = "gpt";
partitions = { partitions = {
boot = { boot = {
name = "boot";
size = "1M"; size = "1M";
type = "EF02"; type = "EF02";
}; };
@@ -25,7 +24,6 @@
}; };
}; };
root = { root = {
name = "root";
size = "100%"; size = "100%";
content = { content = {
type = "lvm_pv"; type = "lvm_pv";
@@ -43,7 +41,6 @@
type = "filesystem"; type = "filesystem";
format = "ext4"; format = "ext4";
mountpoint = "/"; mountpoint = "/";
mountOptions = [ "defaults" ];
}; };
}; };
}; };
-7
View File
@@ -7,16 +7,9 @@
boot.initrd.availableKernelModules = [ boot.initrd.availableKernelModules = [
"ata_piix" "ata_piix"
"uhci_hcd" "uhci_hcd"
"virtio_pci"
"virtio_scsi"
"sd_mod" "sd_mod"
"sr_mod" "sr_mod"
]; ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
networking.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
} }
+2 -6
View File
@@ -1,7 +1,6 @@
{ config, ... }: { config, ... }:
let let
nixosModules = config.flake.modules.nixos; nixosModules = config.flake.modules.nixos;
account = config.facts.account;
in in
{ {
facts.machines.orion = { facts.machines.orion = {
@@ -11,9 +10,7 @@ in
stateVersion = "24.05"; stateVersion = "24.05";
}; };
flake.modules.nixos.orion = flake.modules.nixos.orion = {
{ ... }:
{
imports = [ imports = [
nixosModules.host-base nixosModules.host-base
@@ -22,7 +19,6 @@ in
nixosModules.caddy nixosModules.caddy
nixosModules.server-firewall nixosModules.server-firewall
nixosModules.sudo-ssh-agent-auth nixosModules.sudo-ssh-agent-auth
# nixosModules.vps-insights
nixosModules.vaultwarden nixosModules.vaultwarden
nixosModules.radicale nixosModules.radicale
nixosModules.actual nixosModules.actual
@@ -34,6 +30,6 @@ in
environment.enableAllTerminfo = true; environment.enableAllTerminfo = true;
users.users.${account.name}.linger = true; users.users.${config.facts.account.name}.linger = true;
}; };
} }
+1 -13
View File
@@ -1,9 +1,4 @@
{ { lib, modulesPath, ... }:
config,
lib,
modulesPath,
...
}:
{ {
imports = [ imports = [
(modulesPath + "/installer/scan/not-detected.nix") (modulesPath + "/installer/scan/not-detected.nix")
@@ -17,9 +12,7 @@
"usb_storage" "usb_storage"
"sd_mod" "sd_mod"
]; ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ]; boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
fileSystems."/" = { fileSystems."/" = {
device = "/dev/disk/by-uuid/bda7f8b9-2b3d-4190-8518-baa50490227e"; device = "/dev/disk/by-uuid/bda7f8b9-2b3d-4190-8518-baa50490227e";
@@ -35,10 +28,5 @@
]; ];
}; };
swapDevices = [ ];
networking.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
} }
+4 -12
View File
@@ -9,32 +9,24 @@ in
{ {
facts.machines.polaris = { facts.machines.polaris = {
system = "x86_64-linux"; system = "x86_64-linux";
sshKeys = { sshKeys.personal.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEVORk45HKkX7gaGGp90KsVyUy6t+fKhbWN/grjkf3cQ kiri@polaris";
personal.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEVORk45HKkX7gaGGp90KsVyUy6t+fKhbWN/grjkf3cQ kiri@polaris"; sshKeys.work.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM5DMV6EQzsscgEOE0912mNglUHTEl+LPnaWYjj0y57B kiri@polaris#work";
work.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM5DMV6EQzsscgEOE0912mNglUHTEl+LPnaWYjj0y57B kiri@polaris#work";
};
syncthingId = "6HBAKXB-DB3B4H2-BODCAXF-KD23H5W-6X5LGLC-ZJHZHLG-7U7YMGO-BB6IXQ3"; syncthingId = "6HBAKXB-DB3B4H2-BODCAXF-KD23H5W-6X5LGLC-ZJHZHLG-7U7YMGO-BB6IXQ3";
stateVersion = "24.05"; stateVersion = "24.05";
displays = { displays = {
"LG Electronics LG ULTRAGEAR 103NTYT8R290" = { "LG Electronics LG ULTRAGEAR 103NTYT8R290" = { };
x = 0;
y = 0;
};
"LG Electronics LG ULTRAGEAR 103NTJJ8R332" = { "LG Electronics LG ULTRAGEAR 103NTJJ8R332" = {
primary = true; primary = true;
x = 2560; x = 2560;
y = 0;
}; };
}; };
}; };
flake.modules.nixos.polaris = flake.modules.nixos.polaris = {
{ ... }:
{
imports = [ imports = [
nixosModules.workstation-base nixosModules.workstation-base
nixosModules.openssh nixosModules.openssh
-7
View File
@@ -10,9 +10,6 @@
"usb_storage" "usb_storage"
"sd_mod" "sd_mod"
]; ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
fileSystems."/" = { fileSystems."/" = {
device = "/dev/disk/by-uuid/6d8f6f33-c9d9-4e90-b496-d5b3ef5e9aeb"; device = "/dev/disk/by-uuid/6d8f6f33-c9d9-4e90-b496-d5b3ef5e9aeb";
@@ -28,9 +25,5 @@
]; ];
}; };
swapDevices = [ ];
networking.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
} }
+5 -11
View File
@@ -10,10 +10,8 @@ in
facts.machines.zenith = { facts.machines.zenith = {
system = "x86_64-linux"; system = "x86_64-linux";
sshKeys = { sshKeys.personal.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIQy4k04gU7UpjBgyUQ57kUwxOdt79LvMCiCekXZeZhd kiri@zenith";
personal.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIQy4k04gU7UpjBgyUQ57kUwxOdt79LvMCiCekXZeZhd kiri@zenith"; sshKeys.work.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIHJz5uHKm0/TiMNh/cmzrODHNZ8NgEEZe+47XnJwQGk kiri@zenith#work";
work.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIHJz5uHKm0/TiMNh/cmzrODHNZ8NgEEZe+47XnJwQGk kiri@zenith#work";
};
syncthingId = "5VMGBZJ-NNAVCLY-YWHCJLM-EJ3SCFC-EOGCECD-DGXC7EL-J5NXAAG-4JDEMQG"; syncthingId = "5VMGBZJ-NNAVCLY-YWHCJLM-EJ3SCFC-EOGCECD-DGXC7EL-J5NXAAG-4JDEMQG";
@@ -30,18 +28,14 @@ in
stateVersion = "24.05"; stateVersion = "24.05";
}; };
flake.modules.nixos.zenith = flake.modules.nixos.zenith = {
{ ... }:
{
imports = [ imports = [
nixosModules.workstation-base nixosModules.workstation-base
nixosModules.laptop-power nixosModules.laptop-power
{
hardware.enableRedistributableFirmware = true;
services.fwupd.enable = true;
}
./_hardware.nix ./_hardware.nix
inputs.nixos-hardware.nixosModules.lenovo-yoga-7-14ARH7-amdgpu inputs.nixos-hardware.nixosModules.lenovo-yoga-7-14ARH7-amdgpu
]; ];
services.fwupd.enable = true;
}; };
} }
+1 -1
View File
@@ -14,7 +14,7 @@ in
inputs.home-manager.nixosModules.home-manager inputs.home-manager.nixosModules.home-manager
nixosModules.nix nixosModules.nix
nixosModules.locale nixosModules.locale
nixosModules.sops-password nixosModules.sops
]; ];
home-manager = { home-manager = {
+1 -4
View File
@@ -2,7 +2,6 @@
let let
nixosModules = config.flake.modules.nixos; nixosModules = config.flake.modules.nixos;
homeModules = config.flake.modules.homeManager; homeModules = config.flake.modules.homeManager;
account = config.facts.account;
in in
{ {
flake.modules.nixos.workstation-base = { flake.modules.nixos.workstation-base = {
@@ -32,12 +31,10 @@ in
nixosModules.yookr-dev nixosModules.yookr-dev
]; ];
services.dbus.implementation = "broker";
programs.nix-ld.enable = true; programs.nix-ld.enable = true;
environment.localBinInPath = true; environment.localBinInPath = true;
home-manager.users.${account.name}.imports = [ home-manager.users.${config.facts.account.name}.imports = [
homeModules.workstation-base homeModules.workstation-base
]; ];
}; };
+3 -7
View File
@@ -1,9 +1,5 @@
keys:
- &admin age122w85pqj508ukv0rd388mahecgfckmpgnsgz0zcyec37ljae2epsdnvxpl
- &orion age1upsxyevxdv3xax8jhnffclryuve70k63cjrca69ffds4h2m3nvuql59ufw
creation_rules: creation_rules:
- path_regex: secrets.yaml$ - path_regex: secrets.yaml$
key_groups: age: >-
- age: age122w85pqj508ukv0rd388mahecgfckmpgnsgz0zcyec37ljae2epsdnvxpl,
- *admin age1upsxyevxdv3xax8jhnffclryuve70k63cjrca69ffds4h2m3nvuql59ufw
- *orion
-16
View File
@@ -1,16 +0,0 @@
{ config, ... }:
let
account = config.facts.account;
nixosModules = config.flake.modules.nixos;
in
{
flake.modules.nixos.sops-password =
{ config, ... }:
{
imports = [ nixosModules.sops ];
sops.secrets.hashed-password.neededForUsers = true;
users.users.${account.name}.hashedPasswordFile = config.sops.secrets.hashed-password.path;
};
}
+2 -7
View File
@@ -15,12 +15,7 @@ in
sops.defaultSopsFile = ./secrets.yaml; sops.defaultSopsFile = ./secrets.yaml;
}; };
flake.modules.nixos.sops-admin-key-file = flake.modules.nixos.sops-admin-key-file = {
{ lib, ... }:
let
adminKeyDir = builtins.dirOf sopsAdminKeyPath;
in
{
imports = [ nixosModules.sops ]; imports = [ nixosModules.sops ];
home-manager.sharedModules = [ hmModules.sops ]; home-manager.sharedModules = [ hmModules.sops ];
@@ -28,7 +23,7 @@ in
sops.age.keyFile = sopsAdminKeyPath; sops.age.keyFile = sopsAdminKeyPath;
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"d ${adminKeyDir} 0750 root wheel -" "d ${builtins.dirOf sopsAdminKeyPath} 0750 root wheel -"
"z ${sopsAdminKeyPath} 0640 root wheel -" "z ${sopsAdminKeyPath} 0640 root wheel -"
]; ];
}; };