Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a587f0ab71 | ||
|
|
93f1b191c8 | ||
|
|
a51ed76119 | ||
|
|
5bca7e176a | ||
|
|
684d3f5a75 | ||
|
|
99fca0f746 | ||
|
|
9b1e2fb447 | ||
|
|
9d2379bb3e | ||
|
|
7a650dc7cc | ||
|
|
bb613ac803 | ||
|
|
47362090c3 | ||
|
|
8c5773447e | ||
|
|
ae44982c65 | ||
|
|
c82c1bef9c | ||
|
|
39b2b1d24b | ||
|
|
0022a152e3 | ||
|
|
d9db12c4a5 | ||
|
|
4ac9d1108b | ||
|
|
0c151943de | ||
|
|
dcb8a5e66a | ||
|
|
bdb21a6d50 | ||
|
|
1da34d6232 | ||
|
|
19e0b7f13f |
+17
-9
@@ -186,6 +186,14 @@ fleet's stock "wheel, with a password" policy. What it does **not** implement:
|
|||||||
host aliases, LDAP/SSSD sudoers, `sudoreplay`, and most `Defaults` settings.
|
host aliases, LDAP/SSSD sudoers, `sudoreplay`, and most `Defaults` settings.
|
||||||
Needing any of those means reverting to `security.sudo`.
|
Needing any of those means reverting to `security.sudo`.
|
||||||
|
|
||||||
|
One exception to the password: the EDaaS box sets
|
||||||
|
`security.sudo-rs.wheelNeedsPassword = false`. NixOS-WSL ships that default for
|
||||||
|
`security.sudo` — WSL has no console login, so the trust boundary is the Windows
|
||||||
|
session and the Linux account password is never one the user chose — and the
|
||||||
|
option does not carry across to the `security.sudo-rs` module, which defaults to
|
||||||
|
requiring one. Without the explicit setting, `sudo` on that host prompts for a
|
||||||
|
password nobody knows.
|
||||||
|
|
||||||
If a host ever refuses to escalate, get a root shell that does not go through
|
If a host ever refuses to escalate, get a root shell that does not go through
|
||||||
sudo (`wsl -u root -d NixOS` on the work box; the console or a serial/HDMI login
|
sudo (`wsl -u root -d NixOS` on the work box; the console or a serial/HDMI login
|
||||||
elsewhere) and roll back with `nixos-rebuild switch --rollback`, or pick the
|
elsewhere) and roll back with `nixos-rebuild switch --rollback`, or pick the
|
||||||
@@ -349,12 +357,12 @@ Claude to route new memories there.
|
|||||||
|
|
||||||
## Per-host differences
|
## Per-host differences
|
||||||
|
|
||||||
| | Personal Linux (sway) | macOS | Work WSL (EDaaS) |
|
| | Personal Linux (sway) | macOS | Work WSL (EDaaS) |
|
||||||
| --------------------------- | --------------------- | --------------------- | --------------------------- |
|
| --------------------------- | --------------------- | --------------------- | ---------------------------- |
|
||||||
| Auto-tmux | yes (foot/TTY) | yes (iTerm2) | yes (WSL shell) |
|
| Auto-tmux | yes (foot/TTY) | yes (iTerm2) | yes (WSL shell) |
|
||||||
| `kubectl` → kubecolor | no (no kubectl) | no | yes (work module) |
|
| `kubectl` → kubecolor | no (no kubectl) | no | yes (work module) |
|
||||||
| `sudo` implementation | sudo-rs | Apple sudo + Touch ID | sudo-rs |
|
| `sudo` implementation | sudo-rs (password) | Apple sudo + Touch ID | sudo-rs (passwordless wheel) |
|
||||||
| git email | `iam@emmathe.dev` | `iam@emmathe.dev` | `…@citrix.com` (work) |
|
| git email | `iam@emmathe.dev` | `iam@emmathe.dev` | `…@citrix.com` (work) |
|
||||||
| ssh config managed | yes | yes | no (keeps corporate config) |
|
| ssh config managed | yes | yes | no (keeps corporate config) |
|
||||||
| ssh-agent | yes | launchd | yes (work module) |
|
| ssh-agent | yes | launchd | yes (work module) |
|
||||||
| GUI / theming (desktop.nix) | yes | no | no |
|
| GUI / theming (desktop.nix) | yes | no | no |
|
||||||
|
|||||||
Generated
+31
-31
@@ -3,16 +3,16 @@
|
|||||||
"brew-src": {
|
"brew-src": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1786348930,
|
"lastModified": 1786945682,
|
||||||
"narHash": "sha256-bCQJkbgsAMDp5HQystZLCq11UHiyEuoWbxKulAPYrh8=",
|
"narHash": "sha256-VBESSoJccikdhxh3vp3SQeG7cZXTOulMvVkoSqNDEhs=",
|
||||||
"owner": "Homebrew",
|
"owner": "Homebrew",
|
||||||
"repo": "brew",
|
"repo": "brew",
|
||||||
"rev": "3ecc9eff23feebf1bc73846d74e14a122c93b66f",
|
"rev": "5b90e281d4e0c8fbd6ca4d8358276fb305b8d0bd",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "Homebrew",
|
"owner": "Homebrew",
|
||||||
"ref": "6.0.16",
|
"ref": "6.0.18",
|
||||||
"repo": "brew",
|
"repo": "brew",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@@ -25,11 +25,11 @@
|
|||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "pkgs/firefox-addons",
|
"dir": "pkgs/firefox-addons",
|
||||||
"lastModified": 1786853140,
|
"lastModified": 1787457768,
|
||||||
"narHash": "sha256-O880FlUav75Q5aNlg9znyg/avf1X/W7o/cAtZFLtpWc=",
|
"narHash": "sha256-cbgeu5NTb6DtB+tNs4E6z6K/1XKKM90gVmlkWMJe+gY=",
|
||||||
"owner": "rycee",
|
"owner": "rycee",
|
||||||
"repo": "nur-expressions",
|
"repo": "nur-expressions",
|
||||||
"rev": "ba9568c9c0df6290dc2f34b032ab4cb575e73788",
|
"rev": "25cfc8fdc413d73b3a47e3e86dafcad51cf5c9f9",
|
||||||
"type": "gitlab"
|
"type": "gitlab"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -135,11 +135,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1784288435,
|
"lastModified": 1787424939,
|
||||||
"narHash": "sha256-ReRHaLgr/uVqdD8afFSn+myXIfpHeOhP0yYe0TJqAA8=",
|
"narHash": "sha256-O2tBn84NNuHrnqNVxx/XqsXwfYvS1YwBh+7CBnbCYsk=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "git-hooks.nix",
|
"repo": "git-hooks.nix",
|
||||||
"rev": "43b3c1ab9d40fb1dbb008f451988a91e375825e9",
|
"rev": "809414f0cdadf82cf11b06c2b29ba9b3168b3297",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -155,11 +155,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1786924861,
|
"lastModified": 1787377438,
|
||||||
"narHash": "sha256-hftabkb+73OcGzvwFAjCiQorAhprs9TnU1+FkGO5CIw=",
|
"narHash": "sha256-Sxu1NLTD/Ern6hFGLlZmtKCSct3YQXZI/lls8RE1XeM=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "09ae1b85a6db412d841d60f924b23f881f0d0a38",
|
"rev": "65258d5c65a250189fde2e35f490d15e064c4c62",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -211,11 +211,11 @@
|
|||||||
"brew-src": "brew-src"
|
"brew-src": "brew-src"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1786686423,
|
"lastModified": 1787330919,
|
||||||
"narHash": "sha256-8q3WdB8o3VUI7rOz1OXfioXIaaWbFTAxRJAkWLlfc0s=",
|
"narHash": "sha256-LslMncqN7uOOH5S88WZtO/EVt2HwD8ltUnfyANk+mC0=",
|
||||||
"owner": "zhaofengli",
|
"owner": "zhaofengli",
|
||||||
"repo": "nix-homebrew",
|
"repo": "nix-homebrew",
|
||||||
"rev": "ccabf79a6b9845eb72b51ea1d9c7ce3446350df3",
|
"rev": "b00218e4aec0e5bf07d61a0bb13f842faa582d7b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -231,11 +231,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1786852476,
|
"lastModified": 1787457452,
|
||||||
"narHash": "sha256-IM5CYtf86W4w8eUPpKcY/LpdHElmVBtJhaKnoTKxZEA=",
|
"narHash": "sha256-FJh4esFS3zqNNuKwvN3t6wrJGewqp1AUF9DAEvoKPD8=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nix-index-database",
|
"repo": "nix-index-database",
|
||||||
"rev": "c7962dc97b45129df8d751bedaf37beb5a17706e",
|
"rev": "c51d5c2ba69c907a34e90c9b6b80cd2b93811745",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -272,11 +272,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1786867632,
|
"lastModified": 1787144466,
|
||||||
"narHash": "sha256-ez+ubZlA1RtdjCB18a6zJ9M4u8qoPDy08EcnsW5M3Xw=",
|
"narHash": "sha256-HHfv2/HkNSKbbSyU9iD/g8lbP6r4tl33sSw1W4rXCk0=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixos-hardware",
|
"repo": "nixos-hardware",
|
||||||
"rev": "ff17823245ab9ff7bcae6acf950bd89cba82c38c",
|
"rev": "0471accf8d0a8210b31d947497d179ecc99e0021",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -308,11 +308,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1786711500,
|
"lastModified": 1787414105,
|
||||||
"narHash": "sha256-QvnceIGTBeDvDd9oCn+GvdsnkquliuwbVgpiRH68qaQ=",
|
"narHash": "sha256-WncT27+3BOkgTaJZLnCsf3LcYf9RXMuR9ONSN4rzQ7s=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "02e08985a27c65ffd33d434eeb2e660a2e4dc84d",
|
"rev": "a9e6d84f9c2f9012f5fe7d964a7851352300e61a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -324,11 +324,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unstable": {
|
"nixpkgs-unstable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1786862985,
|
"lastModified": 1787360063,
|
||||||
"narHash": "sha256-FBJRXmbGXiSUDvYEbfLYRkckayyZ6SK1UEqhCrIZ2Cs=",
|
"narHash": "sha256-dt4WdcvsA8/RCe+VZZwqU0X+XMM3wBbGCWA0/sFWzGo=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "e5bdc4a41d4c072fe1e3787eaa0320a384741d44",
|
"rev": "2c423e03bbafcff28bfadc6781a4a8257f205cb5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -347,11 +347,11 @@
|
|||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1786873773,
|
"lastModified": 1787536726,
|
||||||
"narHash": "sha256-Hj/nkhKDv0aJly1PAUstrhrgEYn1mVSkLIYMh90r/Pc=",
|
"narHash": "sha256-aBh5Yk9tX8ZV4k10BJr2fvTq0/+iWGegaCMUOU7YKas=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nixvim",
|
"repo": "nixvim",
|
||||||
"rev": "b397fb9f6950d57355d62bb92457d223464e0115",
|
"rev": "e2c3f9f36326d07340626847543c557e2b95fb50",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -15,3 +15,5 @@
|
|||||||
- [Dev clusters disposable](dev_clusters_disposable.md) — Lyra's dev clusters are recreatable; mutate/break freely, no confirmation needed
|
- [Dev clusters disposable](dev_clusters_disposable.md) — Lyra's dev clusters are recreatable; mutate/break freely, no confirmation needed
|
||||||
- [Nix shell tooling](nix_shell_tooling.md) — any nixpkgs tool runs ad hoc via `nix run`/`nix shell nixpkgs#<pkg>`; a missing command is never a dead end
|
- [Nix shell tooling](nix_shell_tooling.md) — any nixpkgs tool runs ad hoc via `nix run`/`nix shell nixpkgs#<pkg>`; a missing command is never a dead end
|
||||||
- [WSP local build and test](wsp_local_build_and_test.md) — core-services-cloud on this box: dotnet via nix, artifactory creds from `~/.artifactoryenv` sourced per command, how to tell auth failure from a code failure
|
- [WSP local build and test](wsp_local_build_and_test.md) — core-services-cloud on this box: dotnet via nix, artifactory creds from `~/.artifactoryenv` sourced per command, how to tell auth failure from a code failure
|
||||||
|
- [WSP-32957 PIM migration](wsp_32957_pim_migration.md) — AKS RBAC to PIM + AutoPerm decommission; prod is in the Technical Preview subscription, three tenants; resume via `~/code/WSP-32957-CONTINUATION.md`
|
||||||
|
- [Entra group member reads](entra_group_member_reads.md) — `az ad group member list` hides service principal members; use the servicePrincipal cast or transitiveMemberOf, and trust the Terraform plan over it
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
name: entra-group-member-reads
|
||||||
|
description: az ad group member list and Graph /members silently omit service principal members — use the servicePrincipal cast or transitiveMemberOf when a group is expected to hold an SPN.
|
||||||
|
metadata:
|
||||||
|
node_type: memory
|
||||||
|
type: reference
|
||||||
|
---
|
||||||
|
|
||||||
|
`az ad group member list --group <id>` and `GET /groups/{id}/members` both return an **empty collection**, with no error, for a group whose only members are service principals — at least when called with Lyra's user account. The read looks authoritative and is not.
|
||||||
|
|
||||||
|
**Reliable reads instead:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# members, cast to the type that is being hidden
|
||||||
|
az rest --method get --url "https://graph.microsoft.com/v1.0/groups/<gid>/members/microsoft.graph.servicePrincipal?\$select=id,displayName"
|
||||||
|
|
||||||
|
# count, which does not filter
|
||||||
|
az rest --method get --url "https://graph.microsoft.com/v1.0/groups/<gid>/members/\$count" --headers ConsistencyLevel=eventual
|
||||||
|
|
||||||
|
# from the principal's side
|
||||||
|
az rest --method get --url "https://graph.microsoft.com/v1.0/servicePrincipals/<spid>/transitiveMemberOf?\$select=id,displayName"
|
||||||
|
az rest --method post --url "https://graph.microsoft.com/v1.0/servicePrincipals/<spid>/checkMemberGroups" \
|
||||||
|
--body '{"groupIds":["<gid>"]}' --headers "Content-Type=application/json"
|
||||||
|
```
|
||||||
|
|
||||||
|
**How to apply:** any group that deployment or automation identities belong to — `*-cluster-admins`, `*-keyvault`, anything created by `rg-prereqs` — must be checked with one of the above before concluding it is empty. Cross-check against Terraform: a plan reporting "No changes" against a `members` attribute is strong evidence the membership is present, and outranks the `az` read. On 2026-08-28 the plain read produced a Bug (WSP-33432, cancelled) claiming five `*-cluster-admins` groups across three tenants had been emptied; all five held their deployment principals the whole time.
|
||||||
|
|
||||||
|
Related: [[wsp-32957-pim-migration]].
|
||||||
@@ -9,7 +9,9 @@ Field map for the **WSP (Workspace Platform)** Jira project, to create tickets w
|
|||||||
|
|
||||||
**Issue-type IDs:** Epic `10000`, Story `10004`, Task `10008`, Bug `10123`, Sub-task `10009`.
|
**Issue-type IDs:** Epic `10000`, Story `10004`, Task `10008`, Bug `10123`, Sub-task `10009`.
|
||||||
|
|
||||||
**Fast path — use Task, not Bug.** A `Task` requires only `summary` (project/issuetype auto, reporter defaults to caller). A `Bug` requires six extra fields (below), so only pick Bug when it must be a Bug. The sibling infra/remediation tickets in WSP are Tasks.
|
**Fast path — use Task, not Bug.** A `Task` requires `summary` plus **Task Type** `customfield_15622` (added since this note was first written; the create validator enforces it even though `createmeta` omits it, same trap as Bug's `versions`). Options value=id: Dev Task=34065, CQE Task=34066, Investigation=34067, Security=34068, Maintenance=34069, Release=34070 — use `Maintenance` for refactors and tidy-ups, `Dev Task` for feature work. A `Bug` requires six extra fields (below), so only pick Bug when it must be a Bug. The sibling infra/remediation tickets in WSP are Tasks.
|
||||||
|
|
||||||
|
Example Task `additional_fields`: `{"customfield_15622":{"id":"34069"},"components":[{"name":"Multicluster Platform"}]}`
|
||||||
|
|
||||||
**Bug required fields** (enforced by the create validator; note `createmeta` omits `versions` but the API rejects without it):
|
**Bug required fields** (enforced by the create validator; note `createmeta` omits `versions` but the API rejects without it):
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
name: wsp-32957-pim-migration
|
||||||
|
description: State of the WSP AKS-RBAC-to-PIM migration and AutoPerm decommission, and how to resume it
|
||||||
|
metadata:
|
||||||
|
node_type: memory
|
||||||
|
type: project
|
||||||
|
---
|
||||||
|
|
||||||
|
Long-running epic (August 2026) moving WSP's AKS cluster RBAC off **AutoPerm
|
||||||
|
Manager**-maintained `wsp-*` groups onto the per-subscription **`CEO-*` Azure PIM**
|
||||||
|
groups, then retiring AutoPerm. Epic **WSP-32957**; on the critical path for Zensar
|
||||||
|
L1 on-call (WSP-32193). Work happens in **`~/code/multicluster`**
|
||||||
|
(`terraform/cluster`, `terraform/cluster-k8s-resources`, `products/*/environments/*`).
|
||||||
|
|
||||||
|
**Resume from `~/code/WSP-32957-CONTINUATION.md`** — full state, branch list,
|
||||||
|
verified object IDs, findings and next steps. Jira is the durable record; that file
|
||||||
|
is the index. Keep it updated as work lands ([[docs-keep-updated]]).
|
||||||
|
|
||||||
|
**Landed:** WSP-33141 (multicluster PR #1808, merged `b047163c`) added object-ID
|
||||||
|
inputs — `admin_group_oids` on `cluster`, and `cluster_user_group_oids` /
|
||||||
|
`cluster_viewer_group_oids` / `cluster_superuser_group_oids` on
|
||||||
|
`cluster-k8s-resources`. Supplying IDs _replaces_ the display-name lookup and leaves
|
||||||
|
the `data "azuread_group"` unread, which is what will let the legacy groups be
|
||||||
|
deleted. Nothing sets them yet, so behaviour is unchanged. Repoint branches for test
|
||||||
|
(WSP-33067) and staging (WSP-33068) are pushed but have **no PR** — both gated on
|
||||||
|
decisions, not code.
|
||||||
|
|
||||||
|
**Facts that cost real effort to establish, do not re-derive:**
|
||||||
|
|
||||||
|
- **Production runs in `fc7af6ae-…` (_Workspace Platform Technical Preview_), not
|
||||||
|
`d6d75d07-…` (_Workspace Platform Production_)**, which holds no clusters. The
|
||||||
|
epic was wrong about this for its whole life and every production `CEO-*` group
|
||||||
|
name and object ID had to change. Because `CEO-*` names embed the subscription
|
||||||
|
name, **always re-verify object IDs against live Entra rather than trusting the
|
||||||
|
epic table.**
|
||||||
|
- **Three tenants**, not two: `6f4fe054` (prod, prod JP), `335836de` (staging,
|
||||||
|
staging JP, test), `3eae2746` (dev). Each `wsp-*` name is a distinct object in
|
||||||
|
each tenant.
|
||||||
|
- `wsp-staging-cluster-admins` and `wsp-test-cluster-admins` are **empty**, so
|
||||||
|
`wsp-owner` is the _only_ path to `cluster-admin` in staging. Never drop it before
|
||||||
|
`SuperAdmin-*` is proven — hence the staging branch is split into an additive
|
||||||
|
commit and a cutover commit.
|
||||||
|
- **No break-glass exists**: `disableLocalAccounts = True` on every cluster; only the
|
||||||
|
deployment SPNs authenticate non-interactively.
|
||||||
|
- Graph **PIM-for-Groups is unreadable via `az`** (the CLI's first-party client lacks
|
||||||
|
`PrivilegedAccess.Read.AzureADGroup`, on both `v1.0` and `beta`). Use the portal or
|
||||||
|
`Connect-MgGraph -Scopes PrivilegedAccess.Read.AzureADGroup`.
|
||||||
|
|
||||||
|
**Watch for:** the epic gets rewritten by James Weldrake between sessions — re-read
|
||||||
|
the description before acting, and check which child tickets are still live
|
||||||
|
(WSP-33062/33063/33064/33066 were cancelled 2026-08-24, and dev was put out of
|
||||||
|
scope). Verified findings have repeatedly contradicted the epic text
|
||||||
|
([[copilot-review-false-positives]] is the same instinct: check against reality
|
||||||
|
first).
|
||||||
|
|
||||||
|
Queued Slack messages and the leaver report live as `~/code/*.txt` alongside the
|
||||||
|
continuation file; see the table in it for what has and has not been sent
|
||||||
|
([[workflow-review-and-comments]] — show them before they go out).
|
||||||
+35
-1
@@ -287,7 +287,22 @@ in
|
|||||||
plugins = with pkgs.tmuxPlugins; [
|
plugins = with pkgs.tmuxPlugins; [
|
||||||
sensible
|
sensible
|
||||||
vim-tmux-navigator # Ctrl-h/j/k/l across vim splits and tmux panes
|
vim-tmux-navigator # Ctrl-h/j/k/l across vim splits and tmux panes
|
||||||
yank
|
{
|
||||||
|
# On WSL, tmux-yank pipes the selection to clip.exe, which decodes its
|
||||||
|
# stdin as the OEM codepage instead of UTF-8 -- an em dash reaches the
|
||||||
|
# Windows clipboard as three characters. Route through tmux's own
|
||||||
|
# buffer instead: with set-clipboard on, tmux emits OSC 52 and the
|
||||||
|
# terminal takes the text as UTF-8. Windows Terminal honours OSC 52;
|
||||||
|
# iTerm2 does not by default, hence the runtime guard rather than
|
||||||
|
# overriding pbcopy/xsel on every host. yank.tmux bakes the command
|
||||||
|
# into its key bindings when it loads, so this must be set first, which
|
||||||
|
# is what plugin extraConfig gives us.
|
||||||
|
plugin = yank;
|
||||||
|
extraConfig = ''
|
||||||
|
if-shell 'grep -qi microsoft /proc/version 2>/dev/null' \
|
||||||
|
"set -g @override_copy_command 'tmux load-buffer -w -'"
|
||||||
|
'';
|
||||||
|
}
|
||||||
extrakto # prefix+Tab: fzf-grab paths/URLs/text from the pane into the prompt
|
extrakto # prefix+Tab: fzf-grab paths/URLs/text from the pane into the prompt
|
||||||
{
|
{
|
||||||
# Catppuccin Mocha statusline (v2 API: flavour + window options must be
|
# Catppuccin Mocha statusline (v2 API: flavour + window options must be
|
||||||
@@ -298,6 +313,14 @@ in
|
|||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
set -g @catppuccin_flavor 'mocha'
|
set -g @catppuccin_flavor 'mocha'
|
||||||
set -g @catppuccin_window_status_style 'rounded'
|
set -g @catppuccin_window_status_style 'rounded'
|
||||||
|
# Catppuccin's default window text is #T, the pane title, which every
|
||||||
|
# program in the pane is free to overwrite -- the shell writes the
|
||||||
|
# hostname, Claude Code writes its current task, and a hand-set window
|
||||||
|
# name never appears. Show the window name instead, falling back to the
|
||||||
|
# pane title when the window holds a single pane and the two carry the
|
||||||
|
# same information anyway.
|
||||||
|
set -g @catppuccin_window_text ' #{?#{==:#{window_panes},1},#T,#W}'
|
||||||
|
set -g @catppuccin_window_current_text ' #{?#{==:#{window_panes},1},#T,#W}'
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
resurrect # save/restore sessions
|
resurrect # save/restore sessions
|
||||||
@@ -342,6 +365,17 @@ in
|
|||||||
set -g renumber-windows on
|
set -g renumber-windows on
|
||||||
set -g set-clipboard on
|
set -g set-clipboard on
|
||||||
|
|
||||||
|
# Pane titles on the border, but only once a window is split -- a single
|
||||||
|
# pane's title is already in the status bar. pane-border-status takes no
|
||||||
|
# format, so the hook recomputes it whenever the layout changes, which
|
||||||
|
# covers both splitting and closing a pane.
|
||||||
|
set -g pane-border-format " #P #{pane_title} "
|
||||||
|
set -g pane-border-status off
|
||||||
|
set-hook -g window-layout-changed 'set -Fw pane-border-status "#{?#{>:#{window_panes},1},top,off}"'
|
||||||
|
|
||||||
|
# Pane titles have no default binding.
|
||||||
|
bind T command-prompt -p "pane title:" "select-pane -T '%%'"
|
||||||
|
|
||||||
# Catppuccin v2 statusline. Must run after the plugin has loaded;
|
# Catppuccin v2 statusline. Must run after the plugin has loaded;
|
||||||
# home-manager appends this extraConfig after the whole plugin list.
|
# home-manager appends this extraConfig after the whole plugin list.
|
||||||
set -g status-left-length 100
|
set -g status-left-length 100
|
||||||
|
|||||||
@@ -60,6 +60,11 @@
|
|||||||
## patch the script
|
## patch the script
|
||||||
systemd.services.docker-desktop-proxy.script = lib.mkForce ''${config.wsl.wslConf.automount.root}/wsl/docker-desktop/docker-desktop-user-distro proxy --docker-desktop-root ${config.wsl.wslConf.automount.root}/wsl/docker-desktop "C:\Program Files\Docker\Docker\resources"'';
|
systemd.services.docker-desktop-proxy.script = lib.mkForce ''${config.wsl.wslConf.automount.root}/wsl/docker-desktop/docker-desktop-user-distro proxy --docker-desktop-root ${config.wsl.wslConf.automount.root}/wsl/docker-desktop "C:\Program Files\Docker\Docker\resources"'';
|
||||||
|
|
||||||
|
# NixOS-WSL's passwordless wheel default only covers `security.sudo`; the
|
||||||
|
# sudo-rs swap in common-nixos.nix needs it set again. No console login here,
|
||||||
|
# and no account password anyone knows.
|
||||||
|
security.sudo-rs.wheelNeedsPassword = false;
|
||||||
|
|
||||||
features.swayDesktop.enable = false;
|
features.swayDesktop.enable = false;
|
||||||
|
|
||||||
# NOTE: this user's systemd --user lingering -- so the home-manager renovate
|
# NOTE: this user's systemd --user lingering -- so the home-manager renovate
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
# Headless server: the Sway desktop is intentionally not set up. modules/sway.nix is
|
# Headless server: the Sway desktop is intentionally not set up. modules/sway.nix is
|
||||||
# not imported and features.swayDesktop.enable defaults to false (declared in
|
# not imported and features.swayDesktop.enable defaults to false (declared in
|
||||||
# system/modules/features.nix), so this host keeps plain TTY/SSH login.
|
# modules/features.nix), so this host keeps plain TTY/SSH login.
|
||||||
|
|
||||||
# Raspberry Pi boots via U-Boot + extlinux, not GRUB/systemd-boot. The
|
# Raspberry Pi boots via U-Boot + extlinux, not GRUB/systemd-boot. The
|
||||||
# raspberry-pi-5 nixos-hardware profile supplies the kernel, firmware and
|
# raspberry-pi-5 nixos-hardware profile supplies the kernel, firmware and
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
# The features.swayDesktop.enable option is declared in
|
# The features.swayDesktop.enable option is declared in
|
||||||
# system/modules/features.nix (so headless hosts can read/set it without
|
# modules/features.nix (so headless hosts can read/set it without
|
||||||
# importing this module). This module only provides its implementation.
|
# importing this module). This module only provides its implementation.
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
programs.sway = {
|
programs.sway = {
|
||||||
|
|||||||
+2
-2
@@ -29,10 +29,10 @@
|
|||||||
// lib.optionalAttrs (spec ? linger) { inherit (spec) linger; }
|
// lib.optionalAttrs (spec ? linger) { inherit (spec) linger; }
|
||||||
) hostUsers;
|
) hostUsers;
|
||||||
|
|
||||||
programs.firefox = lib.mkIf (config.features.swayDesktop.enable == true) {
|
programs.firefox = lib.mkIf config.features.swayDesktop.enable {
|
||||||
enable = true;
|
enable = true;
|
||||||
};
|
};
|
||||||
programs.thunderbird = lib.mkIf (config.features.swayDesktop.enable == true) {
|
programs.thunderbird = lib.mkIf config.features.swayDesktop.enable {
|
||||||
enable = true;
|
enable = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,23 @@
|
|||||||
pkgs.terraform-docs # generate Terraform module docs
|
pkgs.terraform-docs # generate Terraform module docs
|
||||||
pkgs.yq-go # jq for YAML
|
pkgs.yq-go # jq for YAML
|
||||||
pkgs.gcx # Grafana Cloud CLI (dashboards, SLOs, synthetics, alerts)
|
pkgs.gcx # Grafana Cloud CLI (dashboards, SLOs, synthetics, alerts)
|
||||||
|
|
||||||
|
# WSL ships no xdg-open, so anything that shells out to a browser dies with
|
||||||
|
# `exec: "xdg-open,x-www-browser,www-browser": executable file not found`.
|
||||||
|
# kubelogin's interactive login is the one that bites: it is the login mode
|
||||||
|
# the shared cluster kubeconfig uses. Hand the URL to Windows instead.
|
||||||
|
# (wslu, the usual answer, is gone from nixpkgs -- upstream archived it.)
|
||||||
|
(pkgs.writeShellScriptBin "xdg-open" ''
|
||||||
|
url="$1"
|
||||||
|
if command -v powershell.exe >/dev/null 2>&1; then
|
||||||
|
exec powershell.exe -NoProfile -Command "Start-Process '$url'"
|
||||||
|
fi
|
||||||
|
exec explorer.exe "$url"
|
||||||
|
'')
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Honoured by tools that read $BROWSER rather than calling xdg-open.
|
||||||
|
home.sessionVariables.BROWSER = "xdg-open";
|
||||||
services.ssh-agent.enable = true;
|
services.ssh-agent.enable = true;
|
||||||
|
|
||||||
# Colourised kubectl. enableAlias points `kubectl` at kubecolor, which parses
|
# Colourised kubectl. enableAlias points `kubectl` at kubecolor, which parses
|
||||||
|
|||||||
Reference in New Issue
Block a user