Compare commits

..
12 Commits
Author SHA1 Message Date
kiri ae4a4f0824 terminfo fix 2026-10-10 16:19:08 +02:00
kiri 8d4aa87dd0 update gitea 2026-10-10 16:10:15 +02:00
kiri 71aad7fc55 install wardrowbe on orion 2026-10-10 15:58:44 +02:00
kiri bd287a324a updates 2026-10-10 12:42:42 +02:00
kiri c671bf0bfe update llm-agents 2026-10-03 10:10:37 +02:00
kiri c9cd45e712 install audacity 2026-09-27 12:35:18 +02:00
kiri e887fcd435 updates 2026-09-27 12:35:04 +02:00
kiri 98a9be3615 cleanup 2026-09-22 22:05:11 +02:00
kiri 9c3b7c3735 Merge origin/main into main 2026-09-22 21:51:01 +02:00
kiri 2ef5f4af8f better sql syntax highlighting for WDF 2026-09-20 23:37:55 +02:00
kiri 08ae8b7882 mount sharepoint instead 2026-09-19 11:07:21 +02:00
kiri 7f37c01ef4 install chatgpt desktop and fix default applications 2026-09-19 11:02:59 +02:00
26 changed files with 591 additions and 248 deletions

No files matched your search

+4 -6
View File
@@ -11,12 +11,10 @@ and maintenance entry points. Global instructions remain applicable.
environment, or access live company systems with write authority. Test writes need environment, or access live company systems with write authority. Test writes need
task-specific authority. Moving credentials or synchronizing company content task-specific authority. Moving credentials or synchronizing company content
requires agreement; ordinary repository work does not authorize either. requires agreement; ordinary repository work does not authorize either.
- Follow the user's testing policy: automated tests only when explicitly requested - Do not write or run tests or perform extensive validation unless explicitly
or covered by an established repository policy. A test file alone does not establish requested. For routine changes, use a focused review or small manual check.
a general policy. Use the smallest relevant review, build, or manual check. - Run `just validate` only when explicitly requested. Read its short result first;
- Use `just validate` for a completed configuration batch, or name affected hosts inspect saved logs only for failures or a concrete unresolved concern.
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 - 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. `docs/design.md` and operational detail beside its owner. Use Git for chronology.
Remove temporary state when it no longer serves a purpose. Remove temporary state when it no longer serves a purpose.
+9 -5
View File
@@ -35,6 +35,10 @@ Both workstations import the provisional `work` profile. See
company credentials and always-available read-only mounts. Only company-root and company credentials and always-available read-only mounts. Only company-root and
workbench environments are migrated; product/personal projects remain user-owned. workbench environments are migrated; product/personal projects remain user-owned.
Orion's wardrobe service has its own
[setup and operations guide](modules/capabilities/wardrowbe/README.md).
Provision its SOPS secrets and two DNS records before building or activating Orion.
## Maintenance ## Maintenance
```console ```console
@@ -55,9 +59,9 @@ directory. Failure stops the command; snapshots are removed on exit. It neither
updates inputs nor creates result links or activates systems. The implementation is updates inputs nor creates result links or activates systems. The implementation is
`scripts/validate.py`; `just` and Python are installed on both workstations. `scripts/validate.py`; `just` and Python are installed on both workstations.
Use targeted hosts during iteration and full validation for a completed batch. Run targeted host builds or full validation only when explicitly requested. Routine
Documentation-only changes need review and `git diff --check`, not system builds. changes need a focused review or small manual check. Build, activation and
Build, activation and interactive behavior are separate evidence. interactive behavior are separate evidence.
User-owned activation: User-owned activation:
@@ -76,8 +80,8 @@ provisioning is documented with workbench. Never put plaintext secrets in Nix.
- **Orientation:** Super+Shift+N names the focused workspace; submit an empty name - **Orientation:** Super+Shift+N names the focused workspace; submit an empty name
to clear it. Named empty workspaces persist until cleared. Noctalia shows labels; to clear it. Named empty workspaces persist until cleared. Noctalia shows labels;
terminal titles include the working directory and command. terminal titles include the working directory and command.
- **Icons/vault:** check Oil from Walker and Nautilus device/symbolic icons. Walker's - **Icons/vault:** check Oil from Walker and Nautilus device/symbolic icons. Check
`?` vault provider should request unlocking on use, never during desktop startup. Walker's `?` vault provider; upstream Elephant may query `rbw` during startup.
- **Calendar:** in Noctalia Settings → Services → Calendar, set the Radicale - **Calendar:** in Noctalia Settings → Services → Calendar, set the Radicale
password through the existing Personal account (GNOME Keyring). Add an ICS account password through the existing Personal account (GNOME Keyring). Add an ICS account
named Work using Outlook's published URL. Keep this URL in writable Noctalia named Work using Outlook's published URL. Keep this URL in writable Noctalia
+48 -16
View File
@@ -10,6 +10,7 @@ Complete independent agreed work and obvious fixes autonomously. Leave consequen
unknowns pending and collect recommendations/alternatives into one substantial review unknowns pending and collect recommendations/alternatives into one substantial review
batch. Ask earlier only to prevent broad rework/blockage. The old 2–4-question quota batch. Ask earlier only to prevent broad rework/blockage. The old 2–4-question quota
is superseded. Keep effort focused; no speculative systems or peripheral research. is superseded. Keep effort focused; no speculative systems or peripheral research.
Do not write or run tests or perform extensive validation unless explicitly requested.
Do not activate, push, decrypt for discovery or access company systems with write Do not activate, push, decrypt for discovery or access company systems with write
authority. No subagents unless explicitly authorized by current instructions. authority. No subagents unless explicitly authorized by current instructions.
@@ -17,6 +18,29 @@ Keep this record current by replacing superseded state. Pass-specific procedures
stay here, not in AGENTS. The user must agree before the overall pass is complete. stay here, not in AGENTS. The user must agree before the overall pass is complete.
The unedited `~/sync/notes/setup-todo.md` remains an idea inbox, not authorization. The unedited `~/sync/notes/setup-todo.md` remains an idea inbox, not authorization.
## Wardrowbe on Orion: implemented, awaiting provisioning and activation
- Orion selects the Wardrowbe capability at `wardrobe.jelles.net`, with Tinyauth
password access at `wardrobe-auth.jelles.net`. Trusted household members select
their profile through Wardrowbe's development login after the gateway; profile
identity is not independently verified.
- Podman containers use immutable image digests, a dedicated network and persistent
named volumes. Database/queue health gates migrations and API startup; frontend
and workers wait for the API. Caddy protects all application routes; published
container ports are localhost-only. Internal AI is disabled everywhere. No
Codex bridge, Ollama or paid provider is configured.
- Four new system SOPS keys and both DNS records remain user-provisioned. Missing
SOPS keys must be supplied before Orion builds or activates. See
[Wardrowbe setup](../modules/capabilities/wardrowbe/README.md) for provisioning,
activation, operations, upgrade limits and manual acceptance.
- Focused source/configuration review only. No tests, host evaluation/build,
`just validate`, activation or remote changes. No existing secrets were decrypted
or read for discovery.
- A user-run Orion build failed compiling Contour's SIMD code, pulled in by
`enableAllTerminfo`. Orion now selects Kitty terminfo for workstation SSH clients
instead of the complete terminal set. The pinned NixOS module was inspected;
formatting and focused diff review only. No rebuild was run after this fix.
## Implemented, reviewed and validated; awaiting user checkpoint ## Implemented, reviewed and validated; awaiting user checkpoint
- Flat capability owners; service category flattened. AI, theme, Handy and - Flat capability owners; service category flattened. AI, theme, Handy and
@@ -31,7 +55,7 @@ The unedited `~/sync/notes/setup-todo.md` remains an idea inbox, not authorizati
Noctalia labels; shell titles include directory/command. Garden shell initialization Noctalia labels; shell titles include directory/command. Garden shell initialization
now belongs to company integration instead of every host's generic shell. now belongs to company integration instead of every host's generic shell.
- Kanagawa declares Adwaita/hicolor icon fallback; modest Limine branding/layout. - Kanagawa declares Adwaita/hicolor icon fallback; modest Limine branding/layout.
A pinned Elephant patch defers its startup `rbw list` to the explicit vault query. Walker uses upstream Elephant without a local vault patch. It may query `rbw` at startup.
- GitHub devenv 2.3.2 installed with native Zsh hook and upstream reload defaults. - GitHub devenv 2.3.2 installed with native Zsh hook and upstream reload defaults.
CLI dependencies keep upstream pins/cache; workbench tools use this repository's CLI dependencies keep upstream pins/cache; workbench tools use this repository's
exact nixpkgs pin. Company tools are also installed through the work profile. Product and exact nixpkgs pin. Company tools are also installed through the work profile. Product and
@@ -53,6 +77,16 @@ The unedited `~/sync/notes/setup-todo.md` remains an idea inbox, not authorizati
Validation prepares upstream devenv's import-from-derivation dependencies before Validation prepares upstream devenv's import-from-derivation dependencies before
read-only flake checking, accepts the repository's declared official cache, and read-only flake checking, accepts the repository's declared official cache, and
reports interruption concisely. Full builds still include the tools-only shell. reports interruption concisely. Full builds still include the tools-only shell.
The devenv cache is now in persistent NixOS Nix settings; the flake cache hint
was removed.
- Noctalia tracks upstream's `cachix` branch with its own nixpkgs pin; persistent
Nix settings include the official cache and signing key. The existing 5.2.1
commit is unchanged. Its resulting x86_64 package has a matching signed cache
entry; the previous package with our nixpkgs pin returned 404. No build or
activation was run. For the first activation, pass the cache URL and key from
`modules/capabilities/nix.nix` as `extra-substituters` and
`extra-trusted-public-keys` options to `nh os switch`; afterward Nix uses the
persistent settings.
## Decisions/provisioning still needed ## Decisions/provisioning still needed
@@ -87,8 +121,8 @@ Final full validation passed before the final review deletions:
`/tmp/nixos-validate-t7zv1zux` (session 88912, `/tmp/nixos-validate-t7zv1zux` (session 88912,
completed exit 0). Formatting, devenv preparation, flake check, all three host completed exit 0). Formatting, devenv preparation, flake check, all three host
builds (Orion, Polaris, Zenith), and the tools-only workbench build passed. builds (Orion, Polaris, Zenith), and the tools-only workbench build passed.
This includes the final notes startup ordering, serialized vault-query patch, This includes the final notes startup ordering, FUSE wrapper paths, native Zsh retry
FUSE wrapper paths, native Zsh retry function and SecretSpec package collision fix. function and SecretSpec package collision fix. The vault patch was removed afterward.
No systems were activated. No systems were activated.
Native devenv tools-only startup also passed in an isolated temporary home: Native devenv tools-only startup also passed in an isolated temporary home:
@@ -101,9 +135,9 @@ Evidence: `/tmp/foundation-editor-check/{startup,notes}.log`. No real notebook o
credentials loaded. Built Kanagawa index declares `Inherits=Adwaita,hicolor`. credentials loaded. Built Kanagawa index declares `Inherits=Adwaita,hicolor`.
Python source syntax and local documentation links checked; no automated tests added. Python source syntax and local documentation links checked; no automated tests added.
The final review only removed the unaccepted Kubernetes login wrapper and the The earlier final review removed the unaccepted Kubernetes login wrapper and the
machine-wide devenv cache setting; the flake-scoped cache remains. Full builds were machine-wide devenv cache setting; the flake-scoped cache remains. Full builds were
not repeated for those deletions. not repeated for those deletions. The later Walker patch removal has not been built.
Builds do not establish interactive acceptance. The user checkpoint remains; do not Builds do not establish interactive acceptance. The user checkpoint remains; do not
repeat full builds for documentation-only handoff edits. repeat full builds for documentation-only handoff edits.
@@ -112,18 +146,16 @@ repeat full builds for documentation-only handoff edits.
The final diff was reviewed for correctness, integration and Ponytail complexity. The final diff was reviewed for correctness, integration and Ponytail complexity.
Desktop/capability moves and repository wiring had no material findings. The review Desktop/capability moves and repository wiring had no material findings. The review
removed the speculative Kubernetes login interpreter and kept the devenv cache at removed the speculative Kubernetes login interpreter. The devenv cache was later
flake scope instead of granting it machine-wide trust. The remaining Python has a moved from flake scope to machine-wide Nix settings at the user's request. The
concrete role: in-memory concurrent secret loading with completeness checks, one-time remaining Python has a concrete role: in-memory concurrent secret loading with
encrypted credential reshaping, dynamic OBS rclone configuration, or pre-existing completeness checks, one-time encrypted credential reshaping, dynamic OBS rclone
dictation/transcription behavior. configuration, or pre-existing dictation/transcription behavior.
The user checkpoint is still required before this pass is complete. No implementation The user checkpoint is still required before this pass is complete. The implementation
commit was made in this turn. was committed as `5748b34` after the initial foundation commit `f164d8b`. Later
cleanup removed the Walker patch, Handy test and AI capability README.
Initial local commit `f164d8b` predates this implementation. Four documentation files No activation was recorded during that pass. External workbench analysis/content
were already modified during the design pass; those changes belong to this effort.
No remote changes or activation have occurred. External workbench analysis/content
changes are unrelated and must not be committed here. Previous external loader changes are unrelated and must not be committed here. Previous external loader
copies remain at `/tmp/workbench-environment-before-6k4dwzuo`; only root/workbench environment documentation and ignore entries were updated. copies remain at `/tmp/workbench-environment-before-6k4dwzuo`; only root/workbench environment documentation and ignore entries were updated.
Backups: `/tmp/foundation-workbench-docs-bvrs1hu9`. Old external loaders remain Backups: `/tmp/foundation-workbench-docs-bvrs1hu9`. Old external loaders remain
+15 -10
View File
@@ -22,6 +22,11 @@ a provisional ownership boundary, not a different workstation variant. Steam and
LAN SSH on Polaris, and laptop power/firmware management on Zenith, are deliberate. LAN SSH on Polaris, and laptop power/firmware management on Zenith, are deliberate.
Orion owns shared services. Tailnet administration supplements existing access; Orion owns shared services. Tailnet administration supplements existing access;
public Orion SSH closes only after private access is verified. Web services stay public. public Orion SSH closes only after private access is verified. Web services stay public.
Orion installs Kitty terminfo for SSH sessions from the workstations.
Wardrowbe uses a password gateway for trusted household access, followed by profile
selection inside the app; verified individual identity is deferred. Its internal AI
is disabled. Paid API usage, a Codex bridge and local model integration are deferred.
Nix owns reproducible installation, integration and selected settings. Applications Nix owns reproducible installation, integration and selected settings. Applications
own credentials, sessions, content and deliberately interactive preferences. No own credentials, sessions, content and deliberately interactive preferences. No
@@ -52,8 +57,8 @@ Use store-qualified executables or `runtimeInputs` in packaged scripts; make run
paths and failures explicit. Do not duplicate package lists, generated settings or paths and failures explicit. Do not duplicate package lists, generated settings or
binding references in documentation. binding references in documentation.
These clarify the agreed model; they are not a one-package-per-file rule, a new These clarify the agreed model; they are not a one-package-per-file rule or a
testing policy, or a requirement for additional abstractions. requirement for additional abstractions.
## Daily environment ## Daily environment
@@ -74,9 +79,9 @@ Notes stay in `~/sync/notes`, mostly meeting material; no new taxonomy. HTTP and
database targets remain independently selected and visibly named. Dadbod working database targets remain independently selected and visibly named. Dadbod working
queries and connection state stay outside Git; see the editor's `database.lua`. queries and connection state stay outside Git; see the editor's `database.lua`.
Kanagawa remains the theme with Adwaita fallback icons. rbw unlocks on requested Kanagawa remains the theme with Adwaita fallback icons. Walker uses upstream Elephant
vault access, preserving its lock policy. Outlook PWA owns work email/calendar without a local vault patch; it may query rbw during startup. Outlook PWA owns work
editing. Mailspring owns personal mail; Thunderbird remains a backup. Noctalia email/calendar editing. Mailspring owns personal mail; Thunderbird remains a backup. Noctalia
provides the combined read-only Radicale/Outlook agenda. The user accepts published provides the combined read-only Radicale/Outlook agenda. The user accepts published
ICS link access and supplies the URL privately through runtime settings. Personal ICS link access and supplies the URL privately through runtime settings. Personal
calendar editing stays on the phone; keep khal/todoman. calendar editing stays on the phone; keep khal/todoman.
@@ -118,11 +123,11 @@ validation feedback to repeated reminders. This applies the useful principle of
progressive disclosure from [OpenAI's harness engineering article](https://openai.com/index/harness-engineering/) progressive disclosure from [OpenAI's harness engineering article](https://openai.com/index/harness-engineering/)
without introducing speculative skills, orchestration or evaluation infrastructure. without introducing speculative skills, orchestration or evaluation infrastructure.
`just validate` includes working-tree changes and new nonignored files without staging, `just validate` is available on explicit request. It includes working-tree changes
formats, evaluates and builds. Concise results and saved logs minimize agent overhead. and new nonignored files without staging, formats, evaluates and builds. Targeted
Targeted host builds support iteration; a full run also builds workbench tools. host builds are supported; a full run also builds workbench tools. Builds do not
Builds do not prove activation or interactive behavior. Preserve the user's testing prove activation or interactive behavior. Do not write or run tests or perform
policy; this pass does not create a new automated testing policy. extensive validation unless explicitly requested.
Keep native update and activation commands. `nix flake update` is normal; selective Keep native update and activation commands. `nix flake update` is normal; selective
updates (often `llm-agents`) are appropriate while nixpkgs breakage awaits repair. updates (often `llm-agents`) are appropriate while nixpkgs breakage awaits repair.
Generated
+83 -71
View File
@@ -20,16 +20,16 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1788011267, "lastModified": 1790001035,
"narHash": "sha256-mEq2kU+ljompTToZ44Afvm7d/rHJ5iMBl0tjZuyShJs=", "narHash": "sha256-1/SLBjSYBzDdDhvLQ83wrzBakyS/xNGgtxHGNuveOEQ=",
"owner": "Mic92", "owner": "Mic92",
"repo": "bun2nix", "repo": "bun2nix",
"rev": "5765b0614591f75ee8ba5596e81ae85c167d1071", "rev": "07a5bfc8ac36c5370199343fa620b69f63186e33",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "Mic92", "owner": "Mic92",
"ref": "fix-structured-attrs-hook", "ref": "llm-agents",
"repo": "bun2nix", "repo": "bun2nix",
"type": "github" "type": "github"
} }
@@ -98,11 +98,11 @@
"rust-overlay": "rust-overlay" "rust-overlay": "rust-overlay"
}, },
"locked": { "locked": {
"lastModified": 1789773873, "lastModified": 1791388908,
"narHash": "sha256-lFR8A3JzpXn4Az9quklprOje/+Njyh9+bP+CE1TCIxI=", "narHash": "sha256-eqnGuVYIh6u8XU6UXueceaCi3UfSqbvopeuLYgClEDY=",
"owner": "cachix", "owner": "cachix",
"repo": "devenv", "repo": "devenv",
"rev": "2ba2f9ed32596289874c58981a4d3e56cadd639d", "rev": "27af3b2029a6d1274db8a570fb57292dae5a2551",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -118,11 +118,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1781152676, "lastModified": 1789770686,
"narHash": "sha256-RxWs5ND31KzTG7wvMM+PMfUjyNpmIEr999lqNARaM5o=", "narHash": "sha256-uZkBR7yHdIKUFB5SZdfgh1qkGfI3XmYmI/lTiquxbck=",
"owner": "nix-community", "owner": "nix-community",
"repo": "disko", "repo": "disko",
"rev": "ff8702b4de27f72b4c78573dfb89ec74e36abdf1", "rev": "725ea35e410ad83be4931d1bff7e090eacaf3563",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -143,11 +143,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1775706155, "lastModified": 1790316550,
"narHash": "sha256-h7Rw0vlb0n0Jsk21WJPm7H+1T1bG+PEuxE5cJ2TZl8A=", "narHash": "sha256-Qr/8JQ8zpmmWmZ7UAjhxVdRbSgwQ0uc2r/wUBMe4NBk=",
"owner": "abenz1267", "owner": "abenz1267",
"repo": "elephant", "repo": "elephant",
"rev": "376ee71c66db38683daabd57350bf3f6f086eaf8", "rev": "a0670fb2cf590a3ceed6f799a45947b86a78d5f4",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -198,11 +198,11 @@
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1788450739, "lastModified": 1790898470,
"narHash": "sha256-glZLQlzIn1fXH6PazR2iUmTo7kzzyYSshrWhLS9TqCU=", "narHash": "sha256-zTwuwezD0w3hpga6a6P8emidzAZFyWqlNeRBlpWOOl8=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "31729ca8cbdb4fa927b34e5f4353e6a83f39e993", "rev": "024633cd702b10285db5cb19b40ad48d2399ba60",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -219,11 +219,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1788450739, "lastModified": 1790898470,
"narHash": "sha256-glZLQlzIn1fXH6PazR2iUmTo7kzzyYSshrWhLS9TqCU=", "narHash": "sha256-zTwuwezD0w3hpga6a6P8emidzAZFyWqlNeRBlpWOOl8=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "31729ca8cbdb4fa927b34e5f4353e6a83f39e993", "rev": "024633cd702b10285db5cb19b40ad48d2399ba60",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -281,11 +281,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1788651960, "lastModified": 1791306979,
"narHash": "sha256-v9wJd32eZ2bvhBzVOd7TIjLQd011P7nwOhjKtWlci5I=", "narHash": "sha256-KuLePuhwJM9NLt/gTLHzXaOoSZKGoB3m7NfXjiQ98HY=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "2c0350c759688177331b8f5242311fae8877bdb3", "rev": "fae6e9e42c3b762ab47635cddcfaf6f52374a61b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -318,11 +318,11 @@
"treefmt-nix": "treefmt-nix_2" "treefmt-nix": "treefmt-nix_2"
}, },
"locked": { "locked": {
"lastModified": 1789804571, "lastModified": 1791440787,
"narHash": "sha256-qYKCHhnLrEQ5XOH/5vwc57FSpPNCWC/4+AcWjJa7vPE=", "narHash": "sha256-nRO1wEJmb4wjz+0Mhpg5VWURTqZRBZq3MvMjtPkKuAI=",
"owner": "numtide", "owner": "numtide",
"repo": "llm-agents.nix", "repo": "llm-agents.nix",
"rev": "c2351168cdce1d7ab47961266b999a449f23bfad", "rev": "2109db8971dc18137ff1ac9393627c761609600d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -357,11 +357,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1788036898, "lastModified": 1790176416,
"narHash": "sha256-3NT3yTvoRT7+rxLDNovpyeTDIJkZlBoO72rcu2x9Y9o=", "narHash": "sha256-acP7QvsB+dHghUeL9AwSSZ3qS//skpqjv484aveg1EQ=",
"owner": "cachix", "owner": "cachix",
"repo": "nix", "repo": "nix",
"rev": "b9b81726b38469c55b9706d80d37d6c73cc7f76c", "rev": "bd10d7f563420c6034b9d1dc75c4168fc01e5afc",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -378,11 +378,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1788680125, "lastModified": 1791105314,
"narHash": "sha256-amGSoDobwmp4CFvCn841ws2iuitus+HUJdD/gKgsrJA=", "narHash": "sha256-oTM7QFgRQ+vxBpMBMc7hEYpqBtpXAzXu2xuB/tru84U=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nix-index-database", "repo": "nix-index-database",
"rev": "116ad1c2adb642405ef8916f6a94c8626f971344", "rev": "e740bc4e9d8918d7819a51486d5099930c65c72e",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -398,11 +398,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1788744583, "lastModified": 1791440872,
"narHash": "sha256-oNMrijfyaoRjjWhxHNFSBYyEB4u/zb/AxoKewC56LvE=", "narHash": "sha256-PwiSn1VO3/6BD/Gxw/3Ei2sPB3SccyKyJ7nD72Y+WU4=",
"owner": "BirdeeHub", "owner": "BirdeeHub",
"repo": "nix-wrapper-modules", "repo": "nix-wrapper-modules",
"rev": "e88c449105c6aafa460196d3cbd16718655aee0f", "rev": "d82c328c504648fa952f25fac8332319eb425d4c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -444,11 +444,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1788860136, "lastModified": 1791373788,
"narHash": "sha256-MhPMOFV4pVkygWEbQ8t1De/uQ9cWF1u++tRe2L5tG48=", "narHash": "sha256-c3oMt0QoE+4yiRk6u1m2L9ogjaUw0uTGn9yk808KkgE=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixos-hardware", "repo": "nixos-hardware",
"rev": "62173785b9a18c78b4a15aca2623d02bceb9d077", "rev": "4bc63156b109ee7b4103e3ec30565c8c92bde435",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -463,11 +463,11 @@
"nixpkgs-src": "nixpkgs-src" "nixpkgs-src": "nixpkgs-src"
}, },
"locked": { "locked": {
"lastModified": 1789506231, "lastModified": 1787753358,
"narHash": "sha256-vNkJbtmqyBfFhn/HnzQo4ROuoyYWuy/allqTfJb4kMY=", "narHash": "sha256-Tl77VbWyAKrOfRNQhL6JbQtb/MLzbYa/1RG1gWWfICk=",
"owner": "cachix", "owner": "cachix",
"repo": "devenv-nixpkgs", "repo": "devenv-nixpkgs",
"rev": "c2f38fe7f9e04d9aadd354d380f2bd40531d9737", "rev": "256551e45f6303e142ab4a98be1bf243feb77dc0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -479,11 +479,11 @@
}, },
"nixpkgs-lib": { "nixpkgs-lib": {
"locked": { "locked": {
"lastModified": 1788057806, "lastModified": 1790477367,
"narHash": "sha256-DTQSMxzDWmT0zhguthvegnVkn7CFqGCv4IHCzk5ZUpM=", "narHash": "sha256-oujbxmVKDhci2E4EM3KyAbRUgp5/IsCoiA/LfEF7OOk=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nixpkgs.lib", "repo": "nixpkgs.lib",
"rev": "596e2e3940e09b2abbeb03f75fa1828c57fcd72c", "rev": "f7cd230690d9fc982d06129f9189b101fddcfa3b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -495,11 +495,11 @@
"nixpkgs-src": { "nixpkgs-src": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1789370336, "lastModified": 1787394516,
"narHash": "sha256-6RSEDHIWQtesQKWSu5qRai8L2h4KgCgMEfJHstW99G4=", "narHash": "sha256-pRGOQSClnXNI2iLUG6DYpsGvYcuw0drOutVZFTJNw90=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "c7def046b9a883d46974757852106483d741586f", "rev": "c8f90650c15282fa8656a041bfbbd2403997a9a7",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -511,11 +511,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1789632929, "lastModified": 1790600678,
"narHash": "sha256-RjR8AyvGlWuw16XRj7C1YDEw4E27ciuyPeB2nDMzgTU=", "narHash": "sha256-6k3n7X2UsZ6tAlWTz99RimiZab+17tMUrw9sRUcELiA=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "a32edd7654519351e48e80372a928df336394670", "rev": "f45c6f04c2f013f004bf94e284e95d72898d9393",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -527,33 +527,45 @@
}, },
"nixpkgs_3": { "nixpkgs_3": {
"locked": { "locked": {
"lastModified": 1788834054, "lastModified": 1791336361,
"narHash": "sha256-kkWAS4mE+fmi+u3mxwD9/bfX6DGJ49Tv8O9kzN5r0HQ=", "narHash": "sha256-7WQwlykef1x8n7aoyw4XP6882i2NRs5F6dVCXZ1NI1g=",
"rev": "e9b9cbecbf6c25ca26c150d78570e6332694a129", "rev": "7dd199b0e2993e37b4775ed66b1c291699608c9f",
"type": "tarball", "type": "tarball",
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.11pre1069601.e9b9cbecbf6c/nixexprs.tar.xz" "url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.11pre1086972.7dd199b0e299/nixexprs.tar.xz"
}, },
"original": { "original": {
"type": "tarball", "type": "tarball",
"url": "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz" "url": "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"
} }
}, },
"nixpkgs_4": {
"locked": {
"lastModified": 1790822859,
"narHash": "sha256-VBA0QerzzmL+4wxgtwiCdiLwLpJg2/9xOxhgO3BRJQA=",
"rev": "c59305bab2065cfecc4944690d9eedbb56f3a9fa",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.11pre1083223.c59305bab206/nixexprs.tar.zst"
},
"original": {
"type": "tarball",
"url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.zst"
}
},
"noctalia": { "noctalia": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": "nixpkgs_4"
"nixpkgs"
]
}, },
"locked": { "locked": {
"lastModified": 1788886125, "lastModified": 1791427429,
"narHash": "sha256-5rgQFUGr8GXYyyuy8ynrN39WfZzSiO/wqGWjELBOkEY=", "narHash": "sha256-FzM3cunGIn8oDC1afUsXzGdfI/tK5HjPOHzHXFIAVx4=",
"owner": "noctalia-dev", "owner": "noctalia-dev",
"repo": "noctalia", "repo": "noctalia",
"rev": "d6607b4eafbb92f6d494ebed3e296d4b741c0b58", "rev": "3e059de4aec58ba5b81f6c366b2b234a58f5ab0a",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "noctalia-dev", "owner": "noctalia-dev",
"ref": "cachix",
"repo": "noctalia", "repo": "noctalia",
"type": "github" "type": "github"
} }
@@ -603,11 +615,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1788337237, "lastModified": 1791103873,
"narHash": "sha256-gkSH8VUtCo6hnysNmb9DbTuDepH2t5pv+QWjP75xKAk=", "narHash": "sha256-nFxM+pKoZ8LJAEnUXARyCaOAloWgaW9kZQOSjWzKcTE=",
"owner": "Mic92", "owner": "Mic92",
"repo": "sops-nix", "repo": "sops-nix",
"rev": "fbf759290e0cb0a98dfc813a4eb7d53ad1dacb57", "rev": "dcd241ba97088c22569d1573286e1b9daad340c0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -618,11 +630,11 @@
}, },
"systems": { "systems": {
"locked": { "locked": {
"lastModified": 1681028828, "lastModified": 1791244623,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", "narHash": "sha256-uuanbkFk8a313Zfrxdcz8Us/yQtaxNnKJtX8v0ZPpd8=",
"owner": "nix-systems", "owner": "nix-systems",
"repo": "default", "repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", "rev": "d36eeed142f88b5a636f2e3856a53c61654cf29b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -676,11 +688,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1786901030, "lastModified": 1791203292,
"narHash": "sha256-WSFCsDSE5ffgD2MqzkM2CYjeFiKhRF/dJUN8uedb6YE=", "narHash": "sha256-vuzVwvjeucSnwKwMBpyP8r5IzdrMKCHEdwTrRFwUN7I=",
"owner": "numtide", "owner": "numtide",
"repo": "treefmt-nix", "repo": "treefmt-nix",
"rev": "27b3b12a8e6375f28ebe122f07d230ca5459bbfa", "rev": "03d8ee1bcbc8f9638907bb79edb9673151d0e22b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -698,11 +710,11 @@
"systems": "systems_2" "systems": "systems_2"
}, },
"locked": { "locked": {
"lastModified": 1784217994, "lastModified": 1791441924,
"narHash": "sha256-gxHJsrJZo2qbKK47Kf/9ho3i/HhRwkL8XvPnczGPd9E=", "narHash": "sha256-2SLAXtgtsZgjK+dyzDAV47oQNoip3dsGX+XXrT/udEk=",
"owner": "abenz1267", "owner": "abenz1267",
"repo": "walker", "repo": "walker",
"rev": "42b3ed88abf50bc52638fb2835b7f17e3ea3ac4c", "rev": "a515085fd39fae80acc086c8fbfd9b7771a9a11d",
"type": "github" "type": "github"
}, },
"original": { "original": {
+2 -7
View File
@@ -1,11 +1,6 @@
{ {
description = "NixOS Configuration"; description = "NixOS Configuration";
nixConfig = {
extra-substituters = [ "https://devenv.cachix.org" ];
extra-trusted-public-keys = [ "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw=" ];
};
inputs = { inputs = {
devenv.url = "github:cachix/devenv"; devenv.url = "github:cachix/devenv";
disko = { disko = {
@@ -29,8 +24,8 @@
}; };
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/cachix";
inputs.nixpkgs.follows = "nixpkgs"; # Keep upstream's nixpkgs pin to match its cached builds.
}; };
sops-nix = { sops-nix = {
url = "github:Mic92/sops-nix"; url = "github:Mic92/sops-nix";
-14
View File
@@ -1,14 +0,0 @@
# AI tools
Nix installs the tools, assets, skills and system Codex defaults. Codex owns its
writable user configuration; credentials, session history and other runtime state
remain outside this repository. `instructions/codex.md` owns global agent guidance.
Herdr is optional experimentation. Reload its managed configuration with
Ctrl+Alt+Shift+R (or Ctrl+B then Shift+R); its configuration owns the binding reference.
Home Manager may preserve an earlier regular configuration file with `.bak`.
Ollama supplies `nomic-embed-text` for grounded-docs. The latter runs on localhost
port 6280 with persistent Podman volumes. Removing another tool must not delete
these models or volumes. No mandatory agent persistence or orchestration workflow
is defined here.
+7 -3
View File
@@ -28,6 +28,8 @@ in
}; };
features.plugins = true; features.plugins = true;
# The Nix package lacks the codex-package.json layout the daemon requires.
features.daemon_auto_start = false;
hooks = ai.codexHooks; hooks = ai.codexHooks;
mcp_servers = ai.servers // { mcp_servers = ai.servers // {
context7 = ai.servers.context7 // { context7 = ai.servers.context7 // {
@@ -98,9 +100,11 @@ in
pkgs.nodejs pkgs.nodejs
pkgs.python3 pkgs.python3
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.codex inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.codex
pkgs.llm-agents.pi pkgs.bubblewrap
pkgs.llm-agents.claude-code inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.chatgpt
pkgs.llm-agents.herdr inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.pi
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.claude-code
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.herdr
pkgs.ctx7 pkgs.ctx7
]; ];
@@ -14,9 +14,8 @@
# Validation # Validation
- Write or run automated tests only when explicitly requested or covered by the repository's established testing policy. Do not invent a testing policy or introduce tests into a repository without tests unless explicitly requested. - Do not write or run tests or perform extensive validation unless explicitly requested.
- Validate changes with the smallest relevant build, manual behaviour check, or review of code and output. - For routine changes, use a focused review or small manual check. Report only checks performed.
- Broaden or repeat validation only for changes, failures or unresolved concerns. Report only checks performed.
# Writing # Writing
+9
View File
@@ -8,4 +8,13 @@
jack.enable = true; jack.enable = true;
}; };
}; };
flake.modules.homeManager.audio =
{ pkgs, ... }:
{
home.packages = [
pkgs.reaper
pkgs.bitwig-studio
];
};
} }
+3 -1
View File
@@ -57,7 +57,7 @@
}; };
flake.modules.homeManager.workstation-apps = flake.modules.homeManager.workstation-apps =
{ pkgs, ... }: { lib, pkgs, ... }:
{ {
home.packages = with pkgs; [ home.packages = with pkgs; [
spotify spotify
@@ -65,7 +65,9 @@
dbeaver-bin dbeaver-bin
onlyoffice-desktopeditors onlyoffice-desktopeditors
pinta pinta
audacity
]; ];
xdg.mimeApps.defaultApplicationPackages = lib.mkAfter [ pkgs.onlyoffice-desktopeditors ];
}; };
flake.modules.homeManager.xdg = flake.modules.homeManager.xdg =
-1
View File
@@ -15,7 +15,6 @@ in
settings = { settings = {
server = { server = {
DOMAIN = service.domain;
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";
@@ -1,37 +0,0 @@
"""Synthetic key-state checks; never reads a keyboard or records audio."""
from unittest.mock import Mock, patch
import push_to_talk as ptt
down = [ptt.key.KEY_LEFTMETA, ptt.key.KEY_D]
def check(states, expected, error=None):
device = Mock()
device.active_keys.side_effect = states
device.read_loop.return_value = iter([object(), object()])
if error:
device.read_loop.side_effect = error
with (
patch.object(ptt, "list_devices", return_value=["keyboard"]),
patch.object(ptt, "InputDevice", return_value=device),
patch.object(ptt.signal, "signal"),
patch.object(ptt.subprocess, "run") as run,
):
try:
ptt.main("handy")
except (OSError, KeyboardInterrupt):
assert error is not None
assert [call.args[0][1] for call in run.call_args_list] == expected
device.close.assert_called_once()
check([[]], []) # Released before the listener starts: do not record.
check([down, []], ["--toggle-transcription", "--toggle-transcription"])
check([down, down, down, [ptt.key.KEY_D]], ["--toggle-transcription"] * 2)
check([down, down], ["--toggle-transcription", "--cancel"], OSError("unplugged"))
check([down, down], ["--toggle-transcription", "--cancel"], KeyboardInterrupt())
assert ptt.held([ptt.key.KEY_RIGHTMETA, ptt.key.KEY_D])
assert not ptt.held([ptt.key.KEY_LEFTMETA])
print("Push-to-talk release, repeat, disconnect and cancellation checks passed")
+1
View File
@@ -224,5 +224,6 @@ in
VISUAL = "nvim"; VISUAL = "nvim";
}; };
xdg.mimeApps.defaultApplicationPackages = [ pkgs.neovim ]; xdg.mimeApps.defaultApplicationPackages = [ pkgs.neovim ];
xdg.mimeApps.defaultApplications."text/markdown" = "nvim.desktop";
}; };
} }
@@ -0,0 +1,13 @@
;; extends
(pair
key: [
(property_identifier) @_key
(string
(string_fragment) @_key)
]
value: (template_string) @injection.content
(#eq? @_key "sql")
(#offset! @injection.content 0 1 0 -1)
(#set! injection.include-children)
(#set! injection.language "sql"))
+8
View File
@@ -14,6 +14,14 @@ in
settings = { settings = {
trusted-users = [ "@wheel" ]; trusted-users = [ "@wheel" ];
use-xdg-base-directories = true; use-xdg-base-directories = true;
extra-substituters = [
"https://devenv.cachix.org"
"https://noctalia.cachix.org"
];
extra-trusted-public-keys = [
"devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw="
"noctalia.cachix.org-1:pCOR47nnMEo5thcxNDtzWpOxNFQsBRglJzxWPp3dkU4="
];
experimental-features = [ experimental-features = [
"nix-command" "nix-command"
-12
View File
@@ -23,16 +23,6 @@
... ...
}: }:
let let
elephantPackages = inputs.walker.inputs.elephant.packages.${pkgs.stdenv.hostPlatform.system};
# Upstream initializes rbw at startup. Drop this patch when it supports lazy vault access.
providers = elephantPackages.elephant-providers.overrideAttrs (old: {
patches = (old.patches or [ ]) ++ [ ./demand-driven-vault.patch ];
});
elephant = elephantPackages.elephant-with-providers.overrideAttrs (old: {
installPhase =
builtins.replaceStrings [ "${elephantPackages.elephant-providers}" ] [ "${providers}" ]
old.installPhase;
});
walkerDefaults = builtins.fromTOML (builtins.readFile "${inputs.walker}/resources/config.toml"); walkerDefaults = builtins.fromTOML (builtins.readFile "${inputs.walker}/resources/config.toml");
launcher = pkgs.writeShellApplication { launcher = pkgs.writeShellApplication {
@@ -46,8 +36,6 @@
{ {
imports = [ inputs.walker.homeManagerModules.default ]; imports = [ inputs.walker.homeManagerModules.default ];
programs.elephant.package = elephant;
desktop.commands.launcher = [ (lib.getExe launcher) ]; desktop.commands.launcher = [ (lib.getExe launcher) ];
programs.walker = { programs.walker = {
enable = true; enable = true;
@@ -1,55 +0,0 @@
--- a/internal/providers/bitwarden/setup.go
+++ b/internal/providers/bitwarden/setup.go
@@ -34,7 +34,7 @@
func Setup() {
LoadConfig()
- initItems()
+ // Vault access is initiated by an explicit query, never daemon startup.
}
func LoadConfig() {
--- a/internal/providers/bitwarden/query.go
+++ b/internal/providers/bitwarden/query.go
@@ -7,6 +7,7 @@
"net"
"os/exec"
"strings"
+ "sync"
"time"
"github.com/abenz1267/elephant/v2/pkg/common"
@@ -20,7 +21,24 @@
Folder string `json:"folder"`
}
+var itemsMu sync.Mutex
+
func initItems() {
+ itemsMu.Lock()
+ defer itemsMu.Unlock()
+ initItemsLocked()
+}
+
+func items() []RbwItem {
+ itemsMu.Lock()
+ defer itemsMu.Unlock()
+ if cachedItems == nil {
+ initItemsLocked()
+ }
+ return cachedItems
+}
+
+func initItemsLocked() {
cachedItems = nil
cmd := exec.Command("rbw", "list", "--raw")
output, err := cmd.CombinedOutput()
@@ -40,7 +58,7 @@
entries := []*pb.QueryResponse_Item{}
- for k, v := range cachedItems {
+ for k, v := range items() {
var subtexts []string
actions := []string{ActionCopyPassword, ActionCopyTotp, ActionSyncVault}
if config.AutoTypeSupport {
+131
View File
@@ -0,0 +1,131 @@
# Wardrowbe on Orion
[Wardrowbe](https://github.com/Anyesh/wardrowbe) is served through the existing
Caddy instance. The capability owns container images, routing, runtime secret
templates and startup ordering. Image references and settings live in
`default.nix`; endpoints live in the shared service facts.
Internal AI is disabled. Upload clothes and enter tags manually; automatic tagging
and AI outfit generation are unavailable. Local rotation and background removal
remain available. No AI provider key or notification provider is configured.
Tinyauth provides a cookie-based password gateway. After authenticating there,
Wardrowbe's development login accepts an email/name to select a profile. Use a
consistent email for each person. This is for a trusted household: gateway users
can select one another's profiles. It is not verified individual identity.
## Provision before building or activating Orion
Add these top-level keys with `sops edit modules/secrets/secrets.yaml`, using the
existing system-secret recipients. The keys are deliberately not provisioned by
the configuration change; Orion's SOPS build checks require them to exist.
| Key | Value |
| --- | --- |
| `wardrowbe-postgres-password` | A new 64-character hexadecimal password |
| `wardrowbe-secret-key` | A separate new 64-character hexadecimal signing secret |
| `wardrowbe-nextauth-secret` | A separate new 64-character hexadecimal session secret |
| `wardrowbe-auth-users` | A single-line `kiri:bcrypt-hash` string |
Generate each of the first three separately with `openssl rand -hex 32`. If
OpenSSL is unavailable, use `nix run --quiet nixpkgs#openssl -- rand -hex 32`.
Keep values inside the encrypted document; do not paste them into Nix or chat.
Hexadecimal database passwords work directly in the application's connection URL.
On a workstation with Podman, create the gateway user interactively:
```console
podman run --rm -it ghcr.io/tinyauthapp/tinyauth:v5.2.0@sha256:220aa235c76393a73fece831d7ba967b8698d3907fd8e3c4c952f699c7b142a5 user create --interactive
```
Choose username `kiri` and your password, and answer **No** to Docker formatting.
Put the emitted `username:hash` string
in `wardrowbe-auth-users` as a quoted YAML scalar. Additional gateway accounts can
be added as a comma-separated list on the same line. If Podman is unavailable,
prefix its arguments with `nix run --quiet nixpkgs#podman --` instead of `podman`.
This command only creates a hash; gateway sessions persist separately on Orion.
Create DNS records for `wardrobe.jelles.net` and `wardrobe-auth.jelles.net` pointing
at Orion. Publish an AAAA record only if Orion has working public IPv6. Existing
Caddy firewall access supplies HTTP/HTTPS and automatic certificates.
The private container network reserves `10.89.30.0/24`, with gateway
`10.89.30.1`. It must not overlap Orion's routes. If changing it, update both the
network declaration and Tinyauth's trusted-proxy address before first activation.
Add new source files to Git before using a Git-backed flake. From this repository,
activate explicitly:
```console
nixos-rebuild switch --quiet --target-host orion --sudo --flake .#orion
```
The initial activation downloads container images, including the image-processing
model. Allow time and disk space for those downloads. Containers start on boot;
PostgreSQL and Redis become healthy before the backend runs `alembic upgrade head`.
The frontend and workers wait for the backend's health check. A failed migration
prevents the API from starting. Each container has a 15-minute startup deadline.
## Operation
Run these commands on Orion. Container names and unit names correspond directly
to the declarations in `default.nix`:
```console
sudo systemctl status podman-wardrowbe-backend.service
sudo journalctl -u podman-wardrowbe-backend.service -n 80 --no-pager
sudo podman ps --filter name=wardrowbe
sudo podman volume ls --filter name=wardrowbe
```
Persistent named volumes contain PostgreSQL, Redis's append-only queue, uploaded
images and Tinyauth's SQLite sessions. They are root-owned Podman state under
`/var/lib/containers/storage/volumes`, outside Nix. Container recreation and system
rollback preserve these volumes; do not remove them or prune volumes. The network
is also retained when services stop.
Only Caddy serves public traffic. Frontend, backend and gateway host ports bind to
localhost; database and queue ports remain inside the container network. Every
Wardrowbe route, including API and signed image URLs, passes through the gateway.
The gateway's own login endpoint is public. Gateway cookies are secure, HTTP-only
and scoped to `jelles.net` to support redirects between the two subdomains.
SOPS renders root-only mode-0400 environment files at activation. Changes to their
contents restart the consuming containers. Rotating session secrets signs users out.
Changing `POSTGRES_PASSWORD` alone does **not** change an existing database role's
password: coordinate the database password change with the encrypted value.
## Upgrade and recovery
Review upstream release notes and migrations, then update the image tags and
manifest digests together. Upgrade backend and frontend to the same release;
workers use the backend image. Image updates happen through explicit configuration
activation, with no automatic image updater. Review resource requirements before
enabling AI later.
If startup fails, inspect the failing container unit's journal. After correcting
the cause, start the affected units and any dependents whose startup was blocked.
To restart the complete application in dependency order:
```console
sudo systemctl restart podman-wardrowbe-postgres.service podman-wardrowbe-redis.service podman-wardrowbe-backend.service podman-wardrowbe-worker.service podman-wardrowbe-image-worker.service podman-wardrowbe-frontend.service podman-wardrowbe-auth.service
```
Reverting a NixOS generation restores configuration and image references, not
database schema changes. Do not assume an older application can use a migrated
database; follow upstream recovery guidance. No backup system is added here.
## Manual acceptance after activation
- Open the wardrobe in a private browser window. It should require the gateway
password; unauthenticated API and image requests should also be blocked.
- Authenticate, select a Wardrowbe profile, complete onboarding and upload a photo.
Add tags manually and confirm the photo renders; try a rotation.
- With the gateway session active, open `/api/v1/capabilities` on the wardrobe
origin. Both internal AI capabilities should be `false`.
- Restart the application and confirm the profile, photo and tags remain present.
- Check that both public hostnames have valid HTTPS certificates and that the
existing Orion web services still work.
No tests, builds, activation or browser acceptance checks were performed while
preparing this capability.
+234
View File
@@ -0,0 +1,234 @@
{ config, ... }:
let
nixosModules = config.flake.modules.nixos;
in
{
flake.modules.nixos.wardrowbe =
{ config, lib, ... }:
let
service = config.facts.services.wardrowbe;
auth = config.facts.services.wardrowbe-auth;
podman = lib.getExe config.virtualisation.podman.package;
# Multi-architecture manifest digests; updating a tag alone cannot change an image.
images = {
backend = "ghcr.io/anyesh/wardrowbe:backend-1.10.3@sha256:75fd6f85fb7b91ccbb227b52d616bab11b5130c4e22858579f45736419fb592d";
frontend = "ghcr.io/anyesh/wardrowbe:frontend-1.10.3@sha256:730cf52bd8a03b94473b129e9a988b3dae9818e9377fd88917029156ee7a2e17";
auth = "ghcr.io/tinyauthapp/tinyauth:v5.2.0@sha256:220aa235c76393a73fece831d7ba967b8698d3907fd8e3c4c952f699c7b142a5";
postgres = "docker.io/library/postgres:15-alpine@sha256:f7d23353e1b15400d22ebe31189f4d314b87a4c129cc400c8c2d8d4ca127bf81";
redis = "docker.io/library/redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499";
};
healthOptions = [
"--health-interval=10s"
"--health-timeout=5s"
"--health-start-period=30s"
"--health-retries=6"
"--health-on-failure=kill"
];
backendContainer = {
image = images.backend;
environment = {
# Profile selection is available only behind the password gateway.
DEBUG = "true";
AI_INTERNAL_ENABLED = "false";
AI_VISION_ENABLED = "false";
AI_TEXT_ENABLED = "false";
APP_URL = service.origin;
CORS_ORIGINS = builtins.toJSON [ service.origin ];
REDIS_URL = "redis://wardrowbe-redis:6379/0";
STORAGE_PATH = "/data/uploads";
LOG_LEVEL = "INFO";
};
environmentFiles = [ config.sops.templates."wardrowbe-backend.env".path ];
volumes = [ "wardrowbe-uploads:/data/uploads" ];
};
backendUnits = [
"podman-wardrowbe-backend.service"
"podman-wardrowbe-worker.service"
"podman-wardrowbe-image-worker.service"
];
containers = {
wardrowbe-postgres = {
image = images.postgres;
environment = {
POSTGRES_USER = "wardrobe";
POSTGRES_DB = "wardrobe";
};
environmentFiles = [ config.sops.templates."wardrowbe-postgres.env".path ];
volumes = [ "wardrowbe-postgres:/var/lib/postgresql/data" ];
podman.sdnotify = "healthy";
extraOptions = healthOptions ++ [
"--health-cmd=pg_isready -h 127.0.0.1 -U wardrobe -d wardrobe"
];
};
wardrowbe-redis = {
image = images.redis;
cmd = [
"redis-server"
"--appendonly"
"yes"
];
volumes = [ "wardrowbe-redis:/data" ];
podman.sdnotify = "healthy";
extraOptions = healthOptions ++ [ "--health-cmd=redis-cli ping" ];
};
wardrowbe-backend = backendContainer // {
dependsOn = [
"wardrowbe-postgres"
"wardrowbe-redis"
];
ports = [ "127.0.0.1:8002:8000" ];
cmd = [
"sh"
"-c"
"alembic upgrade head && exec uvicorn app.main:app --host 0.0.0.0 --port 8000"
];
podman.sdnotify = "healthy";
extraOptions = healthOptions ++ [
"--health-cmd=curl --fail --silent http://127.0.0.1:8000/api/v1/health"
];
};
wardrowbe-worker = backendContainer // {
dependsOn = [ "wardrowbe-backend" ];
cmd = [
"arq"
"app.workers.worker.WorkerSettings"
];
};
wardrowbe-image-worker = backendContainer // {
dependsOn = [ "wardrowbe-backend" ];
cmd = [
"arq"
"app.workers.image_worker.ImageWorkerSettings"
];
};
wardrowbe-frontend = {
image = images.frontend;
dependsOn = [ "wardrowbe-backend" ];
environment = {
BACKEND_URL = "http://wardrowbe-backend:8000";
NEXTAUTH_URL = service.origin;
DEV_MODE = "true";
};
environmentFiles = [ config.sops.templates."wardrowbe-frontend.env".path ];
ports = [ "127.0.0.1:3002:3000" ];
podman.sdnotify = "healthy";
extraOptions = healthOptions ++ [
"--health-cmd=node -e \"fetch('http://127.0.0.1:3000').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))\""
];
};
wardrowbe-auth = {
image = images.auth;
environment = {
TINYAUTH_APPURL = auth.origin;
TINYAUTH_AUTH_SECURECOOKIE = "true";
TINYAUTH_AUTH_TRUSTEDPROXIES = "10.89.30.1";
TINYAUTH_LABELPROVIDER = "none";
TINYAUTH_ANALYTICS_ENABLED = "false";
TINYAUTH_DATABASE_PATH = "/data/tinyauth.db";
TINYAUTH_UI_TITLE = "Wardrobe login";
};
environmentFiles = [ config.sops.templates."wardrowbe-auth.env".path ];
volumes = [ "wardrowbe-auth:/data" ];
ports = [ "127.0.0.1:3003:3000" ];
podman.sdnotify = "healthy";
extraOptions = healthOptions ++ [ "--health-cmd=tinyauth healthcheck" ];
};
};
in
{
imports = [
nixosModules.caddy
nixosModules.sops
];
sops.secrets = {
wardrowbe-postgres-password = { };
wardrowbe-secret-key = { };
wardrowbe-nextauth-secret = { };
wardrowbe-auth-users = { };
};
sops.templates = {
"wardrowbe-postgres.env" = {
content = ''
POSTGRES_PASSWORD=${config.sops.placeholder.wardrowbe-postgres-password}
'';
restartUnits = [ "podman-wardrowbe-postgres.service" ];
};
"wardrowbe-backend.env" = {
content = ''
DATABASE_URL=postgresql+asyncpg://wardrobe:${config.sops.placeholder.wardrowbe-postgres-password}@wardrowbe-postgres:5432/wardrobe
SECRET_KEY=${config.sops.placeholder.wardrowbe-secret-key}
'';
restartUnits = backendUnits;
};
"wardrowbe-frontend.env" = {
content = ''
NEXTAUTH_SECRET=${config.sops.placeholder.wardrowbe-nextauth-secret}
'';
restartUnits = [ "podman-wardrowbe-frontend.service" ];
};
"wardrowbe-auth.env" = {
content = ''
TINYAUTH_AUTH_USERS=${config.sops.placeholder.wardrowbe-auth-users}
'';
restartUnits = [ "podman-wardrowbe-auth.service" ];
};
};
virtualisation.oci-containers = {
backend = "podman";
containers = lib.mapAttrs (
_: container:
{
networks = [ "wardrowbe" ];
}
// container
) containers;
};
systemd.services = {
wardrowbe-network = {
description = "Wardrowbe container network";
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
${podman} network exists wardrowbe || \
${podman} network create --subnet 10.89.30.0/24 --gateway 10.89.30.1 wardrowbe
'';
};
}
// lib.mapAttrs' (
name: _:
lib.nameValuePair "podman-${name}" {
requires = [ "wardrowbe-network.service" ];
after = [ "wardrowbe-network.service" ];
serviceConfig.TimeoutStartSec = lib.mkForce 900;
}
) containers;
services.caddy.virtualHosts = {
${auth.domain}.extraConfig = "reverse_proxy 127.0.0.1:3003";
${service.domain}.extraConfig = ''
request_body {
max_size 50MB
}
route {
forward_auth 127.0.0.1:3003 {
uri /api/auth/caddy
}
handle /api/auth/* {
reverse_proxy 127.0.0.1:3002
}
handle /api/* {
reverse_proxy 127.0.0.1:8002
}
handle {
reverse_proxy 127.0.0.1:3002
}
}
'';
};
};
}
@@ -13,6 +13,7 @@
kubelogin-oidc kubelogin-oidc
jq jq
just just
python3Packages.python-openstackclient
python314 python314
rclone rclone
sops sops
+1 -2
View File
@@ -92,8 +92,7 @@
in in
{ {
# devenv already installs its bundled SecretSpec CLI in the workstation profile. # devenv already installs its bundled SecretSpec CLI in the workstation profile.
home.packages = home.packages = lib.remove pkgs.secretspec (import ./_support/tools.nix { inherit pkgs; }).packages;
lib.remove pkgs.secretspec (import ./_support/tools.nix { inherit pkgs; }).packages;
programs.zsh.initContent = lib.mkAfter '' programs.zsh.initContent = lib.mkAfter ''
workbench-reload() { workbench-reload() {
if [[ "''${DEVENV_ROOT:-}" != ${lib.escapeShellArg root} ]]; then if [[ "''${DEVENV_ROOT:-}" != ${lib.escapeShellArg root} ]]; then
+8
View File
@@ -235,6 +235,14 @@ in
scheme = "https"; scheme = "https";
domain = "vault.jelles.net"; domain = "vault.jelles.net";
}; };
wardrowbe = {
scheme = "https";
domain = "wardrobe.jelles.net";
};
wardrowbe-auth = {
scheme = "https";
domain = "wardrobe-auth.jelles.net";
};
}; };
externalDevices.pixel-10.syncthingId = "MTJHEHA-UMZDQZ7-BTMRRLQ-Y7BFPUJ-ZTY6LZX-PDXV3IS-XVJCU7B-EPETBQZ"; externalDevices.pixel-10.syncthingId = "MTJHEHA-UMZDQZ7-BTMRRLQ-Y7BFPUJ-ZTY6LZX-PDXV3IS-XVJCU7B-EPETBQZ";
+4 -2
View File
@@ -10,7 +10,7 @@ in
stateVersion = "24.05"; stateVersion = "24.05";
}; };
flake.modules.nixos.orion = { flake.modules.nixos.orion = { pkgs, ... }: {
imports = [ imports = [
nixosModules.host-base nixosModules.host-base
@@ -24,11 +24,13 @@ in
nixosModules.actual nixosModules.actual
nixosModules.gitea nixosModules.gitea
nixosModules.transcription nixosModules.transcription
nixosModules.wardrowbe
./_hardware.nix ./_hardware.nix
./_disk.nix ./_disk.nix
]; ];
environment.enableAllTerminfo = true; # SSH clients on both workstations use Kitty.
environment.systemPackages = [ pkgs.kitty.terminfo ];
users.users.${config.facts.account.name}.linger = true; users.users.${config.facts.account.name}.linger = true;
}; };
+1
View File
@@ -43,6 +43,7 @@ in
flake.modules.homeManager.workstation-base = { flake.modules.homeManager.workstation-base = {
imports = [ imports = [
homeModules.passwords homeModules.passwords
homeModules.audio
homeModules.clipboard homeModules.clipboard
homeModules.handy homeModules.handy
homeModules.desktop-commands homeModules.desktop-commands
+7 -3
View File
@@ -5,6 +5,10 @@ orion-ip: ENC[AES256_GCM,data:S6fpCWnD8dvchvrHlEo=,iv:72+oRxHUEJ7imJ+sWjGbG+TUrS
hashed-password: ENC[AES256_GCM,data:mhYvALF/VrPMFFYEIGiML1MoRxdOI/J9akZPD+OF5drDTR4J5LBn3EejlbzP8CTCRsyoIF6tigMBxF8ZLz9W7hOVwT70Hr/yXfLWJ2lfBsf7l/SPZ+6NglKppCeQQktm4QKIHTVj7CkbRw==,iv:0RxXkb+vz/QK9dvecCZpWCWCsoPhi2F3OCt6CwUDqX4=,tag:j7QO8xUfWUnSpk4mMQUYmA==,type:str] hashed-password: ENC[AES256_GCM,data:mhYvALF/VrPMFFYEIGiML1MoRxdOI/J9akZPD+OF5drDTR4J5LBn3EejlbzP8CTCRsyoIF6tigMBxF8ZLz9W7hOVwT70Hr/yXfLWJ2lfBsf7l/SPZ+6NglKppCeQQktm4QKIHTVj7CkbRw==,iv:0RxXkb+vz/QK9dvecCZpWCWCsoPhi2F3OCt6CwUDqX4=,tag:j7QO8xUfWUnSpk4mMQUYmA==,type:str]
gemini-api-key-neovim: ENC[AES256_GCM,data:B8FeFt45FsU3aagyLDKXiwmx0mRrsw4C8RQ3EWXwZ+YfWLMvwJad,iv:1HqBD6vc07Ke/PMYXfHqFrWDGw/UMjiiBjLRN33/xHI=,tag:czcrYGbJFi41rYtIPM4qTQ==,type:str] gemini-api-key-neovim: ENC[AES256_GCM,data:B8FeFt45FsU3aagyLDKXiwmx0mRrsw4C8RQ3EWXwZ+YfWLMvwJad,iv:1HqBD6vc07Ke/PMYXfHqFrWDGw/UMjiiBjLRN33/xHI=,tag:czcrYGbJFi41rYtIPM4qTQ==,type:str]
booklore-db-pass: ENC[AES256_GCM,data:dlPGXQ24itEaBRJSJ9WOogWCdF3atFQ2ZtlLGyGq8Tin5OmSZI6lZUzSE+femBW5SBTIlKQvzHEPCs9MT5tyMIqetzGLm+mMN3FDW7si684Cuv9z9Uq5gjAZWh14KQMWYPI=,iv:oLnqu2EDFBVcBpswVRXXeF617YolPxOUx9CscHRRn/8=,tag:Si6gF1EXhcHalk11D3Exlw==,type:str] booklore-db-pass: ENC[AES256_GCM,data:dlPGXQ24itEaBRJSJ9WOogWCdF3atFQ2ZtlLGyGq8Tin5OmSZI6lZUzSE+femBW5SBTIlKQvzHEPCs9MT5tyMIqetzGLm+mMN3FDW7si684Cuv9z9Uq5gjAZWh14KQMWYPI=,iv:oLnqu2EDFBVcBpswVRXXeF617YolPxOUx9CscHRRn/8=,tag:Si6gF1EXhcHalk11D3Exlw==,type:str]
wardrowbe-auth-users: ENC[AES256_GCM,data:0padDqNkWA9p6GXaX3bpyH78tEeOndlX6/Gm0OzsfecnPkJmBJUQZr/328+51Q4eWPRflg4NRm04nejrqohKGcs=,iv:VJQD/ko4B18PbJh0gWdfQJ3xlGeHW4oBlho4e3Bkhk8=,tag:iE7gfUi1khmwIJA/3L8zvA==,type:str]
wardrowbe-postgres-password: ENC[AES256_GCM,data:3t+py1nmtzbdFFMbBtQXQKttRj1MQrz+wcfjQGPYahOaQTIDu1Pzk3KJf+LANCTftpvrwO6N92K0b5q0+1MEzw==,iv:o9Hiew2vx6OinLBOKgPyx0K+WVTAvPopcEJoLtYCZTY=,tag:xi0wueITInYXwvVaLusqyw==,type:str]
wardrowbe-secret-key: ENC[AES256_GCM,data:c9++BrtGsAFHML3gUG/E319jg9kxixH8RfZqs9pymxjYEFJsclaiWmknqeel4luIAuD5ufvDRzBLqxaBSE6rhQ==,iv:YntoeASLdE+lbU+15eIaR4SqWU3PzaWH8MJFlW1FwaM=,tag:aZhDTPc3qbNTZ8N9lvCJgA==,type:str]
wardrowbe-nextauth-secret: ENC[AES256_GCM,data:3UZm0L+0q7C6OVYfCyIghzK80dPKOOz9BLfFCHctD2oMckPl0yfL5S1NqsXULycx+ctwWPV7SQj7/AiD6cyw1w==,iv:SocQDS2GbQ6ZWifmwb7AJC7SNkAMjJWetlTM9TfUC3Q=,tag:WuXajPY4e4zF5T6zC7okTQ==,type:str]
sops: sops:
age: age:
- enc: | - enc: |
@@ -25,7 +29,7 @@ sops:
sWu/+CcQDpUu4tAeBSCUZ/BV2nTvfb94DZGK2dWUFniJaZit5tRZlg== sWu/+CcQDpUu4tAeBSCUZ/BV2nTvfb94DZGK2dWUFniJaZit5tRZlg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1upsxyevxdv3xax8jhnffclryuve70k63cjrca69ffds4h2m3nvuql59ufw recipient: age1upsxyevxdv3xax8jhnffclryuve70k63cjrca69ffds4h2m3nvuql59ufw
lastmodified: "2026-08-08T14:56:00Z" lastmodified: "2026-10-10T13:58:26Z"
mac: ENC[AES256_GCM,data:pXcvH73xNMSkOGytdhJ4Qu1pH8d3qS/FkPfGvbkzQoNLKRlLiArJExPYIkEtFdr7mDhYer91wPtkJu2HyyxCVPkPp8EFaeVdGuFufLQruQjP2KRfR841hohVW4KN53nh1AJT22CW2LTqgS0BnTBrd6wUQWvBHdek99eh8vwgurI=,iv:d8I+vKIgfirDvkHXVSNWJrKUO5vACkbk3VSN8TXSE+Q=,tag:itzAvtD+aBFmDilxZBepLw==,type:str] mac: ENC[AES256_GCM,data:S21qc5jLIoRrR28yiNmQHAU9soV74haGAbYXxsut0PqeRAMzUj4RSdBiR7Mja84r+w148Gx4GqTrPLhVxfj+78lyWcV3MWYbf9rr4tbxUYbhlJPRwVPdeP/JEc4rhPzX7QnUcrlym2zOhumJ/RoUHWW2yUEC/7DmzCqMXH1Kx5U=,iv:p7lwWl76cvELLhMlOdnxFdYdoIt/tVwxL/ihR/T3d7Y=,tag:DWsJYfaIZ2yZqXPktl9V1g==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.12.2 version: 3.13.3