From f164d8b76e4a564c1430e8284541d30d1b8e6862 Mon Sep 17 00:00:00 2001 From: Jelle Spreeuwenberg Date: Sat, 19 Sep 2026 13:28:22 +0200 Subject: [PATCH] Establish repository foundation and shared work environment --- AGENTS.md | 34 ++ README.md | 100 +++++- docs/constellation-work.md | 301 ------------------ docs/constellation.md | 98 ------ docs/current-work.md | 78 +++++ docs/design.md | 107 +++++++ justfile | 7 + modules/capabilities/dev-tools.nix | 31 -- modules/capabilities/git.nix | 143 ++++----- modules/capabilities/yookr-dev.nix | 12 + modules/capabilities/yookr-dev/_packages.nix | 30 ++ .../capabilities/yookr-dev/environment.nix | 34 ++ modules/profiles/work.nix | 22 ++ modules/profiles/workstation-base.nix | 3 +- scripts/validate.py | 94 ++++++ 15 files changed, 571 insertions(+), 523 deletions(-) create mode 100644 AGENTS.md delete mode 100644 docs/constellation-work.md delete mode 100644 docs/constellation.md create mode 100644 docs/current-work.md create mode 100644 docs/design.md create mode 100644 justfile create mode 100644 modules/capabilities/yookr-dev/_packages.nix create mode 100644 modules/capabilities/yookr-dev/environment.nix create mode 100644 modules/profiles/work.nix create mode 100644 scripts/validate.py diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..edcd1fe --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,34 @@ +# Working in this repository + +Read [the design](docs/design.md) and [current work](docs/current-work.md) before +continuing the foundation pass. [README.md](README.md) maps configuration owners +and maintenance entry points. Global instructions remain applicable. + +- Keep design discussion and shared decisions in the main conversation. Discuss + two to four related choices with evidence, a recommendation, and alternatives. + Ask before changes to architecture, terminology, ownership, dependencies, + persistent data, user-visible behavior, or durable workflows. Implement routine + consequences of an accepted batch without repeated confirmation. +- Use subagents only for bounded, independent work when coverage or context benefits + justify it. No fixed team or delegation pattern is required. +- The pass covers existing or clearly intended capabilities. Consult relevant parts + of `~/sync/notes/setup-todo.md` as evidence, not authorization or a backlog. + Do not edit that note without an explicit request. +- Preserve unrelated changes. Coherent local commits are allowed for agreed batches. + Do not push or make other remote changes without explicit approval. System + activation remains user-owned. +- Do not read secret values for discovery, source the workbench credential-loading + environment, or access live company systems with write authority. Test writes need + task-specific authority. Moving credentials or synchronizing company content + requires agreement; ordinary repository work does not authorize either. +- Follow the user's testing policy: automated tests only when explicitly requested + or covered by an established repository policy. A test file alone does not establish + a general policy. Use the smallest relevant review, build, or manual check. +- Use `just validate` for a completed configuration batch, or name affected hosts + for iteration. Read the short result first; inspect saved logs only for failures + or a concrete unresolved concern. Documentation-only edits need no system build. +- Replace superseded state in `docs/current-work.md`; keep durable rationale in + `docs/design.md` and operational detail beside its owner. Use Git for chronology. + Remove temporary state when it no longer serves a purpose. +- Do not declare the pass complete unilaterally. Present the resulting model, + changes, evidence, and deferrals, and ask whether another area needs attention. diff --git a/README.md b/README.md index 566ac3f..bad14cc 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,90 @@ # NixOS configuration -Host identity and topology live in `modules/hosts//default.nix`; shared account, -service, external-device, and server-topology facts live in `modules/facts.nix`. +One shared daily environment on Polaris and Zenith, with shared services on Orion. +NixOS and Home Manager are composed through flake-parts and import-tree. -- Add a package used by both workstations to the relevant Home Manager capability, - or to `workstation-apps` in `modules/capabilities/desktop-defaults.nix` when it needs - no configuration. -- Add a host-specific capability to that host's import list in - `modules/hosts//default.nix`. -- Change the selected terminal or editor by replacing the corresponding - `primary-terminal-*` import in `modules/profiles/workstation-base.nix` or - `primary-editor-*` import in `modules/profiles/host-base.nix`. -- Keep native theme settings with the application. Explicit Kanagawa color mappings - may import `modules/capabilities/_kanagawa.nix`. +## Start here -Common commands: +- [Design](docs/design.md): vocabulary, ownership, and rationale. +- [Agent instructions](AGENTS.md): repository collaboration boundaries. +- [Current work](docs/current-work.md): accepted scope, open decisions, and next step. + +## Configuration map + +| Owner | Location | Responsibility | +| --- | --- | --- | +| Hosts | `modules/hosts//` | Identity, hardware, machine-specific selection | +| Profiles | `modules/profiles/` | Combinations of capabilities | +| Capabilities | `modules/capabilities/` | Behavior and integration across NixOS and Home Manager | +| Facts | `modules/facts.nix` | Shared identity and topology; host facts are declared with hosts | +| Composition | `flake.nix`, `modules/flake-parts.nix` | Inputs, discovery, host construction | +| Secrets | `modules/secrets/` | Existing SOPS declarations and encrypted data | + +Discovery makes named modules available; imports select them. Underscore-prefixed +helpers and directories are excluded from automatic discovery and imported explicitly. +Add shared behavior to its capability and select it through the relevant profile. +Keep machine differences in hosts. A capability can own system and user modules. + +The primary editor is selected in `host-base.nix`, the primary terminal in +`workstation-base.nix`. `modules/capabilities/theme.nix` selects the complete theme; +Kanagawa lives in `modules/capabilities/_themes/kanagawa.nix`. Appearance belongs +there; interaction and layout belong with the capability. + +## Work environment (provisional) + +Both workstations import `modules/profiles/work.nix`: company network/development +integration, Garden tools, and work Git settings. This is a trial boundary, not +an independent workstation variant. SharePoint remains Zenith-only for now. + +The tools-only environment is owned by +`modules/capabilities/yookr-dev/environment.nix`. It shares Garden definitions in +`_packages.nix` with installed tools and uses the system's nixpkgs pin: ```console -nix fmt -nix flake check --no-build -nh os switch -sops edit modules/secrets/secrets.yaml +nix develop --quiet ~/.config/nixos#workbench --command bash --norc ``` + +This does not clear inherited credentials; start from a clean environment for +agent work. It does not load secrets, mount storage, or install Python dependencies. +Workspace `.envrc` files select it; the workbench's human credential and mount +steps remain workspace-owned. Requests, queries, analysis, and `uv.lock` stay there. +After loader changes, the user must review and re-allow the changed `.envrc` files +with direnv. No loader is sourced as part of validation. + +## Maintenance + +Run from this repository: + +```console +just validate # format, evaluate, build every host and the workbench +just validate polaris # same workflow, build only the named host(s) +``` + +The command includes current tracked files and non-ignored new files, respects +working-tree deletions, and never changes the Git index. It resolves the pinned +formatter from a temporary source snapshot, formats included Nix files in the +working tree, then refreshes the snapshot for evaluation and builds. Ignored +untracked files and `.git` are excluded. Avoid concurrent edits during validation: +results describe the snapshot, not subsequent changes. + +Output is limited to stage results and a short excerpt on failure. Full logs remain +in the printed `/tmp/nixos-validate-*` directory; source snapshots are removed on +exit. Failed stages stop the command. Inputs are not updated, no result links are +created, and nothing is activated. Host names are discovered from the flake. + +`just` and Python are already declared for both workstations. The implementation +is `scripts/validate.py`; the justfile is only its entry point. The formatter output +currently targets this repository's sole system, `x86_64-linux`. + +Use full validation after a configuration batch and targeted hosts during iteration. +Documentation-only edits need review, local link checks, and `git diff --check`, +not system builds. Evaluation, build, activation, and confirmed behavior are +separate evidence. Outside this wrapper, ordinary Git-backed flake commands still +require intended new files to be added to Git. + +The user activates with `nh os switch`. Edit encrypted data with +`sops edit modules/secrets/secrets.yaml`; keep plaintext out of Nix expressions +and the store. No new credential-delivery design is selected yet. + +Historical material under `docs/.archive/` is evidence, not current instructions +or a backlog. Consult it only for relevant unresolved questions. diff --git a/docs/constellation-work.md b/docs/constellation-work.md deleted file mode 100644 index 693b9df..0000000 --- a/docs/constellation-work.md +++ /dev/null @@ -1,301 +0,0 @@ -# Constellation: working state - -Read [the design](constellation.md) and this file to resume. Keep this file short: -replace superseded state rather than appending a chronological log. - -## Working agreement - -- Explore the philosophy through concrete use, discuss meaningful choices, then - implement a small coherent change. Curiosity and alternative designs are welcome; - an idea need not prove a defect before we explore it. -- Write succinctly without losing important details. Distinguish accepted choices, - proposals and observations. Ask when intent would change the design; resolve - routine implementation details autonomously. Infer opportunities from the user's - philosophy and actual use; contribute design ideas beyond literal backlog items. - Continue to the next natural step when the user could only usefully say "continue"; - pause for material choices or missing authority, not routine progress. -- Minimize input stops: plan a coherent batch of autonomous work and gather the - material inputs needed for the next batch together. Avoid serial question/action - loops. When pausing, state exactly what input is needed and what it will decide; - otherwise continue with the next logical step rather than ending with an offer. -- The main agent owns alignment, design and integration and may do small work. - Use subagents for bounded independent investigations or implementation with clear - ownership. Begin with at most two workers; avoid overlapping writes, duplicate - investigations and mandatory delegation of trivial tasks. -- Give workers the question, relevant paths, constraints and expected result. - Prefer a compact brief and fresh context when sufficient; retain relevant prior - context for follow-ups. Return conclusions, uncertainty and evidence pointers, - not raw logs. Astra/Sol orchestration is a candidate to learn from, not a fixed rule. -- Read only what can affect the current decision. Use quiet commands and bounded - output; keep large logs in `/tmp` and inspect relevant failures. Do not repeatedly - check settled facts. Consult current documentation for consequential or unfamiliar - interfaces when needed for the task. -- No tests unless explicitly requested. Keep other verification proportional; - report only what was actually checked. Avoid turning small changes into campaigns. -- Keep durable decisions in the design and current questions/next steps here. - Add an area-specific document only when an active topic outgrows this arrangement. - No separate backlog, scratchpad, audit ledger or agent-role files for now. - -## Boundaries - -Repository remotes are read-only. Only the user activates system generations. -Agents must not have write access to live company systems; test writes require -task-specific authority and care. Do not read secret values for layout discovery. -Current exploration does not authorize relocating credentials, synchronizing -company content or deleting existing material. Existing local changes are preserved. - -## Git baseline — 2026-09-09 - -The accumulated NixOS changes are recorded in focused local commits on -`chore/workflow-system`: AI tooling/inputs, editor ergonomics, query persistence -and target context, workspace navigation, Notes, SharePoint, Zellij and TeamViewer. -The original long handoff and archived plans are preserved in commit `4e3a3ac`; -use Git history for chronology. `nvim.log` is retained locally and ignored. -Workbench changes are committed on its local `main`: `eb8da6a` organizes HTTP/SQL -material and `063157c` archives helpers and documents native tools. The ordinary -company-root files are outside either repository and remain user-owned. -This cleanup preserves implementation behavior; a commit does not establish runtime -acceptance. Keep subsequent implementation and its documentation in the same -coherent commit before starting unrelated work. - -## Implemented state - -- **Places:** `~/yookr` has ordinary `.envrc` and `AGENTS.md` files; its environment - loads workbench tools only. Workbench is at `~/yookr/workbench`, company projects - at `~/yookr/projects`, and personal groups at `~/projects/{own,ext,forks}`. - Direct moves retained archives/worktrees; all three linked dashboard worktrees - were repaired. Nix owns tools/integration, not these workspace files. -- **Navigation:** Walker enters Yookr or a repository; Git identity follows the - company root; XDG projects points to `~/projects`. Notes has a desktop entry - opening `~/sync/notes` without creating a daily note. Existing note commands, - project search and Niri daily-note behavior remain. -- **Synchronization:** Bruno collections were previously moved to Trash on Zenith - and their shared Syncthing entry removed. Other devices' files were untouched. - Syncthing was subsequently observed running. Notes and credentials remain in place. -- **API/database work:** workbench `http/` groups requests by service with shared - authentication; `databases/` groups reusable queries by database. Private HTTP - environment files are ignored at any depth. Dadbod working queries persist under - `stdpath('data')/db_ui/working`; write them there without promoting them into Git. - Saved-query storage also contains connection state and must stay outside Git. - Lualine uses native Kulala environment and Dadbod connection/timing components. - These indicators are not access controls. -- **Editor ergonomics:** word-boundary wrapping, count-aware displayed-line `j/k` - in ordinary normal/visual buffers, and default Escape behavior to leave insert - mode. The Neovim smoke tests were removed; earlier smoke-check evidence is in - the archived plan. -- **Agents:** Herdr is selected; agent-deck and tmux remain installed alongside it. - Codex has agent-deck notifications and hooks enabled. Shared Context7/NixOS skills - live under `.agents/skills`. - Zellij is also declared on workstations. Further project workflow work is deferred. -- **SharePoint:** Zenith declares an hourly service syncing `company-sharepoint:` - into `~/.local/share/yookr-reference/sharepoint/Algemeen` and - `company-sharepoint-projecten:` into `Projecten`. Each invocation has - `--max-delete 100`; new files use `UMask=0077`. The latest implementation only - checks for `rclone.conf`; the old environment-file gate is gone. Archived setup - instructions describe an earlier version. Consent, remote configuration and - synchronization state were not inspected during this cleanup. -- **Remote support:** Zenith enables TeamViewer. Temporary Xorg/Xfce settings are - comments only. The purpose beyond remote support and runtime acceptance were - not recorded; preserve the change without calling it a settled design. -- **Inputs:** accumulated lock updates are retained, including an older Elephant - revision (`376ee71c` replacing `23f37238`). No rollback rationale was recorded. - -## Activation and verification - -Earlier handoffs report user activation of the relocated XDG/Git/Walker paths and -Dadbod working-query persistence. Notes and the newer target statusline were last -recorded as pending activation. Only Zenith's filesystem was relocated; other hosts -need their own migration before adopting the shared paths. Reopen shells or -recreate path-dependent development environments as needed; no compatibility links -or cache rewrites were introduced. - -For this cleanup, `nix flake check --no-build --quiet` and diff whitespace checking -passed in both NixOS and workbench. NixOS emitted the expected dirty-tree and unknown -`modules` warnings; workbench emitted only the dirty-tree warning. -No builds, tests, activation, credential reads or company-service calls were -performed. Active-generation differences were not checked; earlier activation -reports are not a fresh comparison with this baseline. - -## Open choices and next discussion - -Current focus: look and feel/theming, informed by `~/sync/notes/setup-todo.md`. -The user accepted complete Nix theme modules with arbitrary plugin/configuration -behavior, rather than Stylix or a palette-only abstraction. The single import in -`modules/capabilities/theme.nix` selects `./_themes/kanagawa.nix`. `_themes` is -excluded from automatic module discovery, so unselected themes are not loaded. -Kanagawa owns GTK/icons/cursor, Qt integration, Kitty, Niri colors, Noctalia -palette/font/wallpaper, Herdr colors, the native Neovim plugin and its setup, and -Limine, Noctalia Greeter and Plymouth appearance. Only Kanagawa is implemented -so far; switching means selecting another module and rebuilding, not a live -theme picker. -Walker shares a compact layout with theme-provided colors: reduced padding, -24px icons, clearer secondary text, and a smaller minimum window height. -Limine's terminal background is fully transparent with no edge gradient so it -does not obscure the wallpaper. Runtime appearance needs user verification. -Noctalia Greeter uses the same tracked green-leaves wallpaper, Lexend font, -Phinger cursor and dark Kanagawa palette as the desktop. Its logo and scheme -selector are hidden while power controls remain available. The pinned greeter -does not expose Niri's pointer acceleration or touchpad settings, so those retain -the greeter compositor defaults. Plymouth's spinner is retained; redesigning its -low-frame-rate animation remains open. Runtime appearance needs user verification. -Verification: flake evaluation and both workstation builds passed. A headless -check of the built Neovim confirmed Kanagawa Wave, inactive-window dimming and -the custom gutter setting. Rendered Herdr TOML, Walker XML and Limine settings -also passed targeted checks. Targeted evaluation confirmed identical greeter -settings for both workstations, and `nix flake check --no-build --quiet` passed -for the greeter change. No system activation or live desktop changes performed. - -Dictation: plain local transcription is selected; rambling cleanup is deferred. -Handy preferences are application-owned again: the Nix settings generator and -startup reset are removed. The existing settings file is left untouched. -Nix installs Handy/wtype and runs Handy as a graphical-session user service. -Super+D launches a short-lived Python/evdev listener: hold to record, release D -or Super to finish. Both keys must be on the same keyboard. Niri owns the shortcut -and disables repeat; no persistent keyboard logger or exclusive input grab runs. -A udev uaccess rule grants the active local user access to keyboard event devices. -Super+Shift+D toggles; Super+Ctrl+D cancels. Both stop any active hold listener -before issuing their Handy command, preventing a later release from restarting -recording. Begin push-to-talk from idle: Handy exposes toggles, not idempotent -start/stop commands. Niri 26.04 itself has no key-release bindings. -Noctalia suppresses microphone activity popups/indicators; camera and screen-sharing -indications remain. Automatic paste and live capture need interactive checks. - -Hang investigation, parked at the user's request: a debugger capture confirmed -Handy's recorder waits for another audio chunk before processing its stop command. -When capture stalls, stopping can therefore hang indefinitely; the transcription -worker was idle. Bluetooth transport failures accompanied the hangs, and the user -reported dictation working after disconnecting the Sony WH-1000XM6. The user wants -its microphone when connected. Keeping the headset in a microphone-enabled HFP -profile was proposed to avoid repeated profile transitions, but was not applied -or verified; the reconnect attempt timed out. No source or Bluetooth configuration -was changed. CPU and Vulkan batch inference both worked; changing inference backend -is not an established fix. Resume this investigation only if requested. -Synthetic listener checks cover release during startup, repeat, modifier-first -release, device disconnect and cancellation, without live input/audio access. -System activation and an interactive hold/release trial remain user-owned. -Formatting, flake evaluation, both workstation builds, synthetic listener checks -and whitespace checking passed. No live input listener or microphone was started. - -The focus is the broader environment, not further API/database polish. Candidate -areas remain places/ownership, navigation/interaction, human/agent collaboration, -and system operation/maintenance; these are discussion areas, not a work queue. - -Herdr's configured keys are accepted: the user reports liking them in practice. -Niri workspace grouping stays informal while the user discovers what fits; Herdr -provides enough structure for now. No workspace rules or fixed layout are needed. -The user reopened Herdr configuration on 2026-09-10 for direct shortcuts and less -interaction friction, using supported configuration only. Layout presets and -launchers are explicitly unwanted; arrange panes manually. Agent evaluation is -explicitly deferred. Do not resume replacement-tool comparisons or ask for an -evaluation task/budget as a prerequisite for this work. - -Read-only inspection found existing Niri overview, workspace movement and tabbed -columns; Kitty has no declared project/session organization. Installed versions: -Kitty 0.48.2, Niri 26.04, tmux 3.7c, Zellij 0.45.1 and Herdr 0.9.0. -Herdr's installed default config confirms remappable navigation and agent jumps. -Current [Herdr keyboard docs](https://herdr.dev/docs/keyboard/) describe direct -chords; [Kitty sessions](https://sw.kovidgoyal.net/kitty/sessions/) supply native -session creation/switching. Session layouts and window overviews do not themselves -provide authoritative agent attention states. tmux detach keeps processes alive; -Zellij resurrection recreates layouts/commands, not process memory. None of these -capabilities was exercised interactively; no sessions were inspected or changed. - -Deferred agent workflow proposal (not accepted or a Niri workspace requirement): -one lead conversation owns each coherent change. Use bounded subagents when work -is independent, with explicit ownership and evidence returned to the lead. Separate -worktrees for independent writing efforts, one integration owner. Keep enduring -repository rules in AGENTS.md, current goals/acceptance in task prompts, and durable -decisions/current state in existing project docs. No fixed agent team is selected. - -Herdr configuration is now declared in `modules/capabilities/ai.nix`, including -the existing Kanagawa theme, workspace-sorted agent panel and disabled toasts. -Home Manager owns `~/.config/herdr/config.toml` after activation; maintain these -preferences in Nix. Its configured `.bak` mechanism preserves the previous regular -file. Direct Ctrl+Alt bindings supplement the existing Ctrl+B prefix shortcuts: - -| Ctrl+Alt + | Action | -| --- | --- | -| H / J / K / L | Focus pane left / down / up / right | -| U / I | Next / previous workspace | -| O / G | Workspace picker / goto picker | -| P / N | Previous / next agent | -| [ / ] | Previous / next tab | -| Enter | New tab | -| V / Minus | Split right / down | -| F | Zoom pane | -| Q | Detach, leaving the session running | -| Shift+H / J / K / L | Swap panes left / down / up / right (Shift on each) | -| Shift+arrows | Resize pane directly | -| R | Resize mode | -| Tab / Shift+Tab | Next / previous pane | -| Backspace | Return to last pane, including across tabs/spaces | -| X / Shift+X | Close pane / tab | -| Shift+P / Shift+A | Rename pane / tab | -| Shift+[ / Shift+] | Move tab earlier / later | -| Shift+Enter | New space | -| Shift+W / Shift+D | Rename / close space | -| W / Shift+B | Open / create worktree | -| Y / E | Copy mode / open scrollback in Neovim | -| Shift+G | Lazygit popup | -| B | Toggle sidebar | -| / | Keybinding help | -| S / Shift+R | Settings / reload configuration | - -Inside workspace navigation, U/I moves down/up and H/J/K/L retains pane navigation. -Niri and Kitty bindings are unchanged. Direct number bindings are removed because -the user finds them unergonomic; built-in prefix shortcuts remain available. - -Use one space per project or worktree, tabs for different activities, and panes -for tools needed together. Start Neovim in a shell, split right with Ctrl+Alt+V -for an agent shell, and optionally split that pane down with Ctrl+Alt+Minus for -tests or a server. Choose and launch the agent manually. No preset is installed. -Ctrl+Alt+Shift+G opens lazygit at 90% width/height in the focused pane's working -directory. Quit lazygit to return; the popup owns input until its command exits. - -Tabs now open immediately with generated names; rename them only when useful. -New spaces already skip naming, and new panes inherit the current directory by -default. Workspace-close confirmation remains enabled to protect running work. -Direct resizing, previous/next agent navigation, last-pane switching, copy mode, -and Neovim scrollback access avoid extra navigation or mode-entry steps. -No documented native setting or CLI action opens Goto directly in search mode; -that limitation remains under the configuration-only constraint. No input macros, -plugins, or package patches are added. - -After activating Nix, reload Herdr with Ctrl+B then Shift+R; subsequent reloads -can use Ctrl+Alt+Shift+R. The expanded shortcuts require interactive verification -through Kitty, including popup working directory, pane swapping/resizing, and -scrollback opening. The earlier bindings were reported working by the user. -Verification (2026-09-10): formatting, flake evaluation, Polaris and Zenith no-link -system builds, and whitespace checks passed. Both hosts generate identical Herdr -TOML, accepted by installed Herdr 0.9.0's `config check`. No activation or live -Herdr session changes were performed. -The earlier activation installed the Herdr config and preserved `config.toml.bak`, -but Home Manager later timed out (below). - -Activation fix (2026-09-09): Home Manager's service switch stopped and restarted -the running SharePoint oneshot, then waited until Home Manager's five-minute start -timeout. Service-state inspection identified SharePoint as the sole pending user -job; its oneshot start timeout is unlimited. This was not a Herdr file collision. -Set `Unit.X-SwitchMethod = "keep-old"` in `sharepoint-reference.nix` so activation -leaves an in-flight sync running; subsequent timer runs use the updated unit. -Verified against pinned Home Manager/sd-switch source. Formatting, flake evaluation, -Zenith build and whitespace checks passed; the focused sd-switch dry run reported -`Keep active sharepoint-reference-sync.service`, with no restart planned. No live -units were changed and no company content or credentials were read. The user must -retry activation to verify the full switch; sync health/content completeness were -not investigated. - -Mail search remains unresolved. The workbench's custom mail reader, inventory -pilot, justfile and old README are archived; native Garden/kubectl/K9s now own the -documented operations. No replacement mail helper or semantic index is selected; -raw mailbox grep is not assumed sufficient. No new ingestion is authorized. - -Human credential delivery remains both test/live via environment variables with -ordinary editor startup. Workbench `.envrc` supplies aggregate credentials; agents -must not source it. Separate read-only agent credentials and access isolation are -explicitly deferred, not prerequisites for unrelated work. Do not resume the -rejected per-connection launchers or per-selection decryption proposal. - -Notes placement and further company material organization remain open. Work is -mostly project-based and notes mostly meetings; do not impose investigation -folders, outer project containers or company-wide editor search without a need. diff --git a/docs/constellation.md b/docs/constellation.md deleted file mode 100644 index 01eda68..0000000 --- a/docs/constellation.md +++ /dev/null @@ -1,98 +0,0 @@ -# Constellation - -An evolving personal computing environment. This is the current design, starting -fresh with iteration two; decisions can change through discussion and use. -See [working state](constellation-work.md) for how we collaborate and where to resume. - -## Philosophy - -Build a quiet, keyboard-driven, text-oriented environment from understandable -tools that compose naturally. Keep information accessible through standard formats -and interfaces, so humans and agents can work with the same underlying resources. -Organize files and workflows consistently, making it intuitive where to look, -where things belong and how to proceed. Let structure guide everyday work while -leaving room for exploration and exceptions. Use graphical applications where -they serve the task well. - -Make the usual path obvious and exceptions understandable. Consistency should -reduce remembering and decision-making without requiring identical bindings or -fixed procedures everywhere. A terminal interface alone does not establish fit. - -Nix configures installed tools and system integration. Working directories, -checkouts, workspace `.envrc` files and context documents are ordinary user-owned -files, maintained directly rather than generated by Home Manager. A workbench may -use a flake to declare its tools without Nix owning the surrounding workspace. - -## What that means so far - -- Themes are complete Nix modules, not just palettes. Select one through - `modules/capabilities/theme.nix`; it may install native theme plugins, configure - their behavior and choose assets for each application. Kanagawa is the first - implementation. Shared interaction/layout choices stay in capability modules. - Stylix is not selected: preserving theme-specific behavior matters more than - forcing applications through one generated color scheme. -- Neovim, Niri, Walker and the terminal feel right: direct, keyboard-driven - interaction with useful connections to the rest of the environment. -- Use Handy for local plain dictation, mainly English with occasional Dutch. - Use native Wayland automatic paste and optional live text with a streaming model. - Handy owns its preferences, models and recordings. Nix installs and starts it - and supplies desktop shortcuts, including a short-lived push-to-talk listener. Cleanup - through Codex, a cloud API or a local language model is deferred. -- Outlook is acceptable as an application, but its interaction and integration - do not fit as naturally. Accessible mail for tools and agents matters separately - from choosing a mail reader. Preserve meaning and source context, not just text. -- Herdr is the chosen agent workspace manager: its spaces, running-agent overview - and attention states fit. Its configured keybindings are accepted in daily use. - Use Ctrl+Alt for direct Herdr actions alongside Super for Niri and Ctrl navigation - in Neovim; retain Herdr's prefix bindings. Agent-deck's startup issues and other - session-manager alternatives are not the current focus. -- Keep Niri workspaces flexible. Herdr supplies enough structure that no strict - desktop grouping, fixed workspace names or project-to-workspace mapping is - needed now. Let actual use inform any later organization. -- Further Herdr configuration, agent workflows and project management are deferred - until the user chooses to revisit them. Existing configuration remains in use. - Alignment, maintainability and human attention matter - alongside speed/cost. Agent-workflow evaluation is deferred; no fixed delegation - pattern has been selected. -- Repositories should contain code and documentation needed by that code, not - become containers for general notes and company material destined for Git remotes. -- Company work includes repositories, reference material, notes, credentials and - investigation tools. Its organization must accommodate those different roles. -- Actual work is mostly project-based; notes are primarily meeting notes so far. - Do not impose a topic/investigation workflow or a fixed future note taxonomy. -- Yookr workbench is an investigation environment, not a product repository. - Its placement and versioning can be considered separately. -- Accepted direction: workbench supplies the environment at the company-workspace - root. Its reusable requests live in `workbench/http/` and queries in - `workbench/databases/`, grouped by service/database; - the company root need not itself be a Git repository. -- Expose native capabilities and useful context rather than a menu of predetermined - investigations. The custom inventory and mail helpers are archived; native - Garden/kubectl/K9s own the documented Kubernetes workflow. Mail retrieval and - broader system access remain exploratory. -- Accepted interaction direction: find HTTP requests by service; start database - exploration from a named connection. Keep targets visible and use the existing - execution vocabulary. Working queries should survive restart without automatically - becoming reusable tracked material. Company-wide Neovim search is deferred: no - concrete use case currently justifies adding it. -- Human credential delivery uses environment variables with both test and live - credentials available. Keep ordinary editor startup; no per-connection launcher - or on-demand decryption workflow. Agent authority is a separate concern. -- Separate read-only company credentials for agents are the eventual direction. - Access isolation is explicitly deferred: new credentials cannot be made now. - Existing agent access boundaries remain in force; this does not block unrelated - design and organization work. - -The company root is `~/yookr`, with repositories in `~/yookr/projects` and the -cross-project workbench in `~/yookr/workbench`. Personal/non-company repositories -live under `~/projects/{own,ext,forks}`. Projects currently mostly live in their -repositories; no extra project containers are needed. Meeting notes remain in -`~/sync/notes`. Remaining layout and credential locations are still being explored. - -## Sources - -`~/sync/notes/setup-todo.md` is the idea inbox, not a committed backlog. -Existing implementation is the starting system, not a fixed future architecture. -Documents under [.archive/](.archive/) are historical evidence; consult only the -relevant parts when needed. Their plans, priorities and old approvals do not define -this iteration. Preserve existing work while reconsidering its design. diff --git a/docs/current-work.md b/docs/current-work.md new file mode 100644 index 0000000..a8a6d99 --- /dev/null +++ b/docs/current-work.md @@ -0,0 +1,78 @@ +# Current work + +## Scope and accepted decisions + +Foundation pass started 2026-09-19. Improve existing capabilities through discussion +and coherent implementation batches; completion requires the user's agreement. + +Accepted: retain host/profile/capability/facts composition and explicit ownership; +Polaris and Zenith have identical intended use; use the four-file harness. Validation +uses a Git-ignore-aware snapshot without index changes, prioritizing low token cost. +The work profile is explicitly provisional. Consolidate the workbench environment +only, using the shared NixOS nixpkgs pin; authored material and the Python lock stay +in the writable workbench. See [design](design.md) for durable boundaries. + +## Completed and evidence + +- Replaced Constellation with the four-file harness after preserving useful knowledge; + older archives remain historical sources. Survey began from clean `3c022ad` on Polaris. +- Implemented `just validate` with optional host arguments. Full runs format, evaluate, + build all hosts, and now build the workbench shell. Logs are brief; details remain + in `/tmp`. The index is untouched by validation. Both prerequisites were already + declared on workstations. +- Added provisional `modules/profiles/work.nix`, imported by both workstations. + It selects existing VPN/development integration, Garden tools, and work Git settings. + Generic development/editor tools remain shared; SharePoint remains Zenith-only. +- Garden definitions now have one owner in `yookr-dev/_packages.nix`, used by both + installed tools and `devShells.x86_64-linux.workbench`. Preserved the workbench's + tools and Python variables; its nixpkgs pin now follows the system as agreed. +- Updated `~/yookr/.envrc` and `~/yookr/workbench/.envrc` to select the shared + environment; updated workbench README/instructions and removed its redundant + flake/lock. Its credential/mount body is unchanged. Requests, queries, analysis, + Python lock, existing changes, and generated data remain in place. Pre-change + migration files: `/tmp/workbench-environment-before-6k4dwzuo`. +- Full validation passed formatting, evaluation, Orion/Polaris/Zenith builds, and + workbench shell build on 2026-09-19. Logs: `/tmp/nixos-validate-4ojjb2q7`. + The existing unknown `modules` output warning remains. Whitespace review passed + in both repositories; loader changes were compared against pre-change copies. + No tests, activation, credential loading, mounts, or remote changes. + +## Next step and user-owned actions + +User reviews and re-allows the two changed `.envrc` files with direnv, and activates +NixOS when ready. Do not run those loaders as an agent. The workbench retains +unrelated uncommitted analysis, documentation, and mount-helper work; do not commit +that work as part of this pass. Replicate the two loader changes and removal of the +old environment definition on Zenith; only Polaris's workspace was changed here. + +Evaluate the provisional work boundary in practice, then discuss secret delivery. +Current constraints: human workbench entry exposes both test/live credentials and +starts read-only object-storage mounts; the tools-only environment does neither +but does not erase inherited credentials. SOPS is already used, with different +key locations for system and company material. No credential values were read. +Clarify encrypted-file/key distribution across machines before proposing changes. +Devenv and secretspec remain candidates, not dependencies. No new live authority +or per-connection launchers are authorized. + +## Questions to carry into relevant areas + +- Desktop integration: terminal/launcher interfaces have inconsistent consumers; + Mailspring coexists with Thunderbird account declarations; Herdr coexists with + agent-deck notifications and multiple session tools. Determine purpose before + removing anything. The disabled nixos-maintainer skill's source remains. +- Cross-host behavior: earlier workspace migration was reported only on Zenith. + Verify Polaris paths before assuming equivalent filesystem state. TeamViewer and + SharePoint currently exist only on Zenith despite identical intended use. +- Runtime evidence: earlier theme/greeter, expanded Herdr shortcuts, Handy + hold/release/paste, and SharePoint activation checks were incomplete. Old build + results do not establish current behavior. Ask in the relevant area. +- Handy hang investigation remains parked unless requested: a prior debugger + capture found the recorder waiting for an audio chunk before handling stop; + Bluetooth failures coincided, and disconnecting Sony WH-1000XM6 helped. The user + wants that microphone. Persistent HFP was proposed but never applied or verified; + inference-backend changes were not an established fix. +- Agent evaluation, new mail ingestion, and company credential isolation were + deferred. Secret-management discussion reopens design, not live access. + +The inbox is not a backlog. Detailed old handoffs remain in Git before this batch; +consult them only when their evidence affects a current choice. diff --git a/docs/design.md b/docs/design.md new file mode 100644 index 0000000..d75773c --- /dev/null +++ b/docs/design.md @@ -0,0 +1,107 @@ +# Repository design + +These are working decisions, subject to evidence from implementation and use. + +## Purpose and vocabulary + +Build a quiet, keyboard-driven, text-oriented environment from understandable tools +and standard interfaces. Humans and agents should use the same underlying resources. +Use graphical applications where they fit. Consistency should reduce remembering, +without forcing identical bindings or procedures everywhere. + +- **Host:** machine identity, hardware, and differences in selection. +- **Profile:** a deliberate combination of capabilities. +- **Capability:** behavior and necessary integration, potentially spanning NixOS + and Home Manager, multiple applications, or several files. +- **Facts:** genuinely shared identity and topology, not a bucket for all settings. +- **Theme:** application-specific appearance, including native plugins and assets. + Interaction and layout remain with capabilities. Kanagawa is selected; themes + need not fit a palette-only abstraction. + +Retain the composition model. Introduce shared interfaces only for actual consumers. +Capability boundaries matter more than one-module-per-file uniformity. + +Polaris and Zenith have identical intended use and should offer the same daily +experience. Hardware remains host-specific. The workstation +profile now imports a provisional `work` profile selecting company network/development +integration, Garden tools, and work Git identity/token integration. Try this boundary +in practice before treating it as settled. Generic editor and development tools +remain shared. Existing host-only services do not +automatically establish a desired difference between machines. + +## Ownership + +Nix owns reproducible installation, integration, and deliberately selected settings. +Applications own credentials, sessions, content, and preferences intentionally left +interactive. Record feature-specific exceptions where they are implemented. + +Current layout: company root `~/yookr`, projects in `~/yookr/projects`, workbench in +`~/yookr/workbench`; personal projects in `~/projects/{own,ext,forks}`; meeting notes +in `~/sync/notes`. Workspace files remain user-owned. This repository owns the tools-only +`devShells.x86_64-linux.workbench` environment and shares Garden package definitions +with the workstation installation. It follows the same nixpkgs pin as the system. +Requests, queries, analysis, findings, private context, runtime data, and the Python +project lock remain in the writable workbench. The ordinary workspace `.envrc` files +select this environment; credential loading and mounts remain unchanged pending +a separate design decision. + +Repositories should hold their code and necessary documentation, not absorb general +company material. Work is mostly project-based and notes mostly meetings; no new +note taxonomy, outer project containers, or company-wide editor search is assumed. + +## Existing experience and constraints + +These useful decisions and operational cautions are carried forward from earlier +work. They are starting evidence, not fresh runtime verification. + +- Niri workspaces remain flexible. Herdr supplies agent workspace organization; + choose agents and arrange panes manually. Preset layouts and launchers were + unwanted. Super belongs to Niri, Ctrl+Alt to direct Herdr actions, and Ctrl + navigation to Neovim; native prefix bindings remain available. Earlier Herdr + bindings were accepted in use; later additions lack a fresh acceptance report. + Configuration and help own the binding reference, rather than a duplicate table. +- Herdr configuration is Home Manager-owned in `modules/capabilities/ai.nix`. + After activation reload with Ctrl+Alt+Shift+R (or Ctrl+B then Shift+R). The existing + Home Manager `.bak` mechanism can preserve previous regular files. No fixed agent + team or agent-workflow evaluation program is selected. +- Handy provides local plain dictation, mainly English with occasional Dutch. + Preferences, models, and recordings remain application-owned. Start push-to-talk + from idle; D and Super must be on the same keyboard. A short-lived listener handles + releases around Handy's toggle commands. Nix grants active-user keyboard event + access for this integration. Cloud/LLM cleanup is deferred. +- Native Garden/kubectl/K9s replaced custom workbench inventory helpers. Reusable + HTTP material is grouped by service and SQL by database. Named targets and editor + indicators help orientation but are not access controls. Mail retrieval remains + unresolved; no new ingestion or semantic index was selected. +- Dadbod working queries persist under `stdpath('data')/db_ui/working`; saving there + does not promote work to reusable repository material. Saved-query storage also + contains connection state and must stay out of Git. See + `modules/capabilities/neovim/lua-config/lua/plugins/database.lua`. +- Prior human credential delivery exposes both test/live environments with normal + editor startup. Per-connection launchers and per-selection decryption were + rejected. Separate read-only agent credentials were desired but unavailable; + isolation was deferred. Credential delivery is reopened for discussion, while + existing access boundaries remain in force. +- SharePoint sync is currently Zenith-only. Its hourly unit uses two configured + rclone company remotes, private file permissions, and a deletion limit per + invocation. `X-SwitchMethod = "keep-old"` prevents activation restarting and waiting + for an in-flight sync; later timer runs use the updated unit. See + `modules/capabilities/sharepoint-reference.nix`. Remote setup, sync completeness, + and the full activation fix need confirmation when this area is reviewed. + +## Knowledge and maintenance + +README owns navigation and commands; AGENTS owns repository collaboration rules; +this document owns durable reasoning; current-work owns resumable state. Keep +implementation details near code and use Git instead of chronological handoffs. +Archived plans are historical sources, not authority. + +A unified validation command is wanted to prevent repeated agent improvisation. +It must include new and modified working-tree files without changing the Git index, +format, evaluate, and build every host for a full configuration batch. Targeted +iteration and lightweight documentation checks are accepted. The priority is low +agent token cost, not minimum elapsed time: one invocation, concise results, and +saved failure logs. Use `just validate` backed by one repository script. Validate a temporary snapshot +of tracked and non-ignored new files using working-tree contents; leave the index +untouched. Git ignores remain the file-inclusion boundary. Build +success does not establish activation or interactive acceptance. diff --git a/justfile b/justfile new file mode 100644 index 0000000..1b9ed63 --- /dev/null +++ b/justfile @@ -0,0 +1,7 @@ +# List repository maintenance commands. +default: + @just --list + +# Format, evaluate, and build all hosts and the workbench; optionally name hosts for iteration. +validate *hosts: + @python3 scripts/validate.py {{hosts}} diff --git a/modules/capabilities/dev-tools.nix b/modules/capabilities/dev-tools.nix index 96f2ded..a3bcb77 100644 --- a/modules/capabilities/dev-tools.nix +++ b/modules/capabilities/dev-tools.nix @@ -1,35 +1,6 @@ { flake.modules.homeManager.dev-tools = { config, pkgs, ... }: - let - gardenctl = pkgs.stdenvNoCC.mkDerivation { - pname = "gardenctl"; - version = "2.14.0"; - src = pkgs.fetchurl { - url = "https://github.com/gardener/gardenctl-v2/releases/download/v2.14.0/gardenctl_v2_linux_amd64"; - hash = "sha256-JCZ0eJLgYbd2ZMCCWFlhX4vOhHKZL40oR2a3EZ3i4Q8="; - }; - dontUnpack = true; - installPhase = '' - install -Dm755 "$src" "$out/bin/gardenctl" - install -d "$out/share/zsh/site-functions" - "$out/bin/gardenctl" completion zsh > "$out/share/zsh/site-functions/_gardenctl" - ''; - }; - gardenlogin = pkgs.stdenvNoCC.mkDerivation { - pname = "gardenlogin"; - version = "0.10.0"; - src = pkgs.fetchurl { - url = "https://github.com/gardener/gardenlogin/releases/download/v0.10.0/gardenlogin_linux_amd64"; - hash = "sha256-X4nHiZ29Mbnhxv/wlyD6PxX1KxzaX2R27N7Muu/gnH8="; - }; - dontUnpack = true; - installPhase = '' - install -Dm755 "$src" "$out/bin/gardenlogin" - ln -s gardenlogin "$out/bin/kubectl-gardenlogin" - ''; - }; - in { home.sessionVariables.CARGO_HOME = "${config.xdg.dataHome}/cargo"; home.packages = with pkgs; [ @@ -40,8 +11,6 @@ scc visidata just - gardenctl - gardenlogin ]; programs.lazygit.enable = true; diff --git a/modules/capabilities/git.nix b/modules/capabilities/git.nix index 28d7eb7..e535ff3 100644 --- a/modules/capabilities/git.nix +++ b/modules/capabilities/git.nix @@ -1,63 +1,40 @@ { config, lib, ... }: let account = config.facts.account; + mkScope = + config: scope: + let + email = account.emails.${scope}.address; + key = config.facts.machine.sshKeys.${scope} or null; + hasSigningKey = key != null; + privateKeyPath = + if hasSigningKey && key.privateKeyPath != null then key.privateKeyPath else "~/.ssh/id_${scope}"; + in + { + allowedSigners = lib.optional hasSigningKey "${email} ${key.publicKey}"; + git = { + user = { + name = account.realName; + inherit email; + } + // lib.optionalAttrs hasSigningKey { + signingKey = "${privateKeyPath}.pub"; + }; + } + // lib.optionalAttrs hasSigningKey { + core.sshCommand = "ssh -i ${privateKeyPath} -o IdentitiesOnly=yes"; + gpg.ssh.allowedSignersFile = "${config.xdg.configHome}/git/allowed_signers"; + }; + }; in { flake.modules.homeManager.git = - { - config, - pkgs, - ... - }: + { config, ... }: let - machine = config.facts.machine; - allowedSignersFile = "${config.xdg.configHome}/git/allowed_signers"; - githubTokenFile = "${config.xdg.configHome}/yookr/github-read-only.token"; - - gh = pkgs.symlinkJoin { - name = "gh-with-read-only-token-${pkgs.gh.version}"; - paths = [ pkgs.gh ]; - nativeBuildInputs = [ pkgs.makeWrapper ]; - meta.mainProgram = "gh"; - postBuild = '' - wrapProgram "$out/bin/gh" \ - --run 'if [[ -z "''${GH_TOKEN:-}" && -r "${githubTokenFile}" ]]; then export GH_TOKEN="$(< "${githubTokenFile}")"; fi' - ''; - }; - - mkScope = - scope: - let - email = account.emails.${scope}.address; - key = machine.sshKeys.${scope} or null; - hasSigningKey = key != null; - privateKeyPath = - if hasSigningKey && key.privateKeyPath != null then key.privateKeyPath else "~/.ssh/id_${scope}"; - in - { - allowedSigners = lib.optional hasSigningKey "${email} ${key.publicKey}"; - git = { - user = { - name = account.realName; - inherit email; - } - // lib.optionalAttrs hasSigningKey { - signingKey = "${privateKeyPath}.pub"; - }; - } - // lib.optionalAttrs hasSigningKey { - core.sshCommand = "ssh -i ${privateKeyPath} -o IdentitiesOnly=yes"; - gpg.ssh.allowedSignersFile = allowedSignersFile; - }; - }; - - personal = mkScope "personal"; - work = mkScope "work"; + personal = mkScope config "personal"; in { - xdg.configFile."git/allowed_signers".text = lib.concatStringsSep "\n" ( - personal.allowedSigners ++ work.allowedSigners ++ [ "" ] - ); + xdg.configFile."git/allowed_signers".text = lib.concatStringsSep "\n" personal.allowedSigners; programs.git = { enable = true; @@ -73,34 +50,52 @@ in email = account.emails.personal.address; }; }; - includes = [ - { - condition = "gitdir:${account.nixosConfigurationPath}/"; - contents = personal.git; - } - { - condition = "gitdir:${config.home.homeDirectory}/yookr/"; - contents = work.git; - } - { - condition = "gitdir:${config.xdg.userDirs.projects}/own/"; - contents = personal.git; - } - { - condition = "gitdir:${config.xdg.userDirs.projects}/ext/"; - contents = personal.git; - } - { - condition = "gitdir:${config.xdg.userDirs.projects}/forks/"; - contents = personal.git; - } - ]; + includes = + map + (path: { + condition = "gitdir:${path}/"; + contents = personal.git; + }) + [ + account.nixosConfigurationPath + "${config.xdg.userDirs.projects}/own" + "${config.xdg.userDirs.projects}/ext" + "${config.xdg.userDirs.projects}/forks" + ]; }; programs.gh = { enable = true; - package = gh; settings.git_protocol = "ssh"; }; }; + + flake.modules.homeManager.work-git = + { config, pkgs, ... }: + let + work = mkScope config "work"; + githubTokenFile = "${config.xdg.configHome}/yookr/github-read-only.token"; + gh = pkgs.symlinkJoin { + name = "gh-with-read-only-token-${pkgs.gh.version}"; + paths = [ pkgs.gh ]; + nativeBuildInputs = [ pkgs.makeWrapper ]; + meta.mainProgram = "gh"; + postBuild = '' + wrapProgram "$out/bin/gh" \ + --run 'if [[ -z "''${GH_TOKEN:-}" && -r "${githubTokenFile}" ]]; then export GH_TOKEN="$(< "${githubTokenFile}")"; fi' + ''; + }; + in + { + xdg.configFile."git/allowed_signers".text = lib.mkAfter ( + lib.concatStringsSep "\n" work.allowedSigners + ); + programs.git.includes = [ + { + condition = "gitdir:${config.home.homeDirectory}/yookr/"; + contents = work.git; + } + ]; + programs.gh.package = gh; + }; } diff --git a/modules/capabilities/yookr-dev.nix b/modules/capabilities/yookr-dev.nix index f671267..8b28dc7 100644 --- a/modules/capabilities/yookr-dev.nix +++ b/modules/capabilities/yookr-dev.nix @@ -1,4 +1,16 @@ { + flake.modules.homeManager.yookr-tools = + { pkgs, ... }: + let + tools = import ./yookr-dev/_packages.nix { inherit pkgs; }; + in + { + home.packages = [ + tools.gardenctl + tools.gardenlogin + ]; + }; + flake.modules.nixos.yookr-dev = { security.pki.certificateFiles = [ ./yookr-dev/zenith-rootCA.pem diff --git a/modules/capabilities/yookr-dev/_packages.nix b/modules/capabilities/yookr-dev/_packages.nix new file mode 100644 index 0000000..ac20644 --- /dev/null +++ b/modules/capabilities/yookr-dev/_packages.nix @@ -0,0 +1,30 @@ +{ pkgs }: +{ + gardenctl = pkgs.stdenvNoCC.mkDerivation { + pname = "gardenctl"; + version = "2.14.0"; + src = pkgs.fetchurl { + url = "https://github.com/gardener/gardenctl-v2/releases/download/v2.14.0/gardenctl_v2_linux_amd64"; + hash = "sha256-JCZ0eJLgYbd2ZMCCWFlhX4vOhHKZL40oR2a3EZ3i4Q8="; + }; + dontUnpack = true; + installPhase = '' + install -Dm755 "$src" "$out/bin/gardenctl" + install -d "$out/share/zsh/site-functions" + "$out/bin/gardenctl" completion zsh > "$out/share/zsh/site-functions/_gardenctl" + ''; + }; + gardenlogin = pkgs.stdenvNoCC.mkDerivation { + pname = "gardenlogin"; + version = "0.10.0"; + src = pkgs.fetchurl { + url = "https://github.com/gardener/gardenlogin/releases/download/v0.10.0/gardenlogin_linux_amd64"; + hash = "sha256-X4nHiZ29Mbnhxv/wlyD6PxX1KxzaX2R27N7Muu/gnH8="; + }; + dontUnpack = true; + installPhase = '' + install -Dm755 "$src" "$out/bin/gardenlogin" + ln -s gardenlogin "$out/bin/kubectl-gardenlogin" + ''; + }; +} diff --git a/modules/capabilities/yookr-dev/environment.nix b/modules/capabilities/yookr-dev/environment.nix new file mode 100644 index 0000000..0d26fea --- /dev/null +++ b/modules/capabilities/yookr-dev/environment.nix @@ -0,0 +1,34 @@ +{ + perSystem = + { pkgs, ... }: + let + tools = import ./_packages.nix { inherit pkgs; }; + in + { + # Tooling only: no credential loading, mounts, or Python dependency installs. + devShells.workbench = pkgs.mkShell { + LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ + pkgs.stdenv.cc.cc.lib + pkgs.zlib + ]; + UV_PYTHON = "${pkgs.python314}/bin/python"; + UV_PYTHON_DOWNLOADS = "never"; + packages = [ + tools.gardenctl + tools.gardenlogin + ] + ++ (with pkgs; [ + k9s + kubectl + kubelogin-oidc + jq + just + python314 + rclone + sops + util-linux + uv + ]); + }; + }; +} diff --git a/modules/profiles/work.nix b/modules/profiles/work.nix new file mode 100644 index 0000000..0a8f9b4 --- /dev/null +++ b/modules/profiles/work.nix @@ -0,0 +1,22 @@ +{ config, ... }: +let + nixosModules = config.flake.modules.nixos; + homeModules = config.flake.modules.homeManager; +in +{ + # Provisional boundary: both workstations select the same company integration. + flake.modules.nixos.work = { + imports = [ + nixosModules.openvpn + nixosModules.yookr-dev + ]; + home-manager.sharedModules = [ homeModules.work ]; + }; + + flake.modules.homeManager.work = { + imports = [ + homeModules.yookr-tools + homeModules.work-git + ]; + }; +} diff --git a/modules/profiles/workstation-base.nix b/modules/profiles/workstation-base.nix index 90f11f8..0579f4e 100644 --- a/modules/profiles/workstation-base.nix +++ b/modules/profiles/workstation-base.nix @@ -27,9 +27,8 @@ in nixosModules.theme nixosModules.ai nixosModules.yunzi-b75-pro - nixosModules.openvpn nixosModules.walker - nixosModules.yookr-dev + nixosModules.work ]; programs.nix-ld.enable = true; diff --git a/scripts/validate.py b/scripts/validate.py new file mode 100644 index 0000000..b8540bb --- /dev/null +++ b/scripts/validate.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +"""Validate current repository files without changing the Git index.""" + +from collections import deque +import json +import os +from pathlib import Path +import shutil +import subprocess +import sys +import tempfile + +ROOT = Path(__file__).resolve().parent.parent + + +def main(): + os.chdir(ROOT) + run_dir = Path(tempfile.mkdtemp(prefix="nixos-validate-", dir="/tmp")) + print(f"Logs: {run_dir}", flush=True) + + def run(label, command): + log = run_dir / f"{label}.log" + print(f"{label} ...", flush=True) + with log.open("wb") as output: + result = subprocess.run(command, stdout=output, stderr=subprocess.STDOUT) + if result.returncode: + print(f"{label}: FAILED ({result.returncode}); {log}", flush=True) + with log.open(errors="replace") as output: + excerpt = "".join(deque(output, maxlen=40)) + print(excerpt[-12000:]) + raise SystemExit(result.returncode) + print(f"{label}: OK", flush=True) + + # Git supplies selection only: contents always come from the working tree. + paths = subprocess.check_output( + ["git", "ls-files", "--cached", "--others", "--exclude-standard", "-z"] + ).split(b"\0") + files = sorted({os.fsdecode(path) for path in paths if path}) + files = [name for name in files if os.path.lexists(ROOT / name)] + + with tempfile.TemporaryDirectory(prefix="nixos-source-", dir="/tmp") as temp: + source = Path(temp) / "source" + + def snapshot(): + if source.exists(): + shutil.rmtree(source) + source.mkdir() + for name in files: + original = ROOT / name + destination = source / name + destination.parent.mkdir(parents=True, exist_ok=True) + if original.is_symlink(): + destination.symlink_to(os.readlink(original)) + elif original.is_file(): + shutil.copy2(original, destination) + else: + raise RuntimeError(f"Unsupported source entry: {name}") + + snapshot() + flake = f"path:{source}" + nix = ["nix", "--quiet"] + locked = ["--no-update-lock-file"] + # The repository currently exposes only x86_64-linux outputs. + nix_files = [str(ROOT / name) for name in files if name.endswith(".nix")] + run("format", nix + ["run", *locked, f"{flake}#formatter.x86_64-linux", "--", *nix_files]) + snapshot() + run("check", nix + ["flake", "check", "--no-build", *locked, flake]) + hosts_file = run_dir / "hosts.json" + # Keep machine-readable output separate from Nix diagnostics. + with hosts_file.open("wb") as output, (run_dir / "hosts.log").open("wb") as errors: + result = subprocess.run( + nix + ["eval", *locked, "--json", f"{flake}#nixosConfigurations", "--apply", "builtins.attrNames"], + stdout=output, stderr=errors, + ) + if result.returncode: + print(f"Host discovery failed; see {run_dir / 'hosts.log'}") + raise SystemExit(result.returncode) + available = json.loads(hosts_file.read_text()) + hosts = list(dict.fromkeys(sys.argv[1:])) or available + unknown = set(hosts) - set(available) + if unknown: + raise RuntimeError(f"Unknown hosts: {', '.join(sorted(unknown))}") + for host in hosts: + run(f"build-{host}", nix + ["build", *locked, "--no-link", f"{flake}#nixosConfigurations.{host}.config.system.build.toplevel"]) + if not sys.argv[1:]: + run("build-workbench", nix + ["build", *locked, "--no-link", f"{flake}#devShells.x86_64-linux.workbench"]) + print(f"Validation passed: {', '.join(hosts)}. Logs: {run_dir}", flush=True) + + +if __name__ == "__main__": + try: + main() + except (OSError, ValueError, RuntimeError, subprocess.CalledProcessError) as error: + sys.exit(str(error))