1 Commits
Author SHA1 Message Date
Noah Masur b0b9827a2e try to configure using nixosModules
doesn't seem like there's that much benefit?
2023-07-10 17:56:15 -06:00
576 changed files with 9025 additions and 24683 deletions
-13
View File
@@ -1,13 +0,0 @@
{
"permissions": {
"allow": [
"WebFetch(domain:github.com)",
"WebFetch(domain:raw.githubusercontent.com)",
"Bash(gh run *)",
"Bash(jj status *)",
"Bash(jj new *)",
"Bash(jj describe *)",
"Bash(nix flake *)"
]
}
}
-166
View File
@@ -1,166 +0,0 @@
name: Arrow (AWS)
run-name: Arrow (AWS) - ${{ inputs.rebuild && 'Rebuild and ' || '' }}${{ inputs.action == 'create' && 'Create' || ( inputs.action == 'destroy' && 'Destroy' || 'No Action' ) }}
env:
TERRAFORM_DIRECTORY: deploy/aws
DEPLOY_IDENTITY_BASE64: ${{ secrets.DEPLOY_IDENTITY_BASE64 }}
ARROW_IDENTITY_BASE64: ${{ secrets.ARROW_IDENTITY_BASE64 }}
ZONE_NAME: masu.rs
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
on:
workflow_dispatch:
inputs:
rebuild:
type: boolean
default: false
action:
type: choice
required: true
default: create
options:
- create
- destroy
- nothing
size:
type: choice
required: false
options:
- t3a.small # 2 GB RAM / $10
permissions:
id-token: write
contents: write
jobs:
build-deploy:
name: Build and Deploy
runs-on: ubuntu-latest
steps:
- name: Checkout Repo Code
uses: actions/checkout@v4
- name: Free Disk Space (Ubuntu)
if: inputs.rebuild && inputs.action != 'destroy'
uses: jlumbroso/free-disk-space@main
with:
tool-cache: true
# Enable access to KVM, required to build an image
- name: Enable KVM group perms
if: inputs.rebuild && inputs.action != 'destroy'
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
# Login to AWS
- name: AWS Assume Role
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::286370965832:role/github_actions_admin
aws-region: us-east-1
# Install Nix
- name: Install Nix
if: inputs.rebuild && inputs.action != 'destroy'
uses: cachix/install-nix-action@v20
# Build the image
- name: Build Image
if: inputs.rebuild && inputs.action != 'destroy'
run: nix build .#arrow-aws
- name: Upload Image to S3
if: inputs.rebuild && inputs.action != 'destroy'
run: |
aws s3 cp \
result/nixos-amazon-image-*.vhd \
s3://${{ secrets.IMAGES_BUCKET }}/arrow.vhd \
# Installs the Terraform binary and some other accessory functions.
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
# Checks whether Terraform is formatted properly. If this fails, you
# should install the pre-commit hook.
- name: Check Formatting
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
terraform fmt -no-color -check -diff -recursive
# Connects to remote state backend and download providers.
- name: Terraform Init
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
terraform init \
-backend-config="bucket=${{ secrets.TERRAFORM_STATE_BUCKET }}" \
-backend-config="key=arrow.tfstate"
# Deploys infrastructure or changes to infrastructure.
- name: Terraform Apply
if: inputs.action == 'create'
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
env:
TF_VAR_ec2_size: ${{ inputs.size }}
TF_VAR_images_bucket: ${{ secrets.IMAGES_BUCKET }}
run: |
terraform apply \
-auto-approve \
-input=false
# Removes infrastructure.
- name: Terraform Destroy
if: inputs.action == 'destroy'
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
env:
TF_VAR_ec2_size: ${{ inputs.size }}
TF_VAR_images_bucket: ${{ secrets.IMAGES_BUCKET }}
run: |
terraform destroy \
-auto-approve \
-input=false
- name: Get Host IP
if: inputs.action == 'create'
id: host
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: terraform output -raw host_ip
- name: Wait on SSH
if: inputs.action == 'create'
run: |
for i in $(seq 1 15); do
if $(nc -z -w 3 ${{ steps.host.outputs.stdout }} 22); then
exit 0
fi
sleep 10
done
- name: Write Identity Keys to Files
if: inputs.action == 'create'
run: |
echo "${{ env.DEPLOY_IDENTITY_BASE64 }}" | base64 -d > deploy_ed25519
chmod 0600 deploy_ed25519
echo "${{ env.ARROW_IDENTITY_BASE64 }}" | base64 -d > arrow_ed25519
chmod 0600 arrow_ed25519
- name: Copy Identity File to Host
if: inputs.action == 'create'
run: |
ssh -i deploy_ed25519 -o StrictHostKeyChecking=accept-new noah@${{ steps.host.outputs.stdout }} 'mkdir -pv .ssh'
scp -i deploy_ed25519 arrow_ed25519 noah@${{ steps.host.outputs.stdout }}:~/.ssh/id_ed25519
- name: Wipe Records
if: ${{ inputs.action == 'destroy' }}
run: |
RECORD_ID=$(curl --request GET \
--url https://api.cloudflare.com/client/v4/zones/${{ env.CLOUDFLARE_ZONE_ID }}/dns_records \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${{ env.CLOUDFLARE_API_TOKEN }}" | jq -r '.result[] | select(.name == "n8n2.${{ env.ZONE_NAME }}") | .id')
curl --request DELETE \
--url https://api.cloudflare.com/client/v4/zones/${{ env.CLOUDFLARE_ZONE_ID }}/dns_records/${RECORD_ID} \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${{ env.CLOUDFLARE_API_TOKEN }}"
-154
View File
@@ -1,154 +0,0 @@
name: Arrow
run-name: Arrow - ${{ inputs.rebuild && 'Rebuild and ' || '' }}${{ inputs.action == 'create' && 'Create' || ( inputs.action == 'destroy' && 'Destroy' || 'No Action' ) }}
env:
TERRAFORM_DIRECTORY: hosts/arrow/vultr
DEPLOY_IDENTITY_BASE64: ${{ secrets.DEPLOY_IDENTITY_BASE64 }}
ARROW_IDENTITY_BASE64: ${{ secrets.ARROW_IDENTITY_BASE64 }}
CLOUDFLARE_R2_ENDPOINT: "${{ secrets.CLOUDFLARE_ACCOUNT_ID }}.r2.cloudflarestorage.com"
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_KEY }}
AWS_DEFAULT_REGION: auto
AWS_ENDPOINT_URL_S3: "https://${{ secrets.CLOUDFLARE_ACCOUNT_ID }}.r2.cloudflarestorage.com"
TF_VAR_vultr_api_key: ${{ secrets.VULTR_API_KEY }}
ZONE_NAME: masu.rs
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
on:
workflow_dispatch:
inputs:
rebuild:
type: boolean
default: false
action:
type: choice
required: true
default: create
options:
- create
- destroy
- nothing
plan:
type: choice
required: false
options:
- vc2-1c-1gb # 25 GB / $5
- vc2-1c-2gb # 55 GB / $10 (default)
- vc2-2c-2gb # 65 GB / $15
- vc2-2c-4gb # 80 GB / $20
- vc2-4c-8gb # 160 GB / $40
- vc2-6c-16gb # 320 GB / $80
jobs:
build-deploy:
name: Build and Deploy
runs-on: ubuntu-latest
steps:
- name: Checkout Repo Code
uses: actions/checkout@v4
# Enable access to KVM, required to build an image
- name: Enable KVM group perms
if: inputs.rebuild && inputs.action != 'destroy'
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
# Install Nix
- name: Install Nix
if: inputs.rebuild && inputs.action != 'destroy'
uses: cachix/install-nix-action@v17
# Build the image
- name: Build Image
if: inputs.rebuild && inputs.action != 'destroy'
run: nix build .#arrow
- name: Upload Image to S3
if: inputs.rebuild && inputs.action != 'destroy'
run: |
aws s3 cp \
result/iso/nixos.iso \
s3://noahmasur-arrow-images/arrow.iso \
--endpoint-url "https://${{ env.CLOUDFLARE_R2_ENDPOINT }}"
# Installs the Terraform binary and some other accessory functions.
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
# Checks whether Terraform is formatted properly. If this fails, you
# should install the pre-commit hook.
- name: Check Formatting
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
terraform fmt -no-color -check -diff -recursive
# Connects to remote state backend and download providers.
- name: Terraform Init
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: terraform init
# Deploys infrastructure or changes to infrastructure.
- name: Terraform Apply
if: inputs.action == 'create'
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
env:
TF_VAR_vultr_plan: ${{ inputs.plan }}
run: |
terraform apply \
-auto-approve \
-input=false
# Removes infrastructure.
- name: Terraform Destroy
if: inputs.action == 'destroy'
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
terraform destroy \
-auto-approve \
-input=false
- name: Get Host IP
if: inputs.action == 'create'
id: host
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: terraform output -raw host_ip
- name: Wait on SSH
if: inputs.action == 'create'
run: |
for i in $(seq 1 15); do
if $(nc -z -w 3 ${{ steps.host.outputs.stdout }} 22); then
exit 0
fi
sleep 10
done
- name: Write Identity Keys to Files
if: inputs.action == 'create'
run: |
echo "${{ env.DEPLOY_IDENTITY_BASE64 }}" | base64 -d > deploy_ed25519
chmod 0600 deploy_ed25519
echo "${{ env.ARROW_IDENTITY_BASE64 }}" | base64 -d > arrow_ed25519
chmod 0600 arrow_ed25519
- name: Copy Identity File to Host
if: inputs.action == 'create'
run: |
ssh -i deploy_ed25519 -o StrictHostKeyChecking=accept-new noah@${{ steps.host.outputs.stdout }} 'mkdir -pv .ssh'
scp -i deploy_ed25519 arrow_ed25519 noah@${{ steps.host.outputs.stdout }}:~/.ssh/id_ed25519
- name: Wipe Records
if: ${{ inputs.action == 'destroy' }}
run: |
RECORD_ID=$(curl --request GET \
--url https://api.cloudflare.com/client/v4/zones/${{ env.CLOUDFLARE_ZONE_ID }}/dns_records \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${{ env.CLOUDFLARE_API_TOKEN }}" | jq -r '.result[] | select(.name == "n8n2.${{ env.ZONE_NAME }}") | .id')
curl --request DELETE \
--url https://api.cloudflare.com/client/v4/zones/${{ env.CLOUDFLARE_ZONE_ID }}/dns_records/${RECORD_ID} \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${{ env.CLOUDFLARE_API_TOKEN }}"
-20
View File
@@ -1,20 +0,0 @@
name: Check Build
on:
workflow_dispatch: # allows manual triggering
jobs:
check:
name: Check
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v11
- name: Check Nixpkgs Inputs
uses: DeterminateSystems/flake-checker-action@v7
- name: Add Nix Cache
uses: DeterminateSystems/magic-nix-cache-action@v6
- name: Check the Flake
run: nix flake check
-200
View File
@@ -1,200 +0,0 @@
name: Flame
run-name: Flame - ${{ inputs.rebuild && 'Rebuild and ' || '' }}${{ inputs.action == 'create' && 'Create' || ( inputs.action == 'destroy' && 'Destroy' || 'No Action' ) }}
env:
TERRAFORM_DIRECTORY: deploy/oracle
DEPLOY_IDENTITY_BASE64: ${{ secrets.DEPLOY_IDENTITY_BASE64 }}
FLAME_IDENTITY_BASE64: ${{ secrets.FLAME_IDENTITY_BASE64 }}
ZONE_NAME: masu.rs
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
OCI_CLI_USER: "ocid1.user.oc1..aaaaaaaa6lro2eoxdajjypjysepvzcavq5yn4qyozjyebxdiaoqziribuqba"
OCI_CLI_TENANCY: "ocid1.tenancy.oc1..aaaaaaaaudwr2ozedhjnrn76ofjgglgug6gexknjisd7gb7tkj3mjdp763da"
OCI_CLI_FINGERPRINT: "dd:d0:da:6d:83:46:8b:b3:d9:45:2b:c7:56:ae:30:94"
OCI_CLI_KEY_CONTENT: "${{ secrets.OCI_PRIVATE_KEY }}"
TF_VAR_oci_private_key: "${{ secrets.OCI_PRIVATE_KEY }}"
OCI_CLI_REGION: "us-ashburn-1"
on:
workflow_dispatch:
inputs:
rebuild:
description: Rebuild Image
type: boolean
default: false
action:
description: Terraform Action
type: choice
required: true
default: create
options:
- create
- destroy
- nothing
permissions:
id-token: write
contents: write
jobs:
build-deploy:
name: Build and Deploy
# runs-on: ubuntu-latest
runs-on: ubuntu-24.04-arm
steps:
- name: Checkout Repo Code
uses: actions/checkout@v4
# - name: Write OCI Key to File
# run: |
# echo "${{ env.OCI_PRIVATE_KEY_BASE64 }}" | base64 -d > OCI_PRIVATE_KEY
# # Enable access to KVM, required to build an image
# - name: Enable KVM group perms
# if: inputs.rebuild && inputs.action != 'destroy'
# run: |
# echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
# sudo udevadm control --reload-rules
# sudo udevadm trigger --name-match=kvm
# sudo apt-get install -y qemu-user-static
# Install Nix
- name: Install Nix
# if: inputs.rebuild && inputs.action != 'destroy'
uses: cachix/[email protected]
with:
enable_kvm: true
extra_nix_config: |
system = aarch64-linux
system-features = aarch64-linux arm-linux kvm
# Build the image
- name: Build Image
if: inputs.rebuild && inputs.action != 'destroy'
run: nix build .#flame-qcow --system aarch64-linux
- name: List Images
if: inputs.rebuild && inputs.action != 'destroy'
run: |
ls -lh result/
echo "IMAGE_NAME=$(ls result/nixos.qcow2) >> $GITHUB_ENV
- name: Upload Image to S3
if: inputs.rebuild && inputs.action != 'destroy'
# env:
# AWS_ACCESS_KEY_ID: "<YOUR_OCI_ACCESS_KEY>"
# AWS_SECRET_ACCESS_KEY: "<YOUR_OCI_SECRET_KEY>"
# AWS_DEFAULT_REGION: "us-ashburn-1" # e.g., us-ashburn-1, us-phoenix-1
# AWS_ENDPOINT_URL: "https://masur.compat.objectstorage.us-ashburn-1.oraclecloud.com"
uses: oracle-actions/[email protected]
with:
command: |
os object put \
--namespace "idptr5akf9pf" \
--bucket-name "noahmasur-images" \
--name "nixos.qcow2" \
--file "${IMAGE_NAME}" \
--part-size 128 \ # Optional: Specify part size in MiB for multipart uploads, default is 128 MiB
--parallel-upload-count 5 # Optional: Number of parallel uploads, default is 3
# Login to AWS
- name: AWS Assume Role
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::286370965832:role/github_actions_admin
aws-region: us-east-1
# Installs the Terraform binary and some other accessory functions.
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
# Checks whether Terraform is formatted properly. If this fails, you
# should install the pre-commit hook.
- name: Check Formatting
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
terraform fmt -no-color -check -diff -recursive
# Connects to remote state backend and download providers.
- name: Terraform Init
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: terraform init -input=false
# Deploys infrastructure or changes to infrastructure.
- name: Terraform Apply
if: inputs.action == 'create'
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
terraform apply \
-auto-approve \
-input=false
# Removes infrastructure.
- name: Terraform Destroy
if: inputs.action == 'destroy'
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
terraform destroy \
-auto-approve \
-input=false
- name: Get Host IP
if: inputs.action == 'create'
id: host
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: terraform output -raw host_ip
- name: Wait on SSH
if: inputs.action == 'create'
run: |
for i in $(seq 1 15); do
if $(nc -z -w 3 ${{ steps.host.outputs.stdout }} 22); then
exit 0
fi
sleep 10
done
- name: Write Identity Keys to Files
if: inputs.action == 'create'
run: |
echo "${{ env.DEPLOY_IDENTITY_BASE64 }}" | base64 -d > deploy_ed25519
chmod 0600 deploy_ed25519
echo "${{ env.FLAME_IDENTITY_BASE64 }}" | base64 -d > flame_ed25519
chmod 0600 flame_ed25519
mkdir -pv "${HOME}/.ssh/"
cp deploy_ed25519 "${HOME}/.ssh/id_ed25519"
- name: Run nixos-anywhere
if: inputs.action == 'create'
run: |
nix run github:nix-community/nixos-anywhere -- --flake github:nmasur/dotfiles#flame --build-on remote --no-reboot --target-host ubuntu@${{ steps.host.outputs.stdout }}
reboot now
- name: Wait on SSH After Reboot
if: inputs.action == 'create'
run: |
for i in $(seq 1 15); do
if $(nc -z -w 3 ${{ steps.host.outputs.stdout }} 22); then
exit 0
fi
sleep 10
done
- name: Copy Identity File to Host
if: inputs.action == 'create'
run: |
ssh -i deploy_ed25519 -o StrictHostKeyChecking=accept-new noah@${{ steps.host.outputs.stdout }} 'mkdir -pv .ssh'
scp -i deploy_ed25519 flame_ed25519 noah@${{ steps.host.outputs.stdout }}:~/.ssh/id_ed25519
# - name: Wipe Records
# if: ${{ inputs.action == 'destroy' }}
# run: |
# RECORD_ID=$(curl --request GET \
# --url https://api.cloudflare.com/client/v4/zones/${{ env.CLOUDFLARE_ZONE_ID }}/dns_records \
# --header 'Content-Type: application/json' \
# --header "Authorization: Bearer ${{ env.CLOUDFLARE_API_TOKEN }}" | jq -r '.result[] | select(.name == "n8n2.${{ env.ZONE_NAME }}") | .id')
# curl --request DELETE \
# --url https://api.cloudflare.com/client/v4/zones/${{ env.CLOUDFLARE_ZONE_ID }}/dns_records/${RECORD_ID} \
# --header 'Content-Type: application/json' \
# --header "Authorization: Bearer ${{ env.CLOUDFLARE_API_TOKEN }}"
-71
View File
@@ -1,71 +0,0 @@
name: Update Flake
on:
workflow_dispatch: # allows manual triggering
schedule:
- cron: '33 3 * * 6' # runs weekly on Saturday at 03:33
permissions:
contents: write
pull-requests: write
checks: write
jobs:
lockfile:
name: Lockfile
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v11
with:
nix-package-url: https://releases.nixos.org/nix/nix-2.18.4/nix-2.18.4-x86_64-linux.tar.xz
- name: Check Nixpkgs Inputs
uses: DeterminateSystems/flake-checker-action@v7
- name: Add Nix Cache
uses: DeterminateSystems/magic-nix-cache-action@v6
- name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@v23
id: update
with:
pr-title: "Update flake.lock" # Title of PR to be created
pr-labels: | # Labels to be set on the PR
dependencies
automated
pr-body: |
Automated changes by the [update-flake-lock](https://github.com/DeterminateSystems/update-flake-lock) GitHub Action.
```
{{ env.GIT_COMMIT_MESSAGE }}
```
- name: Check the Flake
id: check
run: nix flake check
- name: Update Check Status
uses: LouisBrunner/[email protected]
if: always()
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: Update Flake
conclusion: ${{ job.status }}
output: |
{"summary":"${{ steps.check.outputs.stdout }}"}
- name: Enable Pull Request Automerge
if: success()
run: |
gh pr merge \
--rebase \
--auto \
${{ steps.update.outputs.pull-request-number }}
env:
GH_TOKEN: ${{ github.token }}
- name: Close Pull Request If Failed
if: failure()
run: |
gh pr close \
--comment "Auto-closing pull request" \
--delete-branch \
${{ steps.update.outputs.pull-request-number }}
env:
GH_TOKEN: ${{ github.token }}
-1
View File
@@ -1,7 +1,6 @@
.DS_Store
*.bak
*.db
*.qcow2
**/.direnv/**
result
private/**
+25 -46
View File
@@ -8,59 +8,38 @@ configuration may be difficult to translate to a non-Nix system.
## System Features
| Feature | Program | Configuration |
|----------------|-----------------------------------------------------|-----------------------------------------------------------------------------------|
| OS | [NixOS](https://nixos.org) | [Link](./platforms/nixos) |
| Display Server | [X11](https://www.x.org/wiki/) | [Link](./platforms/nixos/modules/nmasur/profiles/gui.nix) |
| Compositor | [Picom](https://github.com/yshui/picom) | [Link](./platforms/home-manager/modules/nmasur/presets/services/picom.nix) |
| Window Manager | [i3](https://i3wm.org/) | [Link](./platforms/home-manager/modules/nmasur/presets/services/i3.nix) |
| Panel | [Polybar](https://polybar.github.io/) | [Link](./platforms/home-manager/modules/nmasur/presets/services/polybar.nix) |
| Font | [Victor Mono](https://rubjo.github.io/victor-mono/) | [Link](./platforms/home-manager/modules/nmasur/presets/fonts.nix) |
| Launcher | [Rofi](https://github.com/davatorium/rofi) | [Link](./platforms/home-manager/modules/nmasur/presets/programs/rofi/default.nix) |
| Feature | Program | Configuration |
| --- | --- | --- |
| OS | [NixOS](https://nixos.org) | [Link](./modules/nixos) |
| Display Server | [X11](https://www.x.org/wiki/) | [Link](./modules/nixos/graphical/xorg.nix) |
| Compositor | [Picom](https://github.com/yshui/picom) | [Link](./modules/nixos/graphical/picom.nix) |
| Window Manager | [i3](https://i3wm.org/) | [Link](./modules/nixos/graphical/i3.nix) |
| Panel | [Polybar](https://polybar.github.io/) | [Link](./modules/nixos/graphical/polybar.nix) |
| Font | [Victor Mono](https://rubjo.github.io/victor-mono/) | [Link](./modules/nixos/graphical/fonts.nix) |
| Launcher | [Rofi](https://github.com/davatorium/rofi) | [Link](./modules/nixos/graphical/rofi.nix) |
## User Features
| Feature | Program | Configuration |
|--------------|----------------------------------------------------------------------------------|-------------------------------------------------------------------------------|
| Dotfiles | [Home-Manager](https://github.com/nix-community/home-manager) | [Link](./platforms/home-manager) |
| Terminal | [Ghostty](https://sw.kovidgoyal.net/kitty/) | [Link](./platforms/home-manager/modules/nmasur/presets/programs/ghostty.nix) |
| Shell | [Fish](https://fishshell.com/) | [Link](./platforms/home-manager/modules/nmasur/presets/programs/fish.nix) |
| Shell Prompt | [Starship](https://starship.rs/) | [Link](./platforms/home-manager/modules/nmasur/presets/programs/starship.nix) |
| Colorscheme | [Gruvbox](https://github.com/morhetz/gruvbox) | [Link](./colorscheme/gruvbox/default.nix) |
| Wallpaper | [Road](https://gitlab.com/exorcist365/wallpapers/-/blob/master/gruvbox/road.jpg) | [Link](./hosts/x86_64-linux/tempest/default.nix) |
| Text Editor | [Neovim](https://neovim.io/) | [Link](./pkgs/applications/editors/neovim/nmasur/neovim/package.nix) |
| Browser | [Firefox](https://www.mozilla.org/en-US/firefox/new/) | [Link](./platforms/home-manager/modules/nmasur/presets/programs/firefox.nix) |
| E-Mail | [Aerc](https://aerc-mail.org/) | [Link](./platforms/home-manager/modules/nmasur/presets/programs/aerc.nix) |
| File Manager | [Nautilus](https://wiki.gnome.org/action/show/Apps/Files) | [Link](./platforms/home-manager/modules/nmasur/presets/programs/nautilus.nix) |
| PDF Reader | [Zathura](https://pwmt.org/projects/zathura/) | [Link](./platforms/home-manager/modules/nmasur/presets/programs/zathura.nix) |
| Video Player | [mpv](https://mpv.io/) | [Link](./platforms/home-manager/modules/nmasur/presets/programs/mpv.nix) |
| Feature | Program | Configuration |
| --- | --- | --- |
| Dotfiles | [Home-Manager](https://github.com/nix-community/home-manager) | [Link](./modules/common) |
| Terminal | [Kitty](https://sw.kovidgoyal.net/kitty/) | [Link](./modules/common/applications/kitty.nix) |
| Shell | [Fish](https://fishshell.com/) | [Link](./modules/common/shell/fish) |
| Shell Prompt | [Starship](https://starship.rs/) | [Link](./modules/common/shell/starhip.nix) |
| Colorscheme | [Gruvbox](https://github.com/morhetz/gruvbox) | [Link](./colorscheme/gruvbox/default.nix) |
| Wallpaper | [Road](https://gitlab.com/exorcist365/wallpapers/-/blob/master/gruvbox/road.jpg) | [Link](./hosts/tempest/default.nix) |
| Text Editor | [Neovim](https://neovim.io/) | [Link](./modules/common/neovim/config) |
| Browser | [Firefox](https://www.mozilla.org/en-US/firefox/new/) | [Link](./modules/common/applications/firefox.nix) |
| E-Mail | [Aerc](https://aerc-mail.org/) | [Link](./modules/common/mail/aerc.nix) |
| File Manager | [Nautilus](https://wiki.gnome.org/action/show/Apps/Files) | [Link](./modules/common/applications/nautilus.nix) |
| PDF Reader | [Zathura](https://pwmt.org/projects/zathura/) | [Link](./modules/common/applications/media.nix) |
| Video Player | [mpv](https://mpv.io/) | [Link](./modules/common/applications/media.nix) |
## macOS Features
| Feature | Program | Configuration |
|----------|---------------------------------------------|--------------------------------------|
| Keybinds | [Hammerspoon](https://www.hammerspoon.org/) | [Link](./platforms/home-manager/modules/nmasur/presets/services/hammerspoon/) |
# Diagram
![Diagram](https://github.com/nmasur/dotfiles/assets/7386960/4cc22285-cea1-4831-b387-a82241184381)
---
# Unique Configurations
This repo contains a few more elaborate elements of configuration.
- [Neovim config](./pkgs/applications/editors/neovim/nmasur/neovim/package.nix)
generated with Nix2Vim and source-controlled plugins,
differing based on installed LSPs, for example. - [Caddy
JSON](./platforms/nixos/modules/nmasur/presets/services/caddy.nix) file (routes,
etc.) based dynamically on enabled services rendered with Nix. - [Grafana
config](./platforms/nixos/modules/nmasur/presets/services/grafana/grafana.nix)
rendered with Nix. - Custom [secrets
deployment](./platforms/nixos/modules/secrets.nix) similar to agenix. - Base16
[colorschemes](./colorscheme/) applied to multiple applications, including
Firefox userChrome.
| --- | --- | --- |
| Keybinds | [Hammerspoon](https://www.hammerspoon.org/) | [Link](./modules/darwin/hammerspoon) |
---
+34
View File
@@ -0,0 +1,34 @@
{ pkgs, ... }: rec {
# Show quick helper
default = import ./help.nix { inherit pkgs; };
# Format primary disk
format-root = import ./format-root.nix { inherit pkgs; };
# Format and install from nothing (deprecated)
installer = import ./installer.nix { inherit pkgs; };
# Display the readme for this repository
readme = import ./readme.nix { inherit pkgs; };
# Rebuild
rebuild = import ./rebuild.nix { inherit pkgs; };
# Load the SSH key for this machine
loadkey = import ./loadkey.nix { inherit pkgs; };
# Encrypt secret for all machines
encrypt-secret = import ./encrypt-secret.nix { inherit pkgs; };
# Re-encrypt secrets for all machines
reencrypt-secrets = import ./reencrypt-secrets.nix { inherit pkgs; };
# Connect machine metrics to Netdata Cloud
netdata = import ./netdata-cloud.nix { inherit pkgs; };
# Run neovim as an app
neovim = import ./neovim.nix { inherit pkgs; };
nvim = neovim;
}
+19
View File
@@ -0,0 +1,19 @@
{ pkgs, ... }: {
# nix run github:nmasur/dotfiles#encrypt-secret > private/mysecret.age
type = "app";
program = builtins.toString (pkgs.writeShellScript "encrypt-secret" ''
printf "\nEnter the secret data to encrypt for all hosts...\n\n" 1>&2
read -p "Secret: " secret
printf "\nEncrypting...\n\n" 1>&2
tmpfile=$(mktemp)
echo "''${secret}" > ''${tmpfile}
${pkgs.age}/bin/age --encrypt --armor --recipients-file ${
builtins.toString ../public-keys
} $tmpfile
rm $tmpfile
'');
}
+39
View File
@@ -0,0 +1,39 @@
{ pkgs, ... }: {
# This script will partition and format drives; use at your own risk!
type = "app";
program = builtins.toString (pkgs.writeShellScript "format-root" ''
set -e
DISK=$1
if [ -z "''${DISK}" ]; then
${pkgs.gum}/bin/gum style --width 50 --margin "1 2" --padding "2 4" \
--foreground "#fb4934" \
"Missing required parameter." \
"Usage: format-root -- <disk>" \
"Flake example: nix run github:nmasur/dotfiles#format-root -- nvme0n1"
echo "(exiting)"
exit 1
fi
${pkgs.disko-packaged}/bin/disko \
--mode create \
--dry-run \
--flake "path:$(pwd)#root" \
--arg disk \""/dev/''${DISK}"\"
${pkgs.gum}/bin/gum confirm \
"This will ERASE ALL DATA on the disk /dev/''${DISK}. Are you sure you want to continue?" \
--default=false
${pkgs.disko-packaged}/bin/disko \
--mode create \
--flake "path:$(pwd)#root" \
--arg disk "/dev/''${DISK}"
'');
}
+23
View File
@@ -0,0 +1,23 @@
{ pkgs, ... }: {
type = "app";
program = builtins.toString (pkgs.writeShellScript "default" ''
${pkgs.gum}/bin/gum style --margin "1 2" --padding "0 2" --foreground "15" --background "55" "Options"
${pkgs.gum}/bin/gum format --type=template -- ' {{ Italic "Run with" }} {{ Color "15" "69" " nix run github:nmasur/dotfiles#" }}{{ Color "15" "62" "someoption" }}{{ Color "15" "69" " " }}.'
echo ""
echo ""
${pkgs.gum}/bin/gum format --type=template -- \
' {{ Color "15" "57" " readme " }} {{ Italic "Documentation for this repository." }}' \
' {{ Color "15" "57" " rebuild " }} {{ Italic "Switch to this configuration." }}' \
' {{ Color "15" "57" " installer " }} {{ Italic "Format and install from nothing." }}' \
' {{ Color "15" "57" " neovim " }} {{ Italic "Test out the Neovim package." }}' \
' {{ Color "15" "57" " loadkey " }} {{ Italic "Load an ssh key for this machine using melt." }}' \
' {{ Color "15" "57" " encrypt-secret " }} {{ Italic "Encrypt a secret for all machines." }}' \
' {{ Color "15" "57" " reencrypt-secrets " }} {{ Italic "Reencrypt all secrets when new machine is added." }}' \
' {{ Color "15" "57" " netdata " }} {{ Italic "Connect a machine to Netdata cloud." }}'
echo ""
echo ""
'');
}
@@ -1,12 +1,11 @@
{ pkgs, lib, ... }:
{ pkgs, ... }: {
# Inspired by https://github.com/cleverca22/nix-tests/blob/master/kexec/justdoit.nix
# This script will partition and format drives; use at your own risk!
# Inspired by https://github.com/cleverca22/nix-tests/blob/master/kexec/justdoit.nix
# This script will partition and format drives; use at your own risk!
if !pkgs.stdenv.hostPlatform.isLinux then
null
else
pkgs.writeShellScriptBin "installer" ''
type = "app";
program = builtins.toString (pkgs.writeShellScript "installer" ''
set -e
DISK=$1
@@ -18,8 +17,8 @@ else
--foreground "#fb4934" \
"Missing required parameter." \
"Usage: installer -- <disk> <host>" \
"Example: installer -- nvme0n1 tempest" \
"Flake example: nix run github:nmasur/dotfiles#installer -- nvme0n1 tempest"
"Example: installer -- nvme0n1 desktop" \
"Flake example: nix run github:nmasur/dotfiles#installer -- nvme0n1 desktop"
echo "(exiting)"
exit 1
fi
@@ -44,4 +43,6 @@ else
mount /dev/disk/by-label/boot /mnt/boot
${pkgs.nixos-install-tools}/bin/nixos-install --flake github:nmasur/dotfiles#''${FLAKE}
''
'');
}
+12
View File
@@ -0,0 +1,12 @@
{ pkgs, ... }: {
type = "app";
program = builtins.toString (pkgs.writeShellScript "loadkey" ''
printf "\nEnter the seed phrase for your SSH key...\n"
printf "\nThen press ^D when complete.\n\n"
${pkgs.melt}/bin/melt restore ~/.ssh/id_ed25519
printf "\n\nContinuing activation.\n\n"
'');
}
+12
View File
@@ -0,0 +1,12 @@
{ pkgs, ... }: {
type = "app";
program = "${
(import ../modules/common/neovim/package {
inherit pkgs;
colors = (import ../colorscheme/nord).dark;
})
}/bin/nvim";
}
+19
View File
@@ -0,0 +1,19 @@
{ pkgs, ... }: {
type = "app";
program = builtins.toString (pkgs.writeShellScript "netdata-cloud" ''
if [ "$EUID" -ne 0 ]; then
echo "Please run as root"
exit 1
fi
mkdir --parents --mode 0750 /var/lib/netdata/cloud.d
printf "\nEnter the claim token for netdata cloud...\n\n"
read -p "Token: " token
echo "''${token}" > /var/lib/netdata/cloud.d/token
chown -R netdata:netdata /var/lib/netdata
${pkgs.netdata}/bin/netdata-claim.sh -id=$(uuidgen)
printf "\n\nNow restart netdata service.\n\n"
'');
}
+9
View File
@@ -0,0 +1,9 @@
{ pkgs, ... }: {
type = "app";
program = builtins.toString (pkgs.writeShellScript "readme" ''
${pkgs.glow}/bin/glow --pager ${builtins.toString ../README.md}
'');
}
+15
View File
@@ -0,0 +1,15 @@
{ pkgs, ... }: {
type = "app";
program = builtins.toString (pkgs.writeShellScript "rebuild" ''
echo ${pkgs.system}
SYSTEM=${if pkgs.stdenv.isDarwin then "darwin" else "linux"}
if [ "$SYSTEM" == "darwin" ]; then
sudo darwin-rebuild switch --flake ${builtins.toString ../.}
else
doas nixos-rebuild switch --flake ${builtins.toString ../.}
fi
'');
}
+27
View File
@@ -0,0 +1,27 @@
{ pkgs, ... }: {
# nix run github:nmasur/dotfiles#reencrypt-secrets ./private
type = "app";
program = builtins.toString (pkgs.writeShellScript "reencrypt-secrets" ''
if [ $# -eq 0 ]; then
echo "Must provide directory to reencrypt."
exit 1
fi
encrypted=$1
for encryptedfile in ''${1}/*; do
tmpfile=$(mktemp)
echo "Decrypting ''${encryptedfile}..."
${pkgs.age}/bin/age --decrypt \
--identity ~/.ssh/id_ed25519 $encryptedfile > $tmpfile
echo "Encrypting ''${encryptedfile}..."
${pkgs.age}/bin/age --encrypt --armor --recipients-file ${
builtins.toString ../public-keys
} $tmpfile > $encryptedfile
rm $tmpfile
done
echo "Finished."
'');
}
-5
View File
@@ -1,5 +0,0 @@
# Colorschemes
Color information for different themes is found here. The colors are sourced
and used with [base16](https://github.com/chriskempson/base16) format
consistently across the system.
+4 -2
View File
@@ -16,7 +16,9 @@
base0B = "#dbbc7f"; # Strings, Inherited Class, Markup Code, Diff Inserted
base0C = "#e69875"; # Support, Regular Expressions, Escape Characters, ...
base0D = "#a7c080"; # Functions, Methods, Attribute IDs, Headings
base0E = "#e67e80"; # Keywords, Storage, Selector, Markup Italic, Diff Changed
base0F = "#d699b6"; # Deprecated, Opening/Closing Embedded Language Tags, ...
base0E =
"#e67e80"; # Keywords, Storage, Selector, Markup Italic, Diff Changed
base0F =
"#d699b6"; # Deprecated, Opening/Closing Embedded Language Tags, ...
};
}
+2 -1
View File
@@ -2,7 +2,8 @@
{
name = "gruvbox-dark"; # Dark, Medium
author = "Dawid Kurek (dawikur@gmail.com), morhetz (https://github.com/morhetz/gruvbox), ElRastaOk (https://www.reddit.com/user/ElRastaOk)";
author =
"Dawid Kurek (dawikur@gmail.com), morhetz (https://github.com/morhetz/gruvbox), ElRastaOk (https://www.reddit.com/user/ElRastaOk)";
dark = {
base00 = "#1D2122"; # ---- This is the change from normal gruvbox
base01 = "#3c3836"; # ---
+2 -1
View File
@@ -1,6 +1,7 @@
{
name = "gruvbox"; # Dark, Medium
author = "Dawid Kurek (dawikur@gmail.com), morhetz (https://github.com/morhetz/gruvbox)";
author =
"Dawid Kurek (dawikur@gmail.com), morhetz (https://github.com/morhetz/gruvbox)";
dark = {
base00 = "#282828"; # ----
base01 = "#3c3836"; # ---
-98
View File
@@ -1,98 +0,0 @@
resource "aws_instance" "instance" {
ami = aws_ami.image.id
iam_instance_profile = aws_iam_instance_profile.instance.name
instance_type = var.ec2_size
vpc_security_group_ids = [aws_security_group.instance.id]
tags = {
Name = "aws-nixos"
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_ec2_instance_state" "instance" {
instance_id = aws_instance.instance.id
state = "running"
}
data "aws_vpc" "vpc" {
default = true
}
resource "aws_security_group" "instance" {
name = "aws-nixos"
description = "Allow SSH and HTTPS"
vpc_id = data.aws_vpc.vpc.id
ingress {
description = "Ping"
from_port = -1
to_port = -1
protocol = "icmp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTPS"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
}
# Setup IAM for the instance to use SSM
data "aws_iam_policy_document" "ec2_assume_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
}
}
data "aws_iam_policy_document" "instance_profile" {
statement {
actions = [
"s3:ListAllMyBuckets",
]
resources = ["*"]
}
}
resource "aws_iam_role" "instance_profile" {
name = "nixos"
assume_role_policy = data.aws_iam_policy_document.ec2_assume_role.json
inline_policy {
name = "instance-profile"
policy = data.aws_iam_policy_document.instance_profile.json
}
}
resource "aws_iam_role_policy_attachment" "instance_ssm" {
role = aws_iam_role.instance_profile.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
resource "aws_iam_instance_profile" "instance" {
name = "nixos"
role = aws_iam_role.instance_profile.name
}
-13
View File
@@ -1,13 +0,0 @@
terraform {
backend "s3" {
region = "us-east-1"
dynamodb_table = "terraform-state-lock"
}
required_version = ">= 1.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.42.0"
}
}
}
-3
View File
@@ -1,3 +0,0 @@
output "host_ip" {
value = aws_instance.instance.public_ip
}
-10
View File
@@ -1,10 +0,0 @@
variable "ec2_size" {
type = string
description = "Size of instance to launch"
default = "t3a.small" # 2 GB RAM ($14/mo)
}
variable "images_bucket" {
description = "Name of the bucket in which to store the NixOS VM images."
type = string
}
-115
View File
@@ -1,115 +0,0 @@
terraform {
backend "s3" {
bucket = "noahmasur-terraform"
key = "flame.tfstate"
region = "us-east-1"
use_lockfile = true
}
required_version = ">= 1.0.0"
required_providers {
oci = {
source = "oracle/oci"
version = "7.7.0"
}
}
}
provider "oci" {
auth = "APIKey"
tenancy_ocid = var.compartment_ocid
user_ocid = "ocid1.user.oc1..aaaaaaaa6lro2eoxdajjypjysepvzcavq5yn4qyozjyebxdiaoqziribuqba"
private_key = var.oci_private_key
fingerprint = "dd:d0:da:6d:83:46:8b:b3:d9:45:2b:c7:56:ae:30:94"
region = "us-ashburn-1"
}
# Get the latest Ubuntu image OCID
# We'll filter for a recent Ubuntu LTS version (e.g., 22.04 or 24.04) and pick the latest.
# Note: Image OCIDs are region-specific. This data source helps find the correct one.
data "oci_core_images" "ubuntu_image" {
compartment_id = var.compartment_ocid
operating_system = "Canonical Ubuntu"
# Adjust this version if you prefer a different Ubuntu LTS (e.g., "24.04")
operating_system_version = "24.04"
shape = var.instance_shape # Filter by the shape to ensure compatibility
sort_by = "TIMECREATED"
sort_order = "DESC"
}
# resource "oci_core_image" "my_custom_image" {
# compartment_id = var.compartment_ocid
# display_name = "noah-nixos"
# image_source_details {
# source_type = "objectStorageTuple" # Use this if specifying namespace, bucket, and object name
# # source_type = "objectStorageUri" # Use this if you have a pre-authenticated request URL (PAR)
# namespace_name = var.object_storage_namespace
# bucket_name = var.object_storage_bucket_name
# object_name = var.object_storage_object_name
# source_image_type = "QCOW2" # e.g., "QCOW2", "VMDK"
# # These properties help OCI understand how to launch instances from this image
# # Adjust based on your custom image's OS and boot mode
# operating_system = "NixOS" # e.g., "CentOS", "Debian", "Windows"
# operating_system_version = "25.05" # e.g., "7", "11", "2019"
# }
# launch_mode = "PARAVIRTUALIZED" # Or "NATIVE", "EMULATED", "CUSTOM"
# # Optional: for specific launch options if your image requires them
# # launch_options {
# # boot_volume_type = "PARAVIRTUALIZED"
# # firmware = "UEFI_64" # Or "BIOS"
# # network_type = "PARAVIRTUALIZED"
# # }
# # Time out for image import operation. Can take a while for large images.
# timeouts {
# create = "60m" # Default is 20m, often needs to be increased
# }
# }
data "oci_identity_availability_domains" "ads" {
compartment_id = var.compartment_ocid
}
resource "oci_core_instance" "my_compute_instance" {
compartment_id = var.compartment_ocid
availability_domain = data.oci_identity_availability_domains.ads.availability_domains[0].name
shape = var.instance_shape
display_name = var.instance_display_name
source_details {
source_type = "image"
# Use the OCID of the latest Ubuntu image found by the data source
source_id = data.oci_core_images.ubuntu_image.images[0].id
# # Use the OCID of the newly imported custom image
# source_id = oci_core_image.my_custom_image.id
# Specify the boot volume size
boot_volume_size_in_gbs = var.boot_volume_size_in_gbs
boot_volume_vpus_per_gb = 20 # Highest free tier option
}
# launch_options {
# is_consistent_volume_naming_enabled = true # Sets boot device path to /dev/oracleoci/oraclevda
# network_type = "PARAVIRTUALIZED" # I think this is the default?
# }
create_vnic_details {
subnet_id = oci_core_subnet.my_public_subnet.id # Use the created subnet's ID
display_name = "primary_vnic"
assign_public_ip = true
hostname_label = "flame"
}
metadata = {
ssh_authorized_keys = var.ssh_public_key
user_data = base64encode(var.cloud_init_script)
}
# Optional: For flexible shapes (e.g., VM.Standard.E4.Flex), you might need to specify OCPUs and memory
shape_config {
ocpus = 4
memory_in_gbs = 24
}
}
-126
View File
@@ -1,126 +0,0 @@
resource "oci_core_vcn" "my_vpc" {
compartment_id = var.compartment_ocid
display_name = "main"
cidr_block = "10.0.0.0/16"
is_ipv6enabled = false
dns_label = "mainvcn" # Must be unique within your tenancy
}
resource "oci_core_internet_gateway" "my_igw" {
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.my_vpc.id
display_name = "main-igw"
enabled = true
}
resource "oci_core_route_table" "my_public_route_table" {
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.my_vpc.id
display_name = "main-public-rt"
# Default route to the Internet Gateway
route_rules {
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = oci_core_internet_gateway.my_igw.id
}
}
resource "oci_core_security_list" "my_public_security_list" {
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.my_vpc.id
display_name = "main-public-sl"
# Egress Rules (Allow all outbound traffic)
egress_security_rules {
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
protocol = "all"
}
# Ingress Rules
ingress_security_rules {
# SSH (TCP 22)
protocol = "6" # TCP
source = "0.0.0.0/0"
source_type = "CIDR_BLOCK"
tcp_options {
min = 22
max = 22
}
}
ingress_security_rules {
# HTTP (TCP 80)
protocol = "6" # TCP
source = "0.0.0.0/0"
source_type = "CIDR_BLOCK"
tcp_options {
min = 80
max = 80
}
}
ingress_security_rules {
# HTTPS (TCP 443)
protocol = "6" # TCP
source = "0.0.0.0/0"
source_type = "CIDR_BLOCK"
tcp_options {
min = 443
max = 443
}
}
ingress_security_rules {
# Custom Minecraft
protocol = "6" # TCP
source = "0.0.0.0/0"
source_type = "CIDR_BLOCK"
tcp_options {
min = 49732
max = 49732
}
}
ingress_security_rules {
# HTTPS (UDP 443) - For QUIC or specific UDP services
protocol = "17" # UDP
source = "0.0.0.0/0"
source_type = "CIDR_BLOCK"
udp_options {
min = 443
max = 443
}
}
ingress_security_rules {
# ICMP (Ping)
protocol = "1" # ICMP
source = "0.0.0.0/0"
source_type = "CIDR_BLOCK"
icmp_options {
type = 3 # Destination Unreachable (common for connectivity checks)
code = 4 # Fragmentation needed
}
}
ingress_security_rules {
protocol = "1" # ICMP
source = "0.0.0.0/0"
source_type = "CIDR_BLOCK"
icmp_options {
type = 8 # Echo Request (ping)
}
}
}
resource "oci_core_subnet" "my_public_subnet" {
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.my_vpc.id
display_name = "main-public-subnet"
cidr_block = "10.0.0.0/24"
prohibit_public_ip_on_vnic = false # Allows instances in this subnet to get public IPs
route_table_id = oci_core_route_table.my_public_route_table.id
security_list_ids = [oci_core_security_list.my_public_security_list.id]
dns_label = "mainsub" # Must be unique within the VCN
}
-19
View File
@@ -1,19 +0,0 @@
output "host_ip" {
description = "The public IP address of the launched instance."
value = oci_core_instance.my_compute_instance.public_ip
}
output "instance_id" {
description = "The OCID of the launched instance."
value = oci_core_instance.my_compute_instance.id
}
output "vpc_ocid" {
description = "The OCID of the created VCN."
value = oci_core_vcn.my_vpc.id
}
output "subnet_ocid" {
description = "The OCID of the created public subnet."
value = oci_core_subnet.my_public_subnet.id
}
-63
View File
@@ -1,63 +0,0 @@
variable "boot_volume_size_in_gbs" {
description = "The size of the boot volume in GBs."
type = number
default = 150
}
variable "cloud_init_script" {
description = "A cloud-init script to run on instance launch."
type = string
default = <<-EOF
#!/bin/bash
echo "Hello from cloud-init!" > /home/ubuntu/cloud-init-output.txt
EOF
}
variable "compartment_ocid" {
description = "The OCID of the compartment where the instance will be created."
type = string
default = "ocid1.tenancy.oc1..aaaaaaaaudwr2ozedhjnrn76ofjgglgug6gexknjisd7gb7tkj3mjdp763da"
}
variable "instance_display_name" {
description = "A user-friendly name for the instance."
type = string
default = "noah-nixos"
}
variable "instance_shape" {
description = "The shape of the OCI compute instance."
type = string
default = "VM.Standard.A1.Flex" # Example shape. Choose one available in your region/AD.
}
variable "object_storage_namespace" {
description = "Your OCI Object Storage namespace (usually your tenancy name)."
type = string
default = "idptr5akf9pf"
}
variable "object_storage_bucket_name" {
description = "The name of the Object Storage bucket where your custom image is located."
type = string
default = "noahmasur-images"
}
variable "object_storage_object_name" {
description = "The object name (file name) of your custom image in Object Storage."
type = string
default = "nixos.qcow2"
}
variable "oci_private_key" {
type = string
description = "API private key for Oracle Cloud management"
sensitive = true
}
variable "ssh_public_key" {
description = "Your public SSH key content."
type = string
# default = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+AbmjGEwITk5CK9y7+Rg27Fokgj9QEjgc9wST6MA3s personal"
default = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKpPU2G9rSF8Q6waH62IJexDCQ6lY+8ZyVufGE3xMDGw actions-deploy"
}
-78
View File
@@ -1,78 +0,0 @@
terraform {
backend "s3" {
bucket = "noahmasur-terraform"
key = "arrow.tfstate"
region = "auto"
skip_credentials_validation = true
skip_metadata_api_check = true
skip_region_validation = true
skip_requesting_account_id = true
skip_s3_checksum = true
use_path_style = true
/*
ENVIRONMENT VARIABLES
---------------------
AWS_ACCESS_KEY_ID - R2 token
AWS_SECRET_ACCESS_KEY - R2 secret
AWS_ENDPOINT_URL_S3 - R2 location: https://ACCOUNT_ID.r2.cloudflarestorage.com
*/
}
required_version = ">= 1.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.42.0"
}
vultr = {
source = "vultr/vultr"
version = "2.19.0"
}
}
}
variable "vultr_api_key" {
type = string
description = "API key for Vultr management"
sensitive = true
}
# https://api.vultr.com/v2/plans
variable "vultr_plan" {
type = string
description = "Size of instance to launch"
default = "vc2-1c-2gb" # 55 GB SSD ($10/mo)
}
provider "aws" {
region = "auto"
skip_credentials_validation = true
skip_metadata_api_check = true
skip_region_validation = true
skip_requesting_account_id = true
}
provider "vultr" {
api_key = var.vultr_api_key
}
resource "vultr_iso_private" "image" {
# url = "https://${var.cloudflare_account_id}.r2.cloudflarestorage.com/${data.aws_s3_bucket.images.id}/${aws_s3_object.image.key}"
url = "https://arrow-images.masu.rs/arrow.iso"
}
resource "vultr_instance" "arrow" {
plan = var.vultr_plan
region = "ewr"
iso_id = vultr_iso_private.image.id
label = "arrow"
tags = ["arrow"]
enable_ipv6 = false
disable_public_ipv4 = false
backups = "disabled"
ddos_protection = false
activation_email = false
}
output "host_ip" {
value = vultr_instance.arrow.main_ip
}
+42
View File
@@ -0,0 +1,42 @@
{ disk, ... }: {
disk = {
boot = {
type = "disk";
device = disk;
content = {
type = "table";
format = "gpt";
partitions = [
# Boot partition
{
name = "ESP";
start = "0";
end = "512MiB";
fs-type = "fat32";
bootable = true;
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
extraArgs = [ "-n boot" ];
};
}
# Root partition ext4
{
name = "root";
start = "512MiB";
end = "100%";
part-type = "primary";
bootable = true;
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
extraArgs = [ "-L nixos" ];
};
}
];
};
};
};
}
+95
View File
@@ -0,0 +1,95 @@
{ pool, disks, ... }: {
disk = lib.genAttrs disks (disk: {
"${disk}" = {
type = "disk";
device = "/dev/${disk}";
content = {
type = "table";
format = "gpt";
partitions = [{
type = "partition";
name = "zfs";
start = "128MiB";
end = "100%";
content = {
type = "zfs";
pool = pool;
};
}];
};
};
});
zpool = {
"${pool}" = {
type = "zpool";
mode = "raidz1";
rootFsOptions = {
compression = "on"; # lz4 by default
"com.sun:auto-snapshot" = "false";
ashift = "12";
};
# mountpoint = "/";
datasets = {
root = {
zfs_type = "filesystem";
mountpoint = null;
options."com.sun:auto-snapshot" = "false";
};
# "media/movies" = {
# zfs_type = "filesystem";
# mountpoint = "/media/movies";
# options.recordsize = "1M";
# };
# "media/tv" = {
# zfs_type = "filesystem";
# mountpoint = "/media/tv";
# options.recordsize = "1M";
# };
# "media/books" = {
# zfs_type = "filesystem";
# mountpoint = "/media/books";
# };
# archive = {
# zfs_type = "filesystem";
# mountpoint = "/archive";
# options.compression = "zstd";
# options."com.sun:auto-snapshot" = "true";
# };
# zfs_unmounted_fs = {
# zfs_type = "filesystem";
# options.mountpoint = "none";
# };
# zfs_legacy_fs = {
# zfs_type = "filesystem";
# options.mountpoint = "legacy";
# mountpoint = "/zfs_legacy_fs";
# };
# zfs_testvolume = {
# zfs_type = "volume";
# size = "10M";
# content = {
# type = "filesystem";
# format = "ext4";
# mountpoint = "/ext4onzfs";
# };
# };
# encrypted = {
# zfs_type = "filesystem";
# size = "20M";
# options = {
# mountpoint = "none";
# encryption = "aes-256-gcm";
# keyformat = "passphrase";
# keylocation = "file:///tmp/secret.key";
# };
# };
# "encrypted/test" = {
# zfs_type = "filesystem";
# size = "2M";
# mountpoint = "/zfs_crypted";
# };
};
};
};
}
-220
View File
@@ -1,220 +0,0 @@
# Changelog
## 2026-09-07
- **Configured OpenID Connect (OIDC) authentication for Paperless-ngx**:
- Added secret management for `paperless-oidc-secret.age` via `secrets.paperless-oidc-secret` owned by `paperless:paperless` (0440).
- Configured `systemd.services.paperless-oidc-secret-secret` to run before and be required by all Paperless units, dynamically generating `/var/private/paperless-env` with `PAPERLESS_SOCIALACCOUNT_PROVIDERS` configured for Pocket ID (`auth.masu.rs`) using `client_id = "e6ff7fce-8e67-4c66-8f32-5ec3db4740a9"`, `oauth_pkce_enabled = true`, and email authentication linking (`email_authentication = true`, `verified_email = true`) in the native `APPS` schema.
- Added `services.paperless.environmentFile = "${config.secretsDirectory}/paperless-env"` to securely pass the OIDC configuration to Paperless services without exposing secrets in the Nix store.
- Configured Paperless settings to enable `allauth.socialaccount.providers.openid_connect` in `PAPERLESS_APPS` and enabled `PAPERLESS_REDIRECT_LOGIN_TO_SSO`.
- Configured reverse proxy trust settings (`PAPERLESS_USE_X_FORWARD_HOST`, `PAPERLESS_PROXY_SSL_HEADER`, and `PAPERLESS_TRUSTED_PROXIES`) to ensure callback URIs preserve the `https://` scheme behind Caddy.
- Fixed `systemd.services.paperless-secret` to order before and be required by `paperless-scheduler.service` instead of non-existent `paperless.service`.
- **Configured real client IP forwarding for Jellyfin reverse proxy**:
- Added a `map` handler to Jellyfin's Caddy route in `platforms/nixos/modules/nmasur/presets/services/jellyfin.nix` to resolve `{client_ip}` using Cloudflare's `CF-Connecting-IP` header when available, falling back to `{http.request.remote.host}` for direct local LAN connections.
- Configured `reverse_proxy.headers.request.set` to forward `X-Real-IP`, `X-Forwarded-For`, and `X-Forwarded-Proto` with the resolved client IP and request scheme to Jellyfin.
- **Configured OpenID Connect (OIDC) authentication for Nextcloud**:
- Added `user_oidc` to `services.nextcloud.extraApps`.
- Added secret management for `nextcloud-oidc-secret.age` via `secrets.nextcloud-oidc-secret` with owner `nextcloud` and group `nextcloud` (0440).
- Configured `systemd.services.nextcloud-oidc-secret-secret` to be required by and order before `nextcloud-setup.service`.
- Set `services.nextcloud.settings.overwriteprotocol = "https"` to ensure correct scheme handling for redirect URIs behind reverse proxy.
- Configured automated idempotent upsert of the Pocket ID provider via `systemd.services.nextcloud-setup.postStart` running `nextcloud-occ user_oidc:provider pocket-id` with client ID `c8a32c58-a781-4f14-9070-f498fdfda438`, `--clientsecret-file`, discovery URI `https://${hostnames.auth}/.well-known/openid-configuration`, `--mapping-uid="preferred_username"`, and `--unique-uid=0` to connect OIDC logins directly to existing local Nextcloud accounts.
- Updated `docs/oidc-services.md` with the verified configuration and callback URI (`https://cloud.masu.rs/apps/user_oidc/code`).
## 2026-09-06
- **Configured OpenID Connect (OIDC) authentication for Immich**:
- Configured `services.immich.settings.oauth` with OIDC settings pointing to Pocket ID (`auth.masu.rs`), using client ID `1f4e0f8d-6cee-4d67-8d53-74bf6c18ae09`.
- Added secret management for `immich-oidc-secret.age` via `secrets.immich-oidc-secret` with owner `immich` and group `shared` (0440).
- Wired client secret substitution using NixOS's native `clientSecret._secret = config.secrets.immich-oidc-secret.dest`, leveraging `utils.genJqSecretsReplacement` with systemd `LoadCredential`.
- Configured `systemd.services.immich-server` to order after `immich-oidc-secret-secret.service`.
- Updated `docs/oidc-services.md` with the verified configuration and redirect URIs.
- **Fixed OpenSSH authorized principals certificate authentication for Cloudflare Tunnel**:
- Replaced manual `environment.etc."ssh/authorized_principals/${username}"` symlink and `Match User` config with NixOS native `users.users.<name>.openssh.authorizedPrincipals`.
- Root cause: `environment.etc` without an explicit `mode` creates symlinks pointing into `/nix/store`, which has group-writable mode `0775` (`nixbld` group). Under `StrictModes yes`, sshd refused authentication with `bad ownership or modes for directory /nix/store`, causing certificate principal matching to fail with `Certificate does not contain an authorized principal`.
- Setting `users.users.<name>.openssh.authorizedPrincipals` causes NixOS to generate `/etc/ssh/authorized_principals.d/<name>` with `mode = "0444"`, copying the file instead of symlinking into the store, and automatically configuring `services.openssh.settings.AuthorizedPrincipalsFile = "/etc/ssh/authorized_principals.d/%u"`.
- Also added `mode = "0444"` to `/etc/ssh/ca.pub` and moved `TrustedUserCAKeys` into `services.openssh.settings`.
- **Configured OpenID Connect (OIDC) authentication for Mealie**:
- Configured `services.mealie.settings` with OIDC settings pointing to Pocket ID (`auth.masu.rs`), using client ID `040925ed-b39e-4442-b8e8-369c948c0cd2`.
- Added secret management for `mealie-oidc-secret.age` via `secrets.mealie-oidc-secret`, using `prefix = "OIDC_CLIENT_SECRET="` to generate an environment file.
- Configured `services.mealie.credentialsFile` to load the client secret via systemd's `EnvironmentFile` without exposing it in the world-readable Nix store or systemd unit file.
- Configured `systemd.services.mealie` to order after `mealie-oidc-secret-secret.service`.
- Updated `docs/oidc-services.md` with the verified configuration and callback URI (`https://cooking.masu.rs/login`).
- **Configured OpenID Connect (OIDC) authentication for Actual Budget**:
- Configured `services.actual.settings` with `loginMethod = "openid"` and `openId` settings pointing to Pocket ID (`auth.masu.rs`), using client ID `92afe9f8-7ef6-42ab-8a06-701df3c7179d`.
- Added secret management for `actualbudget-oidc-secret.age` via `secrets.actualbudget-oidc-secret`, set with owner `actualbudget` and group `shared` (0440).
- Configured `systemd.services.actual` to order after the decrypted secret service and granted the dynamic unit access via `SupplementaryGroups = [ "shared" ]` and `PrivateUsers = false`.
- Updated `docs/oidc-services.md` with the verified callback URI (`https://money.masu.rs/openid/callback`) and NixOS configuration snippet.
## 2026-09-03: Ctrl-b did nothing because home-manager was never switched; two-phase toggle
- "Ctrl-b does nothing" root cause: the binding was built into the flake but never activated. The deployed `~/.config/fish/functions/` had no `heal-autosuggest.fish` and no `\cb` binding (0 matches), while the freshly-built config had both. On this setup a system rebuild does not switch home-manager — activation needs the home rebuild (`rebuild-home` / the Alt-Shift-H binding, i.e. `home-manager switch --flake`) followed by a fresh pane so `config.fish` re-runs `fish_user_key_bindings`. No fix works until it is actually activated; this should be the FIRST check next time a "did nothing" is reported.
- Made `heal-autosuggest` a two-phase toggle (chosen by the user over `exec fish`, to preserve the session): disable autosuggestions, `commandline -f repaint`, re-enable, `commandline -f repaint`. Grounded in `reader.rs` `update_autosuggestion`, which clears the wedged `in_flight_autosuggest_request` only on a repaint taken while autosuggestions are disabled. The old one-liner and the `fish_postexec` hook did a single back-to-back `set 0; set 1` with no disabled-state repaint, which likely never flushed the stuck request — matching the manual cure, which had prompt cycles between the off and the on. Still unverified against the real bug (not reproducible in a harness); confirm by activating, then pressing Ctrl-b in a live lagging shell.
- If the two-phase toggle still does not cure once activated: fall back to `exec fish` on the key (guaranteed per the user's day-one report that a fresh shell always fixes it), and capture a flight-recorder log for the upstream fish report.
## 2026-09-01: postexec hook fires but does not cure — moved heal to a keybinding
- Honest status: the `fish_postexec` self-heal hook IS registered and DOES fire (verified in the real config), yet the lag persists. So toggling `fish_autosuggestion_enabled` off/on from `fish_postexec` does not cure it, even though the user typing the same `set … 0; set … 1` at the prompt does.
- Why (code-level): variable dispatch is synchronous (`env_dispatch.rs``reader_set_autosuggestion_enabled` on every `set`), so the two sets net to no change and schedule a repaint. That repaint only has effect from inside the reader's active input loop. `fish_postexec` runs BETWEEN commands, outside that loop, so its effect is superseded before the next prompt. A key binding runs inside the loop; `fish_postexec` cannot.
- Change: bound **Ctrl-b** to a new `heal-autosuggest` function (toggle + `commandline -f repaint`) via `fish_user_key_bindings` (Ctrl-g was already taken). Verified that when `fish_user_key_bindings` runs to completion — as it does in the real config, since the existing `\cn` etc. work (`__fish_config_interactive.fish:105`) — `\cb` binds to heal-autosuggest and the toggle resets the variable. NOT verified to cure the real lag: the bug still cannot be reproduced in a harness, so only a test in a live lagging shell can confirm. The `fish_postexec`/`fish_cancel` hook is kept (harmless) but is no longer considered the fix.
- Guaranteed fallback if Ctrl-b does not cure: a fresh shell (`exec fish`), which the user has confirmed from the very start always fixes it — Ctrl-b can be rebound to that. The only path to a truly automatic fix is a flight-recorder capture (`~/.local/state/lag-triage/RECORD`) of the reader during an actual episode, then an upstream fish report.
## 2026-08-31 (evening): self-heal hook was never firing — fish cannot autoload event handlers
- The `__autosuggestion_unwedge` hook did not work because it was installed via `programs.fish.functions`, which writes to fish's **autoload** directory — and fish only registers `--on-event` handlers when a function is actually loaded, which never happens for a hook nothing calls by name. Verified in a PTY test: the autoloaded handler never fires; the identical definition `source`d eagerly fires immediately. (The zellij module's `__fish_update_cwd_osc` works as an autoloaded event function only because it overrides a function fish itself loads.)
- Meanwhile the user confirmed the instant back-to-back toggle (`set -g fish_autosuggestion_enabled 0 && set -g fish_autosuggestion_enabled 1`) cures a lagging shell — so the handler body is right; only its registration was broken.
- Fix: the handler is now defined eagerly in `config.fish` via `programs.fish.interactiveShellInit` (lag-triage module), registered on **fish_postexec** (fires after every command — the moment TUIs exit) and **fish_cancel** (fires on Ctrl-C at the prompt), so a bare Ctrl-C is an instant no-command cure. Verified in an interactive PTY against the actual nix-generated snippet: registers at startup, fires on both events, still respects a deliberate manual disable.
- Coverage note: if a wedge forms with no command running (and no Ctrl-C), it heals at the next command; worst-case lag window is "until you run anything or press Ctrl-C".
## 2026-08-31 (later): automatic self-heal hook
- Confirmed by A/B in the live shell: after curing the lag with `set -g fish_autosuggestion_enabled 0`, re-enabling with `1` does **not** bring the lag back — the toggle resets the wedged autosuggestion state rather than merely masking it.
- Added `__autosuggestion_unwedge` (lag-triage module): a `fish_postexec` event handler that toggles `fish_autosuggestion_enabled` off/on after every command — i.e. at the exact moment a TUI has just exited, when the wedge forms. Builtins only, no visible output (verified in an interactive PTY test), and it skips the reset when the user has deliberately disabled autosuggestions.
- Honest caveat: the manual cure had keystrokes between the off and the on; whether the instant off/on inside an event handler resets the same reader-internal state is unproven. The flight recorder therefore STAYS ARMED (`~/.local/state/lag-triage/RECORD`) until the hook has survived normal use for a while. If lag recurs despite the hook: cure manually (`set … 0`, type a few chars, `set … 1`), and keep the flight log for that pid — then the hook needs the stronger form (disable at postexec, re-enable one prompt-cycle later, scoped to TUI commands).
- Limitations by design: the hook fires only in shells that run commands, so a wedge formed without any command executing in that shell (if that is possible — e.g. floating-pane TUIs never touch the pane shell) would not be healed until the next command runs there.
## 2026-08-31: culprit confirmed — fish's autosuggestion pipeline
- A/B test in a live lagging shell (pid 56089): `set -g fish_autosuggestion_enabled 0` (builtin only, nothing else) **instantly cured the lag**. The post-TUI typing lag is in fish 4.8.1's autosuggestion pipeline.
- Sampling that shell afterwards showed it had **only one thread** (the main thread): the poisoned state is main-thread-side bookkeeping, not a hung worker still sitting in the process. Source review (`src/threads/threads.rs`, `src/threads/debounce.rs`): `ThreadPool::perform` silently queues work with no spawn and no wake when it believes `total_threads == max_threads` — a leaked `total_threads` count (workers that died without decrementing, e.g. across a TUI's lifetime) would strand all future autosuggestion work forever; the Debounce then abandons its token every 500ms and re-enqueues per keystroke. The exact step that delays keystroke *echo* is still unproven — the flight recorder (armed via `~/.local/state/lag-triage/RECORD`) logs the reader's per-keystroke behavior and will capture it on the next occurrence in a recorded shell.
- Precedent: fish had a closely-related bug class before (#11841 — unread terminal query responses "causing noticeable lags"). No fish release newer than 4.8.1 exists, so no upstream fix to adopt; an upstream report with the flight-recorder capture is the path to a real fix.
- Practical interim cure (harmless, instant, in the lagging shell): `set -g fish_autosuggestion_enabled 0`, and re-enable with `1` — whether lag returns on re-enable is the next discriminating datum.
## 2026-08-30 (later): sampler attach CURES the lag — wedged-thread evidence + flight recorder
- Major new datum: in a lagging shell, running `mkdir` + `/usr/bin/sample $fish_pid … &` + `disown` **cured the lag instantly**, before any planned reset/toggle test could run. Plain external commands do NOT cure it (the 2026-08-29 triage ran many and the lag survived), so the distinguishing action is the sampler **attaching and suspending/resuming fish's threads**. Conclusion: a fish-internal thread/wait is wedged (missed wakeup or stuck blocking wait), and per-keystroke work at the main commandline stalls against it; suspension/resume kicks it loose. Consistent with: `read` prompts unaffected (no autosuggestion/highlight pipeline), subshells immune (fresh threads), raw input clean. The captured sample (`~/.local/state/lag-triage/fish-sample.txt`) shows only the post-cure state — sampling is a cure, not a capture.
- Therefore the observer must be running BEFORE the lag starts: the `fish-no-query-term` wrapper is now a **flight recorder**`touch ~/.local/state/lag-triage/RECORD`, then every newly spawned pane shell logs `FISH_DEBUG=reader,term-support,proc-termowner,iothread,fd-monitor,topic-monitor` to `~/.local/state/lag-triage/flight/fish-<ts>-<pid>.log` (3-day auto-cleanup; remove RECORD to disable, zero overhead when off). When lag next occurs, the log already contains what each keystroke did during the lag.
- `lag-sample` now takes a PID and should be run from a DIFFERENT pane (`echo $fish_pid` — a builtin — in the lagging shell to get it), since attaching from inside cures the lag.
- **Next-occurrence checklist (in order, least perturbing first):** (1) in the lagging shell, builtins only: `set -g fish_autosuggestion_enabled 0` → type at the real commandline; if cured, the autosuggestion/debounce path is implicated (a worker thread was seen in `HistorySearch::go_to_next_match`); (2) still laggy: `fish_default_key_bindings` → test (vi-mode path); (3) from another pane: `kill -WINCH <pid>` → test, then `kill -CONT <pid>` → test (discriminates reader-wakeup vs generic unwedge; if WINCH cures, a window resize would too); (4) from another pane: `lag-sample <pid>` while typing in the lagging pane; (5) immediately save the flight log for that pid.
## 2026-08-30
- **The post-TUI typing lag is NOT resolved** by the `fish-no-query-term` wrapper: lag recurred in a fresh zellij session after exiting Claude Code, in a shell verified (via `ps eww`) to have `fish_features=no-query-term` in its environment. The query-term reader-degradation bug proven on 2026-08-29 is real (and the wrapper stays as hardening against it), but it is not the mechanism behind this lag. Downgraded the entry below from "root cause" to "a root cause".
- Known constraints on the real mechanism: per-keystroke lag at the main fish commandline; fish `read` prompts unaffected; raw input reaches the pane practical as plain bytes; a subshell/`exec fish` cures it (process-local state). Note the 2026-08-29 triage's reset ladder short-circuited on a false "y" at stage A, so stages BG (mouse/keypad/altscreen/stty/DECSTR resets) were never actually tested against real lag.
- Added `lag-sample` (fish function): stack-samples the lagging fish process plus the zellij server/client via `/usr/bin/sample` for 8s while the user types at the commandline. This directly names where the time goes (fish reader? highlighting/autosuggestion threads? zellij render loop?) instead of inferring it. Next occurrence: run `lag-sample` in the lagging shell, type junk at the prompt until done, then inspect `~/.local/state/lag-triage/sample-*.txt`. Follow with `unlag` (full reset ladder, never yet truly tested), then A/B toggles: `set -g fish_autosuggestion_enabled 0`, `fish_default_key_bindings`.
## 2026-08-29 (a root cause found and fixed — but not THE lag)
- **Root-caused and fixed the recurring post-TUI typing lag** (fish + Zellij + Ghostty) using a `lag-triage` capture from a live lagging shell plus a deterministic PTY reproduction (`presets/programs/lag-triage/upstream_repro.py`):
- **Root cause chain**: (1) fish latches feature flags from its **startup environment**, before `config.fish` runs — so the existing `set -gx fish_features no-query-term` in `shellInit` never applied to the shell that set it, only to its children. (2) Zellij spawns pane shells via `default_shell` with no `fish_features` in the environment, so every pane's fish latched `query-term` **on** (the fish 4.8.1 default; the triage log from the lagging shell confirmed `query-term on` while `$fish_features` was correctly set to `no-query-term`). (3) With query-term on, fish sends OSC 11 + CPR (`\e[6n`) + DA1 (`\e[0c`) after **every** command and waits for replies relayed by Zellij. (4) Reproduced on fish 4.8.1: if the terminal fails to reply during just **one** such cycle — answering everything before and after — that fish process's interactive reader is **permanently degraded** (keystroke echo >3s, never recovers; ~35ms before). In production Zellij drops/mangles a relay during TUI teardown or heavy output (cf. zellij-org/zellij#5158), e.g. after `nh home switch`, nvim, jjui, yazi.
- **Why every previous observation finally makes sense**: subshells and `exec fish` were never "resetting" anything — they *inherited* the exported `fish_features=no-query-term` from config.fish, latched query-term off at startup, and were therefore **immune**. The parent zellij-spawned shell never had the variable at startup and stayed vulnerable. Raw keystroke capture in the lagging pane showed instant plain bytes (input path fine) and no stuck terminal modes — the damage was inside the fish process, exactly as the repro shows.
- **Fix**: `zellij.nix` now spawns panes through a `fish-no-query-term` wrapper (`export fish_features=no-query-term; exec fish`), so the feature is latched off in every pane shell. Verified: interactive fish through the built wrapper with the real config reports `query-term off`; the PTY repro with `no-query-term` in the environment shows ~35ms echo through all failure phases.
- **Correction** to the earlier 2026-08-29 entry: `query-term` does **not** default to off in fish 4.8.1 — it defaults on; it only *appeared* off in non-interactive checks because the user config's `set -gx` takes effect for `fish -c` (no reader latch) but not for interactive shells.
- Upstream: fish-shell should bound the reader's wait for query replies instead of degrading permanently (repro script kept at `presets/programs/lag-triage/upstream_repro.py` for filing); Zellij's reply relaying is the trigger (zellij-org/zellij#5158).
- `lag-triage` now checks `status features` and calls out `query-term on` as the known root cause, and warns that its `read`-prompt typing tests may not exhibit main-commandline lag (which produced a false "fixed by stage A" in the first capture).
## 2026-08-29 (later)
- Added a diagnostic toolkit (`lag-triage` / `unlag` fish functions + `term-probe` binary, `presets/programs/lag-triage/`) for the still-recurring post-TUI typing lag in fish + Zellij + Ghostty, instead of another blind fix. Findings that motivated it:
- All three prior fixes were either no-ops or insufficient: `fish_features = no-query-term` is a **no-op** because `query-term` already defaults to *off* in fish 4.8.1 (verified with `status features`); disabling Ghostty's fish integration inside Zellij and setting `support_kitty_keyboard_protocol = false` did not stop recurrence.
- PTY captures of fish 4.8.1 (`TERM=xterm-256color`, with and without `$ZELLIJ`) show fish never writes Kitty keyboard sequences to the wire — it uses modifyOtherKeys (`\e[>4;1m`), application keypad (`\e=`), bracketed paste (`?2004`), and color-theme reporting (`?2031`), enabling them at every prompt and disabling them before every external command. Crucially, a fresh subshell's startup bytes are identical to the parent's post-command re-enable bytes, so "a subshell fixes the lag" cannot be explained by a simple terminal-state reset — leaving two competing hypotheses that only live capture can separate: (1) fish-internal reader state poisoned by stray/partial escape bytes (e.g. leaked from a closing floating pane), cleared only by a new fish process; (2) Zellij/Ghostty-level stuck state (Zellij 0.45's `StdinAnsiParser` is already a proven source of input delays — see the Alt-Shift-P fix below).
- Also note: the floating-pane TUIs (jjui via Alt-Shift-J, yazi via Alt-Shift-Y, scrollback editor) run in their own panes and never pass through the shell's fish process at all, while `nvim` runs inside the shell pane — the triage log records which path preceded the lag.
- **Next occurrence: run `lag-triage` in the lagging shell BEFORE starting a new shell.** It snapshots the environment, queries pane terminal state (kitty flags, modifyOtherKeys, DEC modes, DA1 round-trip latency), captures raw keystroke bytes+timing bypassing fish, then applies staged resets (kitty pop/clear, modifyOtherKeys off, keypad/cursor, mouse/focus/sync, altscreen, stty, DECSTR) — the stage that cures it names the stuck layer. Logs to `~/.local/state/lag-triage/` for an upstream issue. `unlag` is the one-shot convenience version (if `unlag` never helps but `exec fish` does, the bug is fish-internal).
## 2026-08-29
- Fixed 1.5-second latency when pressing `Alt-Shift-P` to trigger `zellij-session` in Zellij 0.45.0 + Ghostty:
- **Root Cause**: Zellij 0.45.0 introduced `StdinAnsiParser` (`zellij-client/src/stdin_ansi_parser.rs`) using `termwiz::InputParser` to parse ANSI control strings (OSCs, CSIs, DCSs) arriving on stdin. When pressing `Alt-Shift-P` (Option-Shift-P) with `support_kitty_keyboard_protocol = false`, Ghostty sent `\x1bP` (`ESC` + uppercase `P`). In ECMA-48 / VT100 standards, `ESC P` is the 7-bit ASCII representation of `DCS` (Device Control String). `StdinAnsiParser` buffered `\x1bP` waiting for a DCS string payload and string terminator (`ST` / `\x1b\`), hitting a ~1.5-second escape timeout before flushing `\x1bP` as residue to the keyboard handler.
- **Fix**: Added `alt+shift+p=text:\x1b[112;4u` and `super+shift+p=text:\x1b[112;4u` in `ghostty.nix` to send the explicit CSI-u sequence for `Alt+Shift+p` (`'p'` with modifier 4 = `ALT | SHIFT`). `StdinAnsiParser` immediately recognizes `\x1b[112;4u` as non-DCS input and passes it straight to the keyboard handler with 0ms latency.
## 2026-08-26
- Fixed macOS shortcuts (`Cmd+T`, `Ctrl+Tab`, `Cmd+Shift+]`, `Cmd+Shift+[`, `Cmd+K`, `Cmd+Shift+E`) in Zellij + Ghostty after disabling the Kitty keyboard protocol:
- Mapped Ghostty keybindings (`super+t`, `super+shift+]`, `super+shift+[`, `ctrl+tab`, `ctrl+shift+tab`, `super+k`, `super+shift+e`) to send standard `Alt` (`ESC`-prefix) text sequences (`\x1bt`, `\x1b}`, `\x1b{`, `\x1bK`, `\x1bE`).
- Added matching `Alt` keybindings (`Alt t`, `Alt ]`, `Alt }`, `Alt [`, `Alt {`, `Alt Shift k`, `Alt Shift e`) in `zellij.nix` for tab creation, tab navigation, scroll mode, and scrollback editing. Symbols like `]` and `}` are parsed by Zellij's termwiz input engine as distinct character codes (`'}'` vs `']'`), so binding both `Alt }` and `Alt Shift ]` ensures `\x1b}` triggers tab navigation correctly.
- Keeps Kitty keyboard protocol disabled in Zellij (`support_kitty_keyboard_protocol = false`) so no CSI-u flags leak into Fish shell, guaranteeing zero post-TUI typing lag while restoring all shortcuts.
- Fixed persistent Fish typing lag after long TUI sessions (Neovim, jjui, Yazi) inside Zellij + Ghostty, which the `no-query-term` / Ghostty-integration fixes from 2026-08-25 did not resolve:
- Verified on Fish 4.8.1 that the `query-term` feature already defaults to `off`, so exporting `fish_features = no-query-term` is a no-op on this Fish version — it isn't the cause of (or fix for) this class of lag.
- Set `support_kitty_keyboard_protocol = false` in `zellij.nix`. Zellij and Ghostty have several open upstream bugs (zellij-org/zellij#3887, #3723, #4178) where the Kitty keyboard protocol's enhancement-flag stack is left in an elevated state after a full-screen TUI exits without properly popping it. Every subsequent keystroke then arrives as a CSI-u sequence that Fish must wait out an escape-disambiguation timeout to parse, which reads as typing lag that worsens the longer the TUI session ran, and persists until the pane's protocol state resets (e.g. a fresh shell/pane). Disabling the protocol support in Zellij avoids the whole bug class; trades off precise modifier reporting (e.g. distinguishing Ctrl+Shift+key) for TUIs running inside Zellij panes, which this setup doesn't otherwise depend on (Shift+Enter is handled via a literal Ghostty `text:` keybind, not the Kitty protocol).
## 2026-08-25
- Fixed Nix evaluation warnings for `stdenv` deprecation and `gemini-cli`:
- Replaced deprecated `stdenv.isLinux` and `stdenv.isDarwin` checks across module presets and package definitions with `stdenv.hostPlatform.isLinux` and `stdenv.hostPlatform.isDarwin`.
- Replaced deprecated `pkgs.gemini-cli` with `pkgs.antigravity-cli` (and updated binary invocation to `agy`) in `experimental.nix` profile and `daily-summary.nix` launchd service.
## 2026-08-25
- Fixed multi-second hang and permanent typing latency in Fish after exiting TUIs inside Zellij and Ghostty:
- Exported `fish_features = "no-query-term"` in `home.sessionVariables` and added `set -gx fish_features no-query-term` to Fish's top-level `shellInit`. Previous attempt (`set -a fish_features no-query-term` in `interactiveShellInit`) set a local variable inside an anonymous initialization function block that went out of scope immediately after startup. Furthermore, Fish reads `fish_features` at binary launch before interactive init functions run. Without `no-query-term` exported prior to Fish startup, Fish attempted terminal feature queries (Primary Device Attributes `DA1` / `\e[?c` and termcap) whenever a TUI (e.g. Neovim, Lazygit, Yazi) exited and returned control to Fish. Zellij drops or delays DA1 response sequences, causing Fish to block on a multi-second stdin timeout, followed by severe input reader desynchronization and typing latency on every subsequent keystroke.
- Disabled `programs.ghostty.enableFishIntegration` and conditionally sourced Ghostty's shell integration script in `shellInit` only when NOT running inside a multiplexer (`not set -q ZELLIJ` and `not set -q TMUX`). Sourcing Ghostty's shell integration inside Zellij sent duplicate and conflicting OSC 133 prompt markers and DECSCUSR cursor escape sequences to Zellij's PTY parser.
## 2026-08-16
- Fixed Zellij new tab directory tracking by adding `__fish_update_cwd_osc` override in `presets/programs/zellij.nix`. Fish's default OSC 7 sequence includes `$hostname`, which on macOS or dynamic network environments evaluates to `Noah-MacBook-Pro.local` or a domain suffix. Zellij compares the OSC 7 hostname against its system hostname (`Noah-MacBook-Pro`), finds a mismatch, and silently ignores the CWD update, leaving new tabs stuck in a previous directory or session default. Overriding `__fish_update_cwd_osc` to send `file://<PWD>` (empty hostname) ensures Zellij always updates its cached CWD on every `cd` and prompt render.
- Fixed Firefox "profile cannot be loaded" error on macOS by removing `home.file."Library/Application Support/Firefox/installs.ini"`. Hardcoding an installation hash in `installs.ini` broke whenever Firefox was updated or rebuilt in the Nix store because the nix store path changed, causing Firefox to compute a new installation hash, fail to match or write to the read-only `installs.ini` symlink, and error out. Firefox on macOS uses `profiles.ini` (managed by Home Manager) and `MOZ_LEGACY_PROFILES=1` (exported by nixpkgs' launcher wrapper).
## 2026-08-03
- Added `presets/security/corporate-ca.nix` (nix-darwin) and enabled it on the
`lookingglass` host to trust a corporate TLS-intercepting proxy's root CA.
Behind the corp network, Nix fetches failed with `SSL peer certificate ...
self-signed certificate in certificate chain (19)` because Nix's stock Mozilla
CA bundle doesn't contain the interception root. The module appends the cert
to `security.pki.certificateFiles`, which rebuilds
`/etc/ssl/certs/ca-certificates.crt` (read by both the Nix daemon and, via
`NIX_SSL_CERT_FILE`, client-side flake fetches).
The cert is kept **out of this public repo** and referenced by absolute path.
It is passed as a string (not a Nix path literal) so pure flake evaluation
doesn't read it at eval time, and it lives at a root-owned, world-readable
path because the unprivileged `nixbld` build user cannot traverse `$HOME`
(mode `0750`) to read it at build time.
One-time setup on a machine behind the proxy:
```sh
# 1. Extract the self-signed corporate root from any TLS connection it MITMs
# (the last cert in the chain, subject == issuer). Any HTTPS host works:
echo | openssl s_client -connect example.com:443 -servername example.com \
-showcerts 2>/dev/null \
| awk '/BEGIN CERT/{c++} c==2' > /tmp/CorpCA.pem
openssl x509 -in /tmp/CorpCA.pem -noout -subject -issuer # sanity check
# 2. Install to the root-owned path the config points at:
sudo install -d -m 0755 -o root -g wheel /etc/ssl/corp-ca
sudo install -m 0644 -o root -g wheel /tmp/CorpCA.pem \
/etc/ssl/corp-ca/CorpCA.pem
# 3. Bootstrap the first rebuild (which must fetch inputs over the proxy)
# with a combined bundle, then it's permanent:
cat /etc/ssl/certs/ca-certificates.crt /etc/ssl/corp-ca/CorpCA.pem \
> /tmp/combined-ca.crt
NIX_SSL_CERT_FILE=/tmp/combined-ca.crt nh darwin switch . --configuration lookingglass
```
- Fixed the `zellij-session` fish function in `presets/programs/zellij.nix`
truncating the session name (derived from the target directory basename)
to 20 characters. Zellij names each session's Unix-domain IPC socket
`$TMPDIR/zellij-<uid>/<version>/<name>`, and on macOS the socket path is
capped at 103 bytes. The `/var/folders/...` `$TMPDIR` prefix consumes ~79
of those, leaving only ~24 chars for the name, so switching into directories
with long basenames overflowed the socket path. Because `switch-session`
had already detached from the current session by the time the new one failed
to bind, the failure took down the entire terminal instead of erroring
gracefully.
## 2026-09-06
- Configured Grafana OIDC authentication via Pocket ID in `platforms/nixos/modules/nmasur/presets/services/grafana/grafana.nix`.
- Enabled `auth.oauth_allow_insecure_email_lookup = true` in Grafana settings to allow linking an incoming OAuth login to an existing Grafana user account with the same email.
## 2026-07-25
- Added jjui config generation to `jujutsu.nix` in Home Manager to include custom GitHub ruleset bypass commands (`ctrl+b` and `ctrl+shift+b`).
- Added `overlays/cheetah3.nix` to disable `pythonMetadataCheckPhase` for `cheetah3`.
This fixes an issue where the NixOS rebuild fails for `sabnzbd` due to `importlib.metadata.PackageNotFoundError: No package metadata was found for cheetah3` during the Python package evaluation in `nixos-unstable`.
## 2026-07-20
- Added `overlays/paho-mqtt.nix` to disable paho-mqtt's flaky, socket-based
test suite. Its `checkPhase` hangs in the Nix sandbox and times out with a
`KeyboardInterrupt` after ~150s, which was breaking the `flame` rebuild
(paho-mqtt is pulled in transitively, e.g. via mealie). The override is
applied through `pythonPackagesExtensions` so it covers every Python
package set.
-4
View File
@@ -1,4 +0,0 @@
# Documentation
Reference documents for some of the more complicated services and maintenance
tasks.
+6 -12
View File
@@ -49,25 +49,19 @@ move the `windows/alacritty.yml` file to
To get started on a bare macOS installation, first install Nix:
```bash
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
sh -c "$(curl -L https://nixos.org/nix/install)"
```
Launch a new shell. Then use Nix to switch to the macOS configuration:
Then use Nix to build nix-darwin:
```bash
sudo rm /etc/bashrc
sudo rm /etc/nix/nix.conf
export NIX_SSL_CERT_FILE="$HOME/Documents/t2-ca-bundle.pem"
nix \
--extra-experimental-features flakes \
--extra-experimental-features nix-command \
run nix-darwin -- switch \
--flake github:nmasur/dotfiles#lookingglass
nix-build https://github.com/LnL7/nix-darwin/archive/master.tar.gz -A installer
./result/bin/darwin-installer
```
Once installed, you can continue to update the macOS configuration:
Then switch to the macOS configuration:
```bash
darwin-rebuild switch --flake ~/dev/personal/dotfiles
darwin-rebuild switch --flake github:nmasur/dotfiles#lookingglass
```
-275
View File
@@ -1,275 +0,0 @@
# OIDC Service Analysis & Setup Requirements
This document analyzes the services defined in this repository (specifically within `platforms/nixos/modules/nmasur/presets/services/` and related server profiles like `flame` and `swan`), evaluates their OpenID Connect (OIDC) compatibility, and details the requirements for configuring OIDC logins.
---
## Identity Provider Context: Pocket ID
The configuration already includes a central self-hosted identity provider: **Pocket ID** (`platforms/nixos/modules/nmasur/presets/services/pocket-id/pocket-id.nix`), hosted on the communications server (`flame`) behind Caddy at **`https://auth.masu.rs`**.
Pocket ID is an OpenID Connect (OIDC) provider with WebAuthn/Passkey support. It exposes the following standard OIDC endpoints:
- **Issuer URL:** `https://auth.masu.rs`
- **Discovery Endpoint:** `https://auth.masu.rs/.well-known/openid-configuration`
- **Authorization Endpoint:** `https://auth.masu.rs/authorize`
- **Token Endpoint:** `https://auth.masu.rs/api/oidc/token`
- **Userinfo Endpoint:** `https://auth.masu.rs/api/oidc/userinfo`
- **JWKS Endpoint:** `https://auth.masu.rs/.well-known/jwks.json`
---
## 1. Service Compatibility Breakdown
### Tier 1: First-Class / Native OIDC Support
These services support OpenID Connect natively without requiring external authentication proxies or custom code:
| Service | Hostname | OIDC Support Level | Configuration Method |
| :--- | :--- | :--- | :--- |
| **Immich** | `photos.masu.rs` | Native core feature | NixOS config (`services.immich.settings.oauth`) |
| **Gitea** | `git.masu.rs` | Native core feature | NixOS config / CLI or Web UI |
| **Nextcloud** | `cloud.masu.rs` | Native (official `user_oidc` app) | Nextcloud app + `nextcloud-occ user_oidc:provider` |
| **Grafana** | `metrics.masu.rs` | Native (Generic OAuth) | NixOS config (`services.grafana.settings."auth.generic_oauth"`) |
| **Paperless-ngx** | `paper.masu.rs` | Native (`django-allauth`) | NixOS env vars (`PAPERLESS_SOCIALACCOUNT_PROVIDERS`) |
| **Mealie** | `cooking.masu.rs` | Native core feature | NixOS config (`services.mealie.settings` + `credentialsFile`) |
| **Karakeep / Hoarder** | `keep.masu.rs` | Native (NextAuth OIDC) | NixOS env vars (`OAUTH_WELLKNOWN_URL`, etc.) |
| **Audiobookshelf** | `read.masu.rs` | Native core feature (v2.3+) | Web UI (Settings → Authentication) |
| **Actual Budget** | `money.masu.rs` | Native core feature (v24.3+) | NixOS config (`services.actual.settings.openId`) |
---
### Tier 2: OIDC via Plugins or Reverse Proxy Header Auth
These services do not have generic OIDC in their core web UI, but can support single sign-on through plugins or reverse proxy headers (`Remote-User` / `X-Forwarded-User`):
| Service | Hostname | Strategy | Notes |
| :--- | :--- | :--- | :--- |
| **Jellyfin** | `stream.masu.rs` | `jellyfin-plugin-sso` | Web clients work well; TV and native apps typically rely on Quick Connect. |
| **Calibre-Web** | `books.masu.rs` | Reverse proxy header auth | Set `services.calibre-web.options.reverseProxyAuth.enable = true` behind an authenticating reverse proxy. |
| **File Browser** | `files.masu.rs` | Reverse proxy header auth | Set `auth.method = "proxy"` and `auth.header = "X-Forwarded-User"` behind an authenticating reverse proxy. |
| **Navidrome** | `music.masu.rs` | Reverse proxy header auth | Supports `ReverseProxyUserHeader` for web UI; Subsonic API clients (Feishin, etc.) still require native user passwords. |
| **Stalwart** | `contacts.masu.rs` | OIDC Directory / SASL OAuth | Stalwart supports OIDC directories, but CardDAV/CalDAV clients usually require HTTP Basic Auth or application passwords. |
---
### Tier 3: Incompatible or No Native OIDC Support
- **Vaultwarden (`vault.masu.rs`):** Incompatible for vault decryption. Vaultwarden uses client-side zero-knowledge encryption where vault keys are derived from the user's master password. Bitwarden Enterprise SSO relies on a proprietary Key Connector that Vaultwarden does not implement.
- **n8n (`n8n.masu.rs`):** SAML/OIDC SSO is an **Enterprise / commercial-only feature**; it is disabled in the free self-hosted Community edition.
- **The Arr Stack (`download.masu.rs`):** Radarr, Sonarr, Lidarr, Readarr, Prowlarr, Bazarr, and Sabnzbd only support API keys, Basic Auth, or Forms.
- **Transmission (`transmission.masu.rs`):** BitTorrent daemon; supports only HTTP Basic Auth / RPC whitelist.
- **Uptime Kuma (`status.masu.rs`), ntfy (`ntfy.masu.rs`), The Lounge (`irc.masu.rs`), Pgweb (`pg.masu.rs`), Mathesar (`mathesar.masu.rs`), Hister (`hister.masu.rs`):** No native generic OIDC login mechanism.
- **Infrastructure / Daemon Services:** PostgreSQL, InfluxDB, bind, avahi, cloudflared, wireguard, litestream (no user web UI).
---
## 2. General Setup Requirements
Configuring OIDC requires setup across four layers:
### A. Pocket ID Setup
For each service, an OIDC client application must be registered in the Pocket ID admin interface:
1. **Client ID:** A unique identifier slug (e.g. `gitea`, `immich`, `paperless`).
2. **Client Secret:** A cryptographically secure random token.
3. **Redirect URIs / Callback URLs:** The exact target URLs the service exposes for authorization code callbacks.
4. **Scopes:** Usually `openid`, `profile`, and `email`.
### B. Secrets Management (agenix)
All client secrets should be encrypted with `agenix` under the respective service directory:
- Example: `platforms/nixos/modules/nmasur/presets/services/<service>/<service>-oidc.age`
- Defined in NixOS under `config.secrets.<service>-oidc` with appropriate owner/group permissions.
### C. Network & Reverse Proxy (Caddy)
1. **Back-Channel Connectivity:** When a user logs in, the service backend makes a server-to-server HTTPS call to `https://auth.masu.rs/api/oidc/token` to exchange the authorization code for tokens. Services hosted on `swan` (NAS) must be able to resolve and reach `auth.masu.rs` over HTTPS.
2. **Proxy Headers:** Caddy's `reverse_proxy` handles `X-Forwarded-Proto`, `X-Forwarded-Host`, and `X-Forwarded-For` by default. Services should have reverse proxy trust enabled (e.g., `trusted_proxies = ["127.0.0.1"]`) so generated redirect URIs preserve the `https://` scheme.
---
## 3. Detailed Setup Requirements for Tier 1 Services
### 1. Immich (`photos.masu.rs`)
- **Pocket ID Redirect URIs:**
- Web: `https://photos.masu.rs/auth/login`
- Mobile: `app.immich:///oauth-callback`
- **Setup in `immich/immich.nix`:**
```nix
services.immich.settings.oauth = {
enabled = true;
issuerUrl = "https://${hostnames.auth}";
clientId = "1f4e0f8d-6cee-4d67-8d53-74bf6c18ae09";
clientSecret._secret = config.secrets.immich-oidc-secret.dest;
scope = "openid profile email";
autoRegister = true;
buttonText = "Login with Pocket ID";
};
```
### 2. Gitea (`git.masu.rs`)
- **Pocket ID Redirect URI:** `https://git.masu.rs/user/oauth2/pocket-id/callback`
- **Setup in Gitea:**
Can be configured in the Web UI under **Site Administration → Authentication Sources** or via CLI:
```bash
gitea admin auth add-oauth \
--name "Pocket ID" \
--provider openidConnect \
--key "<client_id>" \
--secret "<client_secret>" \
--auto-discover-url "https://auth.masu.rs/.well-known/openid-configuration"
```
- Optional: Enable automatic account linking by matching email.
### 3. Nextcloud (`cloud.masu.rs`)
- **Pocket ID Redirect URI:** `https://cloud.masu.rs/apps/user_oidc/code`
- **Setup in `nextcloud/nextcloud.nix`:**
```nix
secrets.nextcloud-oidc-secret = {
source = ./nextcloud-oidc-secret.age;
dest = "${config.secretsDirectory}/nextcloud-oidc-secret";
owner = "nextcloud";
group = "nextcloud";
permissions = "0440";
};
systemd.services.nextcloud-oidc-secret-secret = {
requiredBy = [ "nextcloud-setup.service" ];
before = [ "nextcloud-setup.service" ];
};
services.nextcloud = {
settings.overwriteprotocol = "https";
extraApps = {
user_oidc = config.services.nextcloud.package.packages.apps.user_oidc;
};
};
systemd.services.nextcloud-setup = {
after = [ "nextcloud-oidc-secret-secret.service" ];
postStart = ''
${config.services.nextcloud.occ}/bin/nextcloud-occ user_oidc:provider pocket-id \
--clientid="c8a32c58-a781-4f14-9070-f498fdfda438" \
--clientsecret-file="${config.secrets.nextcloud-oidc-secret.dest}" \
--discoveryuri="https://${hostnames.auth}/.well-known/openid-configuration" \
--scope="openid profile email" \
--mapping-uid="preferred_username" \
--unique-uid=0
'';
};
```
### 4. Grafana (`metrics.masu.rs`)
- **Pocket ID Redirect URI:** `https://metrics.masu.rs/login/generic_oauth`
- **Setup in `grafana/grafana.nix`:**
```nix
services.grafana.settings = {
auth.oauth_allow_insecure_email_lookup = true;
"auth.generic_oauth" = {
enabled = true;
name = "Pocket ID";
allow_sign_up = true;
client_id = "85d879ed-1a86-4984-b33d-43806500ef98";
client_secret = "$__file{${config.secrets.grafana-oidc-secret.dest}}";
scopes = "openid profile email";
auth_url = "https://${hostnames.auth}/authorize";
token_url = "https://${hostnames.auth}/api/oidc/token";
api_url = "https://${hostnames.auth}/api/oidc/userinfo";
login_attribute_path = "preferred_username";
skip_org_role_sync = true;
};
};
```
### 5. Paperless-ngx (`paper.masu.rs`)
- **Pocket ID Redirect URI:** `https://paper.masu.rs/accounts/oidc/pocket-id/login/callback/`
- **Setup in `paperless/paperless.nix`:**
```nix
services.paperless.settings = {
PAPERLESS_APPS = "allauth.socialaccount.providers.openid_connect";
PAPERLESS_REDIRECT_LOGIN_TO_SSO = true;
};
# Configured via environmentFile to pass the client secret, enable PKCE, and link by email:
# PAPERLESS_SOCIALACCOUNT_PROVIDERS = builtins.toJSON {
# openid_connect = {
# APPS = [
# {
# provider_id = "pocket-id";
# name = "Pocket ID";
# client_id = "e6ff7fce-8e67-4c66-8f32-5ec3db4740a9";
# secret = "...";
# settings = {
# server_url = "https://auth.masu.rs";
# token_auth_method = "client_secret_basic";
# oauth_pkce_enabled = true;
# email_authentication = true;
# verified_email = true;
# };
# }
# ];
# };
# };
```
### 6. Mealie (`cooking.masu.rs`)
- **Pocket ID Redirect URI:** `https://cooking.masu.rs/login`
- **Setup in `mealie/mealie.nix`:**
```nix
services.mealie = {
credentialsFile = config.secrets.mealie-oidc-secret.dest;
settings = {
OIDC_AUTH_ENABLED = "true";
OIDC_SIGNUP_ENABLED = "true";
OIDC_CONFIGURATION_URL = "https://${hostnames.auth}/.well-known/openid-configuration";
OIDC_CLIENT_ID = "040925ed-b39e-4442-b8e8-369c948c0cd2";
OIDC_PROVIDER_NAME = "Pocket ID";
OIDC_USER_CLAIM = "email";
};
};
```
### 7. Karakeep / Hoarder (`keep.masu.rs`)
- **Pocket ID Redirect URI:** `https://keep.masu.rs/api/auth/callback/custom`
- **Setup in `karakeep.nix`:**
```nix
services.karakeep.extraEnvironment = {
OAUTH_WELLKNOWN_URL = "https://auth.masu.rs/.well-known/openid-configuration";
OAUTH_CLIENT_ID = "hoarder";
OAUTH_CLIENT_SECRET = "...";
OAUTH_PROVIDER_NAME = "Pocket ID";
OAUTH_ALLOW_DANGEROUS_EMAIL_ACCOUNT_LINKING = "true";
};
```
### 8. Audiobookshelf (`read.masu.rs`)
- **Pocket ID Redirect URIs:**
- Web: `https://read.masu.rs/auth/openid/callback`
- Mobile: `audiobookshelf://oauth`
- **Setup in Audiobookshelf:**
Configured in the Web UI (**Settings → Authentication → OpenID Connect**):
- Issuer URL: `https://auth.masu.rs`
- Client ID & Client Secret
- Match user by email or username
### 9. Actual Budget (`money.masu.rs`)
- **Pocket ID Redirect URI:** `https://money.masu.rs/openid/callback`
- **Setup in `actualbudget/actualbudget.nix`:**
```nix
services.actual.settings = {
loginMethod = "openid";
openId = {
discoveryURL = "https://${hostnames.auth}/.well-known/openid-configuration";
client_id = "92afe9f8-7ef6-42ab-8a06-701df3c7179d";
client_secret._secret = config.secrets.actualbudget-oidc-secret.dest;
server_hostname = "https://${hostnames.budget}";
authMethod = "openid";
};
};
```
*Note:* The optional end-to-end budget encryption password remains separate from the server authentication.
---
## 4. Forward Auth Architecture for Remaining Services
For services without native OIDC support (such as Calibre-Web, File Browser, Uptime Kuma, and the Arr stack), you can implement **Forward Auth via Caddy**:
1. Deploy an authenticating proxy (such as **OAuth2-Proxy** or **Authelia**) configured with Pocket ID as its OIDC provider.
2. Configure Caddy routes using the `forward_auth` directive to verify user sessions with the proxy before forwarding requests to the target service.
3. For services supporting reverse proxy authentication (Calibre-Web and File Browser), Caddy injects identity headers (e.g. `Remote-User: noah` or `X-Forwarded-User: noah`), enabling seamless single sign-on without requiring separate logins.
-82
View File
@@ -1,82 +0,0 @@
# Repairing Nextcloud
You can run the maintenance commands like this:
```
sudo -u nextcloud nextcloud-occ maintenance:mode --on
sudo -u nextcloud nextcloud-occ maintenance:repair
sudo -u nextcloud nextcloud-occ maintenance:mode --off
```
## Rescan Files
```
sudo -u nextcloud nextcloud-occ files:scan --all
```
## Converting from SQLite to MySQL (mariadb)
First: keep Nextcloud set to SQLite as its dbtype, and separately launch MySQL
as a service by copying the configuration found
[here](https://github.com/NixOS/nixpkgs/blob/nixos-unstable/nixos/modules/services/web-apps/nextcloud.nix).
No password is necessary, since the user-based auth works with UNIX sockets.
You can connect to the MySQL instance like this:
```
sudo -u nextcloud mysql -S /run/mysqld/mysqld.sock
```
Create a blank database for Nextcloud:
```sql
create database nextcloud;
```
Now setup the [conversion](https://docs.nextcloud.com/server/17/admin_manual/configuration_database/db_conversion.html):
```
sudo -u nextcloud nextcloud-occ db:convert-type mysql nextcloud localhost nextcloud
```
Ignore the password prompt. Proceed with the conversion.
Now `config.php` will be updated but the override config from NixOS will not
be. Now update your NixOS configuration:
- Remove the `mysql` service you created.
- Set `dbtype` to `mysql`.
- Set `database.createLocally` to `true`.
Rebuild your configuration.
Now, make sure to enable [4-byte
support](https://docs.nextcloud.com/server/latest/admin_manual/configuration_database/mysql_4byte_support.html)
in the database.
## Backing Up MySQL Database
Use this mysqldump command:
```
sudo -u nextcloud mysqldump -S /run/mysqld/mysqld.sock --default-character-set=utf8mb4 nextcloud > backup.sql
```
## Converting to Postgres
Same as MySQL, but run this command instead:
```
sudo -u nextcloud nextcloud-occ db:convert-type pgsql nextcloud /run/postgresql/ nextcloud
```
Then set the `dbtype` to `pgsql`.
## Backing Up Postgres Database
Use this pg_dump command:
```
sudo -u nextcloud pg_dump nextcloud > backup.sql
```
+43
View File
@@ -0,0 +1,43 @@
# Restoring Nextcloud From Backup
Install the `litestream` package.
```
nix-shell --run fish -p litestream
```
Set the S3 credentials:
```
set -x AWS_ACCESS_KEY_ID (read)
set -x AWS_SECRET_ACCESS_KEY (read)
```
Restore from S3:
```
litestream restore -o nextcloud.db s3://noahmasur-backup.s3.us-west-002.backblazeb2.com/nextcloud
```
Install Nextcloud. Then copy DB:
```
sudo rm /data/nextcloud/data/nextcloud.db*
sudo mv nextcloud.db /data/nextcloud/data/
sudo chown nextcloud:nextcloud /data/nextcloud/data/nextcloud.db
sudo chmod 770 /data/nextcloud/data/nextcloud.db
```
Restart Nextcloud:
```
sudo systemctl restart phpfpm-nextcloud.service
```
Adjust Permissions and Directories:
```
sudo mkdir /data/nextcloud/data/noah/files
sudo chown nextcloud:nextcloud /data/nextcloud/data/noah/files
```
-45
View File
@@ -1,45 +0,0 @@
# ZFS
Swan runs its root on ext4. The ZFS drives are managed imperatively (this
[disko configuration](../disks/zfs.nix) is an unused work-in-progress).
The basic ZFS settings are managed [here](../modules/nixos/hardware/zfs.nix).
## Creating a New Dataset
```
sudo zfs create tank/mydataset
sudo zfs set compression=zstd tank/myzstddataset
sudo zfs set mountpoint=/data/mydataset tank/mydataset
```
## Maintenance
### Get Status
```
sudo zpool status
```
### Replace Disk
```
sudo zdb
sudo zpool status -g # Show by GUID
sudo zpool offline tank <GUID>
sudo zpool status
# Remove old disk, insert new disk
sudo zdb
sudo zpool replace tank <OLD GUID> /dev/disk/by-id/<NEW PATH>
sudo zpool status
```
## Initial Setup
```
sudo zpool create tank raidz1 sda sdb sdc
sudo zpool set ashift=12 tank
sudo zpool set autoexpand=on tank
sudo zpool set compression=on tank
```
Generated
+289 -401
View File
@@ -1,23 +1,52 @@
{
"nodes": {
"cl-nix-lite": {
"inputs": {
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs",
"systems": "systems",
"treefmt-nix": "treefmt-nix"
},
"Comment-nvim-src": {
"flake": false,
"locked": {
"lastModified": 1782994178,
"narHash": "sha256-VGc3/2fe6hnOmNSLlOygEAbeS69v7A7rjKDKRzAgE2Q=",
"owner": "hraban",
"repo": "cl-nix-lite",
"rev": "eb584721e5e799bafe0c6210a8a1a398f90e8ac0",
"lastModified": 1681214440,
"narHash": "sha256-48hy+hiaDJLlgWqC7IeZI3dT+VwWkRo4atQbyPxu/ys=",
"owner": "numToStr",
"repo": "Comment.nvim",
"rev": "e51f2b142d88bb666dcaa77d93a07f4b419aca70",
"type": "github"
},
"original": {
"owner": "hraban",
"repo": "cl-nix-lite",
"owner": "numToStr",
"ref": "v0.8.0",
"repo": "Comment.nvim",
"type": "github"
}
},
"bufferline-nvim-src": {
"flake": false,
"locked": {
"lastModified": 1687763763,
"narHash": "sha256-wbOeylzjjScQXkrDbBU2HtrOZrp2YUK+wQ2aOkgxmRQ=",
"owner": "akinsho",
"repo": "bufferline.nvim",
"rev": "bf2f6b7edd0abf6b0732f5e5c0a8f30e51611c75",
"type": "github"
},
"original": {
"owner": "akinsho",
"ref": "v4.2.0",
"repo": "bufferline.nvim",
"type": "github"
}
},
"cmp-nvim-lsp-src": {
"flake": false,
"locked": {
"lastModified": 1687494203,
"narHash": "sha256-mU0soCz79erJXMMqD/FyrJZ0mu2n6fE0deymPzQlxts=",
"owner": "hrsh7th",
"repo": "cmp-nvim-lsp",
"rev": "44b16d11215dce86f253ce0c30949813c0a90765",
"type": "github"
},
"original": {
"owner": "hrsh7th",
"repo": "cmp-nvim-lsp",
"type": "github"
}
},
@@ -28,11 +57,11 @@
]
},
"locked": {
"lastModified": 1786845137,
"narHash": "sha256-oQFip+v0luP8NIxJzmiW4Wu8bILsbFWom5l0zonl8hQ=",
"lastModified": 1687517837,
"narHash": "sha256-Ea+JTy6NSf+wWIFrgC8gnOnyt01xwmtDEn2KecvaBkg=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "4cff07de74b50e64bdd68cd4e722ab5b6b35ee48",
"rev": "6460468e7a3e1290f132fee4170ebeaa127f6f32",
"type": "github"
},
"original": {
@@ -49,11 +78,11 @@
]
},
"locked": {
"lastModified": 1781152676,
"narHash": "sha256-RxWs5ND31KzTG7wvMM+PMfUjyNpmIEr999lqNARaM5o=",
"lastModified": 1687598357,
"narHash": "sha256-70ciIe8415oQnQypawaqocEaLJcI1XtkqRNmle8vsrg=",
"owner": "nix-community",
"repo": "disko",
"rev": "ff8702b4de27f72b4c78573dfb89ec74e36abdf1",
"rev": "1e7098ee0448dc5d33df394d040f454cd42a809c",
"type": "github"
},
"original": {
@@ -62,47 +91,34 @@
"type": "github"
}
},
"firefox-darwin": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1688605308,
"narHash": "sha256-B9suu7dcdX4a18loO5ul237gqIJ5/+TRuheLj8fJjwM=",
"owner": "bandithedoge",
"repo": "nixpkgs-firefox-darwin",
"rev": "78d28acf685e19d353b2ecb6c38eeb3fc624fc68",
"type": "github"
},
"original": {
"owner": "bandithedoge",
"repo": "nixpkgs-firefox-darwin",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1766808011,
"narHash": "sha256-s83BS0abtIj7vzUf8JE0209KphfHA6qRw/92D9k/F+0=",
"owner": "hraban",
"repo": "flake-compat",
"rev": "6e0aafdc4c4c2043c66f756d5045374b42184fc5",
"type": "github"
},
"original": {
"owner": "hraban",
"ref": "fixed-output",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "NixOS",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_3": {
"flake": false,
"locked": {
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
@@ -111,75 +127,49 @@
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1765835352,
"narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "a34fae9c08a15ad73f295041fec82323541400a9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": [
"nur",
"nixpkgs"
]
},
"locked": {
"lastModified": 1733312601,
"narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": [
"mac-app-util",
"systems"
]
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"lastModified": 1678901627,
"narHash": "sha256-U02riOqrKKzwjsxc/400XnElV+UtPUQWpANPlyazjH0=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"rev": "93a2b84fc4b70d9e089d029deacc3583435c2ed6",
"type": "github"
},
"original": {
"id": "flake-utils",
"type": "indirect"
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_3"
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"lastModified": 1685518550,
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_3": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1685518550,
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
"type": "github"
},
"original": {
@@ -195,11 +185,11 @@
]
},
"locked": {
"lastModified": 1788487777,
"narHash": "sha256-Ro/e1N4ZR8/XaFF+sF9SgfPK79HM5YNEppzFj8p+0Ak=",
"lastModified": 1687627695,
"narHash": "sha256-6Pu7nWb52PRtUmihwuDNShDmsZiXgtXR0OARtH4DSik=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "693e8ce0fb240a73c116a03cfd7b19269c87af88",
"rev": "172d46d4b2677b32277d903bdf4cff77c2cc6477",
"type": "github"
},
"original": {
@@ -209,66 +199,26 @@
"type": "github"
}
},
"mac-app-util": {
"nil": {
"inputs": {
"cl-nix-lite": "cl-nix-lite",
"flake-compat": "flake-compat",
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs_3",
"systems": "systems_2",
"treefmt-nix": "treefmt-nix_2"
},
"locked": {
"lastModified": 1783182903,
"narHash": "sha256-7IteXipJZfFepi5zakxe0jQ5i3vRBQguk0+Gtte1Fu4=",
"owner": "hraban",
"repo": "mac-app-util",
"rev": "039f33deef21782d4db97087f426504951239887",
"type": "github"
},
"original": {
"owner": "hraban",
"repo": "mac-app-util",
"type": "github"
}
},
"nix-caddy-withplugins": {
"inputs": {
"flake-compat": "flake-compat_2",
"nixpkgs": "nixpkgs_5"
},
"locked": {
"lastModified": 1784936738,
"narHash": "sha256-X4w9BrFkisfzla3m1WwDJUicUbI7JRdhzq5aS7rrB0k=",
"owner": "MichailiK",
"repo": "nix-caddy-withplugins",
"rev": "70307cfc568e5434cf42067d797b27c38ea69944",
"type": "github"
},
"original": {
"owner": "MichailiK",
"ref": "nixos-unstable",
"repo": "nix-caddy-withplugins",
"type": "github"
}
},
"nix-index-database": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
],
"rust-overlay": "rust-overlay"
},
"locked": {
"lastModified": 1788080100,
"narHash": "sha256-n14luQo3F/ZLfCEHk1QieiI1nGnW92ZQZeZ0UIb0UMQ=",
"owner": "nix-community",
"repo": "nix-index-database",
"rev": "dbb978fa87faf13398e90ccbc52c343d21c7dd6d",
"lastModified": 1680544266,
"narHash": "sha256-d/TusDXmIo8IT5DNRA21lN+nOVSER8atIx9TJteR6LQ=",
"owner": "oxalica",
"repo": "nil",
"rev": "56a1fa87b98a9508920f4b0ab8fe36d5b54b2362",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-index-database",
"owner": "oxalica",
"ref": "2023-04-03",
"repo": "nil",
"type": "github"
}
},
@@ -280,11 +230,11 @@
]
},
"locked": {
"lastModified": 1779563638,
"narHash": "sha256-9CQKxDKDqVYdDhJW+A955AZwpwZjKUgvu0LbfsL6uyI=",
"lastModified": 1685980282,
"narHash": "sha256-uQyVaoqkiocA8bXKMfrgizuKmz0hUzHye5owFoUd2AQ=",
"owner": "gytis-ivaskevicius",
"repo": "nix2vim",
"rev": "6927414f0de10a18fb6bc9977717088777d50d0d",
"rev": "3836a348503ae27340c7f83f0bc7bcb907f3781d",
"type": "github"
},
"original": {
@@ -295,11 +245,11 @@
},
"nixlib": {
"locked": {
"lastModified": 1736643958,
"narHash": "sha256-tmpqTSWVRJVhpvfSN9KXBvKEXplrwKnSZNAoNPf/S/s=",
"lastModified": 1687049841,
"narHash": "sha256-FBNZQfWtA7bb/rwk92mfiWc85x4hXta2OAouDqO5W8w=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "1418bc28a52126761c02dd3d89b2d8ca0f521181",
"rev": "908af6d1fa3643c5818ea45aa92b21d6385fbbe5",
"type": "github"
},
"original": {
@@ -316,11 +266,11 @@
]
},
"locked": {
"lastModified": 1769813415,
"narHash": "sha256-nnVmNNKBi1YiBNPhKclNYDORoHkuKipoz7EtVnXO50A=",
"lastModified": 1687398392,
"narHash": "sha256-T6kc3NMTpGJk1/dve8PGupeVcxboEb78xtTKhe3LL/A=",
"owner": "nix-community",
"repo": "nixos-generators",
"rev": "8946737ff703382fda7623b9fab071d037e897d5",
"rev": "649171f56a45af13ba693c156207eafbbbf7edfe",
"type": "github"
},
"original": {
@@ -331,160 +281,59 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1779796641,
"narHash": "sha256-ZsIrKmhp4vbBXoXXmR/tBXA/UCsAQiJL9vsgZEduhVY=",
"lastModified": 1687502512,
"narHash": "sha256-dBL/01TayOSZYxtY4cMXuNCBk8UMLoqRZA+94xiFpJA=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "25f538306313eae3927264466c70d7001dcea1df",
"rev": "3ae20aa58a6c0d1ca95c9b11f59a2d12eebc511f",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1765674936,
"narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1788405554,
"narHash": "sha256-r2f1oUwixlgq9zOdYLqJLfS/lWBT60/IITjhTKI59JU=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "a5cc6f2c37bf518436dc8d1c288ccd0c43c2f4c4",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-26.05",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1761236834,
"narHash": "sha256-+pthv6hrL5VLW2UqPdISGuLiUZ6SnAXdd2DdUE+fV2Q=",
"owner": "nixos",
"lastModified": 1686929285,
"narHash": "sha256-WGtVzn+vGMPTXDO0DMNKVFtf+zUSqeW+KKk4Y/Ae99I=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d5faa84122bc0a1fd5d378492efce4e289f8eac1",
"rev": "93fddcf640ceca0be331210ba3101cee9d91c13d",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"owner": "NixOS",
"ref": "nixos-22.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"null-ls-nvim-src": {
"flake": false,
"locked": {
"lastModified": 1782999065,
"narHash": "sha256-5Dgj5+pIQYZKrXUGaLCk7CKfN3MmpwIhO94++WVxvng=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "80d591ed473cfc46329932c2aadac9b435342c7c",
"lastModified": 1686871437,
"narHash": "sha256-MxIZqyRW8jStiDNXt7Bsw8peDLKpqxKEaUuIJsXkGMI=",
"owner": "jose-elias-alvarez",
"repo": "null-ls.nvim",
"rev": "bbaf5a96913aa92281f154b08732be2f57021c45",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-26.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1770107345,
"narHash": "sha256-tbS0Ebx2PiA1FRW8mt8oejR0qMXmziJmPaU1d4kYY9g=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "4533d9293756b63904b7238acb84ac8fe4c8c2c4",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_5": {
"locked": {
"lastModified": 1781074563,
"narHash": "sha256-md8WlXOlfnIeHeOScMTTHFyf2d6iaTwPl2apR5EQ3P4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9ae611a455b90cf061d8f332b977e387bda8e1ca",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_6": {
"locked": {
"lastModified": 1788531059,
"narHash": "sha256-hLD4l3QOGBQhkVp3mQ2lJ/YbEi99qUgKapb40KovZ88=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "801bef6abd86b91e51083066b83fb354a11fc640",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_7": {
"locked": {
"lastModified": 1744536153,
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"owner": "jose-elias-alvarez",
"repo": "null-ls.nvim",
"type": "github"
}
},
"nur": {
"inputs": {
"flake-parts": "flake-parts_2",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1788594396,
"narHash": "sha256-HLvxvUIMxBHl+vv/1OVTLPKrSLTinId97WBIxWWByJ0=",
"lastModified": 1687625402,
"narHash": "sha256-V+vSWypmm/tGbwNXGhqzmiV7vTjV2gNCEh9N7OhNnyA=",
"owner": "nix-community",
"repo": "nur",
"rev": "82ca52a282cd5f90afabcf7c9acd27571b253523",
"rev": "aeaf37c7538965e45700d39e6b5dc9c9a0e0749c",
"type": "github"
},
"original": {
@@ -493,35 +342,99 @@
"type": "github"
}
},
"nvim-lspconfig-src": {
"flake": false,
"locked": {
"lastModified": 1675639052,
"narHash": "sha256-B8IgpypxzCACZ5VcqM6KiWyClaN+KrmemtkwMznmj5Y=",
"owner": "neovim",
"repo": "nvim-lspconfig",
"rev": "255e07ce2a05627d482d2de77308bba51b90470c",
"type": "github"
},
"original": {
"owner": "neovim",
"ref": "v0.1.6",
"repo": "nvim-lspconfig",
"type": "github"
}
},
"nvim-tree-lua-src": {
"flake": false,
"locked": {
"lastModified": 1687132855,
"narHash": "sha256-ZRUoCDBv8rO8ZUBUMLgo33EBbqD9+ZOSET9rkFsA++E=",
"owner": "kyazdani42",
"repo": "nvim-tree.lua",
"rev": "c3c6544ee00333b0f1d6a13735d0dd302dba4f70",
"type": "github"
},
"original": {
"owner": "kyazdani42",
"repo": "nvim-tree.lua",
"type": "github"
}
},
"nvim-treesitter-src": {
"flake": false,
"locked": {
"lastModified": 1681121236,
"narHash": "sha256-iPsPDLhVKJ14iP1/2cCgcY9SCKK/DQz9Y0mQB1DqNiM=",
"owner": "nvim-treesitter",
"repo": "nvim-treesitter",
"rev": "cc360a9beb1b30d172438f640e2c3450358c4086",
"type": "github"
},
"original": {
"owner": "nvim-treesitter",
"ref": "v0.9.0",
"repo": "nvim-treesitter",
"type": "github"
}
},
"root": {
"inputs": {
"Comment-nvim-src": "Comment-nvim-src",
"bufferline-nvim-src": "bufferline-nvim-src",
"cmp-nvim-lsp-src": "cmp-nvim-lsp-src",
"darwin": "darwin",
"disko": "disko",
"firefox-darwin": "firefox-darwin",
"home-manager": "home-manager",
"mac-app-util": "mac-app-util",
"nix-caddy-withplugins": "nix-caddy-withplugins",
"nix-index-database": "nix-index-database",
"nil": "nil",
"nix2vim": "nix2vim",
"nixos-generators": "nixos-generators",
"nixpkgs": "nixpkgs_6",
"nixpkgs-stable": "nixpkgs-stable",
"nixpkgs": "nixpkgs",
"null-ls-nvim-src": "null-ls-nvim-src",
"nur": "nur",
"rust-overlay": "rust-overlay",
"wsl": "wsl",
"zellij-switch": "zellij-switch",
"zenyd-mpv-scripts": "zenyd-mpv-scripts"
"nvim-lspconfig-src": "nvim-lspconfig-src",
"nvim-tree-lua-src": "nvim-tree-lua-src",
"nvim-treesitter-src": "nvim-treesitter-src",
"telescope-nvim-src": "telescope-nvim-src",
"telescope-project-nvim-src": "telescope-project-nvim-src",
"toggleterm-nvim-src": "toggleterm-nvim-src",
"vscode-terraform-snippets": "vscode-terraform-snippets",
"wallpapers": "wallpapers",
"wsl": "wsl"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": "nixpkgs_7"
"flake-utils": [
"nil",
"flake-utils"
],
"nixpkgs": [
"nil",
"nixpkgs"
]
},
"locked": {
"lastModified": 1788591095,
"narHash": "sha256-Vh+BeLWfbTT9AecazIsQ/Tkg/RzJeX3lEduANf256WA=",
"lastModified": 1680488274,
"narHash": "sha256-0vYMrZDdokVmPQQXtFpnqA2wEgCCUXf5a3dDuDVshn0=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "c361047d3a538f547f1617bb6b410411929ac9cc",
"rev": "7ec2ff598a172c6e8584457167575b3a1a5d80d8",
"type": "github"
},
"original": {
@@ -546,21 +459,6 @@
}
},
"systems_2": {
"locked": {
"lastModified": 1689347925,
"narHash": "sha256-ozenz5bFe1UUqOn7f60HRmgc01BgTGIKZ4Xl+HbocGQ=",
"owner": "nix-systems",
"repo": "default-darwin",
"rev": "2235d7e6cc29ae99878133c95e9fe5e157661ffb",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default-darwin",
"type": "github"
}
},
"systems_3": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
@@ -575,70 +473,100 @@
"type": "github"
}
},
"systems_4": {
"telescope-nvim-src": {
"flake": false,
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"lastModified": 1686302912,
"narHash": "sha256-fV3LLRwAPykVGc4ImOnUSP+WTrPp9Ad9OTfBJ6wqTMk=",
"owner": "nvim-telescope",
"repo": "telescope.nvim",
"rev": "776b509f80dd49d8205b9b0d94485568236d1192",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"owner": "nvim-telescope",
"ref": "0.1.2",
"repo": "telescope.nvim",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"telescope-project-nvim-src": {
"flake": false,
"locked": {
"lastModified": 1766000401,
"narHash": "sha256-+cqN4PJz9y0JQXfAK5J1drd0U05D5fcAGhzhfVrDlsI=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "42d96e75aa56a3f70cab7e7dc4a32868db28e8fd",
"lastModified": 1682606566,
"narHash": "sha256-H6lrPjpOUVleKHB0ziI+6dthg9ymitHhEWtcgYJTrKo=",
"owner": "nvim-telescope",
"repo": "telescope-project.nvim",
"rev": "7c64b181dd4e72deddcf6f319e3bf1e95b2a2f30",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"owner": "nvim-telescope",
"repo": "telescope-project.nvim",
"type": "github"
}
},
"treefmt-nix_2": {
"inputs": {
"nixpkgs": "nixpkgs_4"
},
"toggleterm-nvim-src": {
"flake": false,
"locked": {
"lastModified": 1780220602,
"narHash": "sha256-eynAfOmbmxJnkp7YewvCEbShNnnYJ9gLLqkzsYtBPeM=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "db947814a175b7ca6ded66e21383d938df01c227",
"lastModified": 1685434104,
"narHash": "sha256-oiCnBrvft6XxiQtQH8E4F842xhh348SaTpHzaeb+iDY=",
"owner": "akinsho",
"repo": "toggleterm.nvim",
"rev": "95204ece0f2a54c89c4395295432f9aeedca7b5f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"owner": "akinsho",
"ref": "v2.7.0",
"repo": "toggleterm.nvim",
"type": "github"
}
},
"vscode-terraform-snippets": {
"flake": false,
"locked": {
"lastModified": 1614849738,
"narHash": "sha256-v392tyzXV+zyBNt5OCB2NBCK7JcByrTa5Ne/nFtSCJI=",
"owner": "run-at-scale",
"repo": "vscode-terraform-doc-snippets",
"rev": "6ab3e44b566e660f38922cf908e6e547eaa5d4b4",
"type": "github"
},
"original": {
"owner": "run-at-scale",
"repo": "vscode-terraform-doc-snippets",
"type": "github"
}
},
"wallpapers": {
"flake": false,
"locked": {
"lastModified": 1657544922,
"narHash": "sha256-1c1uDz37MhksWC75myv6jao5q2mIzD8X8I+TykXXmWg=",
"owner": "exorcist365",
"repo": "wallpapers",
"rev": "8d2860ac6c05cec0f78d5c9d07510f4ff5da90dc",
"type": "gitlab"
},
"original": {
"owner": "exorcist365",
"repo": "wallpapers",
"type": "gitlab"
}
},
"wsl": {
"inputs": {
"flake-compat": "flake-compat_3",
"nixpkgs": [
"nixpkgs"
]
"flake-compat": "flake-compat",
"flake-utils": "flake-utils_3",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1784642409,
"narHash": "sha256-hcbDqFuySAJawljt5r0sKBCJKYnbtGD0T/ZIozH1Dq0=",
"lastModified": 1687279045,
"narHash": "sha256-LR0dsXd/A07M61jclyBUW0wRojEQteWReKM35zoJXp0=",
"owner": "nix-community",
"repo": "NixOS-WSL",
"rev": "eaeb18da90024448a60eb1ec7132eafa4003404e",
"rev": "a8486b5d191f11d571f15d80b6e265d1712d01cf",
"type": "github"
},
"original": {
@@ -646,46 +574,6 @@
"repo": "NixOS-WSL",
"type": "github"
}
},
"zellij-switch": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"rust-overlay": [
"rust-overlay"
],
"systems": "systems_4"
},
"locked": {
"lastModified": 1781873766,
"narHash": "sha256-V8baEgRBa2SqtCLbQ4EaphIMKsnAogY2oMdttWYhkEs=",
"owner": "mostafaqanbaryan",
"repo": "zellij-switch",
"rev": "ced3c681afe40785b2853e3a976908466786ee79",
"type": "github"
},
"original": {
"owner": "mostafaqanbaryan",
"repo": "zellij-switch",
"type": "github"
}
},
"zenyd-mpv-scripts": {
"flake": false,
"locked": {
"lastModified": 1765050776,
"narHash": "sha256-9gO+GkNoGsxAbMRrBWu0FfXEQtyTmHivlaxlYLpV2YM=",
"owner": "zenyd",
"repo": "mpv-scripts",
"rev": "62f4bb313c6cb6366672e78dea940e9da8fec84a",
"type": "github"
},
"original": {
"owner": "zenyd",
"repo": "mpv-scripts",
"type": "github"
}
}
},
"root": "root",
+185 -245
View File
@@ -1,5 +1,5 @@
{
description = "An opinionated flake containing the NixOS, nix-darwin, and home-manager configurations for multiple systems.";
description = "My system";
# Other flakes that we want to pull from
inputs = {
@@ -7,307 +7,247 @@
# Used for system packages
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
# Used for specific stable packages
nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-26.05";
# Used for MacOS system config
darwin = {
url = "github:lnl7/nix-darwin/master";
url = "github:/lnl7/nix-darwin/master";
inputs.nixpkgs.follows = "nixpkgs";
};
# Used for Windows Subsystem for Linux compatibility
wsl = {
url = "github:nix-community/NixOS-WSL";
inputs.nixpkgs.follows = "nixpkgs";
};
wsl.url = "github:nix-community/NixOS-WSL";
# Used for user packages and dotfiles
home-manager = {
url = "github:nix-community/home-manager/master";
inputs.nixpkgs.follows = "nixpkgs"; # Use system packages list for their inputs
inputs.nixpkgs.follows =
"nixpkgs"; # Use system packages list for their inputs
};
# Community packages; used for Firefox extensions
nur = {
url = "github:nix-community/nur";
nur.url = "github:nix-community/nur";
# Use official Firefox binary for macOS
firefox-darwin = {
url = "github:bandithedoge/nixpkgs-firefox-darwin";
inputs.nixpkgs.follows = "nixpkgs";
};
# Better App install management in macOS
mac-app-util = {
url = "github:hraban/mac-app-util";
# inputs.nixpkgs.follows = "nixpkgs"; # Use system packages list for their inputs
};
# Caddy with decoupled plugin hashes
nix-caddy-withplugins = {
url = "github:MichailiK/nix-caddy-withplugins/nixos-unstable";
# Important: Do not follow nixpkgs here, per the README
};
# Manage disk format and partitioning
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};
# Wallpapers
wallpapers = {
url = "gitlab:exorcist365/wallpapers";
flake = false;
};
# Used to generate NixOS images for other platforms
nixos-generators = {
url = "github:nix-community/nixos-generators";
inputs.nixpkgs.follows = "nixpkgs";
};
nix-index-database = {
url = "github:nix-community/nix-index-database";
inputs.nixpkgs.follows = "nixpkgs";
};
# Convert Nix to Neovim config
nix2vim = {
url = "github:gytis-ivaskevicius/nix2vim";
inputs.nixpkgs.follows = "nixpkgs";
};
# MPV Scripts
zenyd-mpv-scripts = {
url = "github:zenyd/mpv-scripts";
# Nix language server
nil = {
url = "github:oxalica/nil/2023-04-03";
inputs.nixpkgs.follows = "nixpkgs";
};
# Neovim plugins
nvim-lspconfig-src = {
url = "github:neovim/nvim-lspconfig/v0.1.6";
flake = false;
};
cmp-nvim-lsp-src = {
url = "github:hrsh7th/cmp-nvim-lsp";
flake = false;
};
null-ls-nvim-src = {
url = "github:jose-elias-alvarez/null-ls.nvim";
flake = false;
};
Comment-nvim-src = {
url = "github:numToStr/Comment.nvim/v0.8.0";
flake = false;
};
nvim-treesitter-src = {
url = "github:nvim-treesitter/nvim-treesitter/v0.9.0";
flake = false;
};
telescope-nvim-src = {
url = "github:nvim-telescope/telescope.nvim/0.1.2";
flake = false;
};
telescope-project-nvim-src = {
url = "github:nvim-telescope/telescope-project.nvim";
flake = false;
};
toggleterm-nvim-src = {
url = "github:akinsho/toggleterm.nvim/v2.7.0";
flake = false;
};
bufferline-nvim-src = {
url = "github:akinsho/bufferline.nvim/v4.2.0";
flake = false;
};
nvim-tree-lua-src = {
url = "github:kyazdani42/nvim-tree.lua";
flake = false;
};
vscode-terraform-snippets = {
url = "github:run-at-scale/vscode-terraform-doc-snippets";
flake = false;
};
rust-overlay = {
url = "github:oxalica/rust-overlay";
};
# Zellij Switcher
zellij-switch = {
url = "github:mostafaqanbaryan/zellij-switch";
inputs.nixpkgs.follows = "nixpkgs";
inputs.rust-overlay.follows = "rust-overlay";
};
# # Text editor
# helix = {
# url = "github:helix-editor/helix";
# inputs.nixpkgs.follows = "nixpkgs";
# inputs.rust-overlay.follows = "rust-overlay";
# };
# # Nextcloud Apps
# nextcloud-news = {
# # https://github.com/nextcloud/news/releases
# url = "https://github.com/nextcloud/news/releases/download/25.0.0-alpha12/news.tar.gz";
# flake = false;
# };
# nextcloud-external = {
# # https://github.com/nextcloud-releases/external/releases
# url = "https://github.com/nextcloud-releases/external/releases/download/v5.5.2/external-v5.5.2.tar.gz";
# flake = false;
# };
# nextcloud-cookbook = {
# # https://github.com/christianlupus-nextcloud/cookbook-releases/releases/
# url = "https://github.com/christianlupus-nextcloud/cookbook-releases/releases/download/v0.11.2/cookbook-0.11.2.tar.gz";
# flake = false;
# };
# nextcloud-snappymail = {
# # https://github.com/the-djmaze/snappymail/releases
# # https://snappymail.eu/repository/nextcloud
# url = "https://snappymail.eu/repository/nextcloud/snappymail-2.38.2-nextcloud.tar.gz";
# # url = "https://github.com/nmasur/snappymail-nextcloud/releases/download/v2.36.3/snappymail-2.36.3-nextcloud.tar.gz";
# flake = false;
# };
};
outputs =
{ nixpkgs, ... }@inputs:
outputs = { self, nixpkgs, ... }@inputs:
let
hostnames =
let
baseName = "masu.rs";
in
{
audiobooks = "read.${baseName}";
auth = "auth.${baseName}";
bookmarks = "keep.${baseName}";
books = "books.${baseName}";
budget = "money.${baseName}";
contacts = "contacts.${baseName}";
content = "cloud.${baseName}";
download = "download.${baseName}";
files = "files.${baseName}";
# Common overlays to always use
overlays = [
inputs.nur.overlay
inputs.nix2vim.overlay
(import ./overlays/neovim-plugins.nix inputs)
(import ./overlays/calibre-web.nix)
(import ./overlays/disko.nix inputs)
(import ./overlays/tree-sitter.nix inputs)
];
# Global configuration for my systems
globals = let baseName = "masu.rs";
in rec {
user = "noah";
fullName = "Noah Masur";
gitName = fullName;
gitEmail = "7386960+nmasur@users.noreply.github.com";
mail.server = "noahmasur.com";
mail.imapHost = "imap.purelymail.com";
mail.smtpHost = "smtp.purelymail.com";
dotfilesRepo = "git@github.com:nmasur/dotfiles";
nixpkgs.overlays = overlays;
hostnames = {
git = "git.${baseName}";
hister = "hister.${baseName}";
imap = "imap.purelymail.com";
influxdb = "influxdb.${baseName}";
irc = "irc.${baseName}";
mail = "noahmasur.com";
mathesar = "mathesar.${baseName}";
metrics = "metrics.${baseName}";
minecraft = "minecraft.${baseName}";
mumble = "mumble.${baseName}";
n8n = "n8n.${baseName}";
navidrome = "music.${baseName}";
notifications = "ntfy.${baseName}";
paperless = "paper.${baseName}";
photos = "photos.${baseName}";
postgresql = "pg.${baseName}";
prometheus = "prom.${baseName}";
recipes = "cooking.${baseName}";
secrets = "vault.${baseName}";
smtp = "smtp.purelymail.com";
status = "status.${baseName}";
stream = "stream.${baseName}";
transmission = "transmission.${baseName}";
content = "cloud.${baseName}";
books = "books.${baseName}";
download = "download.${baseName}";
};
lib = import ./lib inputs;
flattenAttrset = attrs: builtins.foldl' lib.mergeAttrs { } (builtins.attrValues attrs);
homeModules = builtins.mapAttrs (
system: hosts:
builtins.mapAttrs (
name: module: (builtins.head (lib.attrsToList module.home-manager.users)).value
) hosts
) lib.hosts;
# Disk formatting, only used once
diskoConfigurations = {
root = import ./hosts/x86_64-linux/swan/root.nix;
};
generators = builtins.mapAttrs (
# x86_64-linux = { arrow = ...; swan = ...; }
system: hosts:
(lib.concatMapAttrs (name: module: {
"${name}-aws" = lib.generateImage {
inherit system module;
format = "amazon";
specialArgs = { inherit hostnames; };
# System types to support.
supportedSystems =
[ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];
# Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
in rec {
nixosModules = {
globals = { config }: { config = globals; };
common = import ./modules/common;
nixos = import ./modules/nixos;
darwin = import ./modules/darwin;
};
# Contains my full system builds, including home-manager
# nixos-rebuild switch --flake .#tempest
nixosConfigurations = {
tempest = import ./hosts/tempest { inherit self; };
hydra = import ./hosts/hydra { inherit self; };
flame = import ./hosts/flame { inherit self; };
swan = import ./hosts/swan { inherit self; };
};
# Contains my full Mac system builds, including home-manager
# darwin-rebuild switch --flake .#lookingglass
darwinConfigurations = {
lookingglass = import ./hosts/lookingglass { inherit self; };
};
# For quickly applying home-manager settings with:
# home-manager switch --flake .#tempest
homeConfigurations = {
tempest =
nixosConfigurations.tempest.config.home-manager.users.${globals.user}.home;
lookingglass =
darwinConfigurations.lookingglass.config.home-manager.users."Noah.Masur".home;
};
# Disk formatting, only used once
diskoConfigurations = { root = import ./disks/root.nix; };
packages = let
aws = system: import ./hosts/aws { inherit self system; };
staff = system: import ./hosts/staff { inherit self system; };
neovim = system:
let pkgs = import nixpkgs { inherit system overlays; };
in import ./modules/common/neovim/package {
inherit pkgs;
colors = (import ./colorscheme/gruvbox-dark).dark;
};
"${name}-iso" = lib.generateImage {
inherit system module;
format = "iso";
specialArgs = { inherit hostnames; };
};
"${name}-qcow" = lib.generateImage {
inherit system module;
format = "qcow-efi";
specialArgs = { inherit hostnames; };
# extraModules = [ "${nixpkgs}/nixos/modules/virtualisation/oci-image.nix" ];
};
}) hosts)
) lib.linuxHosts # x86_64-linux = { arrow = ...; swan = ...; }
;
in {
x86_64-linux.aws = aws "x86_64-linux";
x86_64-linux.staff = staff "x86_64-linux";
in
{
# Package Neovim config into standalone package
x86_64-linux.neovim = neovim "x86_64-linux";
x86_64-darwin.neovim = neovim "x86_64-darwin";
aarch64-linux.neovim = neovim "aarch64-linux";
aarch64-darwin.neovim = neovim "aarch64-darwin";
};
inherit lib;
nixosConfigurations = flattenAttrset (
builtins.mapAttrs (
system: hosts:
builtins.mapAttrs (
name: module:
lib.buildNixos {
inherit system module;
specialArgs = { inherit hostnames; };
}
) hosts
) lib.linuxHosts
);
darwinConfigurations = flattenAttrset (
builtins.mapAttrs (
system: hosts:
builtins.mapAttrs (
name: module:
lib.buildDarwin {
inherit system module;
specialArgs = { inherit hostnames; };
}
) hosts
) lib.darwinHosts
);
homeConfigurations = flattenAttrset (
builtins.mapAttrs (
system: hosts:
builtins.mapAttrs (
name: module:
lib.buildHome {
inherit system module;
specialArgs = { inherit hostnames; };
}
) hosts
) homeModules
);
# packages =
# lib.forSystems lib.linuxSystems (
# system: generateImagesForHosts system // lib.pkgsBySystem.${system}.nmasur
# )
# // lib.forSystems lib.darwinSystems (system: lib.pkgsBySystem.${system}.nmasur);
packages = lib.forAllSystems (
system:
# Share the custom packages that I have placed under the nmasur namespace
lib.pkgsBySystem.${system}.nmasur
//
# Share generated images for each relevant host
(if (lib.hasInfix "linux" system) then generators.${system} else { })
# //
# # Oracle
# {
# flame-oci = nixosConfigurations.flame.config.system.build.OCIImage;
# }
);
# Programs that can be run by calling this flake
apps = forAllSystems (system:
let pkgs = import nixpkgs { inherit system overlays; };
in import ./apps { inherit pkgs; });
# Development environments
devShells = lib.forAllSystems (system: {
default = lib.pkgsBySystem.${system}.nmasur.dotfiles-devshell;
});
devShells = forAllSystems (system:
let pkgs = import nixpkgs { inherit system overlays; };
in {
# checks = lib.forAllSystems (
# system:
# let
# pkgs = import nixpkgs {
# inherit system;
# overlays = lib.overlays;
# };
# in
# {
# neovim =
# pkgs.runCommand "neovim-check-health" { buildInputs = [ inputs.self.packages.${system}.neovim ]; }
# ''
# mkdir -p $out
# export HOME=$TMPDIR
# nvim -c "checkhealth" -c "write $out/health.log" -c "quitall"
# # Check for errors inside the health log
# if $(grep "ERROR" $out/health.log); then
# cat $out/health.log
# exit 1
# fi
# '';
# }
# );
formatter = lib.forAllSystems (
system:
let
pkgs = import nixpkgs {
inherit system;
inherit (lib) overlays;
# Used to run commands and edit files in this repo
default = pkgs.mkShell {
buildInputs = with pkgs; [ git stylua nixfmt shfmt shellcheck ];
};
in
pkgs.nixfmt
);
});
# Templates for starting other projects quickly
templates = (import ./templates { inherit lib; });
templates = rec {
default = basic;
basic = {
path = ./templates/basic;
description = "Basic program template";
};
poetry = {
path = ./templates/poetry;
description = "Poetry template";
};
python = {
path = ./templates/python;
description = "Legacy Python template";
};
haskell = {
path = ./templates/haskell;
description = "Haskell template";
};
};
};
}
+12
View File
@@ -0,0 +1,12 @@
# Hosts
| Host | Purpose |
| --- | --- |
| [aws](./aws/default.nix) | AWS AMI |
| [staff](./staff/default.nix) | Live USB stick |
| [flame](./flame/default.nix) | Oracle cloud server |
| [hydra](./hydra/default.nix) | WSL config |
| [lookingglass](./lookingglass/default.nix) | Work MacBook |
| [swan](./swan/default.nix) | Home server |
| [tempest](./tempest/default.nix) | Linux desktop |
@@ -1,54 +0,0 @@
# The Looking Glass
# System configuration for my work Macbook
rec {
networking.hostName = "NYCM-NMASUR2";
networking.computerName = "NYCM-NMASUR2";
nmasur.settings = {
username = "Noah.Masur";
fullName = "Noah Masur";
};
nmasur.profiles = {
base.enable = true;
# work.enable = true;
# extra.enable = true;
# gaming.enable = true;
};
# Corporate network runs a TLS-intercepting proxy. Trust its root CA so Nix
# fetches don't fail with "self-signed certificate in certificate chain". The
# cert lives outside this public repo at a root-owned, world-readable path so
# the unprivileged Nix build user can read it (a copy under $HOME is not
# traversable by nixbld). See docs/CHANGELOG.md to extract and install it.
nmasur.presets.security.corporateCa = {
enable = true;
certFile = "/etc/ssl/corp-ca/CorpCA.pem";
};
home-manager.users."Noah.Masur" = {
nmasur.settings = {
username = nmasur.settings.username;
fullName = nmasur.settings.fullName;
host = "lookingglass";
};
nmasur.profiles = {
common.enable = true;
darwin-base.enable = true;
darwin-gaming.enable = true;
llm-development.enable = true;
power-user.enable = true;
work.enable = true;
experimental.enable = true;
};
nmasur.presets.services.mbsync.user = "noah";
nmasur.presets.programs.git-work.work = {
name = "Noah-Masur_1701";
email = "${nmasur.settings.username}@take2games.com";
};
home.stateVersion = "23.05";
};
system.stateVersion = 5;
}
@@ -1,19 +0,0 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IE1nSGFPdyBYY0c1
bldPMVdwRGhia0xzNm1mSGQ0eVVZZzFLMWVUdFhYVW1KY3QwTERJCm1UUDBqN1Jr
VjRlSjROTFZEQ3UrME81Y2FyRkxjZHFvUFN1eEZNa2cwWkUKLT4gc3NoLWVkMjU1
MTkgWXlTVU1RIFM4ZURETk0yQ1NKN3owaDB5S3djeThtWkphS0xUaVd2MnBlS244
d0V5bUUKKzE5UzZYUlpwTy9ydFQ3L2NmWFZBMEZCWWppYlFZQzhTVFhUc3BBRlpR
dwotPiBzc2gtZWQyNTUxOSBuanZYNUEgbS9HaVFFRjU4VzMvSVZ1SlExZ1BBNWxK
RVRESEVmeWxyMW5Pclk5UU4xOApaZ0hHMnlsb1BuMThsTG82d1k1Rm94VEFMcTBH
TFoxRWpRSXJWQUJVWGN3Ci0+IHNzaC1lZDI1NTE5IENxSU9VQSBOK1AvRW5nNUlK
NnB1UDhKdmFibTEwV2JhOGxKRGg1SnlMdytXaVQ1eW1NCnVIcjNnclVMSEJ3eDVk
eExnY1EvbWg1OW4xR3FydDlzeUdLQW11UGRmclkKLT4gc3NoLWVkMjU1MTkgejFP
Y1p3IHNxbDlpSkpPdEZVRGhwV3NKdTNJV0NTeUZoVVg3b0ZlRWdqelNaTDRWamMK
N1czMXlEcWZzQ1g0dkxBTnNUQjdTdnZMUE9VRGphTjB4SjJyemJiU2w5NAotLS0g
VlgyN24vMGl3a2N6ZGs1SjhOZmttWEl3WEFOK29EOUlkZ0N3blBBcHZQOArP/uJx
SBFvBV0rLzRbo/8nhhizuT2TtKsJSJDIsFJxAOtF8ZwIbYwauBt1tJudtJaXgbv5
JIk53sS3lelOwH11yx0aGcBNCR2UwpuYn1IwoW0qrt3ugi8rmQPRG8c8gSyNn2u7
PnAj/NdAjieLGGLltwdq2wJhkbv800WmKx5sGgPNT1AdBS1b84h5ipQFnFf3Fi0N
FJa9/j5qc7JtWEy4ecU6D03zcZD+CTCoN8SIW4+fldos1u7N2v2X5/YUw7T6jQ==
-----END AGE ENCRYPTED FILE-----
-169
View File
@@ -1,169 +0,0 @@
# The Flame
# System configuration for an Oracle free server
# How to install:
# https://blog.korfuri.fr/posts/2022/08/nixos-on-an-oracle-free-tier-ampere-machine/
# These days, probably use nixos-anywhere instead.
rec {
networking.hostName = "flame";
nmasur.settings = {
username = "noah";
fullName = "Noah Masur";
};
nmasur.profiles = {
base.enable = true;
server.enable = true;
communications.enable = true;
};
home-manager.users."noah" = {
nmasur.settings = {
username = nmasur.settings.username;
fullName = nmasur.settings.fullName;
host = networking.hostName;
};
nmasur.profiles = {
common.enable = true;
linux-base.enable = true;
llm-development.enable = true;
power-user.enable = true;
};
nmasur.presets.programs.helix.enable = true;
home.stateVersion = "25.05";
};
system.stateVersion = "25.05";
# File systems must be declared in order to boot
# # This is the root filesystem containing NixOS
# # I forgot to set a clean label for it
# fileSystems."/" = {
# device = "/dev/disk/by-uuid/e1b6bd50-306d-429a-9f45-78f57bc597c3";
# fsType = "ext4";
# };
# # This is the boot filesystem for systemd-boot
# fileSystems."/boot" = {
# device = "/dev/disk/by-uuid/D5CA-237A";
# fsType = "vfat";
# };
# Allows private remote access over the internet
nmasur.presets.services.cloudflared = {
tunnel = {
id = "bd250ee1-ed2e-42d2-b627-039f1eb5a4d2";
credentialsFile = ./cloudflared-flame.age;
ca = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK/6oyVqjFGX3Uvrc3VS8J9sphxzAnRzKC85xgkHfYgR3TK6qBGXzHrknEj21xeZrr3G2y1UsGzphWJd9ZfIcdA= open-ssh-ca@cloudflareaccess.org";
};
};
# Taken from https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/virtualisation/oci-common.nix
# fileSystems."/" = {
# device = "/dev/disk/by-label/nixos";
# fsType = "ext4";
# autoResize = true;
# };
# fileSystems."/boot" = {
# device = "/dev/disk/by-label/ESP";
# fsType = "vfat";
# };
boot.loader.efi.canTouchEfiVariables = false;
boot.loader.grub = {
device = "nodev";
splashImage = null;
extraConfig = ''
serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
terminal_input --append serial
terminal_output --append serial
'';
efiInstallAsRemovable = true;
efiSupport = true;
};
boot.loader.systemd-boot.enable = false;
# https://docs.oracle.com/en-us/iaas/Content/Compute/Tasks/configuringntpservice.htm#Configuring_the_Oracle_Cloud_Infrastructure_NTP_Service_for_an_Instance
networking.timeServers = [ "169.254.169.254" ];
boot.growPartition = true;
boot.kernelParams = [
"net.ifnames=0"
"nvme.shutdown_timeout=10"
"nvme_core.shutdown_timeout=10"
"libiscsi.debug_libiscsi_eh=1"
"crash_kexec_post_notifiers"
# aarch64-linux
"console=ttyAMA0,115200n8"
# VNC console
"console=tty1"
];
boot.initrd.availableKernelModules = [
"virtio_net"
"virtio_pci"
"virtio_mmio"
"virtio_blk"
"virtio_scsi"
"9p"
"9pnet_virtio"
];
boot.initrd.kernelModules = [
"virtio_balloon"
"virtio_console"
"virtio_rng"
"virtio_gpu"
];
networking.useDHCP = true;
# networking = {
# defaultGateway = "10.0.0.1";
# interfaces.eth0 = {
# ipAddress = throw "set your own";
# prefixLength = 24;
# };
# };
disko.devices = {
disk = {
main = {
type = "disk";
# device = "/dev/oracleoci/oraclevda"; # Consistent volume naming
device = "/dev/sda"; # Consistent volume naming
content = {
type = "gpt";
partitions = {
boot = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
# # Otherwise the instance may not have a working network-online.target,
# # making the fetch-ssh-keys.service fail
# networking.useNetworkd = true;
}
+25
View File
@@ -0,0 +1,25 @@
{ self, system, ... }:
self.inputs.nixos-generators.nixosGenerate {
inherit system;
format = "amazon";
modules = [
self.inputs.home-manager.nixosModules.home-manager
self.nixosModules.globals
self.nixosModules.common
self.nixosModules.nixos
{
networking.hostName = "sheep";
gui.enable = false;
theme.colors = (import ../../colorscheme/gruvbox).dark;
passwordHash = null;
publicKey =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+AbmjGEwITk5CK9y7+Rg27Fokgj9QEjgc9wST6MA3s";
# AWS settings require this
permitRootLogin = "prohibit-password";
}
] ++ [
# Required to fix diskSize errors during build
({ ... }: { amazonImage.sizeMB = 16 * 1024; })
];
}
+14 -29
View File
@@ -1,23 +1,13 @@
# locals {
# image_file = one(fileset(path.root, "../../../result/nixos-amazon-image-*.vhd"))
# }
#
# # Upload image to S3
# resource "aws_s3_object" "image" {
# bucket = var.images_bucket
# key = basename(local.image_file)
# source = local.image_file
# etag = filemd5(local.image_file)
# }
# Use existing image in S3
data "aws_s3_object" "image" {
bucket = var.images_bucket
key = "arrow.vhd"
locals {
image_file = one(fileset(path.root, "result/nixos-amazon-image-*.vhd"))
}
resource "terraform_data" "image_replacement" {
input = data.aws_s3_object.image.etag
# Upload to S3
resource "aws_s3_object" "image" {
bucket = "your_bucket_name"
key = basename(local.image_file)
source = local.image_file
etag = filemd5(local.image_file)
}
# Setup IAM access for the VM Importer
@@ -39,8 +29,8 @@ data "aws_iam_policy_document" "vmimport" {
"s3:ListBucket",
]
resources = [
"arn:aws:s3:::${data.aws_s3_object.image.bucket}",
"arn:aws:s3:::${data.aws_s3_object.image.bucket}/*",
"arn:aws:s3:::${aws_s3_object.image.bucket}",
"arn:aws:s3:::${aws_s3_object.image.bucket}/*",
]
}
statement {
@@ -68,28 +58,23 @@ resource "aws_ebs_snapshot_import" "image" {
disk_container {
format = "VHD"
user_bucket {
s3_bucket = data.aws_s3_object.image.bucket
s3_key = data.aws_s3_object.image.key
s3_bucket = aws_s3_object.image.bucket
s3_key = aws_s3_object.image.key
}
}
role_name = aws_iam_role.vmimport.name
lifecycle {
replace_triggered_by = [terraform_data.image_replacement]
}
}
# Convert to AMI
resource "aws_ami" "image" {
description = "Created with NixOS."
name = replace(basename(data.aws_s3_object.image.key), "/\\.vhd$/", "")
name = replace(basename(local.image_file), "/\\.vhd$/", "")
virtualization_type = "hvm"
root_device_name = "/dev/xvda"
ena_support = true
ebs_block_device {
device_name = "/dev/xvda"
snapshot_id = aws_ebs_snapshot_import.image.id
volume_size = 17
volume_size = 8
}
}
+280
View File
@@ -0,0 +1,280 @@
name: 'Terraform'
env:
AWS_ACCOUNT_NUMBER: ''
AWS_PLAN_ROLE_NAME: github_actions_plan
AWS_APPLY_ROLE_NAME: github_actions_admin
# Always required. Used for authenticating to AWS, but can also act as your
# default region if you don't want to specify in the provider configuration.
AWS_REGION: us-east-1
# You must change these to fit your project.
TF_VAR_project: change-me
TF_VAR_label: change-me
TF_VAR_owner: Your Name Here
# If storing Terraform in a subdirectory, specify it here.
TERRAFORM_DIRECTORY: .
# Pinned versions of tools to use.
# Check for new releases:
# - https://github.com/hashicorp/terraform/releases
# - https://github.com/fugue/regula/releases
# - https://github.com/terraform-linters/tflint/releases
TERRAFORM_VERSION: 1.2.6
REGULA_VERSION: 2.9.0
TFLINT_VERSION: 0.39.1
# Terraform configuration options
TERRAFORM_PARALLELISM: 10
# These variables are passed to Terraform based on GitHub information.
TF_VAR_repo: ${{ github.repository }}
# This workflow is triggered in the following ways.
on:
# Any push or merge to these branches.
push:
branches:
- dev
- prod
# Any pull request targeting these branches (plan only).
pull_request:
branches:
- dev
- prod
# Any manual trigger on these branches.
workflow_dispatch:
branches:
- dev
- prod
# -------------------------------------------------------------------
# The rest of this workflow can operate without adjustments. Edit the
# below content at your own risk!
# -------------------------------------------------------------------
# Used to connect to AWS IAM
permissions:
id-token: write
contents: read
pull-requests: write
# Only run one workflow at a time for each Terraform state. This prevents
# lockfile conflicts, especially during PR vs push.
concurrency: terraform-${{ github.base_ref || github.ref }}
jobs:
terraform:
name: 'Terraform'
# Change this if you need to run your deployment on-prem.
runs-on: ubuntu-latest
steps:
# Downloads the current repo code to the runner.
- name: Checkout Repo Code
uses: actions/checkout@v3
# Enable access to KVM, required to build an image
- name: Enable KVM group perms
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
# Login to AWS
- name: AWS Assume Role
uses: aws-actions/[email protected]
with:
role-to-assume: ${{ env.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
# Install Nix
- name: Install Nix
uses: cachix/install-nix-action@v17
with:
extra_nix_config: |
substituters = s3://insert-cache-bucket https://cache.nixos.org/
trusted-public-keys = insert-cache-bucket:M6PsZjHXcLvbQyPUBLICKEYGVoNwI84g1FBQzouRU= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
# Build the image
- name: Build Image
run: nix build .#aws
# Copy the image to S3
- name: Upload Image to Cache
env:
NIX_CACHE_PRIVATE_KEY: ${{ secrets.NIX_CACHE_PRIVATE_KEY }}
run: |
echo "$NIX_CACHE_PRIVATE_KEY" > cache.key
nix store sign --key-file cache.key $(readlink result)
nix copy --to s3://t2-aws-nixos-test $(readlink result)
rm cache.key
# Exports all GitHub Secrets as environment variables prefixed by
# "TF_VAR_", which exposes them to Terraform. The name of each GitHub
# Secret must match its Terraform variable name exactly.
- name: Export Secrets to Terraform Variables
env:
ALL_SECRETS: ${{ toJson(secrets) }}
run: |
echo "$ALL_SECRETS" \
| jq "to_entries | .[] | \"TF_VAR_\" + ( .key | ascii_downcase ) + \"=\" + .value" \
| tr -d \" >> $GITHUB_ENV
# Installs the Terraform binary and some other accessory functions.
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: ${{ env.TERRAFORM_VERSION }}
# Checks whether Terraform is formatted properly. If this fails, you
# should install the pre-commit hook.
- name: Check Formatting
run: |
terraform fmt -no-color -check -diff -recursive
# Downloads a Terraform code lint test.
- uses: terraform-linters/setup-tflint@v1
name: Setup TFLint
with:
tflint_version: v${{ env.TFLINT_VERSION }}
# Sets up linting with this codebase.
- name: Init TFLint
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: tflint --init
# Lints the current code.
- name: Run TFLint
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
tflint -f compact
find ./modules/* -type d -maxdepth 0 | xargs -I __ tflint -f compact --disable-rule=terraform_required_providers --disable-rule=terraform_required_version __
# Connects to remote state backend and download providers.
- name: Terraform Init
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
terraform init \
-backend-config="role_arn=${{ env.AWS_STATE_ROLE_ARN }}" \
-backend-config="region=us-east-1" \
-backend-config="workspace_key_prefix=accounts/${{ env.AWS_ACCOUNT_NUMBER }}/${{ github.repository }}" \
-backend-config="key=state.tfstate" \
-backend-config="dynamodb_table=global-tf-state-lock"
# Set the Terraform Workspace to the current branch name.
- name: Set Terraform Workspace
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
shell: bash
run: |
export WORKSPACE=${{ github.base_ref || github.ref_name }}
terraform workspace select ${WORKSPACE} || terraform workspace new $WORKSPACE
echo "TF_WORKSPACE=$(echo ${WORKSPACE} | sed 's/\//_/g')" >> $GITHUB_ENV
# Checks differences between current code and infrastructure state.
- name: Terraform Plan
id: plan
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
terraform plan \
-input=false \
-no-color \
-out=tfplan \
-parallelism=${TERRAFORM_PARALLELISM} \
-var-file=variables-${TF_WORKSPACE}.tfvars
# Gets the results of the plan for pull requests.
- name: Terraform Show Plan
id: show
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: terraform show -no-color tfplan
# Adds the results of the plan to the pull request.
- name: Comment Plan
uses: actions/github-script@v6
if: github.event_name == 'pull_request'
env:
STDOUT: "```terraform\n${{ steps.show.outputs.stdout }}```"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
// 1. Retrieve existing bot comments for the PR
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
})
const botComment = comments.find(comment => {
return comment.user.type === 'Bot' && comment.body.includes('Terraform Format and Style')
})
// 2. Prepare format of the comment
const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
#### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
#### Terraform Validation 🤖\`${{ steps.validate.outcome }}\`
<details><summary>Validation Output</summary>
\`\`\`\n
${{ steps.validate.outputs.stdout }}
\`\`\`
</details>
#### Terraform Plan 📖\`${{ steps.plan.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\`\n
${process.env.PLAN}
\`\`\`
</details>
*Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Working Directory: \`${{ env.tf_actions_working_dir }}\`, Workflow: \`${{ github.workflow }}\`*`;
// 3. If we have a comment, update it, otherwise create a new one
if (botComment) {
github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: output
})
} else {
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
}
# Downloads Regula and checks whether the plan meets compliance requirements.
- name: Regula Compliance Check
shell: bash
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
REGULA_URL="https://github.com/fugue/regula/releases/download/v${REGULA_VERSION}/regula_${REGULA_VERSION}_Linux_x86_64.tar.gz"
curl -sL "$REGULA_URL" -o regula.tar.gz
tar xzf regula.tar.gz
terraform show -json tfplan | ./regula run
# Deploys infrastructure or changes to infrastructure.
- name: Terraform Apply
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
working-directory: ${{ env.TERRAFORM_DIRECTORY }}
run: |
terraform apply \
-auto-approve \
-input=false \
-parallelism=${TERRAFORM_PARALLELISM} \
tfplan
-47
View File
@@ -1,47 +0,0 @@
# Return a list of all hosts
nixpkgs:
let
inherit (nixpkgs) lib;
in
{
# darwin-hosts = import ./darwin;
aarch64-darwin-hosts = lib.pipe (lib.filesystem.listFilesRecursive ./aarch64-darwin) [
# Get only files ending in default.nix
(builtins.filter (name: lib.hasSuffix "default.nix" name))
# Import each host function
(map (file: {
name = builtins.baseNameOf (builtins.dirOf file);
value = import file;
}))
# Convert to an attrset of hostname -> host function
(builtins.listToAttrs)
];
aarch64-linux-hosts = lib.pipe (lib.filesystem.listFilesRecursive ./aarch64-linux) [
# Get only files ending in default.nix
(builtins.filter (name: lib.hasSuffix "default.nix" name))
# Remove the first file
(builtins.filter (name: name != ./aarch64-linux/default.nix))
# Import each host function
(map (file: {
name = builtins.baseNameOf (builtins.dirOf file);
value = import file;
}))
# Convert to an attrset of hostname -> host function
(builtins.listToAttrs)
];
x86_64-linux-hosts = lib.pipe (lib.filesystem.listFilesRecursive ./x86_64-linux) [
# Get only files ending in default.nix
(builtins.filter (name: lib.hasSuffix "default.nix" name))
# Import each host function
(map (file: {
# name = lib.removeSuffix ".nix" (builtins.baseNameOf file);
name = builtins.baseNameOf (builtins.dirOf file);
value = import file;
}))
# Convert to an attrset of hostname -> host function
(builtins.listToAttrs)
];
}
+107
View File
@@ -0,0 +1,107 @@
# The Flame
# System configuration for an Oracle free server
# How to install:
# https://blog.korfuri.fr/posts/2022/08/nixos-on-an-oracle-free-tier-ampere-machine/
{ self, ... }:
self.inputs.nixpkgs.lib.nixosSystem {
system = "aarch64-linux";
specialArgs = { };
modules = [
self.inputs.home-manager.nixosModules.home-manager
self.nixosModules.globals
self.nixosModules.common
self.nixosModules.nixos
{
# Hardware
server = true;
networking.hostName = "flame";
imports =
[ (self.inputs.nixpkgs + "/nixos/modules/profiles/qemu-guest.nix") ];
boot.initrd.availableKernelModules = [ "xhci_pci" "virtio_pci" "usbhid" ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/e1b6bd50-306d-429a-9f45-78f57bc597c3";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/D5CA-237A";
fsType = "vfat";
};
# Theming
gui.enable = false;
theme = { colors = (import ../../colorscheme/gruvbox).dark; };
# Disable passwords, only use SSH key
publicKey =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+AbmjGEwITk5CK9y7+Rg27Fokgj9QEjgc9wST6MA3s";
# Programs and services
cloudflare.enable = true; # Proxy traffic with Cloudflare
dotfiles.enable = true; # Clone dotfiles
neovim.enable = true;
services.caddy.enable = true;
services.grafana.enable = true;
services.prometheus.enable = true;
services.gitea.enable = true;
services.vaultwarden.enable = true;
services.minecraft-server.enable = true; # Setup Minecraft server
cloudflareTunnel = {
enable = true;
id = "bd250ee1-ed2e-42d2-b627-039f1eb5a4d2";
credentialsFile = ../../private/cloudflared-flame.age;
ca =
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK/6oyVqjFGX3Uvrc3VS8J9sphxzAnRzKC85xgkHfYgR3TK6qBGXzHrknEj21xeZrr3G2y1UsGzphWJd9ZfIcdA= open-ssh-ca@cloudflareaccess.org";
};
giteaRunner.enable = true;
# Nextcloud backup config
backup.s3 = {
endpoint = "s3.us-west-002.backblazeb2.com";
bucket = "noahmasur-backup";
accessKeyId = "0026b0e73b2e2c80000000005";
};
# # Wireguard config for Transmission
# wireguard.enable = true;
# networking.wireguard.interfaces.wg0 = {
#
# # The local IPs for this machine within the Wireguard network
# # Any inbound traffic bound for these IPs should be kept on localhost
# ips = [ "10.66.13.200/32" "fc00:bbbb:bbbb:bb01::3:dc7/128" ];
#
# peers = [{
#
# # Identity of Wireguard target peer (VPN)
# publicKey = "bOOP5lIjqCdDx5t+mP/kEcSbHS4cZqE0rMlBI178lyY=";
#
# # The public internet address of the target peer
# endpoint = "86.106.143.132:51820";
#
# # Which outgoing IP ranges should be sent through Wireguard
# allowedIPs = [ "0.0.0.0/0" "::0/0" ];
#
# # Send heartbeat signal within the network
# persistentKeepalive = 25;
#
# }];
#
# };
# # VPN port forwarding
# services.transmission.settings.peer-port = 57599;
# # Grant access to Transmission directories from Jellyfin
# users.users.jellyfin.extraGroups = [ "transmission" ];
}
];
}
+45
View File
@@ -0,0 +1,45 @@
# The Hydra
# System configuration for WSL
{ self, ... }:
self.inputs.nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { };
modules = [
self.inputs.wsl.nixosModules.wsl
self.inputs.home-manager.nixosModules.home-manager
self.nixosModules.globals
self.nixosModules.common
self.nixosModules.nixos
self.nixosModules.wsl
{
networking.hostName = "hydra";
identityFile = "/home/${globals.user}/.ssh/id_ed25519";
gui.enable = false;
theme = {
colors = (import ../../colorscheme/gruvbox).dark;
dark = true;
};
passwordHash = inputs.nixpkgs.lib.fileContents ../../password.sha512;
wsl = {
enable = true;
wslConf.automount.root = "/mnt";
defaultUser = globals.user;
startMenuLaunchers = true;
nativeSystemd = true;
wslConf.network.generateResolvConf = true; # Turn off if it breaks VPN
interop.includePath =
false; # Including Windows PATH will slow down Neovim command mode
};
neovim.enable = true;
mail.enable = true;
mail.aerc.enable = true;
mail.himalaya.enable = true;
dotfiles.enable = true;
nixlang.enable = true;
lua.enable = true;
}
];
}
+46
View File
@@ -0,0 +1,46 @@
# The Looking Glass
# System configuration for my work Macbook
{ self, ... }:
self.inputs.darwin.lib.darwinSystem {
system = "x86_64-darwin";
specialArgs = { };
modules = [
self.inputs.home-manager.darwinModules.home-manager
self.nixosModules.common
self.nixosModules.darwin
({ config, lib, ... }: {
config = rec {
user = lib.mkForce "Noah.Masur";
gitName = lib.mkForce "Noah-Masur_1701";
gitEmail = lib.mkForce "${user}@take2games.com";
nixpkgs.overlays = [ self.inputs.firefox-darwin.overlay ];
networking.hostName = "lookingglass";
identityFile = "/Users/${user}/.ssh/id_ed25519";
gui.enable = true;
theme = {
colors = (import ../../colorscheme/gruvbox-dark).dark;
dark = true;
};
mail.user = globals.user;
charm.enable = true;
neovim.enable = true;
mail.enable = true;
mail.aerc.enable = true;
mail.himalaya.enable = false;
kitty.enable = true;
discord.enable = true;
firefox.enable = true;
dotfiles.enable = true;
nixlang.enable = true;
terraform.enable = true;
python.enable = true;
lua.enable = true;
kubernetes.enable = true;
_1password.enable = true;
slack.enable = true;
};
})
];
}
+45
View File
@@ -0,0 +1,45 @@
# The Staff
# ISO configuration for my USB drive
{ self, system, ... }:
self.inputs.nixos-generators.nixosGenerate {
inherit system;
format = "install-iso";
modules = [
self.nixosModules.global
self.nixosModules.common
self.nixosModules.nixos
({ config, pkgs, ... }: {
networking.hostName = "staff";
users.extraUsers.root.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+AbmjGEwITk5CK9y7+Rg27Fokgj9QEjgc9wST6MA3s"
];
services.openssh = {
enable = true;
ports = [ 22 ];
allowSFTP = true;
settings = {
GatewayPorts = "no";
X11Forwarding = false;
PasswordAuthentication = false;
PermitRootLogin = "yes";
};
};
environment.systemPackages = with pkgs; [
git
vim
wget
curl
(import ../../modules/common/neovim/package {
inherit pkgs;
colors = (import ../../colorscheme/gruvbox).dark;
})
];
nix.extraOptions = ''
experimental-features = nix-command flakes
warn-dirty = false
'';
})
];
}
+80
View File
@@ -0,0 +1,80 @@
# The Swan
# System configuration for my home NAS server
{ self, ... }:
self.inputs.nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { };
modules = [
self.inputs.home-manager.nixosModules.home-manager
self.inputs.disko.nixosModules.disko
self.nixosModules.globals
self.nixosModules.common
self.nixosModules.nixos
{
# Hardware
server = true;
networking.hostName = "swan";
boot.initrd.availableKernelModules =
[ "xhci_pci" "ahci" "nvme" "usb_storage" "sd_mod" ];
# Required for transcoding
boot.initrd.kernelModules = [ "amdgpu" ];
boot.kernelParams = [
"radeon.si_support=0"
"amdgpu.si_support=1"
"radeon.cik_support=0"
"amdgpu.cik_support=1"
"amdgpu.dc=1"
];
hardware.enableRedistributableFirmware = true;
powerManagement.cpuFreqGovernor = "powersave";
hardware.cpu.intel.updateMicrocode = true;
# ZFS
zfs.enable = true;
# Generated with: head -c 8 /etc/machine-id
networking.hostId = "600279f4"; # Random ID required for ZFS
disko = {
enableConfig = true;
devices = (import ../../disks/root.nix { disk = "/dev/nvme0n1"; });
};
boot.zfs.extraPools = [ "tank" ];
gui.enable = false;
theme = { colors = (import ../../colorscheme/gruvbox).dark; };
neovim.enable = true;
cloudflare.enable = true;
dotfiles.enable = true;
arrs.enable = true;
services.caddy.enable = true;
services.jellyfin.enable = true;
services.nextcloud.enable = true;
services.calibre-web.enable = true;
services.prometheus.enable = true;
services.samba.enable = true;
cloudflareTunnel = {
enable = true;
id = "646754ac-2149-4a58-b51a-e1d0a1f3ade2";
credentialsFile = ../../private/cloudflared-swan.age;
ca =
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCHF/UMtJqPFrf6f6GRY0ZFnkCW7b6sYgUTjTtNfRj1RdmNic1NoJZql7y6BrqQinZvy7nsr1UFDNWoHn6ah3tg= open-ssh-ca@cloudflareaccess.org";
};
backup.s3 = {
endpoint = "s3.us-west-002.backblazeb2.com";
bucket = "noahmasur-backup";
accessKeyId = "0026b0e73b2e2c80000000005";
};
# Disable passwords, only use SSH key
publicKey =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+AbmjGEwITk5CK9y7+Rg27Fokgj9QEjgc9wST6MA3s";
}
];
}
+103
View File
@@ -0,0 +1,103 @@
# The Tempest
# System configuration for my desktop
{ self, ... }:
self.inputs.nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
self.inputs.home-manager.nixosModules.home-manager
self.nixosModules.globals
self.nixosModules.common
self.nixosModules.nixos
{
# Hardware
physical = true;
networking.hostName = "tempest";
boot.initrd.availableKernelModules =
[ "nvme" "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ];
boot.initrd.kernelModules = [ "amdgpu" ];
boot.kernelModules = [ "kvm-amd" ];
services.xserver.videoDrivers = [ "amdgpu" ];
hardware.enableRedistributableFirmware = true;
powerManagement.cpuFreqGovernor = "performance";
hardware.cpu.amd.updateMicrocode = true;
hardware.fancontrol.enable = true;
hardware.fancontrol.config = ''
# Configuration file generated by pwmconfig, changes will be lost
INTERVAL=10
DEVPATH=hwmon0=devices/pci0000:00/0000:00:03.1/0000:06:00.0/0000:07:00.0/0000:08:00.0
DEVNAME=hwmon0=amdgpu
FCTEMPS=hwmon0/pwm1=hwmon0/temp1_input
FCFANS= hwmon0/pwm1=hwmon0/fan1_input
MINTEMP=hwmon0/pwm1=50
MAXTEMP=hwmon0/pwm1=70
MINSTART=hwmon0/pwm1=100
MINSTOP=hwmon0/pwm1=10
MINPWM=hwmon0/pwm1=10
MAXPWM=hwmon0/pwm1=240
'';
fileSystems."/" = {
device = "/dev/disk/by-label/nixos";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-label/boot";
fsType = "vfat";
};
# Must be prepared ahead
identityFile = "/home/${globals.user}/.ssh/id_ed25519";
passwordHash = self.inputs.nixpkgs.lib.fileContents ../../password.sha512;
# Theming
gui.enable = true;
theme = {
colors = (import ../../colorscheme/gruvbox-dark).dark;
dark = true;
};
wallpaper = "${self.inputs.wallpapers}/gruvbox/road.jpg";
gtk.theme.name = self.inputs.nixpkgs.lib.mkDefault "Adwaita-dark";
# Programs and services
charm.enable = true;
neovim.enable = true;
media.enable = true;
dotfiles.enable = true;
firefox.enable = true;
kitty.enable = true;
_1password.enable = true;
discord.enable = true;
nautilus.enable = true;
obsidian.enable = true;
mail.enable = true;
mail.aerc.enable = true;
mail.himalaya.enable = true;
keybase.enable = true;
mullvad.enable = false;
nixlang.enable = true;
yt-dlp.enable = true;
gaming = {
dwarf-fortress.enable = true;
enable = true;
steam.enable = true;
legendary.enable = true;
lutris.enable = true;
leagueoflegends.enable = true;
ryujinx.enable = true;
};
cloudflareTunnel = {
enable = true;
id = "ac133a82-31fb-480c-942a-cdbcd4c58173";
credentialsFile = ../../private/cloudflared-tempest.age;
ca =
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPY6C0HmdFCaxYtJxFr3qV4/1X4Q8KrYQ1hlme3u1hJXK+xW+lc9Y9glWHrhiTKilB7carYTB80US0O47gI5yU4= open-ssh-ca@cloudflareaccess.org";
};
}
];
}
-1
View File
@@ -1 +0,0 @@
# No x86 Darwin Hosts Currently
-47
View File
@@ -1,47 +0,0 @@
# The Arrow
# System configuration for temporary VM
rec {
# Hardware
networking.hostName = "arrow";
nmasur.settings = {
username = "noah";
fullName = "Noah Masur";
};
nmasur.profiles = {
base.enable = true;
server.enable = true;
};
home-manager.users."noah" = {
nmasur.settings = {
username = nmasur.settings.username;
fullName = nmasur.settings.fullName;
host = networking.hostName;
};
nmasur.profiles = {
common.enable = true;
linux-base.enable = true;
};
home.stateVersion = "23.05";
};
system.stateVersion = "23.05";
# These filesystems are ignored by nixos-generators
# This is the root filesystem containing NixOS
fileSystems."/" = {
device = "/dev/disk/by-label/nixos";
fsType = "ext4";
};
# This is the boot filesystem for Grub
fileSystems."/boot" = {
device = "/dev/disk/by-label/boot";
fsType = "vfat";
};
}
-54
View File
@@ -1,54 +0,0 @@
# The Hydra
# System configuration for WSL
rec {
# Hardware
networking.hostName = "hydra";
nmasur.settings = {
username = "noah";
fullName = "Noah Masur";
};
nmasur.profiles = {
base.enable = true;
wsl.enable = true;
};
home-manager.users."noah" = {
nmasur.settings = {
username = nmasur.settings.username;
fullName = nmasur.settings.fullName;
host = networking.hostName;
};
nmasur.profiles = {
common.enable = true;
linux-base.enable = true;
power-user.enable = true;
};
home.stateVersion = "23.05";
};
system.stateVersion = "23.05";
# This is the root filesystem containing NixOS
fileSystems."/" = {
device = "/dev/disk/by-label/nixos";
fsType = "ext4";
};
# This is the boot filesystem for Grub
fileSystems."/boot" = {
device = "/dev/disk/by-label/boot";
fsType = "vfat";
};
# Not sure what's necessary but too afraid to remove anything
boot.initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"nvme"
"usb_storage"
"sd_mod"
];
}
-68
View File
@@ -1,68 +0,0 @@
# The Staff
# System configuration test
rec {
# Hardware
networking.hostName = "staff";
nmasur.settings = {
username = "noah";
fullName = "Noah Masur";
};
nmasur.profiles = {
base.enable = true;
home.enable = true;
gui.enable = true;
};
nmasur.presets.services.cloudflared.enable = false;
nmasur.presets.services.kanata.enable = false;
nmasur.presets.services.openssh.enable = true;
home-manager.users."noah" = {
nmasur.settings = {
username = nmasur.settings.username;
fullName = nmasur.settings.fullName;
host = networking.hostName;
};
nmasur.profiles = {
common.enable = true;
linux-base.enable = true;
linux-gui.enable = true;
power-user.enable = true;
};
nmasur.presets.services.mbsync = {
user = nmasur.settings.username;
server = "noahmasur.com";
};
home.stateVersion = "23.05";
};
system.stateVersion = "23.05";
# Not sure what's necessary but too afraid to remove anything
# File systems must be declared in order to boot
# Required to have a boot loader to work
boot.loader.systemd-boot.enable = true;
# This is the root filesystem containing NixOS
fileSystems."/" = {
device = "/dev/disk/by-label/nixos";
fsType = "ext4";
};
# This is the boot filesystem for Grub
fileSystems."/boot" = {
device = "/dev/disk/by-label/boot";
fsType = "vfat";
};
# Allows private remote access over the internet
# nmasur.presets.services.cloudflared = {
# tunnel = {
# id = "ac133a82-31fb-480c-942a-cdbcd4c58173";
# credentialsFile = ../../private/cloudflared-tempest.age;
# ca = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPY6C0HmdFCaxYtJxFr3qV4/1X4Q8KrYQ1hlme3u1hJXK+xW+lc9Y9glWHrhiTKilB7carYTB80US0O47gI5yU4= [email protected]";
# };
# };
}
@@ -1,19 +0,0 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IE1nSGFPdyBuU1BO
ZGdOVFRTekN3SFRhQWYwcWt6OHpLZXM0aENsQ3d1VWpxYUhXN3k4CnNwNWpQcU90
elR0eWVyMWFUUkhORFRQeVdsdTErWUgzNW9mN1BWS2szbUUKLT4gc3NoLWVkMjU1
MTkgWXlTVU1RIDRRS0VESG5NNWpxbm1DTjBkY2ExR3I3aE1PUTNVTXBFZ2wwVC9j
WDIrMTgKaXkwWk1xRWp6Z3c3NFZZS0cybHFqcUVRczNMZ05tSmxPNjBlVmRHK2h3
SQotPiBzc2gtZWQyNTUxOSBuanZYNUEgYWI1SkhBZ0Jrb3hzbDRwcnRMeHhGSlpB
aWt5ZmRxaGhmbXFDQWdNamxRbwpXMWo0TjZhbHM3ejFYNVU5WlkxWTJSbVBPOEhr
MUt3akxMU3QzN3Z4M0lFCi0+IHNzaC1lZDI1NTE5IENxSU9VQSA1dmdFRE1QSU55
WUVuOU9OTHJjM0RxVWhmTlEvdk1mNU1iUEwyNlFqRFZvCldleVNGQ1RZdU1tU2J2
dWxGZlVkaDBWNEZRUFNBcEIvTEdkODdDS25GdGsKLT4gc3NoLWVkMjU1MTkgejFP
Y1p3IG5SbVpVZldPSkJXYm9BZ3B6OXZXMlB4T2Z5RHptR1pqbVRVUS9LVEZyUzgK
ZlVTZWlUQ2VoWVNuOGE5Wk5vZGdVVldzbUZKRERyUFN0eVJ4TWpmL2gwcwotLS0g
MlJmTlVkdHhHQ25JWm0xVmdjVDFYM1lHaE9WdnJSVkVMMnNwV0c4eXc0QQpYTchD
DCiEm4rOav4/IwK26gSgi2B4qq4RETyAbF4oa87cvFM8zVXo7W8nF+eWzeWGNZrc
yRFfOYf4gtlNCVLc+UAapvM1rKuw1IOQeJLAwGCHygXKWY7zSXdT2ERACXESvCCz
b7W62NVx0Pc4mAAQB3QU995HFVFuHLa939PPPlkgxqepNWCKrmUFl/qjRpOoVrV6
GwRgIo36PhcfZVCswyVXgcc4M3CCG5ZuWInuuljqTB70OzBVkKCaUeiRaRIZJg==
-----END AGE ENCRYPTED FILE-----
-86
View File
@@ -1,86 +0,0 @@
# The Swan
# System configuration for my home NAS server
rec {
networking.hostName = "swan";
nmasur.settings = {
username = "noah";
fullName = "Noah Masur";
};
nmasur.profiles = {
base.enable = true;
server.enable = true;
home.enable = true;
nas.enable = true;
shared-media.enable = true;
};
home-manager.users."noah" = {
nmasur.settings = {
username = nmasur.settings.username;
fullName = nmasur.settings.fullName;
host = networking.hostName;
};
nmasur.profiles = {
common.enable = true;
linux-base.enable = true;
llm-development.enable = true;
power-user.enable = true;
};
home.stateVersion = "23.05";
};
system.stateVersion = "23.05";
# Temp: disable while this isn't working
services.automatic-timezoned.enable = false;
# Not sure what's necessary but too afraid to remove anything
boot.initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"nvme"
"usb_storage"
"sd_mod"
];
# Required for transcoding
boot.initrd.kernelModules = [ "amdgpu" ];
boot.kernelParams = [
"radeon.si_support=0"
"amdgpu.si_support=1"
"radeon.cik_support=0"
"amdgpu.cik_support=1"
"amdgpu.dc=1"
];
# Required binary blobs to boot on this machine
hardware.enableRedistributableFirmware = true;
# Prioritize efficiency over performance
powerManagement.cpuFreqGovernor = "powersave";
# Allow firmware updates
hardware.cpu.intel.updateMicrocode = true;
# ZFS
# Generated with: head -c 8 /etc/machine-id
networking.hostId = "600279f4"; # Random ID required for ZFS
# Sets root ext4 filesystem instead of declaring it manually
disko = {
enableConfig = true;
devices = (import ./root.nix { disk = "/dev/nvme0n1"; });
};
# Allows private remote access over the internet
nmasur.presets.services.cloudflared = {
tunnel = {
id = "646754ac-2149-4a58-b51a-e1d0a1f3ade2";
credentialsFile = ./cloudflared-swan.age;
ca = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCHF/UMtJqPFrf6f6GRY0ZFnkCW7b6sYgUTjTtNfRj1RdmNic1NoJZql7y6BrqQinZvy7nsr1UFDNWoHn6ah3tg= open-ssh-ca@cloudflareaccess.org";
};
};
}
-39
View File
@@ -1,39 +0,0 @@
{ disk, ... }:
{
disk = {
boot = {
type = "disk";
device = disk;
content = {
type = "gpt";
partitions = {
# Boot partition
ESP = rec {
size = "512MiB";
type = "EF00";
label = "boot";
device = "/dev/disk/by-label/${label}";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
extraArgs = [ "-n ${label}" ];
};
};
# Root partition ext4
root = rec {
size = "100%";
label = "nixos";
device = "/dev/disk/by-label/${label}";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
extraArgs = [ "-L ${label}" ];
};
};
};
};
};
};
}
@@ -1,19 +0,0 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IE1nSGFPdyBMMDZj
dkdOSTc1bHgybjAwcUVHUXFhY0I0NE45K1B5SEh4NGN4T0tpREhjCnViQzVXTVk3
dzB5ZEY5d1hvcHVDSXVubElOdEVQQ0Fja2dnL0ZnVG4wNEUKLT4gc3NoLWVkMjU1
MTkgWXlTVU1RIHRiVkNzRCs4V3EvL2szNG8zSlRYSVloRlVsSTk3a0hnSFMyUzRN
K0lyMzQKWDhIOTVzMGJkaGpVQmNtS0pPQnNTSjRmK1dYck1SUFd5VnVvYnJSWkhs
YwotPiBzc2gtZWQyNTUxOSBuanZYNUEgUy9kWmxsc0p2L0pFWDlPMGlzeVBHNjJ2
a05ycmZtbUJrOXRBVVgxVjVtZwo4RVM3Sms2M2krdjU0K1ZHREhhRkdMcENTbTJr
UEh6cWcvOGNIRUNJZUFvCi0+IHNzaC1lZDI1NTE5IENxSU9VQSA5d0FwTWJzZEpJ
UEJaZi91YXZOOFhGVlVVekEvSVRnK3Y2VFdhL216SVZrCkRURE9OYnIvSGVtMWNl
MWFGSTEwUVBITTlnaWVXc1hpMFpqdFdMcFY2NHMKLT4gc3NoLWVkMjU1MTkgejFP
Y1p3IDhkUk9KTE1UMysvajY3WEx5MFdLeEdlVnBmSUx6TTlnYWdqaWorYm0rRGMK
SlhaRFNLaTZHVkpRR3U5eE5JOTBPWWM3OTFka1ZWSHJMaFFhZG1CSzNVTQotLS0g
THNSTmNzK3JvbVN5S3RaVlQwdDRiOWFkVGZ3WUtyT2IyMWtPSVNtajF5YwoCUWHU
KSe09W6BaVEW2x0ieBXN6KkL4FNPAP8zCom8fNVjyjAZpxd2t0kisJjJ45xg8eG4
x9sj+hG9EsWIFvVpnF7LUIEXcRMjvRlNXED8HmYR0qzUhc89VQ9N4EoQPJYN1dME
kxAzH8p5HBidxfJLpACaG5QHGb97chJXTDrT+ZKt+hPfZ16OhsKBj2s0gvHbcCQH
sdqQT4+FlJshDiRiauTmqF/umnCnzl4+H4Xe8kLVrZxDOAv1iwuTX8zcaNOFNw==
-----END AGE ENCRYPTED FILE-----
-111
View File
@@ -1,111 +0,0 @@
# The Tempest
# System configuration for my desktop
rec {
# Hardware
networking.hostName = "tempest";
nmasur.settings = {
username = "noah";
fullName = "Noah Masur";
};
nmasur.profiles = {
base.enable = true;
home.enable = true;
gui.enable = true;
gaming.enable = true;
};
nmasur.presets.services.grub.enable = true;
home-manager.users."noah" = {
nmasur.settings = {
username = nmasur.settings.username;
fullName = nmasur.settings.fullName;
host = networking.hostName;
};
nmasur.profiles = {
common.enable = true;
linux-base.enable = true;
linux-gui.enable = true;
linux-gaming.enable = true;
llm-development.enable = true;
power-user.enable = true;
developer.enable = true;
experimental.enable = true;
};
home.stateVersion = "23.05";
};
system.stateVersion = "23.05";
# Not sure what's necessary but too afraid to remove anything
boot.initrd.availableKernelModules = [
"nvme"
"xhci_pci"
"ahci"
"usb_storage"
"usbhid"
"sd_mod"
];
# Graphics and VMs
boot.initrd.kernelModules = [ "amdgpu" ];
boot.kernelModules = [ "kvm-amd" "it87" ];
services.xserver.videoDrivers = [ "amdgpu" ];
# Required binary blobs to boot on this machine
hardware.enableRedistributableFirmware = true;
# Prioritize performance over efficiency
powerManagement.cpuFreqGovernor = "performance";
# Allow firmware updates
hardware.cpu.amd.updateMicrocode = true;
# Helps reduce GPU fan noise under idle loads
hardware.fancontrol.enable = true;
hardware.fancontrol.config = ''
# Configuration file generated by pwmconfig, changes will be lost
INTERVAL=10
DEVPATH=hwmon1=devices/pci0000:00/0000:00:03.1/0000:06:00.0/0000:07:00.0/0000:08:00.0
DEVNAME=hwmon1=amdgpu
FCTEMPS=hwmon1/pwm1=hwmon1/temp1_input
FCFANS= hwmon1/pwm1=hwmon1/fan1_input
MINTEMP=hwmon1/pwm1=50
MAXTEMP=hwmon1/pwm1=70
MINSTART=hwmon1/pwm1=100
MINSTOP=hwmon1/pwm1=10
MINPWM=hwmon1/pwm1=10
MAXPWM=hwmon1/pwm1=240
'';
# File systems must be declared in order to boot
# This is the root filesystem containing NixOS
fileSystems."/" = {
device = "/dev/disk/by-label/nixos";
fsType = "ext4";
};
# This is the boot filesystem for Grub
fileSystems."/boot" = {
device = "/dev/disk/by-label/boot";
fsType = "vfat";
};
# Allows private remote access over the internet
nmasur.presets.services.cloudflared = {
tunnel = {
id = "ac133a82-31fb-480c-942a-cdbcd4c58173";
credentialsFile = ./cloudflared-tempest.age;
ca = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPY6C0HmdFCaxYtJxFr3qV4/1X4Q8KrYQ1hlme3u1hJXK+xW+lc9Y9glWHrhiTKilB7carYTB80US0O47gI5yU4= open-ssh-ca@cloudflareaccess.org";
};
};
# Allows requests to force machine to wake up
# This network interface might change, needs to be set specifically for each machine.
# Or set usePredictableInterfaceNames = false
networking.interfaces.enp5s0.wakeOnLan.enable = true;
}
-253
View File
@@ -1,253 +0,0 @@
inputs:
let
lib = inputs.nixpkgs.lib;
in
lib
// rec {
# Returns all files in a directory matching a suffix
filesInDirectoryWithSuffix =
directory: suffix:
lib.pipe (lib.filesystem.listFilesRecursive directory) [
# Get only files ending in .nix
(builtins.filter (name: lib.hasSuffix suffix name))
];
# Returns all files ending in .nix for a directory
nixFiles = directory: filesInDirectoryWithSuffix directory ".nix";
# Returns all files ending in default.nix for a directory
defaultFiles = directory: filesInDirectoryWithSuffix directory "default.nix";
# Imports all files in a directory and passes inputs
importOverlays =
directory:
lib.pipe (nixFiles directory) [
# Import each overlay file
(map (file: (import file) inputs))
];
# Import default files as attrset with keys provided by parent directory
defaultFilesToAttrset =
directory:
lib.pipe (defaultFiles directory) [
# Import each file
(map (file: {
name = builtins.baseNameOf (builtins.dirOf file);
value = import file;
}))
# Convert to an attrset of parent dir name -> file
(builtins.listToAttrs)
];
# [ package1/package.nix package2/package.nix package2/hello.sh ]
buildPkgsFromDirectoryAndPkgs =
directory: pkgs:
lib.pipe (filesInDirectoryWithSuffix directory "package.nix") [
# Apply callPackage to create a derivation
# Must use final.callPackage to avoid infinite recursion
# [ package1.drv package2.drv ]
(builtins.map (name: pkgs.callPackage name { }))
# Convert the list to an attrset with keys from pname or name attr
# { package1 = package1.drv, package2 = package2.drv }
(builtins.listToAttrs (
map (v: {
name = v."pname" or v."name";
value = v;
})
))
];
# Common overlays to always use
overlays = [
inputs.nur.overlays.default
inputs.nix2vim.overlay
(final: prev: {
zellij-switch = inputs.zellij-switch.packages.${prev.stdenv.hostPlatform.system}.default;
})
# inputs.helix.overlays.default
]
++ (importOverlays ../overlays);
# System types to support.
supportedSystems = [
"x86_64-linux"
"aarch64-linux"
"aarch64-darwin"
];
# Split system types by operating system
linuxSystems = builtins.filter (lib.hasSuffix "linux") supportedSystems;
darwinSystems = builtins.filter (lib.hasSuffix "darwin") supportedSystems;
# Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
forSystems = systems: lib.genAttrs systems;
forAllSystems = lib.genAttrs supportedSystems;
# { x86_64-linux = { tempest = { settings = ...; }; }; };
hosts = forAllSystems (system: defaultFilesToAttrset ../hosts/${system});
linuxHosts = lib.filterAttrs (name: value: builtins.elem name linuxSystems) hosts;
darwinHosts = lib.filterAttrs (name: value: builtins.elem name darwinSystems) hosts;
# { system -> pkgs }
pkgsBySystem = forAllSystems (
system:
import inputs.nixpkgs {
inherit system overlays;
config.permittedInsecurePackages = [
"litestream-0.3.13"
"electron-36.9.5"
# Build-time-only dep of karakeep's frontend; CVEs don't reach
# the runtime closure. Remove once nixpkgs bumps it.
"pnpm-9.15.9"
"keybase-gui-6.5.1"
];
config.allowUnfree = true;
}
);
colorscheme = defaultFilesToAttrset ../colorscheme;
homeModule = {
home-manager = {
# Include home-manager config in NixOS
sharedModules = nixFiles ../platforms/home-manager;
# Use the system-level nixpkgs instead of Home Manager's
useGlobalPkgs = lib.mkDefault true;
# Install packages to /etc/profiles instead of ~/.nix-profile, useful when
# using multiple profiles for one user
useUserPackages = lib.mkDefault true;
};
};
buildHome =
{
system,
module,
specialArgs,
}:
inputs.home-manager.lib.homeManagerConfiguration {
pkgs = pkgsBySystem.${system};
modules = [
{ imports = (nixFiles ../platforms/home-manager); }
module
];
extraSpecialArgs = {
inherit colorscheme;
}
// specialArgs;
};
buildNixos =
{
system,
module,
specialArgs,
}:
inputs.nixpkgs.lib.nixosSystem {
inherit specialArgs;
pkgs = pkgsBySystem.${system};
modules = [
inputs.home-manager.nixosModules.home-manager
inputs.nix-index-database.nixosModules.default
inputs.disko.nixosModules.disko
inputs.wsl.nixosModules.wsl
{ imports = (nixFiles ../platforms/nixos); }
module
# (builtins.removeAttrs module [ "home-manager" ])
{
home-manager = {
extraSpecialArgs = {
inherit colorscheme;
}
// specialArgs;
}
// homeModule.home-manager;
}
];
};
buildDarwin =
{
system,
module,
specialArgs,
}:
inputs.darwin.lib.darwinSystem {
inherit system specialArgs;
modules = [
inputs.home-manager.darwinModules.home-manager
inputs.nix-index-database.darwinModules.nix-index
inputs.mac-app-util.darwinModules.default
{
imports = (nixFiles ../platforms/nix-darwin);
nixpkgs.pkgs = pkgsBySystem.${system};
}
# Home Manager is intentionally NOT activated here. It is managed
# standalone via `nh home switch` (see homeConfigurations, extracted
# from the host module's `home-manager.users`). Strip that attr so
# darwin-rebuild doesn't also activate a second, divergent HM
# generation with different store paths (useUserPackages puts packages
# in /etc/profiles/per-user vs. ~/.nix-profile standalone) -- that
# mismatch broke the prompt after every darwin-rebuild until the next
# `nh home switch`.
(builtins.removeAttrs module [ "home-manager" ])
];
};
generatorOptions = {
amazon = {
aws.enable = true;
};
iso = {
nmasur.profiles.wsl.enable = lib.mkForce false;
boot.loader.grub.enable = lib.mkForce false;
};
qcow-efi = {
nmasur.profiles.wsl.enable = lib.mkForce false;
boot.loader.grub.enable = lib.mkForce false;
fileSystems."/boot".device = lib.mkForce "/dev/disk/by-label/ESP";
};
};
generateImage =
{
system,
module,
format,
specialArgs,
}:
inputs.nixos-generators.nixosGenerate {
inherit system format;
pkgs = pkgsBySystem.${system};
modules = [
inputs.home-manager.nixosModules.home-manager
inputs.nix-index-database.nixosModules.default
inputs.disko.nixosModules.disko
inputs.wsl.nixosModules.wsl
{
imports = (nixFiles ../platforms/nixos) ++ (nixFiles ../platforms/generators);
}
generatorOptions.${format}
module
{
home-manager = {
extraSpecialArgs = {
inherit colorscheme;
}
// specialArgs;
}
// homeModule.home-manager;
}
];
specialArgs = {
}
// specialArgs;
};
}
-21
View File
@@ -1,21 +0,0 @@
# Miscellaneous
These files contain important data sourced by the configuration, or simply
information to store for safekeeping later.
---
Creating hashed password for [password.sha512](./password.sha512):
```
mkpasswd -m sha-512
```
---
Getting key for [public-keys](./public-keys):
```
ssh-keyscan -t ed25519 <hostname>
```
-23
View File
@@ -1,23 +0,0 @@
Profile 1: (active)
Name: n/a
Report Rate: 1000Hz
Resolutions:
0: 400dpi (active) (default)
1: 800dpi
2: 1600dpi
3: 2400dpi
4: 0dpi
Button: 0 is mapped to 'button 1'
Button: 1 is mapped to 'button 2'
Button: 2 is mapped to 'button 3'
Button: 3 is mapped to 'button 4'
Button: 4 is mapped to 'button 5'
Button: 5 is mapped to macro '↕F11'
Button: 6 is mapped to macro '↕VOLUMEDOWN'
Button: 7 is mapped to macro '↕VOLUMEUP'
Button: 8 is mapped to 'unknown'
Button: 9 is mapped to 'wheel-right'
Button: 10 is mapped to 'wheel-left'
LED: 0, depth: monochrome, mode: on, color: 000000
LED: 1, depth: monochrome, mode: on, color: 000000
LED: 2, depth: monochrome, mode: on, color: 000000
+8
View File
@@ -0,0 +1,8 @@
# Modules
| Module | Purpose |
| --- | --- |
| [common](./common/default.nix) | User programs and OS-agnostic configuration |
| [darwin](./darwin/default.nix) | macOS-specific configuration |
| [nixos](./nixos/default.nix) | NixOS-specific configuration |
+20
View File
@@ -0,0 +1,20 @@
{ config, pkgs, lib, ... }: {
options = {
_1password = {
enable = lib.mkEnableOption {
description = "Enable 1Password.";
default = false;
};
};
};
config = lib.mkIf
(config.gui.enable && config._1password.enable && pkgs.stdenv.isLinux) {
unfreePackages = [ "1password" "_1password-gui" ];
home-manager.users.${config.user} = {
home.packages = with pkgs; [ _1password-gui ];
};
};
}
+99
View File
@@ -0,0 +1,99 @@
{ config, pkgs, lib, ... }: {
options = {
alacritty = {
enable = lib.mkEnableOption {
description = "Enable Alacritty.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config.alacritty.enable) {
home-manager.users.${config.user} = {
xsession.windowManager.i3.config.terminal = "alacritty";
programs.rofi.terminal = "${pkgs.alacritty}/bin/alacritty";
programs.alacritty = {
enable = true;
settings = {
window = {
dimensions = {
columns = 85;
lines = 30;
};
padding = {
x = 20;
y = 20;
};
opacity = 1.0;
};
scrolling.history = 10000;
font = { size = 14.0; };
key_bindings = [
# Used for word completion in fish_user_key_bindings
{
key = "Return";
mods = "Shift";
chars = "\\x1F";
}
# Used for searching nixpkgs in fish_user_key_bindings
{
key = "N";
mods = "Control|Shift";
chars = "\\x11F";
}
{
key = "H";
mods = "Control|Shift";
mode = "~Vi";
action = "ToggleViMode";
}
{
key = "Return";
mode = "Vi";
action = "ToggleViMode";
}
# Used to enable $ keybind in Vi mode
{
key = 5; # Scancode for key4
mods = "Shift";
mode = "Vi|~Search";
action = "Last";
}
];
colors = {
primary = {
background = config.theme.colors.base00;
foreground = config.theme.colors.base05;
};
cursor = {
text = "#1d2021";
cursor = config.theme.colors.base05;
};
normal = {
black = "#1d2021";
red = config.theme.colors.base08;
green = config.theme.colors.base0B;
yellow = config.theme.colors.base0A;
blue = config.theme.colors.base0D;
magenta = config.theme.colors.base0E;
cyan = config.theme.colors.base0C;
white = config.theme.colors.base05;
};
bright = {
black = config.theme.colors.base03;
red = config.theme.colors.base09;
green = config.theme.colors.base01;
yellow = config.theme.colors.base02;
blue = config.theme.colors.base04;
magenta = config.theme.colors.base06;
cyan = config.theme.colors.base0F;
white = config.theme.colors.base07;
};
};
draw_bold_text_with_bright_colors = false;
};
};
};
};
}
+16
View File
@@ -0,0 +1,16 @@
{ ... }: {
imports = [
./1password.nix
./alacritty.nix
./discord.nix
./firefox.nix
./kitty.nix
./media.nix
./obsidian.nix
./qbittorrent.nix
./slack.nix
./yt-dlp.nix
];
}
+28
View File
@@ -0,0 +1,28 @@
{ config, pkgs, lib, ... }: {
options = {
discord = {
enable = lib.mkEnableOption {
description = "Enable Discord.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config.discord.enable) {
unfreePackages = [ "discord" ];
home-manager.users.${config.user} = {
home.packages = with pkgs; [ discord ];
xdg.configFile."discord/settings.json".text = ''
{
"BACKGROUND_COLOR": "#202225",
"IS_MAXIMIZED": false,
"IS_MINIMIZED": false,
"OPEN_ON_STARTUP": false,
"MINIMIZE_TO_TRAY": false,
"SKIP_HOST_UPDATE": true
}
'';
};
};
}
+184
View File
@@ -0,0 +1,184 @@
{ config, pkgs, lib, ... }:
{
options = {
firefox = {
enable = lib.mkEnableOption {
description = "Enable Firefox.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config.firefox.enable) {
unfreePackages = [
(lib.mkIf config._1password.enable "onepassword-password-manager")
"okta-browser-plugin"
];
home-manager.users.${config.user} = {
programs.firefox = {
enable = true;
package =
if pkgs.stdenv.isDarwin then pkgs.firefox-bin else pkgs.firefox;
profiles.default = {
id = 0;
name = "default";
isDefault = true;
extensions = with pkgs.nur.repos.rycee.firefox-addons; [
ublock-origin
vimium
multi-account-containers
facebook-container
(lib.mkIf config._1password.enable onepassword-password-manager)
okta-browser-plugin
sponsorblock
reddit-enhancement-suite
return-youtube-dislikes
markdownload
darkreader
snowflake
don-t-fuck-with-paste
i-dont-care-about-cookies
wappalyzer
];
settings = {
"app.update.auto" = false;
"browser.aboutConfig.showWarning" = false;
"browser.warnOnQuit" = false;
"browser.quitShortcut.disabled" =
if pkgs.stdenv.isLinux then true else false;
"browser.theme.dark-private-windows" = true;
"browser.toolbars.bookmarks.visibility" = false;
"browser.startup.page" = 3; # Restore previous session
"browser.newtabpage.enabled" = false; # Make new tabs blank
"trailhead.firstrun.didSeeAboutWelcome" =
true; # Disable welcome splash
"dom.forms.autocomplete.formautofill" = false; # Disable autofill
"extensions.formautofill.creditCards.enabled" =
false; # Disable credit cards
"dom.payments.defaults.saveAddress" = false; # Disable address save
"general.autoScroll" = true; # Drag middle-mouse to scroll
"services.sync.prefs.sync.general.autoScroll" =
false; # Prevent disabling autoscroll
"extensions.pocket.enabled" = false;
"toolkit.legacyUserProfileCustomizations.stylesheets" =
true; # Allow userChrome.css
"layout.css.color-mix.enabled" = true;
"ui.systemUsesDarkTheme" =
if config.theme.dark == true then 1 else 0;
"media.ffmpeg.vaapi.enabled" =
true; # Enable hardware video acceleration
"cookiebanners.ui.desktop.enabled" = true; # Reject cookie popups
};
userChrome = ''
:root {
--focus-outline-color: ${config.theme.colors.base04} !important;
--toolbar-color: ${config.theme.colors.base07} !important;
--tab-min-height: 30px !important;
}
/* Background of tab bar */
.toolbar-items {
background-color: ${config.theme.colors.base00} !important;
}
/* Extra tab bar sides on macOS */
.titlebar-spacer {
background-color: ${config.theme.colors.base00} !important;
}
.titlebar-buttonbox-container {
background-color: ${config.theme.colors.base00} !important;
}
#tabbrowser-tabs {
border-inline-start: 0 !important;
}
/* Private Browsing indicator on macOS */
#private-browsing-indicator-with-label {
background-color: ${config.theme.colors.base00} !important;
margin-inline: 0 !important;
padding-inline: 7px;
}
/* Tabs themselves */
.tabbrowser-tab .tab-stack {
border-radius: 5px 5px 0 0;
overflow: hidden;
background-color: ${config.theme.colors.base00};
color: ${config.theme.colors.base06} !important;
}
.tab-content {
border-bottom: 2px solid color-mix(in srgb, var(--identity-tab-color) 40%, transparent);
border-radius: 5px 5px 0 0;
background-color: ${config.theme.colors.base00};
color: ${config.theme.colors.base06} !important;
}
.tab-content[selected=true] {
border-bottom: 2px solid color-mix(in srgb, var(--identity-tab-color) 25%, transparent);
background-color: ${config.theme.colors.base01} !important;
color: ${config.theme.colors.base07} !important;
}
/* Below tab bar */
#nav-bar {
background: ${config.theme.colors.base01} !important;
}
/* URL bar in nav bar */
#urlbar[focused=true] {
color: ${config.theme.colors.base07} !important;
background: ${config.theme.colors.base02} !important;
caret-color: ${config.theme.colors.base05} !important;
}
#urlbar:not([focused=true]) {
color: ${config.theme.colors.base04} !important;
background: ${config.theme.colors.base02} !important;
}
#urlbar ::-moz-selection {
color: ${config.theme.colors.base07} !important;
background: ${config.theme.colors.base02} !important;
}
#urlbar-input-container {
border: 1px solid ${config.theme.colors.base01} !important;
}
#urlbar-background {
background: ${config.theme.colors.base01} !important;
}
/* Text in URL bar */
#urlbar-input, #urlbar-scheme, .searchbar-textbox {
color: ${config.theme.colors.base07} !important;
}
'';
userContent = ''
@-moz-document url-prefix(about:blank) {
* {
background-color:${config.theme.colors.base01} !important;
}
}
'';
extraConfig = "";
};
};
xsession.windowManager.i3.config.keybindings =
lib.mkIf pkgs.stdenv.isLinux {
"${
config.home-manager.users.${config.user}.xsession.windowManager.i3.config.modifier
}+Shift+b" = "exec ${
# Don't name the script `firefox` or it will affect grep
builtins.toString (pkgs.writeShellScript "focus-ff.sh" ''
count=$(ps aux | grep -c firefox)
if [ "$count" -eq 1 ]; then
i3-msg "exec --no-startup-id firefox"
sleep 0.5
fi
i3-msg "[class=firefox] focus"
'')
}";
};
};
};
}
+102
View File
@@ -0,0 +1,102 @@
{ config, pkgs, lib, ... }: {
options = {
kitty = {
enable = lib.mkEnableOption {
description = "Enable Kitty.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config.kitty.enable) {
# Set the Rofi-Systemd terminal for viewing logs
# Using optionalAttrs because only available in NixOS
environment = { } // lib.attrsets.optionalAttrs
(builtins.hasAttr "sessionVariables" config.environment) {
sessionVariables.ROFI_SYSTEMD_TERM = "${pkgs.kitty}/bin/kitty";
};
home-manager.users.${config.user} = {
# Set the i3 terminal
xsession.windowManager.i3.config.terminal =
lib.mkIf pkgs.stdenv.isLinux "kitty";
# Set the Rofi terminal for running programs
programs.rofi.terminal =
lib.mkIf pkgs.stdenv.isLinux "${pkgs.kitty}/bin/kitty";
programs.kitty = {
enable = true;
environment = { };
extraConfig = "";
font.size = 14;
keybindings = {
"shift+enter" = "send_text all \\x1F";
"super+f" = "toggle_fullscreen";
};
settings = {
# Colors (adapted from: https://github.com/kdrag0n/base16-kitty/blob/master/templates/default-256.mustache)
background = config.theme.colors.base00;
foreground = config.theme.colors.base05;
selection_background = config.theme.colors.base05;
selection_foreground = config.theme.colors.base00;
url_color = config.theme.colors.base04;
cursor = config.theme.colors.base05;
active_border_color = config.theme.colors.base03;
inactive_border_color = config.theme.colors.base01;
active_tab_background = config.theme.colors.base00;
active_tab_foreground = config.theme.colors.base05;
inactive_tab_background = config.theme.colors.base01;
inactive_tab_foreground = config.theme.colors.base04;
tab_bar_background = config.theme.colors.base01;
# normal
color0 = config.theme.colors.base00;
color1 = config.theme.colors.base08;
color2 = config.theme.colors.base0B;
color3 = config.theme.colors.base0A;
color4 = config.theme.colors.base0D;
color5 = config.theme.colors.base0E;
color6 = config.theme.colors.base0C;
color7 = config.theme.colors.base05;
# bright
color8 = config.theme.colors.base03;
color9 = config.theme.colors.base08;
color10 = config.theme.colors.base0B;
color11 = config.theme.colors.base0A;
color12 = config.theme.colors.base0D;
color13 = config.theme.colors.base0E;
color14 = config.theme.colors.base0C;
color15 = config.theme.colors.base07;
# extended base16 colors
color16 = config.theme.colors.base09;
color17 = config.theme.colors.base0F;
color18 = config.theme.colors.base01;
color19 = config.theme.colors.base02;
color20 = config.theme.colors.base04;
color21 = config.theme.colors.base06;
# Scrollback
scrolling_lines = 10000;
scrollback_pager_history_size = 10; # MB
scrollback_pager = "${pkgs.neovim}/bin/nvim -c 'normal G'";
# Window
window_padding_width = 6;
tab_bar_edge = "top";
tab_bar_style = "slant";
# Audio
enable_audio_bell = false;
};
};
};
};
}
+73
View File
@@ -0,0 +1,73 @@
{ config, pkgs, lib, ... }: {
options = {
media = {
enable = lib.mkEnableOption {
description = "Enable media programs.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config.media.enable) {
home-manager.users.${config.user} = {
home.packages = with pkgs; [
nsxiv # Image viewer
mupdf # PDF viewer
zathura # PDF viewer
];
# Video player
programs.mpv = {
enable = true;
bindings = { };
config = {
image-display-duration = 2;
hwdec = "auto-safe";
};
scripts = [
# Automatically load playlist entries before and after current file
pkgs.mpvScripts.autoload
# Delete current file after quitting
(pkgs.stdenv.mkDerivation rec {
pname = "mpv-delete-file";
version = "0.1"; # made-up
src = pkgs.fetchFromGitHub {
owner = "zenyd";
repo = "mpv-scripts";
rev = "19ea069abcb794d1bf8fac2f59b50d71ab992130";
sha256 = "sha256-OBCuzCtgfSwj0i/rBNranuu4LRc47jObwQIJgQQoerg=";
} + "/delete_file.lua";
dontBuild = true;
dontUnpack = true;
installPhase =
"install -Dm644 ${src} $out/share/mpv/scripts/delete_file.lua";
passthru.scriptName = "delete_file.lua";
})
];
};
# Set default for opening PDFs
xdg.mimeApps = {
associations.added = {
"application/pdf" = [ "pwmt.zathura-cb.desktop" ];
"image/jpeg" = [ "nsxiv.desktop" ];
"image/*" = [ "nsxiv.desktop" ];
};
associations.removed = {
"application/pdf" = [ "mupdf.desktop" "wine-extension-pdf.desktop" ];
};
defaultApplications = {
"application/pdf" = [ "pwmt.zathura-cb.desktop" ];
"image/jpeg" = [ "nsxiv.desktop" ];
"image/*" = [ "nsxiv.desktop" ];
};
};
};
};
}
+23
View File
@@ -0,0 +1,23 @@
{ config, pkgs, lib, ... }: {
options = {
obsidian = {
enable = lib.mkEnableOption {
description = "Enable Obsidian.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config.obsidian.enable) {
unfreePackages = [ "obsidian" ];
home-manager.users.${config.user} = {
home.packages = with pkgs; [ obsidian ];
};
# Broken on 2023-04-16
nixpkgs.config.permittedInsecurePackages = [ "electron-21.4.0" ];
};
}
@@ -0,0 +1,21 @@
{ config, pkgs, lib, ... }: {
options = {
qbittorrent = {
enable = lib.mkEnableOption {
description = "Enable qBittorrent.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config.qbittorrent.enable) {
home-manager.users.${config.user} = {
home.packages = with pkgs; [ qbittorrent ];
};
};
}
+19
View File
@@ -0,0 +1,19 @@
{ config, pkgs, lib, ... }: {
options = {
slack = {
enable = lib.mkEnableOption {
description = "Enable Slack.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config.slack.enable) {
unfreePackages = [ "slack" ];
home-manager.users.${config.user} = {
home.packages = with pkgs; [ slack ];
};
};
}
+35
View File
@@ -0,0 +1,35 @@
{ config, pkgs, lib, ... }: {
options = {
yt-dlp = {
enable = lib.mkEnableOption {
description = "Enable YouTube downloader.";
default = false;
};
};
};
config = lib.mkIf (config.yt-dlp.enable) {
home-manager.users.${config.user} = {
programs.yt-dlp = {
enable = true;
extraConfig = "";
settings = {
no-continue = true; # Always re-download each fragment
no-overwrites = true; # Don't overwrite existing files
download-archive = "archive.log"; # Log of archives
embed-metadata = true;
embed-thumbnail = true;
embed-subs = true;
sub-langs = "en.*";
concurrent-fragments = 4; # Parallel download chunks
};
};
programs.fish.shellAbbrs.yt = "yt-dlp";
};
};
}
+161
View File
@@ -0,0 +1,161 @@
{ config, lib, pkgs, ... }: {
imports =
[ ./applications ./mail ./neovim ./programming ./repositories ./shell ];
options = {
user = lib.mkOption {
type = lib.types.str;
description = "Primary user of the system";
};
fullName = lib.mkOption {
type = lib.types.str;
description = "Human readable name of the user";
};
userDirs = {
# Required to prevent infinite recursion when referenced by himalaya
download = lib.mkOption {
type = lib.types.str;
description = "XDG directory for downloads";
default =
if pkgs.stdenv.isDarwin then "$HOME/Downloads" else "$HOME/downloads";
};
};
identityFile = lib.mkOption {
type = lib.types.str;
description = "Path to existing private key file.";
default = "/etc/ssh/ssh_host_ed25519_key";
};
gui = {
enable = lib.mkEnableOption {
description = "Enable graphics.";
default = false;
};
};
theme = {
colors = lib.mkOption {
type = lib.types.attrs;
description = "Base16 color scheme.";
default = (import ../colorscheme/gruvbox).dark;
};
dark = lib.mkOption {
type = lib.types.bool;
description = "Enable dark mode.";
default = true;
};
};
homePath = lib.mkOption {
type = lib.types.path;
description = "Path of user's home directory.";
default = builtins.toPath (if pkgs.stdenv.isDarwin then
"/Users/${config.user}"
else
"/home/${config.user}");
};
dotfilesPath = lib.mkOption {
type = lib.types.path;
description = "Path of dotfiles repository.";
default = config.homePath + "/dev/personal/dotfiles";
};
dotfilesRepo = lib.mkOption {
type = lib.types.str;
description = "Link to dotfiles repository.";
};
unfreePackages = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "List of unfree packages to allow.";
default = [ ];
};
hostnames = {
git = lib.mkOption {
type = lib.types.str;
description = "Hostname for git server (Gitea).";
};
metrics = lib.mkOption {
type = lib.types.str;
description = "Hostname for metrics server.";
};
prometheus = lib.mkOption {
type = lib.types.str;
description = "Hostname for Prometheus server.";
};
secrets = lib.mkOption {
type = lib.types.str;
description = "Hostname for passwords and secrets (Vaultwarden).";
};
stream = lib.mkOption {
type = lib.types.str;
description = "Hostname for video/media library (Jellyfin).";
};
content = lib.mkOption {
type = lib.types.str;
description = "Hostname for personal content system (Nextcloud).";
};
books = lib.mkOption {
type = lib.types.str;
description = "Hostname for books library (Calibre-Web).";
};
download = lib.mkOption {
type = lib.types.str;
description = "Hostname for download services.";
};
};
};
config = let stateVersion = "23.05";
in {
nix = {
# Enable features in Nix commands
extraOptions = ''
experimental-features = nix-command flakes
warn-dirty = false
'';
gc = {
automatic = true;
options = "--delete-older-than 7d";
};
settings = {
# Add community Cachix to binary cache
# Don't use with macOS because blocked by corporate firewall
builders-use-substitutes = true;
substituters = lib.mkIf (!pkgs.stdenv.isDarwin)
[ "https://nix-community.cachix.org" ];
trusted-public-keys = lib.mkIf (!pkgs.stdenv.isDarwin) [
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
# Scans and hard links identical files in the store
# Not working with macOS: https://github.com/NixOS/nix/issues/7273
auto-optimise-store = lib.mkIf (!pkgs.stdenv.isDarwin) true;
};
};
# Basic common system packages for all devices
environment.systemPackages = with pkgs; [ git vim wget curl ];
# Use the system-level nixpkgs instead of Home Manager's
home-manager.useGlobalPkgs = true;
# Install packages to /etc/profiles instead of ~/.nix-profile, useful when
# using multiple profiles for one user
home-manager.useUserPackages = true;
# Allow specified unfree packages (identified elsewhere)
# Retrieves package object based on string name
nixpkgs.config.allowUnfreePredicate = pkg:
builtins.elem (lib.getName pkg) config.unfreePackages;
# Pin a state version to prevent warnings
home-manager.users.${config.user}.home.stateVersion = stateVersion;
home-manager.users.root.home.stateVersion = stateVersion;
};
}
+208
View File
@@ -0,0 +1,208 @@
{ config, pkgs, lib, ... }: {
options.mail.aerc.enable = lib.mkEnableOption "Aerc email.";
config = lib.mkIf config.mail.aerc.enable {
home-manager.users.${config.user} = {
home.packages = with pkgs; [
w3m # Render HTML
dante # Socksify for rendering HTML
];
programs.aerc = {
enable = true;
extraBinds = {
# Binds are of the form <key sequence> = <command to run>
# To use '=' in a key sequence, substitute it with "Eq": "<Ctrl+Eq>"
# If you wish to bind #, you can wrap the key sequence in quotes: "#" = quit
global = {
"<C-p>" = ":prev-tab<Enter>";
"<C-n>" = ":next-tab <Enter>";
"<C-t>" = ":term<Enter>";
"?" = ":help keys<Enter>";
};
messages = {
q = ":quit<Enter>";
j = ":next <Enter>";
"<Down>" = ":next<Enter>";
"<C-d>" = ":next 50%<Enter>";
"<C-f>" = ":next 100%<Enter>";
"<PgDn>" = ":next 100%<Enter>";
k = ":prev <Enter>";
"<Up>" = ":prev<Enter>";
"<C-u>" = ":prev 50%<Enter>";
"<C-b>" = ":prev 100%<Enter>";
"<PgUp>" = ":prev 100%<Enter>";
g = ":select 0 <Enter>";
G = ":select -1<Enter>";
J = ":next-folder <Enter>";
K = ":prev-folder<Enter>";
H = ":collapse-folder<Enter>";
L = ":expand-folder<Enter>";
v = ":mark -t<Enter>";
V = ":mark -v<Enter>";
T = ":toggle-threads<Enter>";
"<Enter>" = ":view<Enter>";
d = ":prompt 'Really delete this message?' 'delete-message'<Enter>";
D = ":move Trash<Enter>";
A = ":archive flat<Enter>";
C = ":compose<Enter>";
rr = ":reply -a<Enter>";
rq = ":reply -aq<Enter>";
Rr = ":reply<Enter>";
Rq = ":reply -q<Enter>";
c = ":cf<space>";
"$" = ":term<space>";
"!" = ":term<space>";
"|" = ":pipe<space>";
"/" = ":search<space>";
"\\" = ":filter <space>";
n = ":next-result<Enter>";
N = ":prev-result<Enter>";
"<Esc>" = ":clear<Enter>";
};
"messages:folder=Drafts" = { "<Enter>" = ":recall<Enter>"; };
view = {
"/" = ":toggle-key-passthrough <Enter> /";
q = ":close<Enter>";
O = ":open<Enter>";
S = ":save<space>";
"|" = ":pipe<space>";
D = ":move Trash<Enter>";
A = ":archive flat<Enter>";
"<C-l>" = ":open-link <space>";
f = ":forward <Enter>";
rr = ":reply -a<Enter>";
rq = ":reply -aq<Enter>";
Rr = ":reply<Enter>";
Rq = ":reply -q<Enter>";
H = ":toggle-headers<Enter>";
"<C-k>" = ":prev-part<Enter>";
"<C-j>" = ":next-part<Enter>";
J = ":next <Enter>";
K = ":prev<Enter>";
};
"view::passthrough" = {
"$noinherit" = "true";
"$ex" = "<C-x>";
"<Esc>" = ":toggle-key-passthrough<Enter>";
};
compose = {
# Keybindings used when the embedded terminal is not selected in the compose
# view
"$noinherit" = "true";
"$ex" = "<C-x>";
"<C-k>" = ":prev-field<Enter>";
"<C-j>" = ":next-field<Enter>";
"<A-p>" = ":switch-account -p<Enter>";
"<A-n>" = ":switch-account -n<Enter>";
"<tab>" = ":next-field<Enter>";
"<C-p>" = ":prev-tab<Enter>";
"<C-n>" = ":next-tab<Enter>";
};
"compose::editor" = {
# Keybindings used when the embedded terminal is selected in the compose view
"$noinherit" = "true";
"$ex" = "<C-x>";
"<C-k>" = ":prev-field<Enter>";
"<C-j>" = ":next-field<Enter>";
"<C-p>" = ":prev-tab<Enter>";
"<C-n>" = ":next-tab<Enter>";
};
"compose::review" = {
# Keybindings used when reviewing a message to be sent
y = ":send <Enter>";
n = ":abort<Enter>";
p = ":postpone<Enter>";
q = ":choose -o d discard abort -o p postpone postpone<Enter>";
e = ":edit<Enter>";
a = ":attach<space>";
d = ":detach<space>";
};
terminal = {
"$noinherit" = "true";
"$ex" = "<C-x>";
"<C-p>" = ":prev-tab<Enter>";
"<C-n>" = ":next-tab<Enter>";
};
};
extraConfig = {
general.unsafe-accounts-conf = true;
viewer = { pager = "${pkgs.less}/bin/less -R"; };
filters = {
"text/plain" = "${pkgs.aerc}/libexec/aerc/filters/colorize";
"text/calendar" =
"${pkgs.gawk}/bin/awk -f ${pkgs.aerc}/libexec/aerc/filters/calendar";
"text/html" =
"${pkgs.aerc}/libexec/aerc/filters/html | ${pkgs.aerc}/libexec/aerc/filters/colorize"; # Requires w3m, dante
# "text/*" =
# ''${pkgs.bat}/bin/bat -fP --file-name="$AERC_FILENAME "'';
"message/delivery-status" =
"${pkgs.aerc}/libexec/aerc/filters/colorize";
"message/rfc822" = "${pkgs.aerc}/libexec/aerc/filters/colorize";
"application/x-sh" = "${pkgs.bat}/bin/bat -fP -l sh";
"application/pdf" = "${pkgs.zathura}/bin/zathura -";
"audio/*" = "${pkgs.mpv}/bin/mpv -";
"image/*" = "${pkgs.feh}/bin/feh -";
};
};
};
accounts.email.accounts.home.aerc = {
enable = true;
extraAccounts = {
check-mail = "5m";
check-mail-cmd = "${pkgs.isync}/bin/mbsync -a";
};
};
xdg.desktopEntries.aerc = lib.mkIf pkgs.stdenv.isLinux {
name = "aerc";
exec = "kitty aerc %u";
};
xsession.windowManager.i3.config.keybindings =
lib.mkIf pkgs.stdenv.isLinux {
"${
config.home-manager.users.${config.user}.xsession.windowManager.i3.config.modifier
}+Shift+e" = "exec ${
# Don't name the script `aerc` or it will affect grep
builtins.toString (pkgs.writeShellScript "focus-mail.sh" ''
count=$(ps aux | grep -c aerc)
if [ "$count" -eq 1 ]; then
i3-msg "exec --no-startup-id kitty --class aerc aerc"
sleep 0.25
fi
i3-msg "[class=aerc] focus"
'')
}";
};
programs.fish.shellAbbrs = { ae = "aerc"; };
};
};
}
+95
View File
@@ -0,0 +1,95 @@
{ config, pkgs, lib, ... }: {
imports = [ ./himalaya.nix ./aerc.nix ];
options = {
mail.enable = lib.mkEnableOption "Mail service.";
mail.user = lib.mkOption {
type = lib.types.str;
description = "User name for the email address.";
default = config.user;
};
mail.server = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = "Server name for the email address.";
};
mail.imapHost = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = "Server host for IMAP (reading mail).";
};
mail.smtpHost = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = "Server host for SMTP (sending mail).";
};
};
config = lib.mkIf config.mail.enable {
home-manager.users.${config.user} = {
programs.mbsync = { enable = true; };
services.mbsync = lib.mkIf pkgs.stdenv.isLinux {
enable = true;
frequency = "*:0/5";
postExec = "${pkgs.notmuch}/bin/notmuch new";
};
services.imapnotify.enable = pkgs.stdenv.isLinux;
programs.notmuch.enable = true;
accounts.email = {
maildirBasePath = "${config.homePath}/mail";
accounts = {
home = let address = "${config.mail.user}@${config.mail.server}";
in {
userName = address;
realName = config.fullName;
primary = true;
inherit address;
aliases = map (user: "${user}@${config.mail.server}") [
"me"
"hey"
"admin"
];
alot = { };
flavor = "plain";
imap = {
host = config.mail.imapHost;
port = 993;
tls.enable = true;
};
imapnotify = {
enable = true;
boxes = [ "Inbox" ];
onNotify = "${pkgs.isync}/bin/mbsync -a";
onNotifyPost = lib.mkIf
config.home-manager.users.${config.user}.services.dunst.enable
"${pkgs.libnotify}/bin/notify-send 'New mail arrived'";
};
maildir = { path = "main"; };
mbsync = {
enable = true;
create = "both";
expunge = "both";
remove = "both";
patterns = [ "*" ];
extraConfig.channel = {
CopyArrivalDate = "yes"; # Sync time of original message
};
};
notmuch.enable = true;
passwordCommand =
"${pkgs.age}/bin/age --decrypt --identity ${config.identityFile} ${
pkgs.writeText "mailpass.age"
(builtins.readFile ../../../private/mailpass.age)
}";
smtp = {
host = config.mail.smtpHost;
port = 465;
tls.enable = true;
};
};
};
};
};
};
}
+25
View File
@@ -0,0 +1,25 @@
{ config, lib, ... }: {
options.mail.himalaya.enable = lib.mkEnableOption "Himalaya email.";
config = lib.mkIf config.mail.himalaya.enable {
home-manager.users.${config.user} = {
programs.himalaya = { enable = true; };
accounts.email.accounts.home.himalaya = {
enable = true;
backend = "imap";
sender = "smtp";
settings = {
downloads-dir = config.userDirs.download;
smtp-insecure = true;
};
};
programs.fish.shellAbbrs = { hi = "himalaya"; };
};
};
}
@@ -1,8 +1,4 @@
{ pkgs, ... }:
{
# Plugin for aligning text programmatically
{ pkgs, ... }: {
plugins = [ pkgs.vimPlugins.tabular ];
lua = ''
-- Align
@@ -1,22 +1,16 @@
{ pkgs, ... }:
{
# Shows buffers in a VSCode-style tab layout
{ pkgs, ... }: {
plugins = [
pkgs.vimPlugins.bufferline-nvim
pkgs.vimPlugins.vim-bbye # Better closing of buffers
pkgs.vimPlugins.snipe-nvim # Jump between open buffers
];
setup.bufferline = {
options = {
diagnostics = "nvim_lsp";
always_show_bufferline = false;
separator_style = "slant";
offsets = [ { filetype = "NvimTree"; } ];
offsets = [{ filetype = "NvimTree"; }];
};
};
setup.snipe = { };
lua = ''
-- Move buffers
vim.keymap.set("n", "L", ":BufferLineCycleNext<CR>", { silent = true })
@@ -24,7 +18,5 @@
-- Kill buffer
vim.keymap.set("n", "<Leader>x", " :Bdelete<CR>", { silent = true })
-- Jump to buffer
vim.keymap.set("n", "gb", require("snipe").open_buffer_menu, { silent = true }) '';
'';
}
+20
View File
@@ -0,0 +1,20 @@
{ pkgs, lib, config, ... }: {
options.colors = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
description = "Attrset of base16 colorscheme key value pairs.";
};
config = {
plugins = [ pkgs.vimPlugins.nvim-base16 ];
setup.base16-colorscheme = config.colors;
# Telescope isn't working, shut off for now
lua = ''
require('base16-colorscheme').with_config {
telescope = false,
}
'';
};
}
@@ -1,8 +1,6 @@
{ pkgs, dsl, ... }:
{
{ pkgs, dsl, ... }: {
plugins = [
pkgs.vimPlugins.nvim-cmp
pkgs.vimPlugins.cmp-nvim-lsp
pkgs.vimPlugins.cmp-buffer
pkgs.vimPlugins.cmp-path
@@ -11,6 +9,7 @@
pkgs.vimPlugins.luasnip
pkgs.vimPlugins.cmp_luasnip
pkgs.vimPlugins.cmp-rg
pkgs.vimPlugins.friendly-snippets
];
use.cmp.setup = dsl.callWith {
@@ -25,23 +24,28 @@
end
'';
# Basic completion keybinds
snippet.expand = dsl.rawLua ''
function(args)
require("luasnip").lsp_expand(args.body)
end
'';
mapping = {
"['<C-n>']" =
dsl.rawLua "require('cmp').mapping.select_next_item({ behavior = require('cmp').SelectBehavior.Insert }, { 'i', 'c' })";
"['<C-p>']" =
dsl.rawLua "require('cmp').mapping.select_prev_item({ behavior = require('cmp').SelectBehavior.Insert }, { 'i', 'c' })";
"['<Down>']" =
dsl.rawLua "require('cmp').mapping.select_next_item({ behavior = require('cmp').SelectBehavior.Select }, { 'i', 'c' })";
"['<Up>']" =
dsl.rawLua "require('cmp').mapping.select_prev_item({ behavior = require('cmp').SelectBehavior.Select }, { 'i', 'c' })";
"['<C-n>']" = dsl.rawLua
"require('cmp').mapping.select_next_item({ behavior = require('cmp').SelectBehavior.Insert })";
"['<C-p>']" = dsl.rawLua
"require('cmp').mapping.select_prev_item({ behavior = require('cmp').SelectBehavior.Insert })";
"['<Down>']" = dsl.rawLua
"require('cmp').mapping.select_next_item({ behavior = require('cmp').SelectBehavior.Select })";
"['<Up>']" = dsl.rawLua
"require('cmp').mapping.select_prev_item({ behavior = require('cmp').SelectBehavior.Select })";
"['<C-d>']" = dsl.rawLua "require('cmp').mapping.scroll_docs(-4)";
"['<C-f>']" = dsl.rawLua "require('cmp').mapping.scroll_docs(4)";
"['<C-e>']" = dsl.rawLua "require('cmp').mapping.abort()";
"['<C-y>']" =
dsl.rawLua "require('cmp').mapping.confirm({ behavior = require('cmp').ConfirmBehavior.Insert, select = true, }, { 'i', 'c' })";
"['<C-r>']" =
dsl.rawLua "require('cmp').mapping.confirm({ behavior = require('cmp').ConfirmBehavior.Replace, select = true, }, { 'i', 'c' })";
"['<CR>']" = dsl.rawLua
"require('cmp').mapping.confirm({ behavior = require('cmp').ConfirmBehavior.Replace, select = true, })";
"['<C-r>']" = dsl.rawLua
"require('cmp').mapping.confirm({ behavior = require('cmp').ConfirmBehavior.Replace, select = true, })";
"['<Esc>']" = dsl.rawLua ''
function(_)
cmp.mapping({
@@ -51,7 +55,7 @@
vim.cmd("stopinsert") --- Abort and leave insert mode
end
'';
"['<C-k>']" = dsl.rawLua ''
"['<C-l>']" = dsl.rawLua ''
cmp.mapping(function(_)
if require("luasnip").expand_or_jumpable() then
require("luasnip").expand_or_jump()
@@ -60,33 +64,26 @@
'';
};
# These are where the completion engine gets its suggestions
sources = [
{ name = "nvim_lua"; } # Fills in common Neovim lua functions
{ name = "nvim_lsp"; } # LSP results
{ name = "path"; } # Shell completion from current PATH
{ name = "nvim_lua"; }
{ name = "nvim_lsp"; }
{ name = "luasnip"; }
{ name = "path"; }
{
name = "buffer"; # Grep for text from the current text buffer
name = "buffer";
keyword_length = 3;
max_item_count = 10;
}
{
name = "rg"; # Grep for text from the current directory
name = "rg";
keyword_length = 6;
max_item_count = 10;
option = {
additional_arguments = "--ignore-case";
};
option = { additional_arguments = "--ignore-case"; };
}
];
# Styling of the completion menu
formatting = {
fields = [
"kind"
"abbr"
"menu"
];
fields = [ "kind" "abbr" "menu" ];
format = dsl.rawLua ''
function(entry, vim_item)
local kind_icons = {
@@ -118,6 +115,7 @@
}
vim_item.kind = string.format("%s", kind_icons[vim_item.kind])
vim_item.menu = ({
luasnip = "[Snippet]",
buffer = "[Buffer]",
path = "[Path]",
rg = "[Grep]",
@@ -133,46 +131,26 @@
native_menu = false; # Use cmp menu instead of Vim menu
ghost_text = true; # Show preview auto-completion
};
};
lua' = ''
lua = ''
-- Load snippets
-- Check status: :lua require("luasnip").log.open()
require("luasnip.loaders.from_vscode").lazy_load()
require("luasnip.loaders.from_vscode").lazy_load({ paths = { "${
builtins.toString pkgs.vscode-terraform-snippets
}" } })
-- Use buffer source for `/`
require('cmp').setup.cmdline("/", {
mapping = {
['<C-n>'] = {
c = require('cmp').mapping.select_next_item({ behavior = require('cmp').SelectBehavior.Insert }, { 'i', 'c' })
},
['<C-p>'] = {
c = require('cmp').mapping.select_prev_item({ behavior = require('cmp').SelectBehavior.Insert }, { 'i', 'c' })
},
['<C-y>'] = {
c = require('cmp').mapping.confirm({ behavior = require('cmp').ConfirmBehavior.Insert, select = true, }, { 'i', 'c' })
},
['<C-r>'] = {
c = require('cmp').mapping.confirm({ behavior = require('cmp').ConfirmBehavior.Replace, select = true, }, { 'i', 'c' })
},
},
sources = require('cmp').config.sources({
sources = {
{ name = "buffer", keyword_length = 5 },
}),
},
})
-- Use cmdline & path source for ':'
require('cmp').setup.cmdline(":", {
mapping = {
['<C-n>'] = {
c = require('cmp').mapping.select_next_item({ behavior = require('cmp').SelectBehavior.Insert }, { 'i', 'c' })
},
['<C-p>'] = {
c = require('cmp').mapping.select_prev_item({ behavior = require('cmp').SelectBehavior.Insert }, { 'i', 'c' })
},
['<C-y>'] = {
c = require('cmp').mapping.confirm({ behavior = require('cmp').ConfirmBehavior.Insert, select = true, }, { 'i', 'c' })
},
['<C-r>'] = {
c = require('cmp').mapping.confirm({ behavior = require('cmp').ConfirmBehavior.Replace, select = true, }, { 'i', 'c' })
},
},
sources = require('cmp').config.sources({
{ name = "path" },
}, {
@@ -180,4 +158,5 @@
}),
})
'';
}
@@ -1,5 +1,4 @@
{ pkgs, ... }:
{
{ pkgs, ... }: {
plugins = [ pkgs.vimPlugins.gitsigns-nvim ];
setup.gitsigns = { };
lua = builtins.readFile ./gitsigns.lua;
+76
View File
@@ -0,0 +1,76 @@
{ pkgs, dsl, ... }: {
plugins = [
pkgs.vimPlugins.nvim-lspconfig
pkgs.vimPlugins.lsp-colors-nvim
pkgs.vimPlugins.null-ls-nvim
];
use.lspconfig.lua_ls.setup = dsl.callWith {
settings = { Lua = { diagnostics = { globals = [ "vim" "hs" ]; }; }; };
capabilities = dsl.rawLua "require('cmp_nvim_lsp').default_capabilities()";
cmd = [ "${pkgs.lua-language-server}/bin/lua-language-server" ];
};
use.lspconfig.nil_ls.setup = dsl.callWith {
cmd = [ "${pkgs.nil}/bin/nil" ];
capabilities = dsl.rawLua "require('cmp_nvim_lsp').default_capabilities()";
};
use.lspconfig.pyright.setup = dsl.callWith {
cmd = [ "${pkgs.pyright}/bin/pyright-langserver" "--stdio" ];
};
use.lspconfig.terraformls.setup =
dsl.callWith { cmd = [ "${pkgs.terraform-ls}/bin/terraform-ls" "serve" ]; };
vim.api.nvim_create_augroup = dsl.callWith [ "LspFormatting" { } ];
lua = ''
${builtins.readFile ./lsp.lua}
-- Prevent infinite log size (change this when debugging)
vim.lsp.set_log_level("off")
require("null-ls").setup({
sources = {
require("null-ls").builtins.formatting.stylua.with({ command = "${pkgs.stylua}/bin/stylua" }),
require("null-ls").builtins.formatting.black.with({ command = "${pkgs.black}/bin/black" }),
require("null-ls").builtins.diagnostics.ruff.with({ command = "${pkgs.ruff}/bin/ruff" }),
require("null-ls").builtins.formatting.fish_indent.with({ command = "${pkgs.fish}/bin/fish_indent" }),
require("null-ls").builtins.formatting.nixfmt.with({ command = "${pkgs.nixfmt}/bin/nixfmt" }),
require("null-ls").builtins.formatting.rustfmt.with({ command = "${pkgs.rustfmt}/bin/rustfmt" }),
require("null-ls").builtins.diagnostics.shellcheck.with({ command = "${pkgs.shellcheck}/bin/shellcheck" }),
require("null-ls").builtins.formatting.shfmt.with({
command = "${pkgs.shfmt}/bin/shfmt",
extra_args = { "-i", "4", "-ci" },
}),
require("null-ls").builtins.formatting.terraform_fmt.with({
command = "${pkgs.terraform}/bin/terraform",
extra_filetypes = { "hcl" },
}),
},
on_attach = function(client, bufnr)
if client.supports_method("textDocument/formatting") then
-- Auto-format on save
vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
vim.api.nvim_create_autocmd("BufWritePre", {
group = augroup,
buffer = bufnr,
callback = function()
vim.lsp.buf.format({ bufnr = bufnr })
end,
})
-- Use internal formatting for bindings like gq.
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(args)
vim.bo[args.buf].formatexpr = nil
end,
})
end
end,
})
'';
}
@@ -1,29 +1,17 @@
{
pkgs,
dsl,
lib,
...
}:
{
{ pkgs, dsl, lib, ... }: {
plugins = [
pkgs.vimPlugins.vim-surround # Keybinds for surround characters
pkgs.vimPlugins.vim-eunuch # File manipulation commands
pkgs.vimPlugins.vim-fugitive # Git commands
pkgs.vimPlugins.vim-repeat # Better repeat using .
pkgs.vimPlugins.vim-abolish # Keep capitalization in substitute (Subvert)
pkgs.vimPlugins.markview-nvim # Markdown preview
pkgs.vimPlugins.comment-nvim # Smart comment commands
pkgs.vimPlugins.glow-nvim # Markdown preview popup
pkgs.vimPlugins.nvim-colorizer-lua # Hex color previews
pkgs.vimPlugins.which-key-nvim # Keybind helper
];
# Initialize some plugins
setup.colorizer = {
user_default_options = {
names = false;
};
};
setup.markview = { };
setup.which-key = { };
setup.Comment = { };
setup.colorizer = { };
setup.glow = { };
vim.o = {
termguicolors = true; # Set to truecolor
@@ -41,6 +29,7 @@
scrolloff = 3; # Margin of lines to see while scrolling
splitright = true; # Vertical splits on the right side
splitbelow = true; # Horizontal splits on the bottom side
pastetoggle = "<F3>"; # Use F3 to enter raw paste mode
clipboard = "unnamedplus"; # Uses system clipboard for yanking
updatetime = 300; # Faster diagnostics
mouse = "nv"; # Mouse interaction / scrolling
@@ -52,23 +41,14 @@
relativenumber = true; # Relative numbers instead of absolute
};
# For which-key-nvim
vim.o.timeout = true;
vim.o.timeoutlen = 300;
# Better backup, swap and undo storage
vim.o.backup = true; # Easier to recover and more secure
vim.opt.undofile = true; # Keeps undos after quit
vim.opt.swapfile = false; # Instead of swaps, create backups
vim.o.backupdir = dsl.rawLua ''vim.fn.expand("~/.local/state/nvim/backup//")'';
vim.o.undodir = dsl.rawLua ''vim.fn.expand("~/.local/state/nvim/undo//")'';
vim.bo.swapfile = false; # Instead of swaps, create backups
vim.bo.undofile = true; # Keeps undos after quit
vim.o.backupdir = dsl.rawLua ''vim.fn.stdpath("cache") .. "/backup"'';
# Required for nvim-cmp completion
vim.opt.completeopt = [
"menu"
"menuone"
"noselect"
];
vim.opt.completeopt = [ "menu" "menuone" "noselect" ];
lua = lib.mkBefore ''
vim.loader.enable()
@@ -80,6 +60,10 @@
" Remember last position when reopening file
au BufReadPost * if line("'\"") > 0 && line("'\"") <= line("$") | exe "normal! g`\"" | endif
" LaTeX options
au FileType tex inoremap ;bf \textbf{}<Esc>i
au BufWritePost *.tex silent! execute "!pdflatex -output-directory=%:p:h % >/dev/null 2>&1" | redraw!
" Flash highlight when yanking
au TextYankPost * silent! lua vim.highlight.on_yank { timeout = 250 }
'';
@@ -1,5 +1,4 @@
{ pkgs, ... }:
{
{ pkgs, ... }: {
plugins = [ pkgs.vimPlugins.lualine-nvim ];
setup.lualine = {
options = {
@@ -1,30 +1,28 @@
{ pkgs, lib, ... }:
{
{ pkgs, lib, ... }: {
plugins = [
(pkgs.vimPlugins.nvim-treesitter.withPlugins (_plugins: [
pkgs.nmasur.tree-sitter-ini
pkgs.nmasur.tree-sitter-puppet
pkgs.nmasur.tree-sitter-rasi
pkgs.nmasur.tree-sitter-vimdoc
pkgs.tree-sitter-grammars.tree-sitter-bash
pkgs.tree-sitter-grammars.tree-sitter-c
pkgs.tree-sitter-grammars.tree-sitter-fish
pkgs.tree-sitter-grammars.tree-sitter-hcl
pkgs.tree-sitter-grammars.tree-sitter-json
pkgs.tree-sitter-grammars.tree-sitter-lua
pkgs.tree-sitter-grammars.tree-sitter-markdown
pkgs.tree-sitter-grammars.tree-sitter-markdown-inline
pkgs.tree-sitter-grammars.tree-sitter-nix
pkgs.tree-sitter-grammars.tree-sitter-python
pkgs.tree-sitter-grammars.tree-sitter-toml
pkgs.tree-sitter-grammars.tree-sitter-yaml
]))
(pkgs.vimPlugins.nvim-treesitter.withPlugins (_plugins:
with pkgs.tree-sitter-grammars; [
tree-sitter-bash
tree-sitter-fish
tree-sitter-hcl
tree-sitter-ini
tree-sitter-json
tree-sitter-lua
tree-sitter-markdown
tree-sitter-markdown-inline
tree-sitter-nix
tree-sitter-puppet
tree-sitter-python
tree-sitter-rasi
tree-sitter-toml
tree-sitter-yaml
]))
pkgs.vimPlugins.vim-matchup # Better % jumping in languages
pkgs.vimPlugins.playground # Tree-sitter experimenting
pkgs.vimPlugins.nginx-vim
pkgs.vimPlugins.vim-helm
# pkgs.vimPlugins.hmts-nvim # Tree-sitter injections for home-manager
(pkgs.vimUtils.buildVimPlugin {
(pkgs.vimUtils.buildVimPluginFrom2Nix {
pname = "nmasur";
version = "0.1";
src = ../plugin;
@@ -32,15 +30,9 @@
];
setup."nvim-treesitter.configs" = {
highlight = {
enable = true;
};
indent = {
enable = true;
};
matchup = {
enable = true;
}; # Uses vim-matchup
highlight = { enable = true; };
indent = { enable = true; };
matchup = { enable = true; }; # Uses vim-matchup
textobjects = {
select = {
@@ -74,4 +66,5 @@
-- Use HCL parser with .tf files
vim.treesitter.language.register('hcl', 'terraform')
'';
}
@@ -1,7 +1,4 @@
{ pkgs, dsl, ... }:
{
# Telescope is a fuzzy finder that can work with different sub-plugins
{ pkgs, dsl, ... }: {
plugins = [
pkgs.vimPlugins.telescope-nvim
@@ -21,15 +18,9 @@
};
};
pickers = {
find_files = {
theme = "ivy";
};
oldfiles = {
theme = "ivy";
};
buffers = {
theme = "dropdown";
};
find_files = { theme = "ivy"; };
oldfiles = { theme = "ivy"; };
buffers = { theme = "dropdown"; };
};
extensions = {
fzy_native = { };
@@ -40,4 +31,5 @@
setup.project_nvim = { };
lua = builtins.readFile ./telescope.lua;
}
@@ -12,8 +12,6 @@ vim.api.nvim_create_autocmd("TermOpen", {
end,
})
-- These are all the different types of terminals we can trigger
local terminal = require("toggleterm.terminal").Terminal
local basicterminal = terminal:new()
@@ -21,10 +19,22 @@ function TERM_TOGGLE()
basicterminal:toggle()
end
local nixpkgs = terminal:new({ cmd = "nix repl --expr 'import <nixpkgs>{}'" })
local nixpkgs = terminal:new({ cmd = "nix repl '<nixpkgs>'" })
function NIXPKGS_TOGGLE()
nixpkgs:toggle()
end
local gitwatch = terminal:new({ cmd = "fish --interactive --init-command 'gh run watch'" })
function GITWATCH_TOGGLE()
gitwatch:toggle()
end
local k9s = terminal:new({ cmd = "k9s" })
function K9S_TOGGLE()
k9s:toggle()
end
vim.keymap.set("n", "<Leader>t", TERM_TOGGLE)
vim.keymap.set("n", "<Leader>P", NIXPKGS_TOGGLE)
vim.keymap.set("n", "<Leader>gw", GITWATCH_TOGGLE)
vim.keymap.set("n", "<Leader>9", K9S_TOGGLE)
@@ -0,0 +1,13 @@
{ pkgs, dsl, ... }: {
plugins = [ pkgs.vimPlugins.toggleterm-nvim ];
use.toggleterm.setup = dsl.callWith {
open_mapping = dsl.rawLua "[[<c-\\>]]";
hide_numbers = true;
direction = "float";
};
lua = builtins.readFile ./toggleterm.lua;
}

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