Author SHA1 Message Date
moons-14 a8e9a4dce5 update flake.nix 2026-07-27 20:34:27 +09:00
moons-14 72ff60fb16 x1g9 2026-07-27 19:58:07 +09:00
moons-14 f65318b765 mac 2026-07-27 19:43:42 +09:00
moons-14 0257b2e2fd mac 2026-07-27 19:43:31 +09:00
moons-14 8fec0494ec feat 2026-07-27 19:31:25 +09:00
moons-14 89eeb23d1c add docs 2026-07-27 18:41:42 +09:00
moons-14 bf94d1183f add profiles 2026-07-27 18:31:50 +09:00
moons-14 6bd05887db add systems 2026-07-27 18:02:48 +09:00
moons-14 3e32c9874b add apps 2026-07-27 16:49:48 +09:00
moons-14 684acef46c add apps 2026-07-27 16:10:17 +09:00
moons-14 9061825c63 add apps 2026-07-27 16:10:03 +09:00
moons-14 e703e1b31c add vim, vscode, zed, zellij, zsh 2026-07-27 15:24:37 +09:00
moons-14 79cc69045f add gtk, niri, noctalia, ssh, swaylock 2026-07-27 15:10:46 +09:00
moons-14 5c2ff3cbcf add nix-index, ly 2026-07-27 14:32:59 +09:00
moons-14 c5d4de5a9d add fcitx5, ghostty, git 2026-07-27 14:06:13 +09:00
moons-14 c58bdd30c3 x1g9 2026-07-27 12:34:20 +09:00
moons-14 9771a85e3f x1g9 2026-07-27 11:26:27 +09:00
moons-14 5b1bde7d90 nix registory 2026-07-27 10:58:24 +09:00
moons-14 1d82ab92b6 feat 2026-07-27 10:05:55 +09:00
moons-14 19bc309b8b add document 2026-07-27 06:18:24 +09:00
moons-14 e477c6bee8 init files 2026-07-27 06:13:53 +09:00
moons-14 0171582307 delete 2026-07-27 05:51:17 +09:00
moons-14 dd40b978dd fix 2026-07-27 01:52:41 +09:00
369 changed files with 4187 additions and 7472 deletions
@@ -1,260 +0,0 @@
name: Update Flake Input
description: Update one GitHub-backed Nix flake input and create a pull request
inputs:
input-name:
description: Name of the flake input to update
required: true
github-token:
description: Token used to query GitHub, push the update branch, and manage the pull request
required: true
base-branch:
description: Branch targeted by the pull request
required: false
default: main
minimum-release-age-days:
description: Minimum age of the target commit in days
required: false
default: "3"
skip-delay:
description: Update to the latest revision without applying the minimum age
required: false
default: "false"
auto-merge:
description: Enable squash auto-merge on the pull request
required: false
default: "true"
pr-labels:
description: Comma-separated labels to add when they already exist in the repository
required: false
default: dependencies,automated
outputs:
updated:
description: Whether flake.lock changed
value: ${{ steps.update.outputs.updated }}
current-version:
description: Previous locked revision
value: ${{ steps.update.outputs.current_version }}
new-version:
description: New locked revision
value: ${{ steps.update.outputs.new_version }}
pr-url:
description: URL of the created or updated pull request
value: ${{ steps.pull-request.outputs.pr_url }}
runs:
using: composite
steps:
- name: Update flake input
id: update
shell: bash
env:
GH_TOKEN: ${{ inputs.github-token }}
INPUT_NAME: ${{ inputs.input-name }}
MINIMUM_RELEASE_AGE_DAYS: ${{ inputs.minimum-release-age-days }}
SKIP_DELAY: ${{ inputs.skip-delay }}
run: |
set -euo pipefail
if [[ ! "$MINIMUM_RELEASE_AGE_DAYS" =~ ^[0-9]+$ ]]; then
echo "::error::minimum-release-age-days must be a non-negative integer"
exit 1
fi
node_key="$(
jq -er --arg input "$INPUT_NAME" '
.nodes.root.inputs[$input]
| if type == "array" then .[0] else . end
' flake.lock
)"
input_type="$(jq -r --arg node "$node_key" '.nodes[$node].locked.type // ""' flake.lock)"
input_owner="$(jq -r --arg node "$node_key" '.nodes[$node].locked.owner // ""' flake.lock)"
input_repo="$(jq -r --arg node "$node_key" '.nodes[$node].locked.repo // ""' flake.lock)"
input_ref="$(jq -r --arg node "$node_key" '.nodes[$node].original.ref // ""' flake.lock)"
current_rev="$(jq -er --arg node "$node_key" '.nodes[$node].locked.rev' flake.lock)"
if [ "$input_type" != "github" ] || [ -z "$input_owner" ] || [ -z "$input_repo" ]; then
echo "::error::${INPUT_NAME} is not a GitHub-backed flake input"
exit 1
fi
echo "Input: $INPUT_NAME"
echo "Repository: ${input_owner}/${input_repo}"
echo "Current revision: $current_rev"
if [ "$SKIP_DELAY" = "true" ]; then
nix flake update "$INPUT_NAME"
else
cutoff="$(date --utc --date="${MINIMUM_RELEASE_AGE_DAYS} days ago" +%Y-%m-%dT%H:%M:%SZ)"
api_args=(
--method GET
"repos/${input_owner}/${input_repo}/commits"
-f "until=$cutoff"
-f per_page=1
)
if [ -n "$input_ref" ]; then
api_args+=(-f "sha=$input_ref")
fi
echo "Selecting the newest commit no later than $cutoff"
target_data="$(gh api "${api_args[@]}" --jq '.[0] | {sha: .sha, date: .commit.committer.date}')"
target_rev="$(jq -er '.sha' <<< "$target_data")"
target_date="$(jq -er '.date' <<< "$target_data")"
if [ "$target_rev" = "$current_rev" ]; then
echo "The input is already at the newest eligible revision"
{
echo "updated=false"
echo "current_version=$current_rev"
echo "new_version=$current_rev"
} >> "$GITHUB_OUTPUT"
exit 0
fi
current_date="$(
gh api "repos/${input_owner}/${input_repo}/commits/${current_rev}" \
--jq '.commit.committer.date'
)"
current_timestamp="$(date --date="$current_date" +%s)"
target_timestamp="$(date --date="$target_date" +%s)"
if [ "$target_timestamp" -lt "$current_timestamp" ]; then
echo "The newest eligible revision is older than the current revision; skipping"
{
echo "updated=false"
echo "current_version=$current_rev"
echo "new_version=$current_rev"
} >> "$GITHUB_OUTPUT"
exit 0
fi
nix flake update "$INPUT_NAME" \
--override-input "$INPUT_NAME" "github:${input_owner}/${input_repo}/${target_rev}"
fi
if git diff --quiet -- flake.lock; then
echo "No lock file changes were produced"
{
echo "updated=false"
echo "current_version=$current_rev"
echo "new_version=$current_rev"
} >> "$GITHUB_OUTPUT"
exit 0
fi
new_node_key="$(
jq -er --arg input "$INPUT_NAME" '
.nodes.root.inputs[$input]
| if type == "array" then .[0] else . end
' flake.lock
)"
new_rev="$(jq -er --arg node "$new_node_key" '.nodes[$node].locked.rev' flake.lock)"
echo "New revision: $new_rev"
{
echo "updated=true"
echo "current_version=$current_rev"
echo "new_version=$new_rev"
echo "input_owner=$input_owner"
echo "input_repo=$input_repo"
} >> "$GITHUB_OUTPUT"
- name: Create or update pull request
id: pull-request
if: steps.update.outputs.updated == 'true'
shell: bash
env:
GH_TOKEN: ${{ inputs.github-token }}
INPUT_NAME: ${{ inputs.input-name }}
BASE_BRANCH: ${{ inputs.base-branch }}
CURRENT_REV: ${{ steps.update.outputs.current_version }}
NEW_REV: ${{ steps.update.outputs.new_version }}
INPUT_OWNER: ${{ steps.update.outputs.input_owner }}
INPUT_REPO: ${{ steps.update.outputs.input_repo }}
MINIMUM_RELEASE_AGE_DAYS: ${{ inputs.minimum-release-age-days }}
SKIP_DELAY: ${{ inputs.skip-delay }}
AUTO_MERGE: ${{ inputs.auto-merge }}
PR_LABELS: ${{ inputs.pr-labels }}
run: |
set -euo pipefail
branch_suffix="$(tr -c 'A-Za-z0-9._-' '-' <<< "$INPUT_NAME" | sed 's/-$//')"
branch="update-flake-${branch_suffix}"
current_short="${CURRENT_REV:0:8}"
new_short="${NEW_REV:0:8}"
title="chore(nix): update ${INPUT_NAME} to ${new_short}"
if [ "$SKIP_DELAY" = "true" ]; then
age_note="The minimum release age check was skipped for this manually requested update."
else
age_note="The target commit is at least ${MINIMUM_RELEASE_AGE_DAYS} days old."
fi
body="$(
printf '%s\n' \
"Automated update of the \`${INPUT_NAME}\` flake input." \
"" \
"- Previous revision: [\`${current_short}\`](https://github.com/${INPUT_OWNER}/${INPUT_REPO}/commit/${CURRENT_REV})" \
"- New revision: [\`${new_short}\`](https://github.com/${INPUT_OWNER}/${INPUT_REPO}/commit/${NEW_REV})" \
"- Changes: [compare](https://github.com/${INPUT_OWNER}/${INPUT_REPO}/compare/${CURRENT_REV}...${NEW_REV})" \
"" \
"$age_note"
)"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add flake.lock
git switch -C "$branch"
git commit -m "$title"
git fetch origin "refs/heads/${branch}:refs/remotes/origin/${branch}" || true
git push --force-with-lease origin "HEAD:refs/heads/${branch}"
label_args=()
available_labels="$(gh label list --limit 100 --json name --jq '.[].name')"
IFS=',' read -ra requested_labels <<< "$PR_LABELS"
for label in "${requested_labels[@]}"; do
label="$(xargs <<< "$label")"
if [ -n "$label" ] && grep -Fxq "$label" <<< "$available_labels"; then
label_args+=(--add-label "$label")
elif [ -n "$label" ]; then
echo "::warning::Skipping missing pull request label: $label"
fi
done
pr_number="$(
gh pr list \
--state open \
--head "$branch" \
--json number \
--jq '.[0].number // empty'
)"
if [ -n "$pr_number" ]; then
gh pr edit "$pr_number" \
--title "$title" \
--body "$body" \
"${label_args[@]}"
else
gh pr create \
--base "$BASE_BRANCH" \
--head "$branch" \
--title "$title" \
--body "$body"
pr_number="$(
gh pr list \
--state open \
--head "$branch" \
--json number \
--jq '.[0].number'
)"
if [ "${#label_args[@]}" -gt 0 ]; then
gh pr edit "$pr_number" "${label_args[@]}"
fi
fi
if [ "$AUTO_MERGE" = "true" ]; then
gh pr merge "$pr_number" --auto --squash ||
echo "::warning::Auto-merge could not be enabled; check the repository merge settings"
fi
pr_url="$(gh pr view "$pr_number" --json url --jq '.url')"
echo "pr_url=$pr_url" >> "$GITHUB_OUTPUT"
echo "Pull request: $pr_url"
-150
View File
@@ -1,150 +0,0 @@
name: NixOS CI
on:
pull_request:
branches:
- main
push:
branches:
- main
workflow_dispatch:
permissions:
contents: read
concurrency:
group: nixos-ci-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
validate:
name: Validate flake
runs-on: ubuntu-latest
timeout-minutes: 30
outputs:
hosts: ${{ steps.hosts.outputs.hosts }}
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install Nix
uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0
with:
extra_nix_config: |
experimental-features = nix-command flakes
accept-flake-config = true
access-tokens = github.com=${{ github.token }}
- name: Check flake and evaluate all outputs
run: nix flake check --all-systems --no-build --show-trace
- name: Discover NixOS hosts
id: hosts
run: |
hosts=$(nix eval --json '.#nixosConfigurations' --apply 'configs: builtins.attrNames configs')
echo "hosts=$hosts" >> "$GITHUB_OUTPUT"
echo "Discovered hosts: $hosts"
build:
name: Build ${{ matrix.host }}
needs: validate
if: ${{ needs.validate.outputs.hosts != '[]' }}
runs-on: ubuntu-latest
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
host: ${{ fromJSON(needs.validate.outputs.hosts) }}
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install Nix
uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0
with:
extra_nix_config: |
experimental-features = nix-command flakes
accept-flake-config = true
access-tokens = github.com=${{ github.token }}
- name: Build NixOS system
run: |
nix build ".#nixosConfigurations.${{ matrix.host }}.config.system.build.toplevel" \
--no-link \
--print-build-logs \
--show-trace
report-main-status:
name: Report main status
needs:
- validate
- build
if: ${{ always() && !cancelled() && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
issues: write
env:
CI_FAILED: ${{ needs.validate.result == 'failure' || needs.build.result == 'failure' }}
JOB_RESULTS: ${{ toJSON(needs) }}
steps:
- name: Create or resolve failure issue
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const title = "NixOS CI is failing on main";
const marker = "<!-- nixos-ci-main-failure -->";
const failed = process.env.CI_FAILED === "true";
const jobs = JSON.parse(process.env.JOB_RESULTS);
const failedJobs = Object.entries(jobs)
.filter(([, job]) => job.result === "failure")
.map(([name]) => `\`${name}\``)
.join(", ");
const runUrl = `${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}`;
const commitUrl = `${context.serverUrl}/${owner}/${repo}/commit/${context.sha}`;
const issues = await github.paginate(github.rest.issues.listForRepo, {
owner,
repo,
state: "open",
per_page: 100,
});
const existing = issues.find(
(issue) => !issue.pull_request && issue.title === title && issue.body?.includes(marker),
);
if (failed) {
const body = [
marker,
"The NixOS CI workflow failed after a push to `main`.",
"",
`- Failed jobs: ${failedJobs || "unknown"}`,
`- Commit: [\`${context.sha.slice(0, 7)}\`](${commitUrl})`,
`- Workflow run: [${context.runId}](${runUrl})`,
"",
"This issue is updated on subsequent failures and closed automatically after CI recovers.",
].join("\n");
if (existing) {
await github.rest.issues.update({
owner,
repo,
issue_number: existing.number,
body,
});
} else {
await github.rest.issues.create({ owner, repo, title, body });
}
return;
}
if (existing) {
await github.rest.issues.createComment({
owner,
repo,
issue_number: existing.number,
body: `CI recovered in [workflow run ${context.runId}](${runUrl}).`,
});
await github.rest.issues.update({
owner,
repo,
issue_number: existing.number,
state: "closed",
state_reason: "completed",
});
}
-31
View File
@@ -1,31 +0,0 @@
name: Renovate
on:
schedule:
# Every day at 03:00 JST (18:00 UTC on the previous day).
- cron: "0 18 * * *"
workflow_dispatch:
permissions:
contents: read
concurrency:
group: renovate
cancel-in-progress: false
jobs:
renovate:
name: Update GitHub Actions dependencies
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# Use a PAT or GitHub App token so Renovate PRs trigger the other workflows.
- name: Run Renovate
uses: renovatebot/github-action@3064367f740a1a91cca218698a63902689cce200 # v46.1.20
with:
renovate-version: 43.262.1
token: ${{ secrets.RENOVATE_TOKEN }}
env:
LOG_LEVEL: info
RENOVATE_PLATFORM: github
RENOVATE_REPOSITORIES: ${{ github.repository }}
-106
View File
@@ -1,106 +0,0 @@
name: Update Flake Inputs
on:
schedule:
# Every day at 03:30 JST (18:30 UTC on the previous day).
- cron: "30 18 * * *"
workflow_dispatch:
inputs:
input:
description: Update only this flake input (empty updates all inputs)
required: false
type: string
skip-delay:
description: Update to the latest revision without the three-day delay
required: false
default: false
type: boolean
auto-merge:
description: Enable auto-merge after required checks pass
required: false
default: true
type: boolean
permissions:
contents: write
pull-requests: write
concurrency:
group: update-flake-inputs
cancel-in-progress: false
jobs:
discover:
name: Discover flake inputs
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
matrix: ${{ steps.inputs.outputs.matrix }}
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Build update matrix
id: inputs
env:
REQUESTED_INPUT: ${{ inputs.input }}
run: |
set -euo pipefail
github_inputs="$(
jq -c '
. as $lock
| [
$lock.nodes.root.inputs
| to_entries[]
| .key as $name
| (
.value
| if type == "array" then .[0] else . end
) as $node
| select($lock.nodes[$node].locked.type == "github")
| $name
]
| sort
' flake.lock
)"
if [ -n "$REQUESTED_INPUT" ]; then
if ! jq -e --arg input "$REQUESTED_INPUT" 'index($input) != null' <<< "$github_inputs" >/dev/null; then
echo "::error::Unknown or unsupported flake input: $REQUESTED_INPUT"
exit 1
fi
matrix="$(jq -cn --arg input "$REQUESTED_INPUT" '{input: [$input]}')"
else
matrix="$(jq -cn --argjson inputs "$github_inputs" '{input: $inputs}')"
fi
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
echo "Update matrix: $matrix"
update:
name: Update ${{ matrix.input }}
needs: discover
if: ${{ needs.discover.outputs.matrix != '{"input":[]}' }}
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
max-parallel: 4
matrix: ${{ fromJSON(needs.discover.outputs.matrix) }}
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
token: ${{ secrets.RENOVATE_TOKEN }}
- name: Install Nix
uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0
with:
extra_nix_config: |
experimental-features = nix-command flakes
accept-flake-config = true
access-tokens = github.com=${{ secrets.RENOVATE_TOKEN }}
- name: Update input
uses: ./.github/actions/update-flake-input
with:
input-name: ${{ matrix.input }}
github-token: ${{ secrets.RENOVATE_TOKEN }}
skip-delay: ${{ github.event_name == 'workflow_dispatch' && inputs.skip-delay }}
auto-merge: ${{ github.event_name != 'workflow_dispatch' || inputs.auto-merge }}
+14 -10
View File
@@ -4,6 +4,7 @@
!README.md !README.md
!LICENSE !LICENSE
!AGENTS.md !AGENTS.md
!/docs/
!.github/ !.github/
!.gitea/ !.gitea/
@@ -14,15 +15,18 @@
!/flake.nix !/flake.nix
!/flake.lock !/flake.lock
!/renovate.json
!shells/
!hosts/
!overlays/
!profiles/
!modules/
!docs/
!images/
!secrets/
!/shells/
!/flake/ !/flake/
!/overlays/
!/images/
!/secrets/
!/hosts/
!/libs/
!/modules/
!/tests/
+506 -355
View File
@@ -1,423 +1,574 @@
# AGENTS.md # Repository Guidelines
NixOS + Home Manager flake (v2)。flake-parts ベース。 ## Project Structure and Ownership
## Commands This repository manages NixOS, nix-darwin, and Home Manager configurations as a
flake. `flake.nix` defines inputs and delegates flake outputs through
flake-parts. Keep configuration with the component that owns it, rather than in
the root flake or an unrelated host.
```sh | Path | Responsibility |
nix flake update # flake の更新 | ----------------------- | ----------------------------------------------------------------------------------------------------------------- |
nix fmt # フォーマット (treefmt: nixfmt, deadnix, statix, shfmt, shellcheck, prettier, yamlfmt, taplo, oxfmt) | `modules/applications/` | One software component, including GUI applications, window managers, desktop environments, CLI tools, and editors |
nix develop .#dotnix # 開発シェル (pre-commit hooks, sops, age 入り) | `modules/systems/` | OS foundations such as Nix, boot, locale, Wayland, and networking |
sudo nixos-rebuild build --flake .#<host> # ビルド確認 | `modules/services/` | Daemons, long-running services, and configuration that involves permissions or user groups |
sudo nixos-rebuild switch --flake .#<host> # 適用 | `modules/hardwares/` | Reusable drivers, hardware families, and VM or WSL guest configuration |
``` | `modules/users/` | User identity and the user's NixOS-, nix-darwin-, and Home Manager-specific definitions |
| `modules/profiles/` | Purpose- or form-factor-oriented compositions of multiple units |
| `hosts/` | Machine-specific facts and the profiles or applications selected for each machine |
| `libs/` | Registry, unit discovery, and host construction logic |
| `overlays/` | Package replacements and additions |
| `shells/` | Development shells |
| `flake/` | Supporting flake outputs such as formatters, checks, and Git hooks |
## Conventions Use **unit** as the generic internal term for a Registry-managed component and
**profile** for a unit that composes multiple units. Do not introduce a
`features/` layer. Window managers and desktop environments such as niri and
GNOME belong in `modules/applications/`; do not create a separate `desktop/`
module category.
- User: `moons`, locale: `ja_JP.UTF-8`, timezone: `Asia/Tokyo` Before adding configuration, decide whether it is owned by an application,
- Commits: conventional commits (`feat:`, `chore:`, `fix:`, etc.) system foundation, service, hardware family, user, profile, or individual host.
- リモート: `[email protected]:moons-14/dotfiles.git` Prefer the following placements:
- `environment.systemPackages` にパッケージを追加する際はパッケージ名の横に簡単な説明をコメントで追加する
- 警告を抑制する設定は書かない。根本原因を調査して修正する
- home-manager の `sharedModules` 内で `lib.hm.*` を使う場合は、そのモジュール関数の引数で `lib` を受け取る必要がある(NixOSモジュールの `lib` とは別スコープ)
- `useGlobalPkgs = true` なので、home-manager 内で `nixpkgs.config` を設定しない(NixOSレベルで一括設定)
- `allowUnfree` は `hosts/default.nix` でグローバルに設定済み。各モジュールで個別設定しない
- pre-commit hooks が `git-hooks.nix` で設定済み(treefmt, gitleaks, deadnix, statix, shellcheck)。dev shell で自動有効化
## Secrets | Configuration | Placement |
| ---------------------------------------------------- | ------------------------------------------------ |
| Nix settings shared by every system host | `modules/systems/nix/common.nix` |
| NixOS-only boot configuration | `modules/systems/boot/.../nixos.nix` |
| Ghostty-specific configuration | `modules/applications/ghostty/` |
| niri-specific configuration | `modules/applications/niri/` |
| Applications selected for the niri environment | `modules/profiles/interface/niri/meta.nix` |
| GNOME itself | `modules/applications/gnome/` |
| Docker daemon and Docker group membership | `modules/services/docker/nixos.nix` |
| Reusable ThinkPad-family configuration | `modules/hardwares/thinkpad/` |
| The laptop unit composition | `modules/profiles/platform/laptop/meta.nix` |
| The development-environment unit composition | `modules/profiles/workload/development/meta.nix` |
| A user's OS- and Home Manager-specific configuration | `modules/users/<name>/` |
| x1g13-specific monitor layout | `hosts/x1g13/home.nix` |
| x1g13-specific disk UUID | `hosts/x1g13/nixos.nix` |
| Package replacement or addition | `overlays/` |
| Formatter, checks, or Git hooks | `flake/` |
- **sops-nix** + **age** + **YubiKey** で秘密管理 ## Unit Discovery and Identity
- `.sops.yaml` で暗号化ルール定義、`secrets/` に暗号化済み YAML を配置
- `modules/system/sops.nix` で sops-nix を import、`services.pcscd` (YubiKey用) を有効化
- `modules/system/secret.nix` で `sops.secrets` を宣言
- 平文の秘密をコミットしない(gitleaks が pre-commit で検出)
## Architecture A directory below `modules/` is a unit if, and only if, it directly contains at
least one reserved file. Directories used only for classification, such as
`modules/applications/` or `modules/profiles/interface/`, are namespaces rather
than units when they have no reserved file of their own.
``` The Registry recognizes exactly these five reserved filenames:
flake.nix
├── hosts/default.nix # mkSystem でホスト構成を生成
│ ├── modules/ # 全モジュール(常にインポートされる)
│ │ ├── applications/ # アプリケーション設定
│ │ ├── system/ # システム設定
│ │ ├── drivers/ # ドライバ設定
│ │ ├── features/ # 機能バンドル(application/systemを束ねる)
│ │ └── integrations/ # home-manager 統合
│ └── profiles/ # ホストごとに有効化するfeaturesの組み合わせ
│ ├── interfaces/ # 操作インターフェース (CLI/GUI)
│ ├── platforms/ # ハードウェア (desktop/laptop/thinkpad/vm)
│ └── workloads/ # 用途 (dev/personal/srv/remote/secure-storage)
├── overlays/ # nixpkgs オーバーレイ
├── shells/ # devShells (dotnix)
└── flake/ # formatter.nix, git-hooks.nix
```
### 評価の流れ | File | Target and responsibility |
| ------------ | -------------------------------------------------------------------------------- |
| `common.nix` | System-side configuration fragment shared by NixOS and nix-darwin |
| `nixos.nix` | NixOS-only configuration fragment |
| `darwin.nix` | nix-darwin-only configuration fragment |
| `home.nix` | Home Manager configuration fragment |
| `meta.nix` | Registry descriptor for dependencies, external modules, and descriptive metadata |
``` `common.nix` is never applied to Home Manager. OS-independent Home Manager
profile (featuresの有効化) configuration still belongs in `home.nix`.
→ features (application/systemの有効化 + パッケージ追加)
→ applications (system.nix + home.nix)
→ system (NixOS設定)
```
## Layer Design The Registry derives a unit ID from the path relative to `modules/`, joining
path components with dots. Category names remain plural. It also derives the
enable option by prefixing the same components with `my` and appending `enable`.
### `modules/system/` — NixOS システム設定 | Unit directory | Unit ID | Enable option |
| ---------------------------------- | ------------------------- | ----------------------------------- |
| `modules/applications/ghostty/` | `applications.ghostty` | `my.applications.ghostty.enable` |
| `modules/applications/niri/` | `applications.niri` | `my.applications.niri.enable` |
| `modules/systems/boot/uefi/` | `systems.boot.uefi` | `my.systems.boot.uefi.enable` |
| `modules/services/docker/` | `services.docker` | `my.services.docker.enable` |
| `modules/hardwares/qemu-guest/` | `hardwares.qemu-guest` | `my.hardwares.qemu-guest.enable` |
| `modules/users/moons/` | `users.moons` | `my.users.moons.enable` |
| `modules/profiles/interface/niri/` | `profiles.interface.niri` | `my.profiles.interface.niri.enable` |
OS全体に影響する設定。`config.my.system.*` namespace。 Represent option paths as attribute-path lists, never as Nix source encoded in
strings or evaluated dynamically. Generate and read attributes with helpers such
- audio, boot, camera, disko, fingerprint, fonts, gc, hardware, locale, network, nix, power, secure-boot, sops, user, version, secret as `lib.setAttrByPath` and `lib.getAttrFromPath`:
- 常にインポートされるが、`enable` オプションで実効性を制御
- home-manager の設定は含めない
### `modules/applications/` — アプリケーション設定
個別に有効/無効を切り替えたいアプリケーション。`config.my.applications.*` namespace。
- NixOS設定のみ、または NixOS + Home Manager の両方
- Complex Module は system.nix と home.nix に分離
### `modules/drivers/` — ドライバ設定
ハードウェア固有のドライバ。`config.my.drivers.*` namespace。
### `modules/features/` — 機能バンドル
application や system より抽象度の高い「機能」単位で、複数の application/system を束ねて有効化する層。`config.my.features.*` namespace。
**features がやること:**
1. 複数の `my.applications.*.enable` / `my.system.*.enable` をまとめて有効化
2. application/system に属さないパッケージや設定を直接記述(`environment.systemPackages`、`home.activation` 等)
3. 追加オプションの受け渡し(例: tailscale の `acceptDns` を feature から application に passthrough)
**features がやらないこと:**
- 個別アプリケーションの詳細設定(これは applications 層の責務)
### `profiles/` — ホスト構成
features の `enable` を指定するだけの薄い層。ロジックは書かない。
`profiles/` から `my.system.*.enable` / `my.applications.*.enable` を直接指定しない。
必要な場合は必ず `modules/features/` に feature 層を作り、profile では `my.features.*.enable` のみ指定する。
| カテゴリ | 役割 | 例 |
| ------------- | -------------------- | ------------------------------------------ |
| `interfaces/` | 操作インターフェース | cli-minimal, cli-interactive, gui |
| `platforms/` | ハードウェア固有設定 | desktop, laptop, thinkpad, vm |
| `workloads/` | 用途・ワークロード | dev, personal, srv, remote, secure-storage |
profiles は継承可能:
```nix ```nix
# cli-interactive.nix
{ {
imports = [ ./cli-minimal.nix ]; id = "applications.ghostty";
my.features.cli.interactive.enable = true;
}
```
### `hosts/` — ホスト定義 optionPath = [
"my"
`mkSystem` でホストを定義。profiles のリストを指定: "applications"
"ghostty"
```nix "enable"
nix-example = mkSystem {
host = "nix-example";
system = "x86_64-linux";
profiles = [
"interfaces/cli-interactive"
"platforms/vm"
"workloads/dev"
]; ];
};
kind = "applications";
name = "ghostty";
relativePath = [
"applications"
"ghostty"
];
}
``` ```
`specialArgs` で `inputs`, `username`, `unstable`, `host` が全モジュールに渡される。 For `modules/profiles/interface/niri/`, path inference additionally gives
`kind = "profiles"`, `group = "interface"`, and `name = "niri"`. The path is
always authoritative for identity. `meta.nix` may provide display metadata such
as `description`, but it must not override or alias the unit ID.
**注意:** `installer` ホストは `mkSystem` を使わず直接 `nixosSystem` で定義(インストーラ用)。 ## Unit Files and Fragment Contract
## Module Patterns Only reserved files that a unit actually needs should exist. The Registry
registers present fragments and does not require empty or placeholder files. All
of the following are valid units:
### Simple Module(NixOS のみ) ```text
# Home Manager only
modules/applications/ghostty/
├── home.nix
└── settings.nix
home-manager の設定を含まない。1ファイルで完結: # NixOS only
modules/applications/gnome/
└── nixos.nix
# nix-darwin only
modules/systems/macos-defaults/
└── darwin.nix
# NixOS and Home Manager, with metadata and helpers
modules/applications/niri/
├── nixos.nix
├── home.nix
├── meta.nix
├── settings.nix
└── keybindings.nix
# Metadata only, commonly a composition profile
modules/profiles/interface/niri/
└── meta.nix
# System configuration shared by NixOS and nix-darwin
modules/systems/nix/
└── common.nix
```
If a unit has no `home.nix`, do not generate or apply a Home Manager module for
it. The same rule applies independently to `common.nix`, `nixos.nix`, and
`darwin.nix`.
### Configuration fragments
`common.nix`, `nixos.nix`, `darwin.nix`, and `home.nix` are configuration
fragments to which the Registry adds the enable condition. They return the
configuration for their class directly and must not define top-level `imports`,
`options`, or `config` attributes:
```nix ```nix
# modules/system/audio.nix # modules/services/docker/nixos.nix
{ lib, config, ... }: { primaryUser, ... }:
let
cfg = config.my.system.audio;
in
{ {
options.my.system.audio = { virtualisation.docker = {
enable = lib.mkEnableOption "Audio support (PipeWire)"; enable = true;
autoPrune.enable = true;
}; };
config = lib.mkIf cfg.enable { users.users.${primaryUser}.extraGroups = [
# NixOS設定をここに書く "docker"
];
}
```
Conceptually, the Registry supplies a wrapper like this:
```nix
{ config, lib, ... }@args:
{
config =
lib.mkIf
config.my.services.docker.enable
(import dockerNixosPath args);
}
```
Do not add hand-written `mkEnableOption`, `cfg`, or `mkIf` boilerplate to each
unit. The Registry generates the enable option from the unit path and guards the
fragment.
### Helper files and directories
Every filename other than the five reserved names is an ordinary helper,
regardless of its extension. The Registry neither discovers nor automatically
imports helper files such as `settings.nix`, `keybindings.nix`, `packages.nix`,
`colors.nix`, `rules.nix`, or `helpers.nix`. Import a helper explicitly from the
reserved fragment that uses it:
```nix
# modules/applications/niri/home.nix
{ lib, ... }:
let
settings = import ./settings.nix;
keybindings = import ./keybindings.nix;
in
{
programs.niri.settings = lib.recursiveUpdate settings {
binds = keybindings;
}; };
} }
``` ```
### Simple Module(Home Manager のみ) Do not use a leading underscore to mark a file private; `_settings.nix` has no
special meaning. Give helper files descriptive names instead. When helpers are
NixOS設定を含まず、home-manager のみ: configuration functions, pass the module arguments explicitly and combine them
with normal Nix expressions:
```nix ```nix
# modules/applications/zoom.nix # modules/applications/example/home.nix
{ pkgs, lib, config, ... }: { lib, ... }@args:
let lib.mkMerge [
cfg = config.my.applications.zoom; (import ./packages.nix args)
in (import ./settings.nix args)
]
```
The same discovery rule applies recursively to helper directories:
```text
modules/applications/niri/
├── home.nix
└── parts/
├── appearance.nix
└── keybindings.nix
```
Here `parts/` is not a unit because it directly contains no reserved file. A
helper directory that directly contains `home.nix` or another reserved file is
itself discovered as a unit, so never use reserved filenames inside a directory
that is intended to contain helpers only.
### Registry metadata
`meta.nix` is a Registry descriptor, not a NixOS, nix-darwin, or Home Manager
module. It may declare `description`, `includes`, and class-specific external
module imports:
```nix
# modules/applications/niri/meta.nix
{ inputs, ... }:
{ {
options.my.applications.zoom = { description = "Niri Wayland compositor";
enable = lib.mkEnableOption "Zoom video conferencing";
includes = [
"systems.wayland"
"services.xdg-portal"
];
imports.nixos = [
inputs.niri-flake.nixosModules.niri
];
imports.home = [
inputs.niri-flake.homeModules.niri
];
}
```
External modules, including modules supplied by flake inputs, define Nix module
options and therefore belong in `meta.nix` under `imports.nixos`,
`imports.darwin`, or `imports.home`. Do not place them in a configuration
fragment's top-level `imports`: the Nix module system resolves imports before a
configuration-level enable condition.
`includes` lists units to enable whenever the declaring unit is enabled. Always
use fully qualified unit IDs:
```nix
# modules/profiles/interface/niri/meta.nix
{
includes = [
"applications.niri"
"applications.ghostty"
"applications.noctalia"
"applications.vicinae"
"applications.nautilus"
"services.xdg-portal"
];
}
```
Never omit a prefix such as `applications.` merely because the including unit is
a profile. Fully qualified IDs make ownership explicit and allow moves, name
collisions, and missing dependencies to be detected. Do not enable another unit
by assigning to its enable option from a class fragment; declare the dependency
in `meta.includes`.
Application metadata should include only dependencies technically required for
the application to work. A profile owns the user's choice to adopt several
otherwise independent applications together. For example, niri may include the
Wayland foundation and xdg-desktop-portal as technical dependencies, while
`profiles.interface.niri` selects Ghostty, Vicinae, Noctalia, and Nautilus.
Ghostty must not depend on niri, and niri-specific keybindings remain owned by
the niri unit.
## Profiles
Profiles compose units by purpose or form factor; they do not replace clear
application, system, service, or hardware ownership. Suitable profile namespaces
include:
```text
modules/profiles/
├── base/
├── interface/
│ ├── niri/
│ ├── gnome/
│ ├── cli-minimal/
│ └── cli-interactive/
├── platform/
│ ├── laptop/
│ ├── thinkpad/
│ ├── desktop/
│ ├── vm/
│ └── wsl/
├── workload/
│ ├── development/
│ ├── personal/
│ ├── server/
│ └── remote/
└── security/
└── secure-boot/
```
The `desktop/` name above is a form-factor profile under `profiles/platform/`,
not a top-level module category.
A profile may consist only of `meta.includes`. Small settings that belong only
to the composition and have no useful independent identity may go directly in
the profile's `nixos.nix`, `darwin.nix`, or `home.nix`. Extract configuration to
an appropriate application, system, service, or hardware unit when any of these
conditions holds:
- It should be independently enableable.
- Multiple profiles reuse it.
- It owns separate configuration files.
- Its NixOS, nix-darwin, and Home Manager implementations differ.
- Other units depend on it.
- It involves a daemon, permissions, or user groups.
## Registry Responsibilities
Implement unit discovery with Nix standard functionality such as
`builtins.readDir`. Do not depend on an external indiscriminate auto-import
mechanism, and do not design the repository around `import-tree`. Registry logic
has these responsibilities:
1. Recursively visit directories below `modules/`.
2. Check only the five reserved filenames directly within each directory.
3. Register a directory as a unit when at least one reserved file exists there.
4. Derive the unit ID from the path relative to `modules/`.
5. Record only class fragments that exist.
6. Evaluate `meta.nix` as a descriptor only when it exists.
7. Exclude non-reserved files from discovery and implicit imports.
8. Generate every unit's `my.<unit path>.enable` option.
9. Enable included units from `meta.includes`.
10. Raise a clear evaluation error for a reference to a missing unit ID.
11. Apply only the fragments appropriate to the current host class.
12. Pass `home.nix` to Home Manager only for hosts that enable Home Manager.
A unit record may conceptually look like this; the implementation need not use
this exact representation:
```nix
{
id = "applications.ghostty";
directory = ./applications/ghostty;
fragments = {
common = null;
nixos = null;
darwin = null;
home = ./applications/ghostty/home.nix;
}; };
config = lib.mkIf cfg.enable { meta = { };
home-manager.sharedModules = [ }
{ ```
home.packages = with pkgs; [
zoom-us # Video conferencing application Keep the custom Registry limited to unit discovery, enable-option generation,
]; `includes`, and class dispatch. Do not reimplement general Nix imports or Nix
} module evaluation. In particular, never infer a unit ID from metadata or
implicitly load a non-reserved file.
## Hosts and Class Dispatch
`hosts/` is outside Registry discovery. A host contains machine-specific facts,
differences, and unit selection, not reusable shared configuration. Appropriate
host-owned data includes:
- Generated `hardware-configuration.nix`.
- Disk UUIDs and disko target devices.
- Monitor identifiers, layout, and scale.
- MAC addresses and static IP addresses.
- Kernel parameters required by one machine only.
- `system.stateVersion`.
- Host-specific secret references.
- The profiles, applications, and other units enabled on that host.
A host registry may use a specification like this:
```nix
# hosts/default.nix
{
x1g13 = {
system = "x86_64-linux";
user = "moons";
path = ./x1g13;
profiles = [
"base"
"platform.thinkpad"
"interface.niri"
"interface.gnome"
"workload.development"
"workload.personal"
"security.secure-boot"
];
applications = [
"codex-desktop"
];
units = [
"services.tailscale"
];
};
macbook = {
system = "aarch64-darwin";
user = "moons";
path = ./macbook;
profiles = [
"base"
"platform.laptop"
"workload.development"
"workload.personal"
];
applications = [
"ghostty"
]; ];
}; };
} }
``` ```
### Complex Module(NixOS + Home Manager) Treat entries in `profiles` and `applications` as IDs relative to their
respective category roots. Add the category prefixes during host construction:
ディレクトリ構造で system と home を分離:
```
modules/applications/<app>/
├── default.nix # マスター enable + imports
├── system.nix # NixOS 設定
├── home.nix # Home Manager 設定
└── (other files) # 設定ファイル等
```
**default.nix** — `enable` のみ宣言。`system.enable`/`homeManager.enable` は sub-file に任せる:
```nix ```nix
{ selectedUnits =
lib, [ "users.${spec.user}" ]
config, ++ map (name: "profiles.${name}") spec.profiles
... ++ map (name: "applications.${name}") spec.applications
}: ++ spec.units or [ ];
let
cfg = config.my.applications.<name>;
in
{
imports = [
./home.nix
./system.nix
];
options.my.applications.<name> = {
enable = lib.mkEnableOption "<description>";
};
config = lib.mkIf cfg.enable {
my.applications.<name>.system.enable = lib.mkDefault true;
my.applications.<name>.homeManager.enable = lib.mkDefault true;
};
}
``` ```
**system.nix:** `units` is an escape hatch for fully qualified service, system, hardware, or
other unit IDs. Prefer profiles for the main composition; do not make hosts list
large numbers of low-level units directly.
```nix Host modules use normal Nix module semantics and are not Registry-guarded
{ configuration fragments. For example:
pkgs,
lib,
config,
...
}:
let
cfg = config.my.applications.<name>.system;
in
{
options.my.applications.<name>.system = {
enable = lib.mkEnableOption "<name> system configuration";
};
config = lib.mkIf cfg.enable { ```text
environment.systemPackages = with pkgs; [ hosts/x1g13/
<package> # Description ├── nixos.nix
]; ├── home.nix
}; ├── hardware-configuration.nix
} └── disko.nix
``` ```
**home.nix** — `home-manager.sharedModules` を使用: `hosts/x1g13/nixos.nix` may explicitly load `hardware-configuration.nix` and
`disko.nix` with the normal top-level Nix module `imports`. Do not confuse these
host imports with the prohibition on top-level `imports` in unit configuration
fragments.
```nix Derive the system class from the host's `system`:
{
lib,
config,
...
}:
let
cfg = config.my.applications.<name>;
hmCfg = config.my.applications.<name>.homeManager;
in
{
options.my.applications.<name>.homeManager = {
enable = lib.mkEnableOption "<name> home-manager configuration";
};
config.home-manager.sharedModules = [ - A Linux NixOS host receives `common.nix` and `nixos.nix`.
{ - A nix-darwin host receives `common.nix` and `darwin.nix`.
config = lib.mkIf hmCfg.enable { - A host with integrated Home Manager additionally receives `home.nix`.
# home-manager 設定をここに書く
};
}
];
}
```
**注意点:** Home Manager is additive, not a system class mutually exclusive with NixOS or
nix-darwin. The supported combinations are NixOS plus Home Manager and
nix-darwin plus Home Manager. If standalone Home Manager is supported later, add
an explicit host kind because `system` alone cannot distinguish it from NixOS.
- `default.nix` では `enable` のみ宣言。`system.enable`/`homeManager.enable` は `system.nix`/`home.nix` で宣言する(重複宣言エラー回避) Do not duplicate reusable settings in hosts, but do not force genuinely
- home.nix で親の `cfg` を参照する場合は `cfg` と `hmCfg` の両方を let で定義 machine-specific values into a common unit merely to remove a host-local line.
- Complex Module の home-manager 設定は `home-manager.sharedModules` で記述(`home-manager.users.<user>` は使わない)
### Feature Module ## Coding Style and Implementation Rules
**application/system を束ねる場合:** Use two-space indentation in Nix files and let `nixfmt` decide layout. Prefer
small units, explicit imports, and descriptive kebab-case names, for example
`modules/services/media-server/nixos.nix`. Use camelCase for Nix attributes
unless an upstream option dictates otherwise. Shell snippets must pass `shfmt`
and `shellcheck`; YAML, TOML, and Markdown are formatted by the configured
treefmt tools.
```nix When implementing or modifying modules:
# modules/features/services/container.nix
{ lib, config, ... }:
let
cfg = config.my.features.services.container;
in
{
options.my.features.services.container = {
enable = lib.mkEnableOption "Container runtime (Docker)";
};
config = lib.mkIf cfg.enable { - Do not create `features/` or a top-level `desktop/` module category.
my.applications.docker.enable = true; - Do not add per-unit `mkEnableOption`, `cfg`, or `mkIf` boilerplate; the Registry
}; derives and guards enable options from paths.
} - Put unit dependencies in `meta.includes`, not in direct assignments to another
``` unit's enable option from a class fragment.
- Do not assume any non-reserved file is discovered or loaded automatically.
- Do not require an `_` prefix for helper or private files.
- Do not create unused `common.nix`, `nixos.nix`, `darwin.nix`, `home.nix`, or
`meta.nix` files.
- Do not override a path-derived unit ID from `meta.nix`.
- Keep technical application dependencies separate from the applications a
personal environment chooses to combine in a profile.
- Do not rely on module-list ordering to override values. Use Nix module
priorities such as `lib.mkDefault`, `lib.mkForce`, `lib.mkBefore`, or
`lib.mkAfter` explicitly when required.
- Keep Registry responsibilities narrow; use normal Nix imports and module
evaluation for everything outside discovery, generated enables, includes, and
class dispatch.
**パッケージを直接追加する場合(application/system に属さない):** ## Build, Test, and Development Commands
```nix - `nix develop .#dotnix` enters the main development shell and installs the
# modules/features/gui/capture.nix repository's pre-commit hooks.
{ pkgs, lib, config, ... }: - `nix develop .#android` provides Android platform tools such as `adb` and
let `fastboot`.
cfg = config.my.features.gui.capture; - `nix fmt` formats all supported files through treefmt.
in - `nix flake check` evaluates flake outputs and runs configured checks.
{ - `pre-commit run --all-files` runs formatting, dead-code and static Nix checks,
options.my.features.gui.capture = { shell linting, and secret scanning.
enable = lib.mkEnableOption "Screen capture tools"; - `nix flake update` refreshes pinned inputs in `flake.lock`; review lockfile
}; changes before committing.
config = lib.mkIf cfg.enable { If direnv is installed, `direnv allow` activates the `dotnix` shell from `.envrc`
environment.systemPackages = with pkgs; [ automatically.
slurp # Tool for selecting a region of the screen
grim # Screenshot tool for Wayland
];
};
}
```
**オプションを passthrough する場合:** ## Testing Guidelines
```nix There is no separate unit-test suite. Before submitting changes, run
# modules/features/network/tailscale.nix `nix flake check` and `pre-commit run --all-files`. For system-specific changes,
{ also build or evaluate the affected NixOS, nix-darwin, or Home Manager
options.my.features.network.tailscale = { configuration without switching the live machine. Never commit generated
enable = lib.mkEnableOption "Tailscale VPN"; secrets, `.age` plaintext, or local `.direnv/` state.
acceptDns = lib.mkOption { type = lib.types.bool; default = false; };
};
config = lib.mkIf cfg.enable { For Registry changes, test discovery of each supported fragment combination,
my.applications.tailscale = { dependency closure through `meta.includes`, missing-unit errors, and class
enable = true; dispatch. Verify that helper files are ignored until explicitly imported and
inherit (cfg) acceptDns; that directories without a directly contained reserved file remain namespaces.
};
};
}
```
**home.activation を使う場合(identity 等):** ## Commit and Pull Request Guidelines
```nix Recent history favors short, lowercase, imperative subjects such as `fix` and
# modules/features/identity/ssh-default-key.nix `update action`; automated dependency commits use `chore(deps): ...`. Prefer a
config.home-manager.sharedModules = [ specific summary that states the affected area, such as
( `shells: add deployment tools`. Keep commits focused. Pull requests should
{ lib, ... }: explain the motivation, list affected hosts or profiles, report validation
{ commands, and note any manual migration or secret-management steps. Include
config = lib.mkIf cfg.enable { screenshots only for visible desktop or application configuration changes.
home.activation.generateSshKey = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
# shell script here
'';
};
}
)
];
```
### Profile
features の有効化のみを記述:
```nix
# profiles/platforms/laptop.nix
{
my.features = {
boot.power.enable = true;
connect = {
wifi.enable = true;
bluetooth.enable = true;
};
gui.camera.enable = true;
identity.fingerprint.enable = true;
network.tailscale.enable = true;
};
}
```
## Adding New Features — Checklist
### 新しいアプリケーションを追加する場合
1. `modules/applications/` にモジュール作成(Simple or Complex)
2. `modules/applications/default.nix` の `imports` に追加
3. `modules/features/` の適切なカテゴリに feature を作成(既存の feature に追記でも可)
4. `modules/features/<category>/default.nix` の `imports` に追加
5. `profiles/` の適切な profile で feature を有効化
### 新しいシステム設定を追加する場合
1. `modules/system/` にモジュール作成(Simple Module)
2. `modules/system/default.nix` の `imports` に追加
3. `modules/features/` の適切なカテゴリに feature を作成
4. `modules/features/<category>/default.nix` の `imports` に追加
5. `profiles/` の適切な profile で feature を有効化
### 新しいホストを追加する場合
1. `hosts/<hostname>/default.nix` を作成(`hardware-configuration.nix` を import)
2. `hosts/default.nix` の `flake.nixosConfigurations` に `mkSystem` で追加
3. profiles のリストを指定
## Key Technical Notes
- **nixpkgs channel**: `nixos-26.05` (stable) + `nixpkgs-unstable`
- **unstable パッケージ**: `specialArgs.unstable` 経由で参照(`unstable.<pkg>`)
- **llm-agents**: `inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.<name>` で参照。overlay も `hosts/default.nix` でグローバルに適用
- **nixvim**: `nixpkgs.source = pkgs.path` と `nixpkgs.config.allowUnfree = true` を vim/home/default.nix で設定
- **home-manager**: `useGlobalPkgs = true`, `useUserPackages = true`, `backupFileExtension = "backup"`
- **hostname**: `specialArgs.host` から `modules/system/network/default.nix` で `networking.hostName` に設定
- **stateVersion**: `config.my.stateVersions.nixos` / `config.my.stateVersions.homeManager` で管理(`modules/system/version.nix`)
- **disko**: `modules/system/disko.nix` で disk パーティション管理。ホスト固有の `disko.nix` を import
- **stylix**: `inputs.stylix` でテーマ管理
+2 -190
View File
@@ -1,191 +1,3 @@
# dotfiles # moons14 dotfiles
My NixOS + Home Manager configurations built with flake-parts. My NixOS + Home Manager configurations build with flake.
## Overview
- **OS**: NixOS 26.05 (stable) + nixpkgs-unstable
- **Window Manager**: Niri (Wayland)
- **Shell**: Zsh
- **Terminal**: Ghostty
- **Editor**: Neovim (nixvim), VSCode
- **Launcher**: Vicinae
- **Theme**: Stylix (Dracula)
- **Secrets**: sops-nix + age + YubiKey
## Hosts
| Host | Description | Profiles |
| ------------- | --------------- | -------------------------------------------- |
| `x1g13` | ThinkPad laptop | gui, thinkpad, dev, personal, secure-storage |
| `nix-example` | VM | cli-interactive, vm, dev, remote |
| `installer` | NixOS installer | (standalone) |
## Directory Structure
```
.
├── flake.nix # Flake inputs and outputs
├── flake/
│ ├── formatter.nix # treefmt configuration (nixfmt, deadnix, statix, etc.)
│ └── git-hooks.nix # pre-commit hooks
├── hosts/
│ ├── default.nix # mkSystem helper and host definitions
│ ├── x1g13/ # ThinkPad host config
│ ├── nix-example/ # VM host config
│ └── installer/ # Installer ISO config
├── modules/
│ ├── applications/ # Application configs (NixOS + Home Manager)
│ │ ├── niri/ # Wayland compositor
│ │ ├── ghostty/ # Terminal emulator
│ │ ├── vim/ # Neovim (nixvim)
│ │ ├── vscode/ # VSCode
│ │ ├── zsh/ # Shell
│ │ ├── zellij/ # Terminal multiplexer
│ │ ├── git/ # Git config
│ │ ├── docker.nix # Container runtime
│ │ ├── tailscale.nix # VPN
│ │ ├── claude/ # Claude Code
│ │ ├── opencode.nix # OpenCode
│ │ └── ... # chrome, discord, zoom, slack, etc.
│ ├── system/ # NixOS system configs
│ │ ├── audio.nix # PipeWire
│ │ ├── boot/ # Bootloader (systemd-boot, lanzaboote)
│ │ ├── disko.nix # Disk partitioning
│ │ ├── fonts.nix # Fonts
│ │ ├── network/ # Networking
│ │ ├── sops.nix # Secrets management
│ │ ├── user/ # User accounts
│ │ └── ...
│ ├── features/ # Feature bundles (abstraction layer)
│ │ ├── application/ # browser, communication
│ │ ├── boot/ # UEFI
│ │ ├── cli/ # base, interactive, shell
│ │ ├── connect/ # WiFi, Bluetooth
│ │ ├── dev/ # agent, nix, python, bun, java, arduino
│ │ ├── gui/ # desktop, terminal, audio, editor, capture
│ │ ├── identity/ # SSH key, fingerprint
│ │ ├── network/ # Tailscale
│ │ ├── services/ # container, KDE
│ │ └── storage/ # disko
│ ├── drivers/ # Hardware drivers (Intel)
│ └── integrations/ # Home Manager integration
├── profiles/
│ ├── interfaces/ # cli-minimal, cli-interactive, gui
│ ├── platforms/ # desktop, laptop, thinkpad, vm
│ └── workloads/ # dev, personal, srv, remote, secure-storage
├── overlays/ # nixpkgs overlays
├── shells/ # devShells (pre-commit hooks, sops, age)
├── secrets/ # Encrypted secrets (sops)
└── docs/ # Documentation
```
## Architecture
```
profile (enable features)
→ features (bundle applications/system + add packages)
→ applications (system.nix + home.nix)
→ system (NixOS config)
```
### Module Patterns
**Simple Module** — Single file for NixOS-only or Home Manager-only configs:
```nix
{ lib, config, ... }:
let cfg = config.my.system.audio;
in {
options.my.system.audio.enable = lib.mkEnableOption "Audio";
config = lib.mkIf cfg.enable { ... };
}
```
**Complex Module** — Directory with `default.nix`, `system.nix`, `home.nix`:
```
modules/applications/<app>/
├── default.nix # Master enable + imports
├── system.nix # NixOS config
└── home.nix # Home Manager config (sharedModules)
```
**Feature Module** — Bundles multiple applications/system modules:
```nix
{ lib, config, ... }:
let cfg = config.my.features.gui.desktop;
in {
options.my.features.gui.desktop.enable = lib.mkEnableOption "Desktop";
config = lib.mkIf cfg.enable {
my.applications = { niri.enable = true; gtk.enable = true; ... };
};
}
```
**Profile** — Thin layer that only enables features:
```nix
{
my.features = {
gui.desktop.enable = true;
dev.agent.enable = true;
};
}
```
## Packages
### CLI
- **Shell**: Zsh with zoxide, direnv
- **Terminal multiplexer**: Zellij
- **Editor**: Neovim (nixvim)
- **Tools**: ripgrep, curl, wget, htop, btop, fastfetch, unzip, unrar
### GUI
- **Compositor**: Niri
- **Terminal**: Ghostty, Alacritty
- **Editor**: VSCode
- **Browser**: Chrome
- **Launcher**: Vicinae
- **File manager**: Nautilus
- **Communication**: Discord, Zoom, Slack
### Development
- **AI agents**: Claude Code, Codex, OpenCode, Grok
- **Languages**: Python, Bun (JavaScript/TypeScript), Java, Arduino
- **Container**: Docker
- **Nix**: nh, nixfmt, deadnix, statix
### System
- **VPN**: Tailscale
- **Secrets**: sops-nix, age
- **Boot**: systemd-boot, lanzaboote (Secure Boot)
- **Disk**: disko
- **Theme**: Stylix
## Commands
```sh
nix flake update # Update flake inputs
nix fmt # Format code
nix develop .#dotnix # Enter dev shell
sudo nixos-rebuild switch --flake .#<host> # Apply config
sudo nixos-rebuild build --flake .#<host> # Build without applying
```
## Inspired
- [Zaney/zaneyos](https://gitlab.com/Zaney/zaneyos)
- [fa0311/.zshrc](https://gist.github.com/fa0311/d37d53ff39c73c54c883379e8e3732df)
- [AsianLovesLinux/Niri](https://github.com/AsianLovesLinux/Niri)
- [natsukium/dotfiles](https://github.com/natsukium/dotfiles)
- [dracula](https://github.com/dracula)
- [akazdayo/nix-configs](https://github.com/akazdayo/nix-configs)
- [yutakobayashidev/dotnix](https://github.com/yutakobayashidev/dotnix)
- [kawaemon/dotfiles](https://github.com/kawaemon/dotfiles)
Generated
+51 -72
View File
@@ -107,11 +107,11 @@
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
}, },
"locked": { "locked": {
"lastModified": 1784823421, "lastModified": 1785087211,
"narHash": "sha256-/EM7Cr2Ai0VjNbKv+eIW0+iXT/NBW6WbPkCbf9w+u/o=", "narHash": "sha256-lmIZA1LPcCB7vPagN3lS5mcSwVgN5GAvOxHS7CjqWzI=",
"owner": "ilysenko", "owner": "ilysenko",
"repo": "codex-desktop-linux", "repo": "codex-desktop-linux",
"rev": "efcf40b5ab41323c8fa8eef5526c6a50c45fd8cd", "rev": "bc7b92cf38c74b49dbff568257542eabbc62cf55",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -376,11 +376,11 @@
"zon2nix": "zon2nix" "zon2nix": "zon2nix"
}, },
"locked": { "locked": {
"lastModified": 1784838920, "lastModified": 1785080636,
"narHash": "sha256-MTNHCKPqwjVcYh+DglaR3cQugMCz1kWDajV/nx2PDhw=", "narHash": "sha256-ACiF5qaL4yiLSMesIPi4+4Aftw5QiMSR2qzp+5hlMvo=",
"owner": "moons-14", "owner": "moons-14",
"repo": "ghostty", "repo": "ghostty",
"rev": "5c258c2530878166893d805fba7079b9742f1fc5", "rev": "f11e5199a6bd2a2e1a536d3c225a7d9a39827ea4",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -469,27 +469,6 @@
"type": "github" "type": "github"
} }
}, },
"home-manager_3": {
"inputs": {
"nixpkgs": [
"nix-hazkey",
"nixpkgs"
]
},
"locked": {
"lastModified": 1778444552,
"narHash": "sha256-f18pIiR9q/p1vHY93gmAum7aHhQOG49oGvAB9+lptRo=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "dcebe66f958673729896eec2de4abfd86ef22d21",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"lanzaboote": { "lanzaboote": {
"inputs": { "inputs": {
"crane": "crane", "crane": "crane",
@@ -522,11 +501,11 @@
"treefmt-nix": "treefmt-nix" "treefmt-nix": "treefmt-nix"
}, },
"locked": { "locked": {
"lastModified": 1784838505, "lastModified": 1785094920,
"narHash": "sha256-v9wgz4KSm259C+Yb9/Y/tPyylXto/bTyOQyiV599Ads=", "narHash": "sha256-RPhNusC9jaFUkdgb6COZofPz1QLqmm8k2k9Wh7KcQII=",
"owner": "numtide", "owner": "numtide",
"repo": "llm-agents.nix", "repo": "llm-agents.nix",
"rev": "b358b1d5b458d6bd9814d02b70fcd3c0cd61886f", "rev": "56dea2a2de7d50461c502db975e766c2c2d71e84",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -545,11 +524,11 @@
"xwayland-satellite-unstable": "xwayland-satellite-unstable" "xwayland-satellite-unstable": "xwayland-satellite-unstable"
}, },
"locked": { "locked": {
"lastModified": 1784686714, "lastModified": 1784874881,
"narHash": "sha256-6HCWRBQq/U2NPY2msnnysnWczZYzEzhS1UZURmrr2f0=", "narHash": "sha256-u4jhSIf/Un0qZB+Cn3hTTaHSI20XeAxYbA5o4faqdJs=",
"owner": "sodiboo", "owner": "sodiboo",
"repo": "niri-flake", "repo": "niri-flake",
"rev": "4dfd38bad6150c07be6cc3fd7682787765092eea", "rev": "ef7a2a3d719af46b906c22a3ebfb7d65627b2cd2",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -591,24 +570,24 @@
"type": "github" "type": "github"
} }
}, },
"nix-hazkey": { "nix-darwin": {
"inputs": { "inputs": {
"home-manager": "home-manager_3",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
] ]
}, },
"locked": { "locked": {
"lastModified": 1779529080, "lastModified": 1783744694,
"narHash": "sha256-CHa5L3I71NbPUNJp1gmmwbh91tKsZPyuRK50mLHjAVY=", "narHash": "sha256-2cp6N3rrwnGYLTx9l6N+NI+kwrCWxvJUbj5WJhvB29A=",
"owner": "aster-void", "owner": "nix-darwin",
"repo": "nix-hazkey", "repo": "nix-darwin",
"rev": "24cb2926666836988e78ceebeb67ad6c5a387ac3", "rev": "c3e90c89649b07d1a96e4b9dd6cd0d6e44b91a74",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "aster-void", "owner": "nix-darwin",
"repo": "nix-hazkey", "ref": "nix-darwin-26.05",
"repo": "nix-darwin",
"type": "github" "type": "github"
} }
}, },
@@ -619,11 +598,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1784440659, "lastModified": 1785046085,
"narHash": "sha256-Q5kNLlWngt7TaIIZoxDKWMHjiSaNRVqr70FqWCRRfr4=", "narHash": "sha256-UiK+mmZJuLWQVhJ5b2wDzogIYWAesyRm6LA3h3Ulh3Y=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nix-index-database", "repo": "nix-index-database",
"rev": "4f8d52a3598b0dc7db7a5e7b419e3edd9d1ecfdb", "rev": "11665045df8b9938ef811a3bfdc65cffb02b4b70",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -735,11 +714,11 @@
}, },
"nixpkgs-unstable": { "nixpkgs-unstable": {
"locked": { "locked": {
"lastModified": 1784555310, "lastModified": 1784963784,
"narHash": "sha256-/FCliTPgiuV1owejZFNx3Ch9irdvkOfOFl+HHZ+DrtM=", "narHash": "sha256-IZAjgNI19TdwYus6QUIMvU0cw0vWVb/5oYwpyxQXL1E=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "421eebfd0ec7bccd4abe826ce62d7e6e83129493", "rev": "38affae6a5768f9b61f81355c7558ee971b2afb1",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -812,11 +791,11 @@
}, },
"nixpkgs_4": { "nixpkgs_4": {
"locked": { "locked": {
"lastModified": 1784555310, "lastModified": 1784872115,
"narHash": "sha256-/FCliTPgiuV1owejZFNx3Ch9irdvkOfOFl+HHZ+DrtM=", "narHash": "sha256-THPEF2po0fsoH8gNtp+Ae0XFDJH3N/ol7xO3v6VMTJU=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "421eebfd0ec7bccd4abe826ce62d7e6e83129493", "rev": "335f0738cb2fa9708f3f428e39d2eae975d1338d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -828,11 +807,11 @@
}, },
"nixpkgs_5": { "nixpkgs_5": {
"locked": { "locked": {
"lastModified": 1784497964, "lastModified": 1784796856,
"narHash": "sha256-vlHUuqAcbcH2RKmHbPiuQzbv1pnzzavXnI62RD0bqCU=", "narHash": "sha256-wWFrV5/Qbm+lyt5x20E/bSbfJiGKMo4RCxZV8cl/WZI=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "241313f4e8e508cb9b13278c2b0fa25b9ca27163", "rev": "e2587caef70cea85dd97d7daab492899902dbf5d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -873,11 +852,11 @@
}, },
"nixpkgs_8": { "nixpkgs_8": {
"locked": { "locked": {
"lastModified": 1784707089, "lastModified": 1784856561,
"narHash": "sha256-2V/6imsUgB7mPZlHY54oeVBRDoZbPKnvzwkAHUSSufk=", "narHash": "sha256-J+Bx1Z6Oeoj2FgnBhRMKyUhhtDoOpTgXYaVLZpDjW4A=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "b3fe9581c9061c749abef42b6d4ee7b7c05c33fa", "rev": "597283ad8aa0b331c788e97c4c262d58877074ef",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -933,11 +912,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1784838755, "lastModified": 1785099099,
"narHash": "sha256-aTAnFf29sIVE9hpolWePKscnJfkJRdyFxEkdeP4y9YQ=", "narHash": "sha256-hwmc/ov72HfpuZvLX7KvaHFw3cI4KTD+O5znR9a7pcI=",
"owner": "noctalia-dev", "owner": "noctalia-dev",
"repo": "noctalia", "repo": "noctalia",
"rev": "45ec7e33885540427d766c4a54e8f813dfdc9db9", "rev": "02a846f5c947da00f3d4acdf7cf00f056d92fe3d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1004,7 +983,7 @@
"lanzaboote": "lanzaboote", "lanzaboote": "lanzaboote",
"llm-agents": "llm-agents", "llm-agents": "llm-agents",
"niri-flake": "niri-flake", "niri-flake": "niri-flake",
"nix-hazkey": "nix-hazkey", "nix-darwin": "nix-darwin",
"nix-index-database": "nix-index-database", "nix-index-database": "nix-index-database",
"nixos-hardware": "nixos-hardware", "nixos-hardware": "nixos-hardware",
"nixos-wsl": "nixos-wsl", "nixos-wsl": "nixos-wsl",
@@ -1207,16 +1186,16 @@
}, },
"systems_6": { "systems_6": {
"locked": { "locked": {
"lastModified": 1689347949, "lastModified": 1681028828,
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems", "owner": "nix-systems",
"repo": "default-linux", "repo": "default",
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nix-systems", "owner": "nix-systems",
"repo": "default-linux", "repo": "default",
"type": "github" "type": "github"
} }
}, },
@@ -1360,11 +1339,11 @@
"systems": "systems_7" "systems": "systems_7"
}, },
"locked": { "locked": {
"lastModified": 1784839524, "lastModified": 1785027961,
"narHash": "sha256-B87U5HDB/6JPxSnNQwnIiwtqUWvyxzqs7lV/GPzva68=", "narHash": "sha256-F8yaTqRGGKzl5QTtMM+4I2zUCpOq4or7zuzTmXSiTMA=",
"owner": "vicinaehq", "owner": "vicinaehq",
"repo": "vicinae", "repo": "vicinae",
"rev": "632ca79e9f8fc9721384921f28ec069ff48aaf53", "rev": "65c973b55df4b8f9a80e1663feeca570e3bf016c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1383,11 +1362,11 @@
"vicinae": "vicinae_2" "vicinae": "vicinae_2"
}, },
"locked": { "locked": {
"lastModified": 1784504910, "lastModified": 1785084836,
"narHash": "sha256-fzPBEJZiRvc/FNMdpbdcfaZzF01U4IQenHW9IQFzhos=", "narHash": "sha256-iww/OcxGK8isAgNh8k4E1IJkR5bGUvEK/K+q86g2ISk=",
"owner": "vicinaehq", "owner": "vicinaehq",
"repo": "extensions", "repo": "extensions",
"rev": "ca74eede9a778a9373c8f5fd221b0a5026dcd1ef", "rev": "2d5176bcb19498ff862ca1caa5eb97f03f60faac",
"type": "github" "type": "github"
}, },
"original": { "original": {
+8 -14
View File
@@ -11,6 +11,11 @@
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
nix-darwin = {
url = "github:nix-darwin/nix-darwin/nix-darwin-26.05";
inputs.nixpkgs.follows = "nixpkgs";
};
# Hardware / Platform # Hardware / Platform
nixos-hardware.url = "github:NixOS/nixos-hardware/master"; nixos-hardware.url = "github:NixOS/nixos-hardware/master";
nixos-wsl.url = "github:nix-community/NixOS-WSL"; nixos-wsl.url = "github:nix-community/NixOS-WSL";
@@ -36,10 +41,7 @@
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
noctalia = { noctalia.url = "github:noctalia-dev/noctalia/cachix";
url = "github:noctalia-dev/noctalia";
inputs.nixpkgs.follows = "nixpkgs";
};
# Editor # Editor
nixvim = { nixvim = {
@@ -91,13 +93,7 @@
}; };
# Systems # Systems
systems.url = "github:nix-systems/default-linux"; systems.url = "github:nix-systems/default";
# Japanese Input Method
nix-hazkey = {
url = "github:aster-void/nix-hazkey";
inputs.nixpkgs.follows = "nixpkgs";
};
}; };
outputs = outputs =
@@ -111,10 +107,8 @@
imports = [ imports = [
./overlays ./overlays
./hosts
./shells ./shells
./flake/formatter.nix ./flake
./flake/git-hooks.nix
]; ];
}; };
} }
+7
View File
@@ -0,0 +1,7 @@
{
imports = [
./formatter.nix
./git-hooks.nix
./registry.nix
];
}
+54
View File
@@ -0,0 +1,54 @@
{
inputs,
lib,
...
}:
let
dotfilesLib = import ../libs {
inherit inputs lib;
root = ../.;
};
hostSpecsPath = ../hosts/default.nix;
hostSpecs =
if builtins.pathExists hostSpecsPath then
let
value = import hostSpecsPath;
in
if builtins.isFunction value then
value (
builtins.intersectAttrs (builtins.functionArgs value) {
inherit inputs lib;
}
)
else
value
else
{ };
configurations = dotfilesLib.hosts.mkConfigurations hostSpecs;
in
{
flake = {
inherit (configurations) darwinConfigurations nixosConfigurations;
lib = dotfilesLib;
};
perSystem =
{ pkgs, system, ... }:
let
nixosChecks =
lib.mapAttrs' (name: nixos: lib.nameValuePair "nixos-${name}" nixos.config.system.build.toplevel)
(
lib.filterAttrs (
_: nixos: nixos.pkgs.stdenv.hostPlatform.system == system
) configurations.nixosConfigurations
);
in
{
checks = {
registry = import ../tests/registry.nix {
inherit inputs lib pkgs;
};
}
// nixosChecks;
};
}
+22 -107
View File
@@ -1,113 +1,28 @@
{ {
inputs, x1g9 = {
config, system = "x86_64-linux";
lib, stateVersion = "26.05";
... user = "moons";
}: path = ./x1g9;
let
inherit (inputs.nixpkgs.lib) nixosSystem;
username = "moons"; profiles = [
"base"
mkSystem = "interface.gui"
{ "platform.thinkpad"
host, "workload.personal"
system, "workload.tailscale.client"
profiles ? [ ], ];
extraModules ? [ ],
}:
let
unstable = import inputs.nixpkgs-unstable {
inherit system;
config = {
allowUnfree = true;
};
};
in
assert lib.assertMsg (lib.elem system config.systems)
"mkSystem: system '${system}' not in valid systems: ${lib.generators.toPretty { } config.systems}";
nixosSystem {
inherit system;
modules = [
{
nixpkgs.config.allowUnfree = true;
nixpkgs.overlays = builtins.attrValues inputs.self.overlays;
}
../modules
./${host}/default.nix
]
++ map (p: ../profiles/${p}.nix) profiles
++ extraModules;
specialArgs = {
inherit
inputs
username
unstable
host
;
};
};
nixosConfigurations = {
nix-example = mkSystem {
host = "nix-example";
system = "x86_64-linux";
profiles = [
"interfaces/cli-interactive"
"platforms/vm"
"workloads/dev"
"workloads/remote"
];
};
ops = mkSystem {
host = "ops";
system = "x86_64-linux";
profiles = [
"interfaces/cli-interactive"
"platforms/vm"
"workloads/remote"
];
};
internal-app-01 = mkSystem {
host = "internal-app-01";
system = "x86_64-linux";
profiles = [
"interfaces/cli-interactive"
"platforms/vm"
"workloads/srv"
];
};
x1g13 = mkSystem {
host = "x1g13";
system = "x86_64-linux";
profiles = [
"interfaces/gui"
"platforms/thinkpad"
"workloads/dev"
"workloads/personal"
"workloads/secure-storage"
"workloads/tailscale/client"
];
};
installer = nixosSystem {
system = "x86_64-linux";
modules = [
./installer/default.nix
];
specialArgs = {
inherit inputs;
};
};
}; };
in
{
flake = {
inherit nixosConfigurations;
checks.x86_64-linux = lib.mapAttrs' ( m2 = {
name: nixos: lib.nameValuePair "nixos-${name}" nixos.config.system.build.toplevel system = "aarch64-darwin";
) nixosConfigurations; stateVersion = "26.05";
user = "moons";
path = ./m2;
profiles = [
"base"
"interface.cli-minimal"
];
}; };
} }
-193
View File
@@ -1,193 +0,0 @@
{
pkgs,
lib,
modulesPath,
...
}:
{
imports = [
"${modulesPath}/installer/cd-dvd/installation-cd-minimal.nix"
];
boot.zfs.forceImportRoot = false;
networking = {
hostName = "nixos-installer";
networkmanager = {
enable = true;
wifi.powersave = false;
};
};
services.openssh = {
enable = true;
settings = {
PermitRootLogin = "prohibit-password";
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
PubkeyAuthentication = "yes";
};
};
users.users.root.openssh.authorizedKeys.keys = [
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIKhxDkucmeCor6CKoXAua7DgDSzuXrZOtpdkyzQxz5+aAAAABHNzaDo= moons@moons14.com"
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIN6hZJyng/5LgFKPjR6uZAd/00UkO0vN0uQOoIvfSELdAAAABHNzaDo= moons@moons14.com"
];
environment.systemPackages = with pkgs; [
git # Clone dotfiles repository
disko # Disk partitioning
sops # Secrets management
age # Age encryption
ssh-to-age # Convert SSH keys to age
age-plugin-yubikey # YubiKey support
yubikey-manager # YubiKey management
pcsc-tools # Smart card tools
mkpasswd # Password hash generation
rsync # File synchronization
vim # Text editor
wget # Download files
curl # HTTP client
jq # JSON processor
parted # Partition tools
cryptsetup # LUKS encryption
btrfs-progs # Btrfs filesystem tools
];
services.pcscd.enable = true;
environment.etc."installer-help.txt".text = ''
╔══════════════════════════════════════════════════════════════╗
║ NixOS Installer ISO ║
╠══════════════════════════════════════════════════════════════╣
║ ║
║ SSH Access: ║
║ ssh root@<ip-address> ║
║ ║
║ Network Setup: ║
║ Wired: Auto-configured via DHCP ║
║ WiFi: nmcli device wifi connect <SSID> --ask ║
║ ║
║ Installation Workflow: ║
║ ║
║ 1. Clone dotfiles: ║
║ git clone git@github.com:moons-14/dotfiles.git ~/dotfiles║
║ ║
║ 2. Generate SSH host key for new host: ║
║ ssh-keygen -t ed25519 -f /tmp/ssh_host_ed25519_key -N "" ║
║ ║
║ 3. Get age public key from SSH host key: ║
║ ssh-to-age -i /tmp/ssh_host_ed25519_key.pub ║
║ ║
║ 4. Add age key to .sops.yaml: ║
║ cd ~/dotfiles ║
║ # Edit .sops.yaml and add the age key ║
║ # Add new host entry to creation_rules ║
║ ║
║ 5. Re-encrypt secrets: ║
║ sops updatekeys secrets/common/system.yaml ║
║ sops updatekeys secrets/hosts/<host>/*.yaml ║
║ ║
║ 6. Create disko.nix for new host: ║
║ # Check disk devices ║
║ lsblk -f ║
║ ║
║ # Create hosts/<host>/disko.nix ║
║ # Example: LUKS + btrfs ║
║ # See hosts/x1g13/disko.nix for reference ║
║ ║
║ 7. Partition disk with disko: ║
║ nix run github:nix-community/disko -- \ ║
║ --mode disko hosts/<host>/disko.nix ║
║ ║
║ 8. Copy host key to installed system: ║
║ mkdir -p /mnt/etc/ssh ║
║ cp /tmp/ssh_host_ed25519_key* /mnt/etc/ssh/ ║
║ chmod 600 /mnt/etc/ssh/ssh_host_ed25519_key ║
║ ║
║ 9. Install NixOS: ║
║ nixos-install --flake ~/dotfiles#<host> ║
║ ║
║ Disko Configuration Examples: ║
║ ║
║ Simple (no encryption): ║
║ disko.devices.disk.main = { ║
║ type = "disk"; ║
║ device = "/dev/sda"; ║
║ content = { ║
║ type = "gpt"; ║
║ partitions = { ║
║ ESP = { size = "512M"; type = "EF00"; ║
║ content = { type = "filesystem"; ║
║ format = "vfat"; mountpoint = "/boot"; }; }; ║
║ root = { size = "100%"; ║
║ content = { type = "filesystem"; ║
║ format = "ext4"; mountpoint = "/"; }; }; ║
║ }; ║
║ }; ║
║ }; ║
║ ║
║ LUKS + btrfs (see hosts/x1g13/disko.nix): ║
║ - Use partuuid for device path ║
║ - Set askPassword = true for LUKS ║
║ - Configure btrfs subvolumes ║
║ ║
╚══════════════════════════════════════════════════════════════╝
'';
systemd.services.installer-banner = {
description = "Display installer help on console";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.coreutils}/bin/cat /etc/installer-help.txt";
StandardOutput = "tty";
TTYPath = "/dev/tty1";
};
};
systemd.services.display-ip = {
description = "Display IP address on console";
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = pkgs.writeShellScript "display-ip" ''
sleep 2
echo ""
echo "=== Network Interfaces ==="
${pkgs.iproute2}/bin/ip -4 addr show | ${pkgs.gnugrep}/bin/grep inet
echo ""
echo "=== SSH Access ==="
for ip in $(${pkgs.iproute2}/bin/ip -4 addr show | ${pkgs.gnugrep}/bin/grep -oP 'inet \K[\d.]+' | ${pkgs.gnugrep}/bin/grep -v '127.0.0.1'); do
echo " ssh root@$ip"
done
echo ""
'';
StandardOutput = "tty";
TTYPath = "/dev/tty1";
};
};
nix = {
settings = {
experimental-features = [
"nix-command"
"flakes"
];
trusted-users = [ "root" ];
};
extraOptions = ''
experimental-features = nix-command flakes
'';
};
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
system.stateVersion = "26.05";
}
@@ -1,40 +0,0 @@
# Do not modify this file! It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ lib, modulesPath, ... }:
{
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
];
boot.initrd.availableKernelModules = [
"ata_piix"
"uhci_hcd"
"virtio_pci"
"virtio_scsi"
"sd_mod"
"sr_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/1b12ab98-2537-4207-a3f4-bb8ba7b53b00";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/8365-C778";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}
+10
View File
@@ -0,0 +1,10 @@
{ hostName, ... }:
{
# networking.hostName and networking.localHostName are derived from the
# registry name; computerName controls the user-visible macOS name.
networking.computerName = hostName;
# Keep this value stable after the first activation. It is independent of
# the Home Manager stateVersion in hosts/default.nix.
system.stateVersion = 7;
}
-6
View File
@@ -1,6 +0,0 @@
{ ... }:
{
imports = [
./hardware-configuration.nix
];
}
@@ -1,43 +0,0 @@
# Do not modify this file! It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
];
boot.initrd.availableKernelModules = [
"ata_piix"
"uhci_hcd"
"virtio_pci"
"virtio_scsi"
"sd_mod"
"sr_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/8f0eaec6-5dc9-4821-aa8d-fb6809b5a5bf";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/201C-961B";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}
-56
View File
@@ -1,56 +0,0 @@
{ ... }:
{
imports = [
./hardware-configuration.nix
];
networking = {
useDHCP = false;
interfaces = {
ens18 = {
useDHCP = false;
ipv4.addresses = [
{
address = "10.50.128.20";
prefixLength = 24;
}
];
};
ens19 = {
useDHCP = false;
ipv4.addresses = [
{
address = "10.50.7.101";
prefixLength = 24;
}
];
};
ens20 = {
useDHCP = false;
ipv4.routes = [
{
address = "10.50.64.0";
prefixLength = 24;
via = "10.50.82.1";
}
];
ipv4.addresses = [
{
address = "10.50.82.10";
prefixLength = 24;
}
];
};
};
defaultGateway = {
address = "10.50.128.1";
interface = "ens18";
};
};
}
-44
View File
@@ -1,44 +0,0 @@
# Do not modify this file! It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
];
boot.initrd.availableKernelModules = [
"ata_piix"
"uhci_hcd"
"virtio_pci"
"virtio_scsi"
"sd_mod"
"sr_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/69fa2193-1e4f-438a-8898-5de8a3f36e5b";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/D09B-4277";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}
-10
View File
@@ -1,10 +0,0 @@
{ ... }:
{
imports = [
./hardware-configuration.nix
./disko.nix
];
boot.initrd.luks.devices.cryptroot.device =
"/dev/disk/by-partuuid/311d0f9c-f35f-42e6-b6fc-a4d67dd21b2e";
}
-99
View File
@@ -1,99 +0,0 @@
_:
let
espPart = "/dev/disk/by-partuuid/a53e3b19-67de-40de-9ded-3eac3117689a";
nixosPart = "/dev/disk/by-partuuid/311d0f9c-f35f-42e6-b6fc-a4d67dd21b2e";
btrfsMountOptions = [
"compress=zstd"
"noatime"
"ssd"
"space_cache=v2"
];
in
{
disko.enableConfig = true;
disko.devices.disk = {
esp = {
type = "disk";
device = espPart;
destroy = false;
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [
"umask=0077"
];
};
};
nixos = {
type = "disk";
device = nixosPart;
destroy = false;
content = {
type = "luks";
name = "cryptroot";
askPassword = true;
settings = {
allowDiscards = true;
};
extraFormatArgs = [
"--type"
"luks2"
"--pbkdf"
"argon2id"
"--label"
"NixOS-LUKS"
];
content = {
type = "btrfs";
extraArgs = [
"-f"
"-L"
"NixOS"
];
subvolumes = {
"@root" = {
mountpoint = "/";
mountOptions = btrfsMountOptions;
};
"@home" = {
mountpoint = "/home";
mountOptions = btrfsMountOptions;
};
"@nix" = {
mountpoint = "/nix";
mountOptions = btrfsMountOptions;
};
"@log" = {
mountpoint = "/var/log";
mountOptions = btrfsMountOptions;
};
"@swap" = {
mountpoint = "/.swapvol";
mountOptions = [
"noatime"
];
swap.swapfile.size = "32G";
};
};
};
};
};
};
}
-33
View File
@@ -1,33 +0,0 @@
# Do not modify this file! It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [
"xhci_pci"
"thunderbolt"
"nvme"
"usb_storage"
"sd_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
swapDevices = [ ];
networking.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}
+52
View File
@@ -0,0 +1,52 @@
# Do not modify this file! It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [
"xhci_pci"
"thunderbolt"
"nvme"
"usb_storage"
"sd_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/16b29578-6836-414b-a5e1-863bc21c5fc3";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/209A-C8C9";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
swapDevices = [ ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.wlp0s20f3.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}
@@ -1,4 +1,3 @@
{ ... }:
{ {
imports = [ imports = [
./hardware-configuration.nix ./hardware-configuration.nix
+17
View File
@@ -0,0 +1,17 @@
{
inputs,
lib ? inputs.nixpkgs.lib,
root,
}:
let
registry = import ./registry.nix {
inherit inputs lib;
modulesRoot = root + "/modules";
};
hosts = import ./hosts.nix {
inherit inputs lib registry;
};
in
{
inherit hosts registry;
}
+185
View File
@@ -0,0 +1,185 @@
{
inputs,
lib,
registry,
}:
let
ensure =
condition: message: value:
if condition then value else throw "host registry: ${message}";
isLinux = system: lib.hasSuffix "-linux" system;
isDarwin = system: lib.hasSuffix "-darwin" system;
hostFile =
spec: name:
let
path = spec.path + "/${name}";
in
if builtins.pathExists path then path else null;
selectedUnits =
spec:
[ "users.${spec.user}" ]
++ map (name: "profiles.${name}") (spec.profiles or [ ])
++ map (name: "applications.${name}") (spec.applications or [ ])
++ (spec.units or [ ]);
validateSpec =
name: spec:
ensure (builtins.isAttrs spec) "${name}: host specification must be an attribute set" (
ensure (spec ? system && builtins.isString spec.system) "${name}: system is required" (
ensure (isLinux spec.system || isDarwin spec.system)
"${name}: unsupported system '${spec.system}'; expected a Linux NixOS or Darwin system"
(
ensure (spec ? user && builtins.isString spec.user && spec.user != "") "${name}: user is required" (
ensure (spec ? path && builtins.pathExists spec.path)
"${name}: path must name an existing host directory"
(
ensure
(
spec ? stateVersion
&& builtins.isString spec.stateVersion
&& builtins.match "[0-9][0-9]\\.[0-9][0-9]" spec.stateVersion != null
)
"${name}: stateVersion is required and must have the form YY.MM"
(
ensure
(lib.all
(field: builtins.isList (spec.${field} or [ ]) && lib.all builtins.isString (spec.${field} or [ ]))
[
"profiles"
"applications"
"units"
]
)
"${name}: profiles, applications, and units must be lists of strings"
(ensure (builtins.isBool (spec.homeManager or true)) "${name}: homeManager must be a boolean" spec)
)
)
)
)
)
);
mkSpecialArgs = name: spec: {
inherit inputs registry;
inherit (spec) system;
hostName = name;
primaryUser = spec.user;
};
mkHomeManagerModule =
name: spec: selected:
let
homePath = hostFile spec "home.nix";
homeModules = [
(registry.mkModule { class = "home"; })
(registry.mkSelectionModule selected)
{ home.stateVersion = spec.stateVersion; }
]
++ lib.optional (homePath != null) homePath;
in
{
imports = [ inputs.home-manager.nixosModules.home-manager ];
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
extraSpecialArgs = mkSpecialArgs name spec;
users.${spec.user}.imports = homeModules;
};
};
mkDarwinHomeManagerModule =
name: spec: selected:
let
homePath = hostFile spec "home.nix";
homeModules = [
(registry.mkModule { class = "home"; })
(registry.mkSelectionModule selected)
{ home.stateVersion = spec.stateVersion; }
]
++ lib.optional (homePath != null) homePath;
in
{
imports = [ inputs.home-manager.darwinModules.home-manager ];
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
extraSpecialArgs = mkSpecialArgs name spec;
users.${spec.user}.imports = homeModules;
};
};
mkNixos =
name: rawSpec:
let
spec = validateSpec name rawSpec;
selected = registry.validateUnitIds (selectedUnits spec);
nixosPath = hostFile spec "nixos.nix";
modules = [
(registry.mkModule { class = "nixos"; })
(registry.mkSelectionModule selected)
{
networking.hostName = lib.mkDefault name;
system.stateVersion = spec.stateVersion;
}
]
++ lib.optional (spec.homeManager or true) (mkHomeManagerModule name spec selected)
++ lib.optional (nixosPath != null) nixosPath;
in
inputs.nixpkgs.lib.nixosSystem {
inherit (spec) system;
specialArgs = mkSpecialArgs name spec;
inherit modules;
};
mkDarwin =
name: rawSpec:
let
spec = validateSpec name rawSpec;
selected = registry.validateUnitIds (selectedUnits spec);
darwinPath = hostFile spec "darwin.nix";
modules = [
(registry.mkModule { class = "darwin"; })
(registry.mkSelectionModule selected)
{
networking.hostName = lib.mkDefault name;
system.primaryUser = lib.mkDefault spec.user;
}
]
++ lib.optional (spec.homeManager or true) (mkDarwinHomeManagerModule name spec selected)
++ lib.optional (darwinPath != null) darwinPath;
in
ensure (inputs ? nix-darwin) "${name}: the nix-darwin input is required" (
inputs.nix-darwin.lib.darwinSystem {
inherit (spec) system;
specialArgs = mkSpecialArgs name spec;
inherit modules;
}
);
mkConfigurations =
hostSpecs:
let
validated = lib.mapAttrs validateSpec hostSpecs;
in
{
nixosConfigurations = lib.mapAttrs mkNixos (
lib.filterAttrs (_: spec: isLinux spec.system) validated
);
darwinConfigurations = lib.mapAttrs mkDarwin (
lib.filterAttrs (_: spec: isDarwin spec.system) validated
);
};
in
{
inherit
mkConfigurations
mkDarwin
mkNixos
selectedUnits
;
}
+324
View File
@@ -0,0 +1,324 @@
{
inputs,
lib,
modulesRoot,
}:
let
reservedFiles = {
common = "common.nix";
nixos = "nixos.nix";
darwin = "darwin.nix";
home = "home.nix";
meta = "meta.nix";
};
isFile = kind: kind == "regular" || kind == "symlink";
ensure =
condition: message: value:
if condition then value else throw "unit registry: ${message}";
callWithAvailableArgs =
value: availableArgs:
if builtins.isFunction value then
value (builtins.intersectAttrs (builtins.functionArgs value) availableArgs)
else
value;
pathFor =
relativePath:
if relativePath == [ ] then
modulesRoot
else
modulesRoot + "/${lib.concatStringsSep "/" relativePath}";
entryIsFile = entries: name: builtins.hasAttr name entries && isFile entries.${name};
normalizeMeta =
unit:
let
metaPath = unit.fragments.meta;
importedValue =
if metaPath == null then
{ }
else
callWithAvailableArgs (import metaPath) {
inherit inputs lib unit;
};
imported =
ensure (builtins.isAttrs importedValue) "${unit.id}: meta.nix must return an attribute set"
importedValue;
allowedKeys = [
"description"
"includes"
"imports"
];
unknownKeys = lib.filter (name: !(builtins.elem name allowedKeys)) (builtins.attrNames imported);
description = imported.description or null;
includes = imported.includes or [ ];
imports = imported.imports or { };
allowedImportKeys = [
"nixos"
"darwin"
"home"
];
unknownImportKeys =
if builtins.isAttrs imports then
lib.filter (name: !(builtins.elem name allowedImportKeys)) (builtins.attrNames imports)
else
[ ];
normalized = {
inherit description includes;
imports = {
nixos = imports.nixos or [ ];
darwin = imports.darwin or [ ];
home = imports.home or [ ];
};
};
in
ensure (unknownKeys == [ ])
"${unit.id}: meta.nix has unsupported keys: ${lib.concatStringsSep ", " unknownKeys}"
(
ensure (description == null || builtins.isString description)
"${unit.id}: meta.description must be a string"
(
ensure (builtins.isList includes && lib.all builtins.isString includes)
"${unit.id}: meta.includes must be a list of fully qualified unit IDs"
(
ensure (lib.unique includes == includes) "${unit.id}: meta.includes contains duplicate unit IDs" (
ensure (builtins.isAttrs imports) "${unit.id}: meta.imports must be an attribute set" (
ensure (unknownImportKeys == [ ])
"${unit.id}: meta.imports has unsupported classes: ${lib.concatStringsSep ", " unknownImportKeys}"
(
ensure (lib.all builtins.isList [
normalized.imports.nixos
normalized.imports.darwin
normalized.imports.home
]) "${unit.id}: every meta.imports.<class> value must be a list" normalized
)
)
)
)
)
);
makeUnit =
relativePath: entries:
let
directory = pathFor relativePath;
id = lib.concatStringsSep "." relativePath;
fragments = lib.mapAttrs (
_class: fileName: if entryIsFile entries fileName then directory + "/${fileName}" else null
) reservedFiles;
baseUnit = {
inherit
id
directory
fragments
relativePath
;
optionPath = [ "my" ] ++ relativePath ++ [ "enable" ];
kind = builtins.head relativePath;
name = lib.last relativePath;
}
//
lib.optionalAttrs (builtins.length relativePath > 2 && builtins.head relativePath == "profiles")
{
group = builtins.elemAt relativePath 1;
};
in
ensure (relativePath != [ ]) "the modules root cannot itself be a unit" (
ensure (lib.all (component: component != "" && !(lib.hasInfix "." component)) relativePath)
"${id}: path components must be non-empty and must not contain dots"
(baseUnit // { meta = normalizeMeta baseUnit; })
);
walk =
relativePath:
let
directory = pathFor relativePath;
entries = builtins.readDir directory;
hasReservedFile = lib.any (fileName: entryIsFile entries fileName) (
builtins.attrValues reservedFiles
);
childDirectories = lib.filter (name: entries.${name} == "directory") (builtins.attrNames entries);
current = lib.optional hasReservedFile (makeUnit relativePath entries);
children = lib.concatMap (name: walk (relativePath ++ [ name ])) childDirectories;
in
current ++ children;
discoveredUnits =
ensure (builtins.pathExists modulesRoot) "modules root does not exist: ${toString modulesRoot}"
(walk [ ]);
unitsById = builtins.listToAttrs (map (unit: lib.nameValuePair unit.id unit) discoveredUnits);
dependencyValidation = lib.foldl' (
valid: unit:
lib.foldl' (
inner: includedId:
if builtins.hasAttr includedId unitsById then
inner
else
throw "unit registry: ${unit.id} includes missing unit '${includedId}'"
) valid unit.meta.includes
) true discoveredUnits;
units = builtins.seq dependencyValidation unitsById;
unitIds = builtins.attrNames units;
getUnit =
id: if builtins.hasAttr id units then units.${id} else throw "unit registry: unknown unit '${id}'";
validateUnitIds =
ids:
ensure (
builtins.isList ids && lib.all builtins.isString ids
) "selected units must be a list of strings" (map (id: builtins.seq (getUnit id) id) ids);
optionDefinitions = lib.foldl' lib.recursiveUpdate { } (
map (
unit:
lib.setAttrByPath unit.optionPath (
lib.mkOption {
type = lib.types.bool;
default = false;
description =
if unit.meta.description == null then
"Whether to enable the ${unit.id} unit."
else
"Whether to enable ${unit.meta.description}.";
}
)
) discoveredUnits
);
enabled = config: unit: lib.getAttrFromPath unit.optionPath config;
enableUnit = id: lib.setAttrByPath (getUnit id).optionPath true;
includeConfig =
config: unit: lib.mkIf (enabled config unit) (lib.mkMerge (map enableUnit unit.meta.includes));
fragmentClasses = {
nixos = [
"common"
"nixos"
];
darwin = [
"common"
"darwin"
];
home = [ "home" ];
};
applyFragment =
{
config,
fragmentPath,
options,
specialArgs,
unit,
}:
let
fragment = import fragmentPath;
directArgs = specialArgs // {
inherit
config
lib
options
specialArgs
unit
;
};
fragmentArgSpec = builtins.functionArgs fragment;
fragmentArgs = builtins.listToAttrs (
lib.concatMap (
name:
if builtins.hasAttr name directArgs then
[ (lib.nameValuePair name directArgs.${name}) ]
else if fragmentArgSpec.${name} then
[ ]
else
[ (lib.nameValuePair name config._module.args.${name}) ]
) (builtins.attrNames fragmentArgSpec)
);
resultValue = if builtins.isFunction fragment then fragment fragmentArgs else fragment;
result =
ensure (builtins.isAttrs resultValue)
"${unit.id}: ${builtins.baseNameOf fragmentPath} must return an attribute set"
resultValue;
forbiddenKeys = lib.filter (name: builtins.hasAttr name result) [
"imports"
"options"
"config"
];
in
ensure (forbiddenKeys == [ ])
"${unit.id}: ${builtins.baseNameOf fragmentPath} is a configuration fragment and cannot define top-level ${lib.concatStringsSep ", " forbiddenKeys}"
result;
externalImports = class: lib.concatMap (unit: unit.meta.imports.${class}) discoveredUnits;
mkModule =
{ class }:
ensure (builtins.hasAttr class fragmentClasses) "unsupported module class '${class}'" (
builtins.seq dependencyValidation (
{
config,
lib,
options,
specialArgs,
...
}:
let
fragmentConfigs = lib.concatMap (
unit:
lib.filter (value: value != null) (
map (
fragmentClass:
let
fragmentPath = unit.fragments.${fragmentClass};
in
if fragmentPath == null then
null
else
lib.mkIf (enabled config unit) (applyFragment {
inherit
config
fragmentPath
options
specialArgs
unit
;
})
) fragmentClasses.${class}
)
) discoveredUnits;
in
{
imports = externalImports class;
options = optionDefinitions;
config = lib.mkMerge ((map (includeConfig config) discoveredUnits) ++ fragmentConfigs);
}
)
);
mkSelectionModule =
selectedIds:
let
checkedIds = validateUnitIds (lib.unique selectedIds);
in
{
config = lib.mkMerge (map enableUnit checkedIds);
};
in
{
inherit
getUnit
mkModule
mkSelectionModule
unitIds
units
validateUnitIds
;
}
-26
View File
@@ -1,26 +0,0 @@
{
lib,
config,
...
}:
let
cfg = config.my.applications."1password";
in
{
options.my.applications."1password" = {
enable = lib.mkEnableOption "1Password password manager";
};
config = lib.mkIf cfg.enable {
programs._1password.enable = true;
programs._1password-gui = {
enable = true;
polkitPolicyOwners = [ "moons" ];
};
programs.ssh.startAgent = lib.mkForce false;
programs.gnupg.agent.enableSSHSupport = lib.mkForce false;
services.gnome.gcr-ssh-agent.enable = lib.mkForce false;
};
}
@@ -0,0 +1,8 @@
{
# The macOS app must live in /Applications for its background integrations,
# including the SSH agent, to work correctly.
homebrew = {
enable = true;
casks = [ "1password" ];
};
}
+7
View File
@@ -0,0 +1,7 @@
{ pkgs, ... }:
{
home.packages = [
pkgs._1password-cli
]
++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux [ pkgs._1password-gui ];
}
+12
View File
@@ -0,0 +1,12 @@
{ primaryUser, lib, ... }:
{
programs._1password.enable = true;
programs._1password-gui = {
enable = true;
polkitPolicyOwners = [ primaryUser ];
};
programs.ssh.startAgent = lib.mkForce false;
programs.gnupg.agent.enableSSHSupport = lib.mkForce false;
services.gnome.gcr-ssh-agent.enable = lib.mkForce false;
}
-29
View File
@@ -1,29 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.my.applications.arduino;
arduinoIdeX11 = pkgs.arduino-ide.overrideAttrs (old: {
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ pkgs.makeWrapper ];
postFixup = (old.postFixup or "") + ''
wrapProgram $out/bin/arduino-ide \
--add-flags "--ozone-platform=x11"
'';
});
in
{
options.my.applications.arduino = {
enable = lib.mkEnableOption "Arduino development tools";
};
config = lib.mkIf cfg.enable {
environment.systemPackages = with pkgs; [
arduino-cli # Arduino command-line interface
arduinoIdeX11 # Arduino IDE with X11 support
];
};
}
+4
View File
@@ -0,0 +1,4 @@
{ pkgs, ... }:
{
home.packages = [ pkgs.arduino-cli ];
}
+14
View File
@@ -0,0 +1,14 @@
{ pkgs, ... }:
let
arduinoIdeX11 = pkgs.arduino-ide.overrideAttrs (old: {
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ pkgs.makeWrapper ];
postFixup = (old.postFixup or "") + ''
wrapProgram $out/bin/arduino-ide \
--add-flags "--ozone-platform=x11"
'';
});
in
{
environment.systemPackages = [ arduinoIdeX11 ];
}
-23
View File
@@ -1,23 +0,0 @@
{
lib,
config,
...
}:
let
cfg = config.my.applications.btop;
in
{
imports = [
./home.nix
./system.nix
];
options.my.applications.btop = {
enable = lib.mkEnableOption "btop system monitor";
};
config = lib.mkIf cfg.enable {
my.applications.btop.system.enable = lib.mkDefault true;
my.applications.btop.homeManager.enable = lib.mkDefault true;
};
}
+3 -3
View File
@@ -1,5 +1,5 @@
#Bashtop theme with nord palette (https://www.nordtheme.com) # Bashtop theme with Nord palette (https://www.nordtheme.com)
#by Justin Zobel <[email protected]> # by Justin Zobel <[email protected]>
# Colors should be in 6 or 2 character hexadecimal or single spaced rgb decimal: "#RRGGBB", "#BW" or "0-255 0-255 0-255" # Colors should be in 6 or 2 character hexadecimal or single spaced rgb decimal: "#RRGGBB", "#BW" or "0-255 0-255 0-255"
# example for white: "#ffffff", "#ff" or "255 255 255". # example for white: "#ffffff", "#ff" or "255 255 255".
@@ -18,7 +18,7 @@ theme[main_fg]="#BD93F9"
# Title color for boxes # Title color for boxes
theme[title]="#f8f8f2" theme[title]="#f8f8f2"
# Higlight color for keyboard shortcuts # Highlight color for keyboard shortcuts
theme[hi_fg]="#ff79c6" theme[hi_fg]="#ff79c6"
# Background color of selected item in processes box # Background color of selected item in processes box
+5 -22
View File
@@ -1,25 +1,8 @@
{ {
lib, programs.btop = {
config, enable = true;
...
}:
let
cfg = config.my.applications.btop.homeManager;
in
{
options.my.applications.btop.homeManager = {
enable = lib.mkEnableOption "btop home-manager configuration";
};
config.home-manager.sharedModules = [ settings.color_theme = "dracula";
{ themes.dracula = builtins.readFile ./dracula.theme;
config = lib.mkIf cfg.enable { };
programs.btop = {
enable = true;
settings.color_theme = "dracula";
themes.dracula = builtins.readFile ./dracula.theme;
};
};
}
];
} }
-20
View File
@@ -1,20 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.my.applications.btop.system;
in
{
options.my.applications.btop.system = {
enable = lib.mkEnableOption "btop system configuration";
};
config = lib.mkIf cfg.enable {
environment.systemPackages = with pkgs; [
btop # Resource monitor that shows usage and stats
];
};
}
-44
View File
@@ -1,44 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.my.applications.chrome;
in
{
options.my.applications.chrome = {
enable = lib.mkEnableOption "Google Chrome browser";
};
config = lib.mkIf cfg.enable {
home-manager.sharedModules = [
{
home.packages = with pkgs; [
google-chrome # Popular web browser from Google
];
xdg.desktopEntries."google-chrome" = {
name = "Google Chrome";
genericName = "Web Browser";
exec = "${pkgs.google-chrome}/bin/google-chrome-stable --enable-features=TouchpadOverscrollHistoryNavigation %U";
terminal = false;
icon = "google-chrome";
categories = [
"Network"
"WebBrowser"
];
startupNotify = true;
type = "Application";
};
xdg.mimeApps.defaultApplications = {
"text/html" = "google-chrome.desktop";
"x-scheme-handler/http" = "google-chrome.desktop";
"x-scheme-handler/https" = "google-chrome.desktop";
};
}
];
};
}
+4
View File
@@ -0,0 +1,4 @@
{ pkgs, ... }:
{
home.packages = [ pkgs.google-chrome ];
}
+25
View File
@@ -0,0 +1,25 @@
{ pkgs, ... }:
let
chromeLauncher = pkgs.makeDesktopItem {
name = "google-chrome";
desktopName = "Google Chrome";
genericName = "Web Browser";
exec = "${pkgs.google-chrome}/bin/google-chrome-stable --enable-features=TouchpadOverscrollHistoryNavigation %U";
icon = "google-chrome";
terminal = false;
categories = [
"Network"
"WebBrowser"
];
startupNotify = true;
};
in
{
environment.systemPackages = [ chromeLauncher ];
xdg.mime.defaultApplications = {
"text/html" = "google-chrome.desktop";
"x-scheme-handler/http" = "google-chrome.desktop";
"x-scheme-handler/https" = "google-chrome.desktop";
};
}
-23
View File
@@ -1,23 +0,0 @@
{
lib,
config,
...
}:
let
cfg = config.my.applications.claude;
in
{
imports = [
./home.nix
./system.nix
];
options.my.applications.claude = {
enable = lib.mkEnableOption "Claude Code AI assistant";
};
config = lib.mkIf cfg.enable {
my.applications.claude.system.enable = lib.mkDefault true;
my.applications.claude.homeManager.enable = lib.mkDefault true;
};
}
+12 -21
View File
@@ -1,27 +1,18 @@
{ {
lib, inputs,
config, pkgs,
... ...
}: }:
let
cfg = config.my.applications.claude.homeManager;
in
{ {
options.my.applications.claude.homeManager = { home.packages = [
enable = lib.mkEnableOption "Claude Code home-manager configuration"; inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.claude-code
};
config.home-manager.sharedModules = [
{
config = lib.mkIf cfg.enable {
home.file.".claude/settings.json".text = builtins.toJSON {
statusLine = {
type = "command";
command = "bun x ccusage statusline --no-offline";
padding = 0;
};
};
};
}
]; ];
home.file.".claude/settings.json".text = builtins.toJSON {
statusLine = {
type = "command";
command = "bun x ccusage statusline --no-offline";
padding = 0;
};
};
} }
-20
View File
@@ -1,20 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.my.applications.claude.system;
in
{
options.my.applications.claude.system = {
enable = lib.mkEnableOption "Claude Code system configuration";
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [
pkgs.llm-agents.claude-code # AI coding assistant
];
};
}
-65
View File
@@ -1,65 +0,0 @@
{
inputs,
pkgs,
lib,
config,
...
}:
let
cfg = config.my.applications.codexDesktop;
codexCliPackage = pkgs.llm-agents.codex;
codexDesktopPackage =
inputs.codex-desktop-linux.packages.${pkgs.stdenv.hostPlatform.system}.codex-desktop-computer-use-ui;
codexDesktopLauncher = pkgs.makeDesktopItem {
name = "codex";
desktopName = "Codex";
genericName = "ChatGPT Desktop";
comment = "Run Codex Desktop on Linux";
exec = "env CODEX_CLI_PATH=${lib.getExe' codexCliPackage "codex"} BAMF_DESKTOP_FILE_HINT=codex.desktop CHROME_DESKTOP=codex.desktop ${lib.getExe' codexDesktopPackage "codex-desktop"} %u";
icon = "codex-desktop";
terminal = false;
categories = [ "Development" ];
keywords = [
"codex"
"chatgpt"
"openai"
"ai"
"assistant"
];
startupNotify = true;
startupWMClass = "codex-desktop";
actions = {
new-window = {
name = "New Window";
exec = "env CODEX_CLI_PATH=${lib.getExe' codexCliPackage "codex"} BAMF_DESKTOP_FILE_HINT=codex.desktop CHROME_DESKTOP=codex.desktop CODEX_MULTI_LAUNCH=1 ${lib.getExe' codexDesktopPackage "codex-desktop"} --new-instance";
};
};
extraConfig = {
X-GNOME-WMClass = "codex-desktop";
};
};
in
{
imports = [
inputs.codex-desktop-linux.nixosModules.default
];
options.my.applications.codexDesktop = {
enable = lib.mkEnableOption "ChatGPT Desktop for Linux";
};
config = lib.mkIf cfg.enable {
my.applications.codex.enable = true;
programs.codexDesktopLinux = {
enable = true;
package = codexDesktopPackage;
cliPackage = codexCliPackage;
computerUseUi.enable = true;
};
environment.systemPackages = [
codexDesktopLauncher # Vicinae-searchable Codex Desktop launcher alias
];
};
}
@@ -0,0 +1,8 @@
{
# The former Codex app cask is deprecated in favor of ChatGPT, whose desktop
# application includes the current Codex experience on macOS.
homebrew = {
enable = true;
casks = [ "chatgpt" ];
};
}
@@ -0,0 +1,10 @@
{ inputs, ... }:
{
description = "Codex Desktop for Linux";
includes = [ "applications.codex" ];
imports.nixos = [
inputs.codex-desktop-linux.nixosModules.default
];
}
@@ -0,0 +1,37 @@
{
inputs,
lib,
pkgs,
...
}:
let
codexCliPackage = inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.codex;
codexDesktopPackage =
inputs.codex-desktop-linux.packages.${pkgs.stdenv.hostPlatform.system}.codex-desktop-computer-use-ui;
launcher = pkgs.makeDesktopItem {
name = "codex";
desktopName = "Codex";
genericName = "ChatGPT Desktop";
comment = "Run Codex Desktop";
exec = "env CODEX_CLI_PATH=${lib.getExe' codexCliPackage "codex"} BAMF_DESKTOP_FILE_HINT=codex.desktop CHROME_DESKTOP=codex.desktop ${lib.getExe' codexDesktopPackage "codex-desktop"} %u";
icon = "codex-desktop";
terminal = false;
categories = [ "Development" ];
startupNotify = true;
startupWMClass = "codex-desktop";
actions.new-window = {
name = "New Window";
exec = "env CODEX_CLI_PATH=${lib.getExe' codexCliPackage "codex"} BAMF_DESKTOP_FILE_HINT=codex.desktop CHROME_DESKTOP=codex.desktop CODEX_MULTI_LAUNCH=1 ${lib.getExe' codexDesktopPackage "codex-desktop"} --new-instance";
};
};
in
{
programs.codexDesktopLinux = {
enable = true;
package = codexDesktopPackage;
cliPackage = codexCliPackage;
computerUseUi.enable = true;
};
environment.systemPackages = [ launcher ];
}
-20
View File
@@ -1,20 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.my.applications.codex;
in
{
options.my.applications.codex = {
enable = lib.mkEnableOption "Codex AI coding assistant";
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [
pkgs.llm-agents.codex # OpenAI Codex CLI
];
};
}
+6
View File
@@ -0,0 +1,6 @@
{ inputs, pkgs, ... }:
{
home.packages = [
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.codex
];
}
-47
View File
@@ -1,47 +0,0 @@
{
imports = [
./1password.nix
./arduino.nix
./btop
./chrome.nix
./claude
./codex-desktop.nix
./codex.nix
./direnv.nix
./discord.nix
./docker.nix
./fcitx5
./ghostty
./git
./gnupg
./grok.nix
./gnome.nix
./greetd.nix
./ly
./gtk
./java
./kde.nix
./nautilus.nix
./nh.nix
./niri
./nix-index
./noctalia
./opencode.nix
./openssh.nix
./slack.nix
./ssh
./swayidle.nix
./swaylock
./tailscale.nix
./vicinae.nix
./vim
./vscode
./wayland.nix
./yazi.nix
./zed
./zellij
./zoom.nix
./zoxide.nix
./zsh
];
}
-16
View File
@@ -1,16 +0,0 @@
{ lib, config, ... }:
let
cfg = config.my.applications.direnv;
in
{
options.my.applications.direnv = {
enable = lib.mkEnableOption "direnv environment variable manager";
};
config = lib.mkIf cfg.enable {
programs.direnv = {
enable = true;
nix-direnv.enable = true;
};
};
}
+6
View File
@@ -0,0 +1,6 @@
{
programs.direnv = {
enable = true;
nix-direnv.enable = true;
};
}
-23
View File
@@ -1,23 +0,0 @@
{
lib,
config,
...
}:
let
cfg = config.my.applications.discord;
in
{
options.my.applications.discord = {
enable = lib.mkEnableOption "Discord (Vesktop)";
};
config = lib.mkIf cfg.enable {
home-manager.sharedModules = [
{
programs.vesktop = {
enable = true;
};
}
];
};
}
+3
View File
@@ -0,0 +1,3 @@
{
programs.vesktop.enable = true;
}
-34
View File
@@ -1,34 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.my.applications.docker;
in
{
options.my.applications.docker = {
enable = lib.mkEnableOption "Docker container runtime";
};
config = lib.mkIf cfg.enable {
virtualisation.docker = {
enable = true;
autoPrune = {
enable = true;
dates = "weekly";
};
daemon.settings = {
ipv6 = true;
"fixed-cidr-v6" = "fd00:30::/64";
ip6tables = true;
};
};
environment.systemPackages = with pkgs; [
docker # Container runtime
oxker # Docker TUI Tool
];
};
}
+6
View File
@@ -0,0 +1,6 @@
{
homebrew = {
enable = true;
casks = [ "orbstack" ];
};
}
+7
View File
@@ -0,0 +1,7 @@
{ pkgs, ... }:
{
home.packages = [
pkgs.docker-client
pkgs.oxker
];
}
+5
View File
@@ -0,0 +1,5 @@
{
description = "Docker command-line client and NixOS daemon";
includes = [ "services.docker" ];
}
-23
View File
@@ -1,23 +0,0 @@
{
lib,
config,
...
}:
let
cfg = config.my.applications.fcitx5;
in
{
imports = [
./home.nix
./system.nix
];
options.my.applications.fcitx5 = {
enable = lib.mkEnableOption "fcitx5 input method";
};
config = lib.mkIf cfg.enable {
my.applications.fcitx5.system.enable = lib.mkDefault true;
my.applications.fcitx5.homeManager.enable = lib.mkDefault true;
};
}
+3 -21
View File
@@ -1,24 +1,6 @@
{ {
lib, home.file.".config/fcitx5/config" = {
config, recursive = true;
... source = ./config;
}:
let
cfg = config.my.applications.fcitx5.homeManager;
in
{
options.my.applications.fcitx5.homeManager = {
enable = lib.mkEnableOption "fcitx5 home-manager configuration";
}; };
config.home-manager.sharedModules = [
{
config = lib.mkIf cfg.enable {
home.file.".config/fcitx5/config" = {
recursive = true;
source = ./config;
};
};
}
];
} }
+34
View File
@@ -0,0 +1,34 @@
{ pkgs, ... }:
{
i18n.inputMethod = {
enable = true;
type = "fcitx5";
fcitx5 = {
waylandFrontend = true;
addons = with pkgs; [
fcitx5-mozc-ut
fcitx5-gtk
kdePackages.fcitx5-qt
qt6Packages.fcitx5-configtool
];
settings.inputMethod = {
GroupOrder."0" = "Default";
"Groups/0" = {
Name = "Default";
"Default Layout" = "jp";
DefaultIM = "mozc";
};
"Groups/0/Items/0" = {
Name = "keyboard-jp";
Layout = "";
};
"Groups/0/Items/1" = {
Name = "mozc";
Layout = "";
};
};
};
};
}
-68
View File
@@ -1,68 +0,0 @@
{
pkgs,
lib,
config,
inputs,
...
}:
let
system = pkgs.stdenv.hostPlatform.system;
cfg = config.my.applications.fcitx5.system;
in
{
options.my.applications.fcitx5.system = {
enable = lib.mkEnableOption "fcitx5 system configuration";
};
imports = [
inputs.nix-hazkey.nixosModules.hazkey
];
config = lib.mkIf cfg.enable {
services.hazkey = {
enable = true;
server.package = inputs.nix-hazkey.packages.${system}.hazkey-server.override {
enableVulkan = true;
};
installHazkeySettings = false;
installFcitx5Addon = false;
};
environment.systemPackages = [ inputs.nix-hazkey.packages.${system}.hazkey-settings ];
i18n.inputMethod = {
enable = true;
type = "fcitx5";
fcitx5 = {
waylandFrontend = true;
addons = with pkgs; [
inputs.nix-hazkey.packages.${system}.fcitx5-hazkey
fcitx5-mozc-ut
fcitx5-gtk
kdePackages.fcitx5-qt
qt6Packages.fcitx5-configtool
];
settings.inputMethod = {
GroupOrder = {
"0" = "Default";
};
"Groups/0" = {
Name = "Default";
"Default Layout" = "jp";
DefaultIM = "mozc";
};
"Groups/0/Items/0" = {
Name = "keyboard-jp";
Layout = "";
};
"Groups/0/Items/1" = {
Name = "mozc";
Layout = "";
};
};
};
};
};
}
-40
View File
@@ -1,40 +0,0 @@
theme = dracula
background-blur-radius = 20
background-opacity = 0.9
font-family = BlexMono Nerd Font Mono
mouse-hide-while-typing = true
window-decoration = true
# keybind
# Copy/Paste
keybind = performable:ctrl+shift+c=copy_to_clipboard
keybind = ctrl+shift+v=paste_from_clipboard
# create new tab
keybind = ctrl+shift+t=new_tab
# move tabs
keybind = ctrl+alt+left_bracket=previous_tab
keybind = ctrl+alt+right_bracket=next_tab
# close tab
keybind = ctrl+alt+q=close_window
# font size
keybind = ctrl+shift+semicolon=increase_font_size:1
keybind = ctrl+shift+minus=increase_font_size:1
# quick terminal
keybind = global:super+space=toggle_quick_terminal
quick-terminal-position = top
quick-terminal-size = 100%
gtk-quick-terminal-layer = overlay
quick-terminal-keyboard-interactivity = exclusive
quick-terminal-autohide = false
quit-after-last-window-closed = false
shell-integration-features = ssh-terminfo,ssh-env
+6
View File
@@ -0,0 +1,6 @@
{
homebrew = {
enable = true;
casks = [ "ghostty" ];
};
}
-23
View File
@@ -1,23 +0,0 @@
{
lib,
config,
...
}:
let
cfg = config.my.applications.ghostty;
in
{
imports = [
./home.nix
./system.nix
];
options.my.applications.ghostty = {
enable = lib.mkEnableOption "ghostty terminal emulator";
};
config = lib.mkIf cfg.enable {
my.applications.ghostty.system.enable = lib.mkDefault true;
my.applications.ghostty.homeManager.enable = lib.mkDefault true;
};
}
+4 -5
View File
@@ -1,17 +1,17 @@
# MIT License # MIT License
# #
# Copyright (c) 2023 Dracula Theme # Copyright (c) 2023 Dracula Theme
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights # in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is # copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions: # furnished to do so, subject to the following conditions:
# #
# The above copyright notice and this permission notice shall be included in all # The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software. # copies or substantial portions of the Software.
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -42,4 +42,3 @@ cursor-color = #f8f8f2
cursor-text = #282a36 cursor-text = #282a36
selection-foreground = #f8f8f2 selection-foreground = #f8f8f2
selection-background = #44475a selection-background = #44475a
+39 -73
View File
@@ -1,83 +1,49 @@
{ {
pkgs,
lib,
config,
inputs, inputs,
pkgs,
... ...
}: }:
let let
cfg = config.my.applications.ghostty.homeManager; package =
if pkgs.stdenv.hostPlatform.isLinux then
system = pkgs.stdenv.hostPlatform.system; inputs.ghostty.packages.${pkgs.stdenv.hostPlatform.system}.ghostty-releasefast
else
ghosttyPkg = inputs.ghostty.packages.${system}.ghostty-releasefast; null;
in in
{ {
options.my.applications.ghostty.homeManager = { programs.ghostty = {
enable = lib.mkEnableOption "ghostty home-manager configuration"; enable = true;
inherit package;
systemd.enable = pkgs.stdenv.hostPlatform.isLinux;
settings = {
theme = "dracula";
background-blur-radius = 20;
background-opacity = 0.9;
font-family = "BlexMono Nerd Font Mono";
mouse-hide-while-typing = true;
window-decoration = "auto";
keybind = [
"performable:ctrl+shift+c=copy_to_clipboard"
"ctrl+shift+v=paste_from_clipboard"
"ctrl+shift+t=new_tab"
"ctrl+alt+left_bracket=previous_tab"
"ctrl+alt+right_bracket=next_tab"
"ctrl+alt+q=close_window"
"ctrl+shift+semicolon=increase_font_size:1"
"ctrl+shift+minus=decrease_font_size:1"
];
quick-terminal-position = "top";
quick-terminal-size = "98%,100%";
quick-terminal-autohide = false;
quick-terminal-keyboard-interactivity = "on-demand";
gtk-quick-terminal-layer = "top";
quit-after-last-window-closed = false;
shell-integration-features = "no-ssh-env,no-ssh-terminfo";
};
}; };
config.home-manager.sharedModules = [ xdg.configFile."ghostty/themes/dracula".source = ./dracula.theme;
(
{ lib, ... }:
{
config = lib.mkIf cfg.enable {
programs.ghostty = {
enable = true;
package = ghosttyPkg;
systemd.enable = true;
settings = {
theme = "dracula";
background-blur-radius = 20;
background-opacity = 0.9;
font-family = "BlexMono Nerd Font Mono";
mouse-hide-while-typing = true;
window-decoration = "auto";
keybind = [
# Copy/Paste
"performable:ctrl+shift+c=copy_to_clipboard"
"ctrl+shift+v=paste_from_clipboard"
# Create new tab
"ctrl+shift+t=new_tab"
# Move tabs
"ctrl+alt+left_bracket=previous_tab"
"ctrl+alt+right_bracket=next_tab"
# Close window
"ctrl+alt+q=close_window"
# Font size
"ctrl+shift+semicolon=increase_font_size:1"
"ctrl+shift+minus=decrease_font_size:1"
];
# Quick terminal
quick-terminal-position = "top";
quick-terminal-size = "98%,100%";
quick-terminal-autohide = false;
quick-terminal-keyboard-interactivity = "on-demand";
gtk-quick-terminal-layer = "top";
quit-after-last-window-closed = false;
shell-integration-features = "no-ssh-env,no-ssh-terminfo";
};
};
xdg.configFile."ghostty/themes/dracula".source = ./dracula.theme;
};
}
)
];
} }
-26
View File
@@ -1,26 +0,0 @@
{
pkgs,
lib,
config,
inputs,
...
}:
let
cfg = config.my.applications.ghostty.system;
system = pkgs.stdenv.hostPlatform.system;
ghosttyPkg = inputs.ghostty.packages.${system}.ghostty-releasefast;
in
{
options.my.applications.ghostty.system = {
enable = lib.mkEnableOption "ghostty system configuration";
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [
ghosttyPkg # A fast and minimal terminal emulator for Wayland
ghosttyPkg.terminfo # Terminfo database for ghostty
];
};
}
-35
View File
@@ -1,35 +0,0 @@
{
lib,
config,
...
}:
let
cfg = config.my.applications.git;
in
{
imports = [
./home.nix
./system.nix
];
options.my.applications.git = {
enable = lib.mkEnableOption "git version control";
userName = lib.mkOption {
type = lib.types.singleLineStr;
default = "moons";
description = "Default Git user.name.";
};
userEmail = lib.mkOption {
type = lib.types.singleLineStr;
default = "moons@moons14.com";
description = "Default Git user.email.";
};
};
config = lib.mkIf cfg.enable {
my.applications.git.system.enable = lib.mkDefault true;
my.applications.git.homeManager.enable = lib.mkDefault true;
};
}
+39 -72
View File
@@ -1,13 +1,5 @@
{ { pkgs, ... }:
pkgs,
lib,
config,
...
}:
let let
cfg = config.my.applications.git;
hmCfg = config.my.applications.git.homeManager;
signingKeyPath = ".ssh/1password-git-signing.pub"; signingKeyPath = ".ssh/1password-git-signing.pub";
signingKeyFile = "~/${signingKeyPath}"; signingKeyFile = "~/${signingKeyPath}";
@@ -35,77 +27,52 @@ let
''; '';
in in
{ {
options.my.applications.git.homeManager = { home.packages = [ pkgs.gh ];
enable = lib.mkEnableOption "git home-manager configuration";
signingPublicKey = lib.mkOption { home.file.${signingKeyPath}.text = ''
type = lib.types.nullOr lib.types.singleLineStr; ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPLwReAiwhXoO34S2+MrvqUhi8IWp4IzUq4OSp3niJdq 1password-git-signing
default = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPLwReAiwhXoO34S2+MrvqUhi8IWp4IzUq4OSp3niJdq 1password-git-signing"; '';
example = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPLwReAiwhXoO34S2+MrvqUhi8IWp4IzUq4OSp3niJdq 1password-git-signing";
description = "SSH public key copied from the 1Password SSH key item used for Git signing.";
};
signingKey = lib.mkOption { programs.git = {
type = lib.types.str; enable = true;
default = signingKeyFile;
readOnly = true;
description = "SSH public key path used for Git commit and tag signing.";
};
};
config = lib.mkIf hmCfg.enable { ignores = [
assertions = [ ".direnv/"
{ ".envrc"
assertion = hmCfg.signingPublicKey != null && hmCfg.signingPublicKey != ""; "!.envrc.example"
message = "my.applications.git.homeManager.signingPublicKey must be set to the public key copied from 1Password.";
}
]; ];
home-manager.sharedModules = [ signing = {
{ key = signingKeyFile;
home.file.${signingKeyPath}.text = hmCfg.signingPublicKey + "\n"; format = "ssh";
signByDefault = true;
};
programs.git = { settings = {
enable = true; user = {
name = "moons";
email = "moons@moons14.com";
};
ignores = [ push.default = "simple";
".direnv/" credential.helper = "cache --timeout=7200";
".envrc" init.defaultBranch = "main";
"!.envrc.example" log.decorate = "full";
]; log.date = "iso";
merge.conflictStyle = "diff3";
signing = { gpg.ssh.program = "${gitSshSign}";
key = hmCfg.signingKey;
format = "ssh";
signByDefault = true;
};
settings = { alias = {
user.name = cfg.userName; br = "branch --sort=-committerdate";
user.email = cfg.userEmail; co = "checkout";
df = "diff";
push.default = "simple"; com = "commit -a";
credential.helper = "cache --timeout=7200"; gs = "stash";
init.defaultBranch = "main"; gp = "pull";
log.decorate = "full"; lg = "log --graph --pretty=format:'%Cred%h%Creset - %C(yellow)%d%Creset %s %C(green)(%cr)%C(bold blue) <%an>%Creset' --abbrev-commit";
log.date = "iso"; st = "status";
merge.conflictStyle = "diff3"; };
};
gpg.ssh.program = "${gitSshSign}";
alias = {
br = "branch --sort=-committerdate";
co = "checkout";
df = "diff";
com = "commit -a";
gs = "stash";
gp = "pull";
lg = "log --graph --pretty=format:'%Cred%h%Creset - %C(yellow)%d%Creset %s %C(green)(%cr)%C(bold blue) <%an>%Creset' --abbrev-commit";
st = "status";
};
};
};
}
];
}; };
} }
-21
View File
@@ -1,21 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.my.applications.git.system;
in
{
options.my.applications.git.system = {
enable = lib.mkEnableOption "git system configuration";
};
config = lib.mkIf cfg.enable {
environment.systemPackages = with pkgs; [
git # Distributed version control system
gh # GitHub CLI
];
};
}
-94
View File
@@ -1,94 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.my.applications.gnome;
in
{
options.my.applications.gnome = {
enable = lib.mkEnableOption "GNOME desktop environment";
};
config = lib.mkIf cfg.enable {
services.desktopManager.gnome.enable = true;
# ly is the display manager for switching between installed sessions.
services.displayManager.gdm.enable = lib.mkForce false;
home-manager.sharedModules = [
{
dconf.settings = {
"org/gnome/desktop/sound" = {
event-sounds = false;
input-feedback-sounds = false;
};
"org/gnome/desktop/wm/keybindings" = {
close = [ "<Super>q" ];
show-desktop = [ ];
};
"org/gnome/settings-daemon/plugins/media-keys" = {
home = [ ];
screensaver = [ "<Super>l" ];
custom-keybindings = [
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/"
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/"
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom2/"
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom3/"
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom4/"
];
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0" = {
name = "Open Terminal";
command = "ghostty";
binding = "<Super>t";
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1" = {
name = "Run Application";
command = "vicinae toggle";
binding = "<Super>d";
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom2" = {
name = "Open File Manager";
command = "nautilus --new-window";
binding = "<Super>e";
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom3" = {
name = "Clipboard History";
command = "vicinae vicinae://extensions/vicinae/clipboard/history";
binding = "<Super>v";
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom4" = {
name = "Log Out";
command = "gnome-session-quit --logout --no-prompt";
binding = "<Super><Shift>e";
};
"org/gnome/shell" = {
favorite-apps = [
"google-chrome.desktop"
"code.desktop"
"com.mitchellh.ghostty.desktop"
"slack.desktop"
"vesktop.desktop"
];
};
};
}
];
environment.systemPackages = with pkgs; [
gnome-tweaks # GNOME desktop customization tool
gnome-extension-manager # GNOME Shell extension manager
];
};
}
+56
View File
@@ -0,0 +1,56 @@
{ pkgs, lib, ... }:
lib.mkIf pkgs.stdenv.hostPlatform.isLinux {
dconf.settings = {
"org/gnome/desktop/sound" = {
event-sounds = false;
input-feedback-sounds = false;
};
"org/gnome/desktop/wm/keybindings" = {
close = [ "<Super>q" ];
show-desktop = [ ];
};
"org/gnome/settings-daemon/plugins/media-keys" = {
home = [ ];
screensaver = [ "<Super>l" ];
custom-keybindings = [
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/"
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/"
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom2/"
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom3/"
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom4/"
];
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0" = {
name = "Open Terminal";
command = "ghostty";
binding = "<Super>t";
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1" = {
name = "Run Application";
command = "vicinae toggle";
binding = "<Super>d";
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom2" = {
name = "Open File Manager";
command = "nautilus --new-window";
binding = "<Super>e";
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom3" = {
name = "Clipboard History";
command = "vicinae vicinae://extensions/vicinae/clipboard/history";
binding = "<Super>v";
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom4" = {
name = "Log Out";
command = "gnome-session-quit --logout --no-prompt";
binding = "<Super><Shift>e";
};
"org/gnome/shell".favorite-apps = [
"google-chrome.desktop"
"code.desktop"
"com.mitchellh.ghostty.desktop"
"slack.desktop"
"vesktop.desktop"
];
};
}
+10
View File
@@ -0,0 +1,10 @@
{ pkgs, lib, ... }:
{
services.desktopManager.gnome.enable = true;
services.displayManager.gdm.enable = lib.mkForce false;
environment.systemPackages = [
pkgs.gnome-tweaks
pkgs.gnome-extension-manager
];
}
-23
View File
@@ -1,23 +0,0 @@
{
lib,
config,
...
}:
let
cfg = config.my.applications.gnupg;
in
{
imports = [
./home.nix
./system.nix
];
options.my.applications.gnupg = {
enable = lib.mkEnableOption "GnuPG agent";
};
config = lib.mkIf cfg.enable {
my.applications.gnupg.system.enable = lib.mkDefault true;
my.applications.gnupg.homeManager.enable = lib.mkDefault true;
};
}
+6 -19
View File
@@ -1,22 +1,9 @@
{ pkgs, ... }:
{ {
lib, home.packages = [ pkgs.gnupg ];
config,
...
}:
let
hmCfg = config.my.applications.gnupg.homeManager;
in
{
options.my.applications.gnupg.homeManager = {
enable = lib.mkEnableOption "GnuPG home-manager configuration";
};
config.home-manager.sharedModules = [ services.gpg-agent = {
{ enable = false;
config = lib.mkIf hmCfg.enable { enableSshSupport = false;
services.gpg-agent.enable = false; };
services.gpg-agent.enableSshSupport = false;
};
}
];
} }
+6
View File
@@ -0,0 +1,6 @@
{
programs.gnupg.agent = {
enable = true;
enableSSHSupport = false;
};
}
-20
View File
@@ -1,20 +0,0 @@
{
lib,
config,
...
}:
let
cfg = config.my.applications.gnupg.system;
in
{
options.my.applications.gnupg.system = {
enable = lib.mkEnableOption "GnuPG system configuration";
};
config = lib.mkIf cfg.enable {
programs.gnupg.agent = {
enable = true;
enableSSHSupport = false;
};
};
}
-26
View File
@@ -1,26 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.my.applications.greetd;
in
{
options.my.applications.greetd = {
enable = lib.mkEnableOption "greetd login manager";
};
config = lib.mkIf cfg.enable {
services.greetd = {
enable = true;
settings = {
default_session = {
user = "greeter";
command = "${pkgs.tuigreet}/bin/tuigreet --time --remember --cmd niri-session";
};
};
};
};
}
-24
View File
@@ -1,24 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.my.applications.grok;
grok = pkgs.llm-agents.grok.overrideAttrs (_old: {
versionCheckProgram = "${placeholder "out"}/libexec/grok/grok-launcher";
});
in
{
options.my.applications.grok = {
enable = lib.mkEnableOption "Grok AI assistant";
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [
grok
];
};
}
+9
View File
@@ -0,0 +1,9 @@
{ inputs, pkgs, ... }:
let
grok = inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.grok.overrideAttrs (_: {
versionCheckProgram = "${placeholder "out"}/libexec/grok/grok-launcher";
});
in
{
home.packages = [ grok ];
}
-23
View File
@@ -1,23 +0,0 @@
{
lib,
config,
...
}:
let
cfg = config.my.applications.gtk;
in
{
imports = [
./home.nix
./system.nix
];
options.my.applications.gtk = {
enable = lib.mkEnableOption "GTK theme configuration";
};
config = lib.mkIf cfg.enable {
my.applications.gtk.system.enable = lib.mkDefault true;
my.applications.gtk.homeManager.enable = lib.mkDefault true;
};
}
+15 -33
View File
@@ -1,36 +1,18 @@
{ pkgs, ... }:
{ {
pkgs, gtk = {
lib, enable = true;
config, theme = {
... name = "Dracula";
}: package = pkgs.dracula-theme;
let };
cfg = config.my.applications.gtk.homeManager; cursorTheme = {
in package = pkgs.adwaita-icon-theme;
{ name = "Adwaita";
options.my.applications.gtk.homeManager = { };
enable = lib.mkEnableOption "GTK home-manager configuration"; iconTheme = {
package = pkgs.papirus-icon-theme;
name = "Papirus-Dark";
};
}; };
config.home-manager.sharedModules = [
{
config = lib.mkIf cfg.enable {
gtk = {
enable = true;
theme = {
name = "Dracula";
package = pkgs.dracula-theme;
};
cursorTheme = {
package = pkgs.adwaita-icon-theme;
name = "Adwaita";
};
iconTheme = {
package = pkgs.papirus-icon-theme;
name = "Papirus-Dark";
};
};
};
}
];
} }
+5
View File
@@ -0,0 +1,5 @@
{
programs.dconf.enable = true;
programs.seahorse.enable = true;
services.gnome.gnome-keyring.enable = true;
}
-20
View File
@@ -1,20 +0,0 @@
{
lib,
config,
...
}:
let
cfg = config.my.applications.gtk.system;
in
{
options.my.applications.gtk.system = {
enable = lib.mkEnableOption "GTK system configuration";
};
config = lib.mkIf cfg.enable {
programs.dconf.enable = true;
programs.seahorse.enable = true;
services.gnome.gnome-keyring.enable = true;
};
}
-23
View File
@@ -1,23 +0,0 @@
{
lib,
config,
...
}:
let
cfg = config.my.applications.java;
in
{
imports = [
./home.nix
./system.nix
];
options.my.applications.java = {
enable = lib.mkEnableOption "Java runtime";
};
config = lib.mkIf cfg.enable {
my.applications.java.system.enable = lib.mkDefault true;
my.applications.java.homeManager.enable = lib.mkDefault true;
};
}
+7 -18
View File
@@ -1,23 +1,12 @@
{ pkgs, ... }:
{ {
lib, programs.java = {
config, enable = true;
... package = pkgs.jdk25;
}:
let
cfg = config.my.applications.java.homeManager;
in
{
options.my.applications.java.homeManager = {
enable = lib.mkEnableOption "Java home-manager configuration";
}; };
config.home-manager.sharedModules = [ home.packages = with pkgs; [
{ maven
config = lib.mkIf cfg.enable { gradle
programs.java = {
enable = true;
};
};
}
]; ];
} }
-27
View File
@@ -1,27 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.my.applications.java.system;
in
{
options.my.applications.java.system = {
enable = lib.mkEnableOption "Java system configuration";
};
config = lib.mkIf cfg.enable {
programs.java = {
enable = true;
package = pkgs.jdk25;
};
environment.systemPackages = with pkgs; [
jdk25 # Java Development Kit 25
maven # Java Build Tool
gradle # Java Build Tool
];
};
}
-19
View File
@@ -1,19 +0,0 @@
{
lib,
config,
...
}:
let
cfg = config.my.applications.kde;
in
{
options.my.applications.kde = {
enable = lib.mkEnableOption "KDE Connect";
};
config = lib.mkIf cfg.enable {
programs.kdeconnect = {
enable = true;
};
};
}
+6
View File
@@ -0,0 +1,6 @@
{
homebrew = {
enable = true;
casks = [ "kde-connect" ];
};
}
+3
View File
@@ -0,0 +1,3 @@
{
programs.kdeconnect.enable = true;
}
-36
View File
@@ -1,36 +0,0 @@
{
lib,
config,
pkgs,
...
}:
let
cfg = config.my.applications.ly;
indyzLinuxfire = pkgs.fetchurl {
url = "https://codeberg.org/attachments/f336d6ac-8331-4323-91fc-0e4619803401";
hash = "sha256-fRm0wlkq9/GdLrVBOzMEnQG/i2ng+uGIzq0u9hu3m9g=";
};
in
{
options.my.applications.ly = {
enable = lib.mkEnableOption "ly TUI display manager";
};
config = lib.mkIf cfg.enable {
services.displayManager.defaultSession = lib.mkDefault "niri";
services.displayManager.ly = {
enable = true;
settings = {
default_session = "niri";
animate = true;
animation = "dur_file";
dur_file_path = "${indyzLinuxfire}";
dur_offset_alignment = "center";
full_color = true;
};
};
};
}
-22
View File
@@ -1,22 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.my.applications.nautilus;
in
{
options.my.applications.nautilus = {
enable = lib.mkEnableOption "Nautilus file manager";
};
config = lib.mkIf cfg.enable {
environment.systemPackages = with pkgs; [
nautilus # GNOME file manager
gvfs # GNOME virtual file system
sushi # Nautilus file previewer
];
};
}
+8
View File
@@ -0,0 +1,8 @@
{ pkgs, lib, ... }:
lib.mkIf pkgs.stdenv.hostPlatform.isLinux {
home.packages = [
pkgs.nautilus
pkgs.gvfs
pkgs.sushi
];
}
-27
View File
@@ -1,27 +0,0 @@
{
username,
lib,
config,
...
}:
let
cfg = config.my.applications.nh;
in
{
options.my.applications.nh = {
enable = lib.mkEnableOption "nh Nix CLI helper";
};
config = lib.mkIf cfg.enable {
programs.nh = {
enable = true;
flake = "/home/${username}/dotfiles";
clean = {
enable = true;
dates = "weekly";
extraArgs = "--keep-since 14d --keep 10";
};
};
};
}
+5
View File
@@ -0,0 +1,5 @@
{ config, pkgs, ... }:
{
home.packages = [ pkgs.nh ];
home.sessionVariables.NH_FLAKE = "${config.home.homeDirectory}/dotfiles";
}
+12
View File
@@ -0,0 +1,12 @@
{ primaryUser, ... }:
{
programs.nh = {
enable = true;
flake = "/home/${primaryUser}/dotfiles";
clean = {
enable = true;
dates = "weekly";
extraArgs = "--keep-since 14d --keep 10";
};
};
}
-23
View File
@@ -1,23 +0,0 @@
{
lib,
config,
...
}:
let
cfg = config.my.applications.niri;
in
{
imports = [
./home.nix
./system.nix
];
options.my.applications.niri = {
enable = lib.mkEnableOption "niri window manager";
};
config = lib.mkIf cfg.enable {
my.applications.niri.system.enable = lib.mkDefault true;
my.applications.niri.homeManager.enable = lib.mkDefault true;
};
}
+193 -268
View File
@@ -1,273 +1,198 @@
{ { lib, pkgs, ... }:
inputs,
pkgs,
lib,
config,
...
}:
let let
cfg = config.my.applications.niri.homeManager; keybindings = import ./keybindings.nix;
defaultKeyBind = import ./defaultKeyBind.nix;
in in
{ {
options.my.applications.niri.homeManager = { programs.niri.settings = {
enable = lib.mkEnableOption "niri home-manager configuration"; input = {
}; touchpad = {
natural-scroll = true;
config.home-manager.sharedModules = [ scroll-factor = 4.0;
inputs.niri-flake.homeModules.niri scroll-method = "two-finger";
{ click-method = "clickfinger";
config = lib.mkIf cfg.enable { drag = true;
programs.niri.package = pkgs.niri; drag-lock = true;
programs.niri.settings = {
input.touchpad = {
natural-scroll = true;
scroll-factor = 4.0;
scroll-method = "two-finger";
click-method = "clickfinger";
drag = true;
drag-lock = true;
};
input.keyboard.xkb = {
layout = "jp";
options = "ctrl:nocaps";
};
input.mouse = {
accel-profile = "flat";
accel-speed = -0.1;
};
input.warp-mouse-to-focus.enable = true;
input.focus-follows-mouse = {
enable = true;
max-scroll-amount = "0%";
};
spawn-at-startup = [
{ command = [ "noctalia" ]; }
{
command = [
"${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"
];
}
];
cursor.size = 16;
outputs = {
# x1g13 monitor
"eDP-1" = {
scale = 1.2;
position = {
x = 2240;
y = 1440;
};
};
# LG WQHD monitor
"HDMI-A-1" = {
scale = 1.0;
position = {
x = 2560;
y = 0;
};
};
# DELL monitor
"DP-3" = {
scale = 1.5;
position = {
x = 0;
y = 0;
};
};
};
layout = {
focus-ring = {
active.color = "#bd93f9";
inactive.color = "#6272a4";
};
border = {
active.color = "#ffc87f";
inactive.color = "#505050";
urgent.color = "#9b0000";
};
shadow = {
color = "#0007";
};
background-color = "transparent";
};
binds = defaultKeyBind // {
"Mod+T" = {
action.spawn = "ghostty";
hotkey-overlay.title = "Open a Terminal: ghostty";
};
"Mod+D" = {
action.spawn = [
"vicinae"
"toggle"
];
hotkey-overlay.title = "Run an Application: vicinae";
};
"Mod+E" = {
action.spawn = [
"nautilus"
"--new-window"
];
hotkey-overlay.title = "Open File Manager: nautilus";
};
"Mod+L" = {
action.spawn = [
(lib.getExe' pkgs.systemd "loginctl")
"lock-session"
];
hotkey-overlay.title = "Lock the Screen";
};
"Mod+V" = {
action.spawn = [
"vicinae"
"vicinae://launch/clipboard/history?toggle=true"
];
hotkey-overlay.title = "Clipboard History";
};
"Mod+Space" = {
action.spawn = [
"ghostty"
"+toggle-quick-terminal"
];
hotkey-overlay.title = "Toggle Quick Terminal: ghostty";
};
# Focus monitor
"Mod+Shift+Left" = {
action.focus-monitor-left = [ ];
hotkey-overlay.title = "Focus Monitor Left";
};
"Mod+Shift+Right" = {
action.focus-monitor-right = [ ];
hotkey-overlay.title = "Focus Monitor Right";
};
"Mod+Shift+Up" = {
action.focus-monitor-up = [ ];
hotkey-overlay.title = "Focus Monitor Up";
};
"Mod+Shift+Down" = {
action.focus-monitor-down = [ ];
hotkey-overlay.title = "Focus Monitor Down";
};
# Move focused window to monitor
"Mod+Shift+Ctrl+Left" = {
action.move-window-to-monitor-left = [ ];
hotkey-overlay.title = "Move Window to Monitor Left";
};
"Mod+Shift+Ctrl+Right" = {
action.move-window-to-monitor-right = [ ];
hotkey-overlay.title = "Move Window to Monitor Right";
};
"Mod+Shift+Ctrl+Up" = {
action.move-window-to-monitor-up = [ ];
hotkey-overlay.title = "Move Window to Monitor Up";
};
"Mod+Shift+Ctrl+Down" = {
action.move-window-to-monitor-down = [ ];
hotkey-overlay.title = "Move Window to Monitor Down";
};
"XF86AudioRaiseVolume" = {
action.spawn = [
"noctalia"
"msg"
"volume-up"
];
};
"XF86AudioLowerVolume" = {
action.spawn = [
"noctalia"
"msg"
"volume-down"
];
};
"XF86AudioMute" = {
action.spawn = [
"noctalia"
"msg"
"volume-mute"
];
};
"XF86AudioMicMute" = {
action.spawn = [
"noctalia"
"msg"
"mic-mute"
];
};
"XF86MonBrightnessUp" = {
action.spawn = [
"noctalia"
"msg"
"brightness-up"
];
};
"XF86MonBrightnessDown" = {
action.spawn = [
"noctalia"
"msg"
"brightness-down"
];
};
"XF86Favorites" = {
action.spawn = [
"noctalia"
"msg"
"caffeine-toggle"
];
};
};
window-rules = [
{
geometry-corner-radius = {
top-left = 20.0;
top-right = 20.0;
bottom-left = 20.0;
bottom-right = 20.0;
};
clip-to-geometry = true;
}
{
matches = [ { app-id = "^dev\\.noctalia\\.Noctalia$"; } ];
open-floating = true;
default-column-width = {
fixed = 1080;
};
default-window-height = {
fixed = 920;
};
}
];
layer-rules = [
{
matches = [ { namespace = "^noctalia-wallpaper"; } ];
place-within-backdrop = true;
}
];
overview = {
workspace-shadow = {
enable = false;
};
};
};
}; };
}
]; keyboard.xkb = {
layout = "jp";
options = "ctrl:nocaps";
};
mouse = {
accel-profile = "flat";
accel-speed = -0.1;
};
warp-mouse-to-focus.enable = true;
focus-follows-mouse = {
enable = true;
max-scroll-amount = "0%";
};
};
spawn-at-startup = [
{ command = [ "noctalia" ]; }
{
command = [
"${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"
];
}
];
cursor.size = 16;
layout = {
focus-ring = {
active.color = "#bd93f9";
inactive.color = "#6272a4";
};
border = {
active.color = "#ffc87f";
inactive.color = "#505050";
urgent.color = "#9b0000";
};
shadow.color = "#0007";
background-color = "transparent";
};
binds = keybindings // {
"Mod+T" = {
action.spawn = "ghostty";
hotkey-overlay.title = "Open a Terminal: ghostty";
};
"Mod+D" = {
action.spawn = [
"vicinae"
"toggle"
];
hotkey-overlay.title = "Run an Application: vicinae";
};
"Mod+E" = {
action.spawn = [
"nautilus"
"--new-window"
];
hotkey-overlay.title = "Open File Manager: nautilus";
};
"Mod+L" = {
action.spawn = [
(lib.getExe' pkgs.systemd "loginctl")
"lock-session"
];
hotkey-overlay.title = "Lock the Screen";
};
"Mod+V" = {
action.spawn = [
"vicinae"
"vicinae://launch/clipboard/history?toggle=true"
];
hotkey-overlay.title = "Clipboard History";
};
"Mod+Space" = {
action.spawn = [
"ghostty"
"+toggle-quick-terminal"
];
hotkey-overlay.title = "Toggle Quick Terminal: ghostty";
};
"Mod+Shift+Left" = {
action.focus-monitor-left = [ ];
hotkey-overlay.title = "Focus Monitor Left";
};
"Mod+Shift+Right" = {
action.focus-monitor-right = [ ];
hotkey-overlay.title = "Focus Monitor Right";
};
"Mod+Shift+Up" = {
action.focus-monitor-up = [ ];
hotkey-overlay.title = "Focus Monitor Up";
};
"Mod+Shift+Down" = {
action.focus-monitor-down = [ ];
hotkey-overlay.title = "Focus Monitor Down";
};
"Mod+Shift+Ctrl+Left" = {
action.move-window-to-monitor-left = [ ];
hotkey-overlay.title = "Move Window to Monitor Left";
};
"Mod+Shift+Ctrl+Right" = {
action.move-window-to-monitor-right = [ ];
hotkey-overlay.title = "Move Window to Monitor Right";
};
"Mod+Shift+Ctrl+Up" = {
action.move-window-to-monitor-up = [ ];
hotkey-overlay.title = "Move Window to Monitor Up";
};
"Mod+Shift+Ctrl+Down" = {
action.move-window-to-monitor-down = [ ];
hotkey-overlay.title = "Move Window to Monitor Down";
};
"XF86AudioRaiseVolume".action.spawn = [
"noctalia"
"msg"
"volume-up"
];
"XF86AudioLowerVolume".action.spawn = [
"noctalia"
"msg"
"volume-down"
];
"XF86AudioMute".action.spawn = [
"noctalia"
"msg"
"volume-mute"
];
"XF86AudioMicMute".action.spawn = [
"noctalia"
"msg"
"mic-mute"
];
"XF86MonBrightnessUp".action.spawn = [
"noctalia"
"msg"
"brightness-up"
];
"XF86MonBrightnessDown".action.spawn = [
"noctalia"
"msg"
"brightness-down"
];
"XF86Favorites".action.spawn = [
"noctalia"
"msg"
"caffeine-toggle"
];
};
window-rules = [
{
geometry-corner-radius = {
top-left = 20.0;
top-right = 20.0;
bottom-left = 20.0;
bottom-right = 20.0;
};
clip-to-geometry = true;
}
{
matches = [ { app-id = "^dev\\.noctalia\\.Noctalia$"; } ];
open-floating = true;
default-column-width.fixed = 1080;
default-window-height.fixed = 920;
}
];
layer-rules = [
{
matches = [ { namespace = "^noctalia-wallpaper"; } ];
place-within-backdrop = true;
}
];
overview.workspace-shadow.enable = false;
};
} }

Some files were not shown because too many files have changed in this diff Show More