Compare commits
41 Commits
a8b63e71c8
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| b63abca296 | |||
| 1311aadffb | |||
| f903ddeee5 | |||
| 33f3ddd7e9 | |||
| 1cdedf824c | |||
| beb856714e | |||
| fcb2067059 | |||
| cebd236b1f | |||
| 8cc818f6b2 | |||
| 305a7a5115 | |||
| 526888cd26 | |||
| 8d97d09b07 | |||
| 3f481e0a16 | |||
| 15dea7a249 | |||
| e1bace9044 | |||
| 09f2d2b013 | |||
| d195efdb0e | |||
| 3277c810a5 | |||
| f2baf3daf6 | |||
| 931470ee0a | |||
| 41b30788fe | |||
| 01ebff3596 | |||
| ed2c899915 | |||
| c548ead4f7 | |||
| 3b8cd7b742 | |||
| d71408b567 | |||
| a8147d9ae5 | |||
| 2b1950d4e3 | |||
| 322927e2b0 | |||
| 4cae9fe706 | |||
| b5b164b543 | |||
| 08db384f60 | |||
| 3b2cd0c3cf | |||
| 13a4467166 | |||
| 4c0b0fb780 | |||
| a09d1b49c2 | |||
| 8d381ef9f4 | |||
| 79d51c3f58 | |||
| 83fb796a9f | |||
| 4efc44e964 | |||
| 3970c60016 |
96
.gitea/workflows/deploy-nomad.yaml
Normal file
96
.gitea/workflows/deploy-nomad.yaml
Normal file
@@ -0,0 +1,96 @@
|
||||
# ABOUTME: Reusable workflow for building Nix Docker images and deploying to Nomad.
|
||||
# ABOUTME: Called by service repos with: uses: alo/alo-cluster/.gitea/workflows/deploy-nomad.yaml@master
|
||||
|
||||
name: Deploy to Nomad
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
service_name:
|
||||
required: true
|
||||
type: string
|
||||
description: "Nomad job name (must match job ID in services/*.hcl)"
|
||||
flake_output:
|
||||
required: false
|
||||
type: string
|
||||
default: "dockerImage"
|
||||
description: "Flake output to build (default: dockerImage)"
|
||||
registry:
|
||||
required: false
|
||||
type: string
|
||||
default: "gitea.v.paler.net"
|
||||
description: "Container registry hostname"
|
||||
secrets:
|
||||
REGISTRY_USERNAME:
|
||||
required: true
|
||||
REGISTRY_PASSWORD:
|
||||
required: true
|
||||
NOMAD_ADDR:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: nix
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
echo "Building .#${{ inputs.flake_output }}..."
|
||||
nix build ".#${{ inputs.flake_output }}" --out-link result
|
||||
|
||||
- name: Push to registry
|
||||
run: |
|
||||
echo "Pushing to ${{ inputs.registry }}/alo/${{ inputs.service_name }}:latest..."
|
||||
skopeo copy \
|
||||
--dest-creds "${{ secrets.REGISTRY_USERNAME }}:${{ secrets.REGISTRY_PASSWORD }}" \
|
||||
--insecure-policy \
|
||||
docker-archive:result \
|
||||
"docker://${{ inputs.registry }}/alo/${{ inputs.service_name }}:latest"
|
||||
|
||||
- name: Deploy to Nomad
|
||||
env:
|
||||
NOMAD_ADDR: ${{ secrets.NOMAD_ADDR }}
|
||||
SERVICE: ${{ inputs.service_name }}
|
||||
run: |
|
||||
echo "Deploying $SERVICE to Nomad..."
|
||||
|
||||
# Fetch current job, update UUID to force deployment
|
||||
JOB=$(curl -sS "$NOMAD_ADDR/v1/job/$SERVICE")
|
||||
NEW_UUID=$(cat /proc/sys/kernel/random/uuid)
|
||||
echo "New deployment UUID: $NEW_UUID"
|
||||
UPDATED_JOB=$(echo "$JOB" | jq --arg uuid "$NEW_UUID" '.Meta.uuid = $uuid')
|
||||
|
||||
# Submit updated job
|
||||
RESULT=$(echo "{\"Job\": $UPDATED_JOB}" | curl -sS -X POST "$NOMAD_ADDR/v1/jobs" \
|
||||
-H "Content-Type: application/json" -d @-)
|
||||
echo "Submit result: $RESULT"
|
||||
|
||||
# Monitor deployment
|
||||
sleep 3
|
||||
DEPLOY_ID=$(curl -sS "$NOMAD_ADDR/v1/job/$SERVICE/deployments" | jq -r '.[0].ID')
|
||||
echo "Deployment ID: $DEPLOY_ID"
|
||||
|
||||
if [ "$DEPLOY_ID" = "null" ]; then
|
||||
echo "ERROR: No deployment created. Ensure job has 'update' stanza with 'auto_revert = true'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Monitoring deployment..."
|
||||
for i in $(seq 1 30); do
|
||||
STATUS=$(curl -sS "$NOMAD_ADDR/v1/deployment/$DEPLOY_ID" | jq -r '.Status')
|
||||
echo "[$i/30] Deployment status: $STATUS"
|
||||
case $STATUS in
|
||||
successful)
|
||||
echo "Deployment successful!"
|
||||
exit 0
|
||||
;;
|
||||
failed|cancelled)
|
||||
echo "Deployment failed or cancelled"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
sleep 10
|
||||
done
|
||||
echo "Timeout waiting for deployment"
|
||||
exit 1
|
||||
@@ -1,51 +0,0 @@
|
||||
{ pkgs, lib, ... }:
|
||||
{
|
||||
# Desktop profile: Graphical desktop with Hyprland
|
||||
# Extends workstation-node with desktop environment
|
||||
imports = [
|
||||
./workstation-node.nix
|
||||
];
|
||||
|
||||
# omarchy-nix enables NetworkManager, but we use useDHCP globally
|
||||
networking.networkmanager.enable = lib.mkForce false;
|
||||
|
||||
# Enable Hyprland (Wayland compositor)
|
||||
programs.hyprland = {
|
||||
enable = true;
|
||||
xwayland.enable = true; # For compatibility with X11 apps if needed
|
||||
};
|
||||
|
||||
# Essential desktop services
|
||||
services.dbus.enable = true;
|
||||
|
||||
# polkit for privilege escalation
|
||||
security.polkit.enable = true;
|
||||
|
||||
# Enable sound with pipewire
|
||||
security.rtkit.enable = true;
|
||||
services.pipewire = {
|
||||
enable = true;
|
||||
alsa.enable = true;
|
||||
alsa.support32Bit = true;
|
||||
pulse.enable = true;
|
||||
};
|
||||
|
||||
# Fonts
|
||||
fonts.packages = with pkgs; [
|
||||
noto-fonts
|
||||
noto-fonts-cjk-sans
|
||||
noto-fonts-emoji
|
||||
liberation_ttf
|
||||
fira-code
|
||||
fira-code-symbols
|
||||
];
|
||||
|
||||
# Environment variables for Wayland
|
||||
environment.sessionVariables = {
|
||||
NIXOS_OZONE_WL = "1"; # Hint electron apps to use Wayland
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
prusa-slicer
|
||||
];
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.7 MiB |
79
common/desktop/default.nix
Normal file
79
common/desktop/default.nix
Normal file
@@ -0,0 +1,79 @@
|
||||
# ABOUTME: NixOS desktop environment module for Hyprland
|
||||
# ABOUTME: Configures greetd, audio, bluetooth, fonts, and system services
|
||||
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
imports = [
|
||||
../workstation-node.nix
|
||||
];
|
||||
|
||||
# Force NetworkManager off - we use useDHCP globally
|
||||
networking.networkmanager.enable = lib.mkForce false;
|
||||
|
||||
# Hyprland window manager
|
||||
programs.hyprland = {
|
||||
enable = true;
|
||||
xwayland.enable = true;
|
||||
};
|
||||
|
||||
# greetd display manager with tuigreet
|
||||
services.greetd = {
|
||||
enable = true;
|
||||
settings = {
|
||||
default_session = {
|
||||
command = "${pkgs.tuigreet}/bin/tuigreet --time --cmd Hyprland";
|
||||
user = "greeter";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Essential desktop services
|
||||
services.dbus.enable = true;
|
||||
|
||||
# polkit for privilege escalation
|
||||
security.polkit.enable = true;
|
||||
|
||||
# DNS resolution
|
||||
services.resolved.enable = true;
|
||||
|
||||
# Bluetooth support
|
||||
hardware.bluetooth = {
|
||||
enable = true;
|
||||
powerOnBoot = true;
|
||||
};
|
||||
services.blueman.enable = true;
|
||||
|
||||
# Audio with PipeWire
|
||||
security.rtkit.enable = true;
|
||||
services.pipewire = {
|
||||
enable = true;
|
||||
alsa.enable = true;
|
||||
alsa.support32Bit = true;
|
||||
pulse.enable = true;
|
||||
jack.enable = true;
|
||||
};
|
||||
|
||||
# direnv support
|
||||
programs.direnv.enable = true;
|
||||
|
||||
# Fonts
|
||||
fonts.packages = with pkgs; [
|
||||
noto-fonts
|
||||
noto-fonts-cjk-sans
|
||||
noto-fonts-color-emoji
|
||||
liberation_ttf
|
||||
fira-code
|
||||
fira-code-symbols
|
||||
nerd-fonts.caskaydia-mono
|
||||
];
|
||||
|
||||
# Environment variables for Wayland
|
||||
environment.sessionVariables = {
|
||||
NIXOS_OZONE_WL = "1";
|
||||
};
|
||||
|
||||
# Additional desktop packages
|
||||
environment.systemPackages = with pkgs; [
|
||||
prusa-slicer
|
||||
];
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
boot.loader.systemd-boot = {
|
||||
enable = true;
|
||||
configurationLimit = 5;
|
||||
memtest86.enable = lib.mkIf (pkgs.system == "x86_64-linux") true;
|
||||
memtest86.enable = lib.mkIf (pkgs.stdenv.hostPlatform.system == "x86_64-linux") true;
|
||||
};
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
}
|
||||
|
||||
206
docs/CICD_SETUP.md
Normal file
206
docs/CICD_SETUP.md
Normal file
@@ -0,0 +1,206 @@
|
||||
# CI/CD Setup for Nomad Services
|
||||
|
||||
Guide for adding automated builds and deployments to a service.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### 1. Service Repository
|
||||
|
||||
Your service needs a `flake.nix` that exports a Docker image:
|
||||
|
||||
```nix
|
||||
{
|
||||
outputs = { self, nixpkgs, ... }: {
|
||||
# The workflow looks for this output by default
|
||||
dockerImage = pkgs.dockerTools.buildImage {
|
||||
name = "gitea.v.paler.net/alo/<service>";
|
||||
tag = "latest";
|
||||
# ... image config
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Important**: Use `extraCommands` instead of `runAsRoot` in your Docker build - the CI runner doesn't have KVM.
|
||||
|
||||
### 2. Nomad Job
|
||||
|
||||
Your job in `services/<name>.hcl` needs:
|
||||
|
||||
```hcl
|
||||
job "<service>" {
|
||||
# Required: UUID changes trigger deployments
|
||||
meta {
|
||||
uuid = uuidv4()
|
||||
}
|
||||
|
||||
# Required: enables deployment tracking and auto-rollback
|
||||
update {
|
||||
max_parallel = 1
|
||||
health_check = "checks"
|
||||
min_healthy_time = "30s"
|
||||
healthy_deadline = "5m"
|
||||
auto_revert = true
|
||||
}
|
||||
|
||||
# Required: pulls new image on each deployment
|
||||
task "app" {
|
||||
config {
|
||||
force_pull = true
|
||||
}
|
||||
|
||||
# Recommended: health check for deployment validation
|
||||
service {
|
||||
check {
|
||||
type = "http"
|
||||
path = "/healthz"
|
||||
interval = "10s"
|
||||
timeout = "5s"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Create Workflow
|
||||
|
||||
Add `.gitea/workflows/deploy.yaml` to your service repo:
|
||||
|
||||
```yaml
|
||||
name: Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
uses: alo/alo-cluster/.gitea/workflows/deploy-nomad.yaml@master
|
||||
with:
|
||||
service_name: <your-service> # Must match Nomad job ID
|
||||
secrets: inherit
|
||||
```
|
||||
|
||||
### 2. Add Secrets
|
||||
|
||||
In Gitea → Your Repo → Settings → Actions → Secrets, add:
|
||||
|
||||
| Secret | Value |
|
||||
|--------|-------|
|
||||
| `REGISTRY_USERNAME` | Your Gitea username |
|
||||
| `REGISTRY_PASSWORD` | Gitea access token with `packages:write` |
|
||||
| `NOMAD_ADDR` | `http://nomad.service.consul:4646` |
|
||||
|
||||
### 3. Push
|
||||
|
||||
Push to `master` branch. The workflow will:
|
||||
1. Build your Docker image with Nix
|
||||
2. Push to Gitea registry
|
||||
3. Update the Nomad job to trigger deployment
|
||||
4. Monitor until deployment succeeds or fails
|
||||
|
||||
## Workflow Options
|
||||
|
||||
The shared workflow accepts these inputs:
|
||||
|
||||
| Input | Default | Description |
|
||||
|-------|---------|-------------|
|
||||
| `service_name` | (required) | Nomad job ID |
|
||||
| `flake_output` | `dockerImage` | Flake output to build |
|
||||
| `registry` | `gitea.v.paler.net` | Container registry |
|
||||
|
||||
Example with custom flake output:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
deploy:
|
||||
uses: alo/alo-cluster/.gitea/workflows/deploy-nomad.yaml@master
|
||||
with:
|
||||
service_name: myservice
|
||||
flake_output: packages.x86_64-linux.docker
|
||||
secrets: inherit
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
Push to master
|
||||
↓
|
||||
Build: nix build .#dockerImage
|
||||
↓
|
||||
Push: skopeo → gitea.v.paler.net/alo/<service>:latest
|
||||
↓
|
||||
Deploy: Update job meta.uuid → Nomad creates deployment
|
||||
↓
|
||||
Monitor: Poll deployment status for up to 5 minutes
|
||||
↓
|
||||
Success: Deployment healthy
|
||||
OR
|
||||
Failure: Nomad auto-reverts to previous version
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Build fails with KVM error
|
||||
|
||||
```
|
||||
Required system: 'x86_64-linux' with features {kvm}
|
||||
```
|
||||
|
||||
Use `extraCommands` instead of `runAsRoot` in your `docker.nix`:
|
||||
|
||||
```nix
|
||||
# Bad - requires KVM
|
||||
runAsRoot = ''
|
||||
mkdir -p /tmp
|
||||
'';
|
||||
|
||||
# Good - no KVM needed
|
||||
extraCommands = ''
|
||||
mkdir -p tmp
|
||||
chmod 1777 tmp
|
||||
'';
|
||||
```
|
||||
|
||||
### No deployment created
|
||||
|
||||
Ensure your Nomad job has the `update` stanza with `auto_revert = true`.
|
||||
|
||||
### Image not updating
|
||||
|
||||
Check that `force_pull = true` is set in the Nomad job's Docker config.
|
||||
|
||||
### Deployment fails health checks
|
||||
|
||||
- Check your `/healthz` endpoint works
|
||||
- Increase `healthy_deadline` if startup is slow
|
||||
- Check `nomad alloc logs <alloc-id>` for errors
|
||||
|
||||
### Workflow can't access alo-cluster
|
||||
|
||||
If Gitea can't pull the reusable workflow, you may need to make alo-cluster public or use a token. As a fallback, copy the workflow content directly.
|
||||
|
||||
## Manual Deployment
|
||||
|
||||
If CI fails, you can deploy manually:
|
||||
|
||||
```bash
|
||||
cd <service-repo>
|
||||
nix build .#dockerImage
|
||||
skopeo copy --dest-authfile ~/.docker/config.json \
|
||||
docker-archive:result \
|
||||
docker://gitea.v.paler.net/alo/<service>:latest
|
||||
nomad run /path/to/alo-cluster/services/<service>.hcl
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
Nomad auto-reverts on health check failure. For manual rollback:
|
||||
|
||||
```bash
|
||||
nomad job history <service> # List versions
|
||||
nomad job revert <service> <version> # Revert to specific version
|
||||
```
|
||||
354
docs/HOMELAB_AGENT.md
Normal file
354
docs/HOMELAB_AGENT.md
Normal file
@@ -0,0 +1,354 @@
|
||||
# ABOUTME: Vision and design document for an AI agent that manages the homelab cluster.
|
||||
# ABOUTME: Covers emergent capabilities, technical approach, and implementation strategy.
|
||||
|
||||
# Homelab Agent: Vision and Design
|
||||
|
||||
## The Core Idea
|
||||
|
||||
Not automation. Not "LLM-powered autocomplete for infrastructure." Emergent capabilities.
|
||||
|
||||
The same shift Claude Code brought to programming: you describe outcomes, it handles implementation. You become a "product manager" for your infrastructure instead of an "infrastructure engineer."
|
||||
|
||||
The cluster stops being infrastructure you manage and becomes an environment that responds to intent.
|
||||
|
||||
## What Makes This Different From Automation
|
||||
|
||||
**Automation**: "If disk > 90%, delete old logs"
|
||||
|
||||
**Emergent**: "Disk is 95% full. What's using space? ...Postgres WAL. Can I safely checkpoint? Last backup was 2h ago, load is low, yes. Running checkpoint... down to 60%. I should note that WAL retention might need tuning."
|
||||
|
||||
The difference:
|
||||
- Novel problem-solving (not pattern matching)
|
||||
- Contextual safety reasoning
|
||||
- Adaptation to the specific situation
|
||||
- Learning for the future
|
||||
|
||||
## Examples of Genuinely New Capabilities
|
||||
|
||||
### 1. Intent-Driven Infrastructure
|
||||
|
||||
> "I want to run Synapse for Matrix"
|
||||
|
||||
Agent figures out: Nomad job spec, storage location, Traefik routing, TLS, Consul registration, backup config. Creates it, deploys it, validates it.
|
||||
|
||||
You don't need to know Nomad job format or Traefik labels. You describe the outcome.
|
||||
|
||||
### 2. Proactive Evolution (The Best One)
|
||||
|
||||
The agent doesn't wait for problems or instructions:
|
||||
|
||||
- "Synapse 1.98 has a security fix. I've tested it in a local build, no config changes needed. Deploy?"
|
||||
- "Your NFS server has been primary for 47 days. Want me to test failover to make sure it still works?"
|
||||
- "I noticed arr services all have the same resource limits but Sonarr consistently uses more. Adjusted."
|
||||
- "There's a new NixOS module for Traefik that simplifies your current setup. Here's the diff."
|
||||
|
||||
Not monitoring. Stewardship.
|
||||
|
||||
### 3. The Cluster Has Opinions
|
||||
|
||||
> You: "I want to add Plex"
|
||||
>
|
||||
> Agent: "You already have Jellyfin, which does the same thing. If you want Plex specifically for its mobile app, I can set it up to share Jellyfin's media library. Or if you want to switch entirely, I can migrate watch history. What's the actual goal?"
|
||||
|
||||
Not a command executor. A collaborator that understands your system.
|
||||
|
||||
### 4. "Bring This Into the Cluster"
|
||||
|
||||
You're running something in Docker on a random VM:
|
||||
|
||||
> "Bring this into the cluster"
|
||||
|
||||
Agent: connects, inspects, figures out dependencies, writes Nomad job, sets up storage, migrates data, routes traffic, validates, decommissions old instance.
|
||||
|
||||
You didn't need to know how.
|
||||
|
||||
### 5. Cross-Cutting Changes
|
||||
|
||||
> "Add authentication to all public-facing services"
|
||||
|
||||
Agent identifies which services are public, understands the auth setup (Pocket ID + traefik-oidc-auth), modifies each service's config, tests that auth works.
|
||||
|
||||
Single coherent change across everything, without knowing every service yourself.
|
||||
|
||||
### 6. Emergent Debugging
|
||||
|
||||
Not runbooks. Actual reasoning:
|
||||
|
||||
> "The blog is slow"
|
||||
|
||||
Agent checks service health (fine), node resources (fine), network latency (fine), database queries (ah, slow query), traces to missing index, adds index, validates performance improved.
|
||||
|
||||
Solved a problem nobody wrote a runbook for.
|
||||
|
||||
### 7. Architecture Exploration
|
||||
|
||||
> "What if we added a third Nomad server for better quorum?"
|
||||
|
||||
Agent reasons about current topology, generates the config, identifies what would change, shows blast radius. Thinking partner for infrastructure decisions.
|
||||
|
||||
## Why Nix Makes This Possible
|
||||
|
||||
Traditional infrastructure: state is scattered and implicit. Nix: everything is declared.
|
||||
|
||||
- **Full system understanding** - agent can read the flake and understand EVERYTHING
|
||||
- **Safe experimentation** - build without deploying, rollback trivially
|
||||
- **Reproducibility** - "what was the state 3 days ago?" can be rebuilt exactly
|
||||
- **Composition** - agent can generate valid configs that compose correctly
|
||||
- **The ecosystem** - 80k+ packages, thousands of modules the agent can navigate
|
||||
|
||||
> "I want a VPN that works with my phone"
|
||||
|
||||
Agent knows Nix, finds WireGuard module, configures it, generates QR codes, opens firewall. You didn't learn WireGuard.
|
||||
|
||||
## The Validation Pattern
|
||||
|
||||
Just like code has linting and tests, infrastructure actions need validation:
|
||||
|
||||
| Phase | Code | Infrastructure |
|
||||
|-------|------|----------------|
|
||||
| Static | Lint, typecheck | Config parses, secrets exist, no port conflicts |
|
||||
| Pre-flight | — | Cluster healthy, dependencies up, quorum intact |
|
||||
| Post-action | Unit tests | Service started, health checks pass, metrics flowing |
|
||||
| Invariants | CI | NFS mounted, Consul quorum, replication current |
|
||||
|
||||
The agent can take actions confidently because it validates outcomes.
|
||||
|
||||
## The Reality Check
|
||||
|
||||
Some of this works today. Some would fail spectacularly. Some would fail silently and idiotically. Just like Claude Code for coding.
|
||||
|
||||
Therefore:
|
||||
- Tight loop with the human operator
|
||||
- Assume the human is competent and knowledgeable
|
||||
- Agent amplifies expertise, doesn't replace it
|
||||
- Escalate when uncertain
|
||||
|
||||
## Technical Approach
|
||||
|
||||
### Runtime: Claude Code (Not Agent SDK)
|
||||
|
||||
Two options were considered:
|
||||
|
||||
| Tool | Pro/Max Subscription | API Billing |
|
||||
|------|---------------------|-------------|
|
||||
| Claude Code CLI | Yes | Yes |
|
||||
| Claude Agent SDK | No | Required |
|
||||
|
||||
Claude Code can use existing Max subscription. Agent SDK requires separate API billing.
|
||||
|
||||
For v1, use Claude Code as the runtime:
|
||||
|
||||
```bash
|
||||
claude --print "prompt" \
|
||||
--allowedTools "Bash,Read,Edit" \
|
||||
--permission-mode acceptEdits
|
||||
```
|
||||
|
||||
Graduate to Agent SDK later if limitations are hit.
|
||||
|
||||
### Trigger Architecture
|
||||
|
||||
On-demand Claude Code sessions, triggered by:
|
||||
- **Timer** - periodic health/sanity check
|
||||
- **Alert** - alertmanager webhook
|
||||
- **Event** - systemd OnFailure, consul watch
|
||||
- **Manual** - invoke with a goal
|
||||
|
||||
Each trigger provides context and a goal. Claude Code does the rest.
|
||||
|
||||
### Structure
|
||||
|
||||
```
|
||||
agent/
|
||||
├── triggers/
|
||||
│ ├── scheduled-check # systemd timer
|
||||
│ ├── on-alert # webhook handler
|
||||
│ └── on-failure # systemd OnFailure target
|
||||
├── gather-context.sh # snapshot of cluster state
|
||||
└── goals/
|
||||
├── health-check.md # verify health, fix if safe
|
||||
├── incident.md # investigate alert, fix or escalate
|
||||
└── proactive.md # look for improvements
|
||||
```
|
||||
|
||||
### Example: Scheduled Health Check
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
CONTEXT=$(./gather-context.sh)
|
||||
GOAL=$(cat goals/health-check.md)
|
||||
|
||||
claude --print "
|
||||
## Context
|
||||
$CONTEXT
|
||||
|
||||
## Goal
|
||||
$GOAL
|
||||
|
||||
## Constraints
|
||||
- You can read any file in this repo
|
||||
- You can run nomad/consul/systemctl commands
|
||||
- You can edit Nix/HCL files and run deploy
|
||||
- Before destructive actions, validate with nix build or nomad plan
|
||||
- If uncertain about safety, output a summary and stop
|
||||
"
|
||||
```
|
||||
|
||||
### Context Gathering
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
echo "=== Nomad Jobs ==="
|
||||
nomad job status
|
||||
|
||||
echo "=== Consul Members ==="
|
||||
consul members
|
||||
|
||||
echo "=== Failed Systemd Units ==="
|
||||
systemctl --failed
|
||||
|
||||
echo "=== Recent Errors (last hour) ==="
|
||||
journalctl --since "1 hour ago" -p err --no-pager | tail -100
|
||||
```
|
||||
|
||||
## Edge Cases and the Nix Promise
|
||||
|
||||
The NixOS promise mostly works, but sometimes doesn't:
|
||||
- Mount option changes that require reboot
|
||||
- Transition states where switch fails even if end state is correct
|
||||
- Partial application where switch "succeeds" but change didn't take effect
|
||||
|
||||
This is where the agent adds value: it can detect when a change needs special handling, apply the appropriate strategy, and verify the change actually took effect.
|
||||
|
||||
## Capturing Knowledge
|
||||
|
||||
Document edge cases as they're discovered:
|
||||
|
||||
```markdown
|
||||
## CIFS/NFS mount option changes
|
||||
Switch may fail or succeed without effect. Strategy:
|
||||
1. Try normal deploy
|
||||
2. If mount options don't match after, reboot required
|
||||
3. If deploy fails with mount busy, local switch + reboot
|
||||
```
|
||||
|
||||
The agent reads this, uses it as context, but can also reason about novel situations.
|
||||
|
||||
## Path to CI/CD
|
||||
|
||||
Eventually: push to main triggers deploy via agent.
|
||||
|
||||
```
|
||||
push to main
|
||||
|
|
||||
build all configs (mechanical)
|
||||
|
|
||||
agent: "what changed? is this safe to auto-deploy?"
|
||||
|
|
||||
├─ clean change -> deploy, validate, done
|
||||
├─ needs reboot -> deploy, schedule reboot, validate after
|
||||
├─ risky change -> notify for manual approval
|
||||
└─ failed -> diagnose, retry with different strategy, or escalate
|
||||
|
|
||||
post-deploy verification
|
||||
|
|
||||
notification
|
||||
```
|
||||
|
||||
The agent is the intelligence layer on top of mechanical CI/CD.
|
||||
|
||||
## Research: What Others Are Doing (January 2026)
|
||||
|
||||
### Existing Projects & Approaches
|
||||
|
||||
**n8n + Ollama Stack**
|
||||
The most common pattern is n8n (workflow orchestration) + Ollama (local LLM). Webhooks from
|
||||
monitoring (Netdata/Prometheus) trigger AI-assisted diagnosis. Philosophy from one practitioner:
|
||||
"train an employee, not a bot" — build trust, gradually grant autonomy.
|
||||
|
||||
Sources:
|
||||
- [Virtualization Howto: Self-Healing Home Lab](https://www.virtualizationhowto.com/2025/10/how-i-built-a-self-healing-home-lab-that-fixes-itself/)
|
||||
- [addROM: AI Agent for Homelab with n8n](https://addrom.com/unleashing-the-power-of-an-ai-agent-for-homelab-management-with-n8n/)
|
||||
|
||||
**Local Infrastructure Agent (Kelcode)**
|
||||
Architecture: user question → tool router → query processor → LLM response. Connects to
|
||||
Kubernetes, Prometheus, Harbor Registry.
|
||||
|
||||
Key insight: "The AI's output definition must be perfectly synchronized with the software
|
||||
it's trying to use." Their K8s tool failed because the prompt generated kubectl commands
|
||||
while the code expected structured data objects.
|
||||
|
||||
Uses phi4-mini via Ollama for routing decisions after testing multiple models.
|
||||
|
||||
Source: [Kelcode: Building a Homelab Agentic Ecosystem](https://kelcode.co.uk/building-a-homelab-agentic-ecosystem-part1/)
|
||||
|
||||
**nixai**
|
||||
AI assistant specifically for NixOS. Searches NixOS Wiki, Nixpkgs Manual, nix.dev, Home Manager
|
||||
docs. Diagnoses issues from piped logs/errors. Privacy-first: defaults to local Ollama.
|
||||
|
||||
Limited scope — helper tool, not autonomous agent. But shows NixOS-specific tooling is possible.
|
||||
|
||||
Source: [NixOS Discourse: Introducing nixai](https://discourse.nixos.org/t/introducing-nixai-your-ai-powered-nixos-companion/65168)
|
||||
|
||||
**AI-Friendly Infrastructure (The Merino Wolf)**
|
||||
Key insight: make infrastructure "AI-friendly" through structured documentation. CLAUDE.md
|
||||
provides comprehensive context — "structured knowledge transfer."
|
||||
|
||||
Lessons:
|
||||
- "Context investment pays dividends" — comprehensive documentation is the most valuable asset
|
||||
- Layered infrastructure design mirrors how both humans and AI think
|
||||
- Rule-based guidance enforces safety practices automatically
|
||||
|
||||
Source: [The Merino Wolf: AI-Powered Homelab](https://themerinowolf.com/posts/ai-powered-homelab/)
|
||||
|
||||
**Claude Code Infrastructure Patterns**
|
||||
Solves "skills don't activate automatically" problem using hooks (UserPromptSubmit, PostToolUse)
|
||||
+ skill-rules.json for auto-activation.
|
||||
|
||||
500-line rule with progressive disclosure: main file for high-level guidance, resource files
|
||||
for deep dives. Claude loads materials incrementally as needed.
|
||||
|
||||
Persistence pattern across context resets using three-file structures (plan, context, tasks).
|
||||
|
||||
Born from 6 months managing TypeScript microservices (50k+ lines).
|
||||
|
||||
Source: [diet103/claude-code-infrastructure-showcase](https://github.com/diet103/claude-code-infrastructure-showcase)
|
||||
|
||||
### Patterns That Work
|
||||
|
||||
- Local LLMs (Ollama) + workflow orchestration (n8n) is the popular stack
|
||||
- Start with read-only/diagnostic agents, gradually add write access
|
||||
- Pre-approved command lists for safety (e.g., 50 validated bash commands max)
|
||||
- Structured documentation as foundation — AI is only as good as its context
|
||||
- Multi-step tool use: agent plans, then executes steps, observing results
|
||||
|
||||
### What's Missing in the Space
|
||||
|
||||
- Nobody's doing true "emergent capabilities" yet — mostly tool routing
|
||||
- Most projects are Kubernetes/Docker focused, not NixOS
|
||||
- Few examples of proactive stewardship (our example #2)
|
||||
- Limited examples of agents that understand the whole system coherently
|
||||
|
||||
### Community Skepticism
|
||||
|
||||
From Reddit discussions: doubts exist about using LLM agents in production. Although LLMs can
|
||||
automate specific tasks, they frequently need human involvement for intricate decision-making.
|
||||
|
||||
This validates our approach: tight loop with a competent human, not autonomous operation.
|
||||
|
||||
### The Gap We'd Fill
|
||||
|
||||
- NixOS-native agent leveraging declarative config as source of truth
|
||||
- True emergence — not just tool routing, but reasoning about novel situations
|
||||
- Proactive evolution, not just reactive troubleshooting
|
||||
- Tight human loop with a competent operator
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Build trigger infrastructure (systemd timer, basic webhook handler)
|
||||
2. Write context gathering scripts
|
||||
3. Define goal prompts for common scenarios
|
||||
4. Test with scheduled health checks
|
||||
5. Iterate based on what works and what doesn't
|
||||
6. Document edge cases as they're discovered
|
||||
7. Gradually expand scope as confidence grows
|
||||
@@ -4,4 +4,4 @@
|
||||
* renovate system of some kind
|
||||
* vector (or other log ingestion) everywhere, consider moving it off docker if possible
|
||||
* monitor backup-persist success/fail
|
||||
|
||||
* gitea organization is public -> at least from the internal network, anyone can pull images and probably also clone repos. there should be absolutely zero secrets in the repos (and the ones that are now should be changed before stored somewhere else) and the nomad workers should authenticate to pull images
|
||||
|
||||
603
flake.lock
generated
603
flake.lock
generated
@@ -1,42 +1,5 @@
|
||||
{
|
||||
"nodes": {
|
||||
"aquamarine": {
|
||||
"inputs": {
|
||||
"hyprutils": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"hyprutils"
|
||||
],
|
||||
"hyprwayland-scanner": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"hyprwayland-scanner"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1753216019,
|
||||
"narHash": "sha256-zik7WISrR1ks2l6T1MZqZHb/OqroHdJnSnAehkE0kCk=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "aquamarine",
|
||||
"rev": "be166e11d86ba4186db93e10c54a141058bdce49",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "aquamarine",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"base16-schemes": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@@ -62,11 +25,11 @@
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1764963023,
|
||||
"narHash": "sha256-sBBWc0loJiUq1eiSEI0KmHp0XfvqYN8NuwpshPQ0Avg=",
|
||||
"lastModified": 1768591869,
|
||||
"narHash": "sha256-Tph/rfG5Oebz1VQJiJXHMQEFpzXYV98d5fRkmepQK3Y=",
|
||||
"owner": "nix-community",
|
||||
"repo": "browser-previews",
|
||||
"rev": "f3a217a7f332d17b64bce21ca32f1a10a318162a",
|
||||
"rev": "46e6d0179b14a83401120a4481d170582e8f35f4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -84,11 +47,11 @@
|
||||
"utils": "utils"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1762286984,
|
||||
"narHash": "sha256-9I2H9x5We6Pl+DBYHjR1s3UT8wgwcpAH03kn9CqtdQc=",
|
||||
"lastModified": 1766051518,
|
||||
"narHash": "sha256-znKOwPXQnt3o7lDb3hdf19oDo0BLP4MfBOYiWkEHoik=",
|
||||
"owner": "serokell",
|
||||
"repo": "deploy-rs",
|
||||
"rev": "9c870f63e28ec1e83305f7f6cb73c941e699f74f",
|
||||
"rev": "d5eff7f948535b9c723d60cd8239f8f11ddc90fa",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -105,11 +68,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1762521437,
|
||||
"narHash": "sha256-RXN+lcx4DEn3ZS+LqEJSUu/HH+dwGvy0syN7hTo/Chg=",
|
||||
"lastModified": 1764011051,
|
||||
"narHash": "sha256-M7SZyPZiqZUR/EiiBJnmyUbOi5oE/03tCeFrTiUZchI=",
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"rev": "07bacc9531f5f4df6657c0a02a806443685f384a",
|
||||
"rev": "17ed8d9744ebe70424659b0ef74ad6d41fc87071",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -125,11 +88,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1764627417,
|
||||
"narHash": "sha256-D6xc3Rl8Ab6wucJWdvjNsGYGSxNjQHzRc2EZ6eeQ6l4=",
|
||||
"lastModified": 1768923567,
|
||||
"narHash": "sha256-GVJ0jKsyXLuBzRMXCDY6D5J8wVdwP1DuQmmvYL/Vw/Q=",
|
||||
"owner": "nix-community",
|
||||
"repo": "disko",
|
||||
"rev": "5a88a6eceb8fd732b983e72b732f6f4b8269bef3",
|
||||
"rev": "00395d188e3594a1507f214a2f15d4ce5c07cb28",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -152,11 +115,11 @@
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1765232406,
|
||||
"narHash": "sha256-qsUrJzMTGv3LkIVRa+2hnWSPBypRoGehiiEVE0ZYIow=",
|
||||
"lastModified": 1768914064,
|
||||
"narHash": "sha256-MbRHoA4AWpDYebuGAAWtw/3UB11TZK195m1Vh7IIMkg=",
|
||||
"owner": "nix-community",
|
||||
"repo": "ethereum.nix",
|
||||
"rev": "cf721965fe81d1a099059ffed410d6344dc54708",
|
||||
"rev": "f254a491fa97bc45d7ccb36547314f6cf57f7b70",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -181,32 +144,16 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat_2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1747046372,
|
||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1762980239,
|
||||
"narHash": "sha256-8oNVE8TrD19ulHinjaqONf9QWCKK+w4url56cdStMpM=",
|
||||
"lastModified": 1768135262,
|
||||
"narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "52a2caecc898d0b46b2b905f058ccc5081f842da",
|
||||
"rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -223,11 +170,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1763759067,
|
||||
"narHash": "sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q=",
|
||||
"lastModified": 1765835352,
|
||||
"narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "2cccadc7357c0ba201788ae99c4dfa90728ef5e0",
|
||||
"rev": "a34fae9c08a15ad73f295041fec82323541400a9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -290,11 +237,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1762247499,
|
||||
"narHash": "sha256-dPBqjoBcP3yczY7EUQP6BXf58wauRl+lZVZ/fabgq3E=",
|
||||
"lastModified": 1767517855,
|
||||
"narHash": "sha256-LnZosb07bahYAyFw07JFzSXslx9j1dCe+npWDZdPFZg=",
|
||||
"owner": "shazow",
|
||||
"repo": "foundry.nix",
|
||||
"rev": "ae6473c7190edea0e505f433293688014b556b29",
|
||||
"rev": "ee376e8a93f537c2865dda9811e748e4567a7aaf",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -304,29 +251,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"pre-commit-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"home-manager": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -334,308 +258,52 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1763992789,
|
||||
"narHash": "sha256-WHkdBlw6oyxXIra/vQPYLtqY+3G8dUVZM8bEXk0t8x4=",
|
||||
"lastModified": 1768603898,
|
||||
"narHash": "sha256-vRV1dWJOCpCal3PRr86wE2WTOMfAhTu6G7bSvOsryUo=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "44831a7eaba4360fb81f2acc5ea6de5fde90aaa3",
|
||||
"rev": "2a63d0e9d2c72ac4d4150ebb242cf8d86f488c8c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"ref": "release-25.05",
|
||||
"ref": "release-25.11",
|
||||
"repo": "home-manager",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprcursor": {
|
||||
"home-manager_2": {
|
||||
"inputs": {
|
||||
"hyprlang": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"hyprlang"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"impermanence",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1753964049,
|
||||
"narHash": "sha256-lIqabfBY7z/OANxHoPeIrDJrFyYy9jAM4GQLzZ2feCM=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprcursor",
|
||||
"rev": "44e91d467bdad8dcf8bbd2ac7cf49972540980a5",
|
||||
"lastModified": 1768598210,
|
||||
"narHash": "sha256-kkgA32s/f4jaa4UG+2f8C225Qvclxnqs76mf8zvTVPg=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "c47b2cc64a629f8e075de52e4742de688f930dc6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprcursor",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprgraphics": {
|
||||
"inputs": {
|
||||
"hyprutils": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"hyprutils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1754305013,
|
||||
"narHash": "sha256-u+M2f0Xf1lVHzIPQ7DsNCDkM1NYxykOSsRr4t3TbSM4=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprgraphics",
|
||||
"rev": "4c1d63a0f22135db123fc789f174b89544c6ec2d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprgraphics",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprland": {
|
||||
"inputs": {
|
||||
"aquamarine": "aquamarine",
|
||||
"hyprcursor": "hyprcursor",
|
||||
"hyprgraphics": "hyprgraphics",
|
||||
"hyprland-protocols": "hyprland-protocols",
|
||||
"hyprland-qtutils": "hyprland-qtutils",
|
||||
"hyprlang": "hyprlang",
|
||||
"hyprutils": "hyprutils",
|
||||
"hyprwayland-scanner": "hyprwayland-scanner",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"pre-commit-hooks": "pre-commit-hooks",
|
||||
"systems": "systems_5",
|
||||
"xdph": "xdph"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1755184403,
|
||||
"narHash": "sha256-VI+ZPD/uIFjzYW8IcyvBgvwyDIvUe4/xh/kOHTbITX8=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "Hyprland",
|
||||
"rev": "60d769a89908c29e19100059985db15a7b6bab6a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "Hyprland",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprland-protocols": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1749046714,
|
||||
"narHash": "sha256-kymV5FMnddYGI+UjwIw8ceDjdeg7ToDVjbHCvUlhn14=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-protocols",
|
||||
"rev": "613878cb6f459c5e323aaafe1e6f388ac8a36330",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-protocols",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprland-qt-support": {
|
||||
"inputs": {
|
||||
"hyprlang": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"hyprland-qtutils",
|
||||
"hyprlang"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"hyprland-qtutils",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"hyprland-qtutils",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1749154592,
|
||||
"narHash": "sha256-DO7z5CeT/ddSGDEnK9mAXm1qlGL47L3VAHLlLXoCjhE=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qt-support",
|
||||
"rev": "4c8053c3c888138a30c3a6c45c2e45f5484f2074",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qt-support",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprland-qtutils": {
|
||||
"inputs": {
|
||||
"hyprland-qt-support": "hyprland-qt-support",
|
||||
"hyprlang": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"hyprlang"
|
||||
],
|
||||
"hyprutils": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"hyprland-qtutils",
|
||||
"hyprlang",
|
||||
"hyprutils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1753819801,
|
||||
"narHash": "sha256-tHe6XeNeVeKapkNM3tcjW4RuD+tB2iwwoogWJOtsqTI=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qtutils",
|
||||
"rev": "b308a818b9dcaa7ab8ccab891c1b84ebde2152bc",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qtutils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprlang": {
|
||||
"inputs": {
|
||||
"hyprutils": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"hyprutils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1753622892,
|
||||
"narHash": "sha256-0K+A+gmOI8IklSg5It1nyRNv0kCNL51duwnhUO/B8JA=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprlang",
|
||||
"rev": "23f0debd2003f17bd65f851cd3f930cff8a8c809",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprlang",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprutils": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1754481650,
|
||||
"narHash": "sha256-6u6HdEFJh5gY6VfyMQbhP7zDdVcqOrCDTkbiHJmAtMI=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"rev": "df6b8820c4a0835d83d0c7c7be86fbc555f1f7fd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprwayland-scanner": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1751897909,
|
||||
"narHash": "sha256-FnhBENxihITZldThvbO7883PdXC/2dzW4eiNvtoV5Ao=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprwayland-scanner",
|
||||
"rev": "fcca0c61f988a9d092cbb33e906775014c61579d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprwayland-scanner",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"impermanence": {
|
||||
"inputs": {
|
||||
"home-manager": "home-manager_2",
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1737831083,
|
||||
"narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=",
|
||||
"lastModified": 1768835187,
|
||||
"narHash": "sha256-6nY0ixjGjPQCL+/sUC1B1MRiO1LOI3AkRSIywm3i3bE=",
|
||||
"owner": "nix-community",
|
||||
"repo": "impermanence",
|
||||
"rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170",
|
||||
"rev": "0d633a69480bb3a3e2f18c080d34a8fa81da6395",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -670,11 +338,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1765080594,
|
||||
"narHash": "sha256-5t3d655GqLblE+p5xN5ntkeRfZBesN7vehOWcRcU88M=",
|
||||
"lastModified": 1765267181,
|
||||
"narHash": "sha256-d3NBA9zEtBu2JFMnTBqWj7Tmi7R5OikoU2ycrdhQEws=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-index-database",
|
||||
"rev": "4194c582d0a3f440382ee00b729ea5cc5ef59754",
|
||||
"rev": "82befcf7dc77c909b0f2a09f5da910ec95c5b78f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -685,11 +353,11 @@
|
||||
},
|
||||
"nixos-hardware": {
|
||||
"locked": {
|
||||
"lastModified": 1764440730,
|
||||
"narHash": "sha256-ZlJTNLUKQRANlLDomuRWLBCH5792x+6XUJ4YdFRjtO4=",
|
||||
"lastModified": 1768736227,
|
||||
"narHash": "sha256-qgGq7CfrYKc3IBYQ7qp0Z/ZXndQVC5Bj0N8HW9mS2rM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixos-hardware",
|
||||
"rev": "9154f4569b6cdfd3c595851a6ba51bfaa472d9f3",
|
||||
"rev": "d447553bcbc6a178618d37e61648b19e744370df",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -701,27 +369,27 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1764939437,
|
||||
"narHash": "sha256-4TLFHUwXraw9Df5mXC/vCrJgb50CRr3CzUzF0Mn3CII=",
|
||||
"owner": "NixOS",
|
||||
"lastModified": 1768564909,
|
||||
"narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "00d2457e2f608b4be6fe8b470b0a36816324b0ae",
|
||||
"rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-25.05",
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1761765539,
|
||||
"narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=",
|
||||
"lastModified": 1765674936,
|
||||
"narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "719359f4562934ae99f5443f20aa06c2ffff91fc",
|
||||
"rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -747,11 +415,11 @@
|
||||
},
|
||||
"nixpkgs-unstable": {
|
||||
"locked": {
|
||||
"lastModified": 1764290847,
|
||||
"narHash": "sha256-VwPgoDgnd628GdE3KyLqTyPF1WWh0VwT5UoKygoi8sg=",
|
||||
"lastModified": 1768456270,
|
||||
"narHash": "sha256-NgaL2CCiUR6nsqUIY4yxkzz07iQUlUCany44CFv+OxY=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "cd5fedfc384cb98d9fd3827b55f4522f49efda42",
|
||||
"rev": "f4606b01b39e09065df37905a2133905246db9ed",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -763,11 +431,11 @@
|
||||
},
|
||||
"nixpkgs-unstable_2": {
|
||||
"locked": {
|
||||
"lastModified": 1764950072,
|
||||
"narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=",
|
||||
"lastModified": 1768564909,
|
||||
"narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "f61125a668a320878494449750330ca58b78c557",
|
||||
"rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -779,16 +447,16 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1754725699,
|
||||
"narHash": "sha256-iAcj9T/Y+3DBy2J0N+yF9XQQQ8IEb5swLFzs23CdP88=",
|
||||
"lastModified": 1768773494,
|
||||
"narHash": "sha256-XsM7GP3jHlephymxhDE+/TKKO1Q16phz/vQiLBGhpF4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "85dbfc7aaf52ecb755f87e577ddbe6dbbdbc1054",
|
||||
"rev": "77ef7a29d276c6d8303aece3444d61118ef71ac2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"ref": "nixos-25.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -802,11 +470,11 @@
|
||||
"systems": "systems_4"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1765210697,
|
||||
"narHash": "sha256-Gq6/MRmBhNjGdMDFvZBcnPcfuw/j/dk6N1Y9R+HSA7Q=",
|
||||
"lastModified": 1768910181,
|
||||
"narHash": "sha256-YRU0IHMzXluZxr0JDfq9jtblb4DV7MIB5wj2jYMFKQc=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixvim",
|
||||
"rev": "05c57f2e74d39e87ef0696e450b7e817dde5378d",
|
||||
"rev": "5b138edcb2f1c3ed4b29eca3658f04f0639b98b3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -815,55 +483,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"omarchy-nix": {
|
||||
"inputs": {
|
||||
"home-manager": [
|
||||
"home-manager"
|
||||
],
|
||||
"hyprland": "hyprland",
|
||||
"nix-colors": "nix-colors",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1762999930,
|
||||
"narHash": "sha256-uKyxLwiN6sD6EmRSno66y1a8oqISr1XiWxbWHoMJT7I=",
|
||||
"owner": "henrysipp",
|
||||
"repo": "omarchy-nix",
|
||||
"rev": "308e0f85a0deb820c01cfbe1b4faee1daab4da12",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "henrysipp",
|
||||
"repo": "omarchy-nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_2",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1754416808,
|
||||
"narHash": "sha256-c6yg0EQ9xVESx6HGDOCMcyRSjaTpNJP10ef+6fRcofA=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "9c52372878df6911f9afc1e2a1391f55e4dfc864",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"browser-previews": "browser-previews",
|
||||
@@ -872,12 +491,12 @@
|
||||
"ethereum-nix": "ethereum-nix",
|
||||
"home-manager": "home-manager",
|
||||
"impermanence": "impermanence",
|
||||
"nix-colors": "nix-colors",
|
||||
"nix-index-database": "nix-index-database",
|
||||
"nixos-hardware": "nixos-hardware",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"nixpkgs-unstable": "nixpkgs-unstable_2",
|
||||
"nixvim": "nixvim",
|
||||
"omarchy-nix": "omarchy-nix",
|
||||
"sops-nix": "sops-nix"
|
||||
}
|
||||
},
|
||||
@@ -888,11 +507,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1765231718,
|
||||
"narHash": "sha256-qdBzo6puTgG4G2RHG0PkADg22ZnQo1JmSVFRxrD4QM4=",
|
||||
"lastModified": 1768863606,
|
||||
"narHash": "sha256-1IHAeS8WtBiEo5XiyJBHOXMzECD6aaIOJmpQKzRRl64=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "7fd1416aba1865eddcdec5bb11339b7222c2363e",
|
||||
"rev": "c7067be8db2c09ab1884de67ef6c4f693973f4a2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -961,21 +580,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_5": {
|
||||
"locked": {
|
||||
"lastModified": 1689347949,
|
||||
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default-linux",
|
||||
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default-linux",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"treefmt-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -984,11 +588,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1762938485,
|
||||
"narHash": "sha256-AlEObg0syDl+Spi4LsZIBrjw+snSVU4T8MOeuZJUJjM=",
|
||||
"lastModified": 1768158989,
|
||||
"narHash": "sha256-67vyT1+xClLldnumAzCTBvU0jLZ1YBcf4vANRWP3+Ak=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4",
|
||||
"rev": "e96d59dff5c0d7fddb9d113ba108f03c3ef99eca",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -1014,53 +618,6 @@
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"xdph": {
|
||||
"inputs": {
|
||||
"hyprland-protocols": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"hyprland-protocols"
|
||||
],
|
||||
"hyprlang": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"hyprlang"
|
||||
],
|
||||
"hyprutils": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"hyprutils"
|
||||
],
|
||||
"hyprwayland-scanner": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"hyprwayland-scanner"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"omarchy-nix",
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1753633878,
|
||||
"narHash": "sha256-js2sLRtsOUA/aT10OCDaTjO80yplqwOIaLUqEe0nMx0=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "xdg-desktop-portal-hyprland",
|
||||
"rev": "371b96bd11ad2006ed4f21229dbd1be69bed3e8a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "xdg-desktop-portal-hyprland",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
||||
63
flake.nix
63
flake.nix
@@ -5,7 +5,7 @@
|
||||
deploy-rs.url = "github:serokell/deploy-rs";
|
||||
deploy-rs.inputs.nixpkgs.follows = "nixpkgs";
|
||||
impermanence.url = "github:nix-community/impermanence";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
|
||||
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
disko.url = "github:nix-community/disko";
|
||||
disko.inputs.nixpkgs.follows = "nixpkgs";
|
||||
@@ -14,7 +14,7 @@
|
||||
inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
};
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager/release-25.05";
|
||||
url = "github:nix-community/home-manager/release-25.11";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
nix-index-database = {
|
||||
@@ -33,11 +33,7 @@
|
||||
url = "github:nix-community/browser-previews";
|
||||
inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
};
|
||||
omarchy-nix = {
|
||||
url = "github:henrysipp/omarchy-nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.home-manager.follows = "home-manager";
|
||||
};
|
||||
nix-colors.url = "github:misterio77/nix-colors";
|
||||
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
|
||||
};
|
||||
|
||||
@@ -53,7 +49,7 @@
|
||||
impermanence,
|
||||
sops-nix,
|
||||
browser-previews,
|
||||
omarchy-nix,
|
||||
nix-colors,
|
||||
nixos-hardware,
|
||||
...
|
||||
}@inputs:
|
||||
@@ -62,13 +58,13 @@
|
||||
|
||||
overlay-unstable = final: prev: {
|
||||
unstable = import nixpkgs-unstable {
|
||||
inherit (prev) system;
|
||||
system = prev.stdenv.hostPlatform.system;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
};
|
||||
|
||||
overlay-browser-previews = final: prev: {
|
||||
browser-previews = browser-previews.packages.${prev.system};
|
||||
browser-previews = browser-previews.packages.${prev.stdenv.hostPlatform.system};
|
||||
};
|
||||
|
||||
mkHost =
|
||||
@@ -93,37 +89,27 @@
|
||||
home-manager.nixosModules.home-manager
|
||||
(
|
||||
{ lib, ... }:
|
||||
lib.mkMerge [
|
||||
{
|
||||
home-manager = {
|
||||
useGlobalPkgs = true;
|
||||
useUserPackages = true;
|
||||
users.ppetru = {
|
||||
imports = [
|
||||
inputs.nix-index-database.homeModules.nix-index
|
||||
inputs.nixvim.homeModules.nixvim
|
||||
./home
|
||||
] ++ lib.optionals (profile == "desktop") [
|
||||
omarchy-nix.homeManagerModules.default
|
||||
];
|
||||
};
|
||||
extraSpecialArgs = {
|
||||
inherit profile;
|
||||
};
|
||||
{
|
||||
home-manager = {
|
||||
useGlobalPkgs = true;
|
||||
useUserPackages = true;
|
||||
users.ppetru = {
|
||||
imports = [
|
||||
inputs.nix-index-database.homeModules.nix-index
|
||||
inputs.nixvim.homeModules.nixvim
|
||||
./home
|
||||
] ++ lib.optionals (profile == "desktop") [
|
||||
nix-colors.homeManagerModules.default
|
||||
];
|
||||
};
|
||||
}
|
||||
(lib.optionalAttrs (profile == "desktop") {
|
||||
omarchy = {
|
||||
full_name = "Petru Paler";
|
||||
email_address = "petru@paler.net";
|
||||
theme = "tokyo-night";
|
||||
monitors = [ "DP-1,preferred,auto,1.5" ];
|
||||
extraSpecialArgs = {
|
||||
inherit profile nix-colors;
|
||||
};
|
||||
})
|
||||
]
|
||||
};
|
||||
}
|
||||
)
|
||||
] ++ nixpkgs.lib.optionals (profile == "desktop") [
|
||||
omarchy-nix.nixosModules.default
|
||||
./common/desktop
|
||||
] ++ modules;
|
||||
specialArgs = {
|
||||
inherit inputs self;
|
||||
@@ -193,7 +179,8 @@
|
||||
};
|
||||
};
|
||||
alo-cloud-1 = {
|
||||
hostname = "49.13.163.72";
|
||||
hostname = "alo-cloud-1";
|
||||
#hostname = "49.13.163.72";
|
||||
profiles = {
|
||||
system = {
|
||||
user = "root";
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
{ pkgs, profile ? "cli", ... }:
|
||||
{ pkgs, lib, profile ? "cli", ... }:
|
||||
let
|
||||
# Handle both file and directory imports for profiles
|
||||
# desktop is a directory, others are files
|
||||
profilePath =
|
||||
if builtins.pathExists ./programs/${profile}/default.nix
|
||||
then ./programs/${profile}
|
||||
else ./programs/${profile}.nix;
|
||||
in
|
||||
{
|
||||
imports = [ ./programs/${profile}.nix ];
|
||||
imports = [ profilePath ];
|
||||
|
||||
home = {
|
||||
packages = (import ./packages.nix { inherit pkgs profile; }).packages;
|
||||
|
||||
@@ -1,13 +1,31 @@
|
||||
# ABOUTME: Desktop profile package list
|
||||
# ABOUTME: Extends workstation with GUI and Wayland tools
|
||||
{ pkgs }:
|
||||
let
|
||||
workstationProfile = import ./workstation.nix { inherit pkgs; };
|
||||
|
||||
# Hyprland ecosystem packages
|
||||
hyprlandPkgs = with pkgs; [
|
||||
hyprshot
|
||||
hyprpicker
|
||||
hyprsunset
|
||||
brightnessctl
|
||||
pamixer
|
||||
playerctl
|
||||
gnome-themes-extra
|
||||
pavucontrol
|
||||
wl-clip-persist
|
||||
clipse
|
||||
];
|
||||
|
||||
# Desktop GUI applications
|
||||
desktopPkgs = with pkgs; [
|
||||
browser-previews.google-chrome
|
||||
foot # Wayland-native terminal emulator
|
||||
wofi # Application launcher for Wayland
|
||||
nautilus
|
||||
blueberry
|
||||
libnotify
|
||||
];
|
||||
in
|
||||
{
|
||||
packages = workstationProfile.packages ++ desktopPkgs;
|
||||
packages = workstationProfile.packages ++ hyprlandPkgs ++ desktopPkgs;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ let
|
||||
serverProfile = import ./server.nix { inherit pkgs; };
|
||||
|
||||
cliPkgs = with pkgs; [
|
||||
ast-grep
|
||||
yq
|
||||
unstable.beads
|
||||
unstable.claude-code
|
||||
unstable.codex
|
||||
unstable.gemini-cli
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
imports = [ ./workstation.nix ];
|
||||
|
||||
# Override ghostty to use unstable version (1.2.0+) for ssh-terminfo support
|
||||
programs.ghostty.package = pkgs.unstable.ghostty;
|
||||
|
||||
wayland.windowManager.hyprland = {
|
||||
enable = true;
|
||||
settings = {
|
||||
# Remap CapsLock to Super (Mod4)
|
||||
"$mod" = "SUPER";
|
||||
|
||||
input = {
|
||||
kb_options = "caps:super";
|
||||
};
|
||||
|
||||
"$browser" = "google-chrome-stable --new-window --ozone-platform=wayland";
|
||||
};
|
||||
};
|
||||
|
||||
# Extend ghostty configuration from omarchy-nix
|
||||
programs.ghostty.settings = {
|
||||
# Automatically handle TERM compatibility for SSH (requires ghostty 1.2.0+)
|
||||
shell-integration-features = "ssh-terminfo";
|
||||
};
|
||||
}
|
||||
104
home/programs/desktop/btop.nix
Normal file
104
home/programs/desktop/btop.nix
Normal file
@@ -0,0 +1,104 @@
|
||||
# ABOUTME: Btop system monitor configuration with nix-colors theming
|
||||
# ABOUTME: Creates a custom theme file and configures btop settings
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
cfg = import ./config.nix;
|
||||
palette = config.colorScheme.palette;
|
||||
in
|
||||
{
|
||||
home.file.".config/btop/themes/${cfg.theme}.theme".text = ''
|
||||
# Main text color
|
||||
theme[main_fg]="${palette.base05}"
|
||||
|
||||
# Title color for boxes
|
||||
theme[title]="${palette.base05}"
|
||||
|
||||
# Highlight color for keyboard shortcuts
|
||||
theme[hi_fg]="${palette.base0D}"
|
||||
|
||||
# Background color of selected item in processes box
|
||||
theme[selected_bg]="${palette.base01}"
|
||||
|
||||
# Foreground color of selected item in processes box
|
||||
theme[selected_fg]="${palette.base05}"
|
||||
|
||||
# Color of inactive/disabled text
|
||||
theme[inactive_fg]="${palette.base04}"
|
||||
|
||||
# Misc colors for processes box
|
||||
theme[proc_misc]="${palette.base0D}"
|
||||
|
||||
# Box outline colors
|
||||
theme[cpu_box]="${palette.base0B}"
|
||||
theme[mem_box]="${palette.base09}"
|
||||
theme[net_box]="${palette.base0E}"
|
||||
theme[proc_box]="${palette.base0C}"
|
||||
|
||||
# Box divider line
|
||||
theme[div_line]="${palette.base04}"
|
||||
|
||||
# Temperature graph colors
|
||||
theme[temp_start]="${palette.base0B}"
|
||||
theme[temp_mid]="${palette.base0A}"
|
||||
theme[temp_end]="${palette.base08}"
|
||||
|
||||
# CPU graph colors
|
||||
theme[cpu_start]="${palette.base0B}"
|
||||
theme[cpu_mid]="${palette.base0A}"
|
||||
theme[cpu_end]="${palette.base08}"
|
||||
|
||||
# Mem/Disk meters
|
||||
theme[free_start]="${palette.base0B}"
|
||||
theme[cached_start]="${palette.base0A}"
|
||||
theme[available_start]="${palette.base09}"
|
||||
theme[used_start]="${palette.base08}"
|
||||
|
||||
# Network graph colors
|
||||
theme[download_start]="${palette.base0E}"
|
||||
theme[download_mid]="${palette.base0D}"
|
||||
theme[download_end]="${palette.base0C}"
|
||||
theme[upload_start]="${palette.base0E}"
|
||||
theme[upload_mid]="${palette.base0D}"
|
||||
theme[upload_end]="${palette.base0C}"
|
||||
'';
|
||||
|
||||
programs.btop = {
|
||||
enable = true;
|
||||
settings = {
|
||||
color_theme = cfg.theme;
|
||||
theme_background = false;
|
||||
truecolor = true;
|
||||
force_tty = false;
|
||||
vim_keys = true;
|
||||
rounded_corners = true;
|
||||
graph_symbol = "braille";
|
||||
shown_boxes = "cpu mem net proc";
|
||||
update_ms = 2000;
|
||||
proc_sorting = "cpu lazy";
|
||||
proc_colors = true;
|
||||
proc_gradient = false;
|
||||
proc_per_core = false;
|
||||
proc_mem_bytes = true;
|
||||
proc_cpu_graphs = true;
|
||||
show_uptime = true;
|
||||
check_temp = true;
|
||||
show_coretemp = true;
|
||||
temp_scale = "celsius";
|
||||
show_cpu_freq = true;
|
||||
clock_format = "%X";
|
||||
background_update = true;
|
||||
mem_graphs = true;
|
||||
show_swap = true;
|
||||
swap_disk = true;
|
||||
show_disks = true;
|
||||
only_physical = true;
|
||||
use_fstab = true;
|
||||
show_io_stat = true;
|
||||
net_auto = true;
|
||||
net_sync = true;
|
||||
show_battery = true;
|
||||
log_level = "WARNING";
|
||||
};
|
||||
};
|
||||
}
|
||||
21
home/programs/desktop/config.nix
Normal file
21
home/programs/desktop/config.nix
Normal file
@@ -0,0 +1,21 @@
|
||||
# ABOUTME: Shared configuration values for desktop environment
|
||||
# ABOUTME: Centralizes user info, theme, fonts, and display settings
|
||||
|
||||
{
|
||||
user = {
|
||||
fullName = "Petru Paler";
|
||||
email = "petru@paler.net";
|
||||
};
|
||||
|
||||
theme = "tokyo-night";
|
||||
base16Theme = "tokyo-night-dark";
|
||||
|
||||
primaryFont = "Liberation Sans 11";
|
||||
monoFont = "CaskaydiaMono Nerd Font";
|
||||
|
||||
scale = 1.5;
|
||||
monitors = [ "DP-1,preferred,auto,1.5" ];
|
||||
|
||||
# Wallpaper for tokyo-night theme
|
||||
wallpaper = "1-Pawel-Czerwinski-Abstract-Purple-Blue.jpg";
|
||||
}
|
||||
59
home/programs/desktop/default.nix
Normal file
59
home/programs/desktop/default.nix
Normal file
@@ -0,0 +1,59 @@
|
||||
# ABOUTME: Desktop environment home-manager configuration
|
||||
# ABOUTME: Imports all desktop modules and sets up nix-colors theming
|
||||
|
||||
{ config, pkgs, lib, nix-colors, ... }:
|
||||
let
|
||||
cfg = import ./config.nix;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
../workstation.nix
|
||||
./ghostty.nix
|
||||
./hyprland
|
||||
./waybar.nix
|
||||
./wofi.nix
|
||||
./mako.nix
|
||||
./hyprpaper.nix
|
||||
./hypridle.nix
|
||||
./hyprlock.nix
|
||||
./starship.nix
|
||||
./vscode.nix
|
||||
./btop.nix
|
||||
./git.nix
|
||||
];
|
||||
|
||||
# Set up nix-colors with our theme
|
||||
colorScheme = nix-colors.colorSchemes.${cfg.base16Theme};
|
||||
|
||||
# Override ghostty to use unstable version (1.2.0+) for ssh-terminfo support
|
||||
programs.ghostty.package = pkgs.unstable.ghostty;
|
||||
|
||||
# Extend ghostty configuration
|
||||
programs.ghostty.settings = {
|
||||
shell-integration-features = "ssh-terminfo";
|
||||
};
|
||||
|
||||
# GTK theme (dark for tokyo-night)
|
||||
gtk = {
|
||||
enable = true;
|
||||
theme = {
|
||||
name = "Adwaita-dark";
|
||||
package = pkgs.gnome-themes-extra;
|
||||
};
|
||||
};
|
||||
|
||||
# Enable neovim (placeholder for future config)
|
||||
programs.neovim.enable = true;
|
||||
|
||||
# direnv
|
||||
programs.direnv = {
|
||||
enable = true;
|
||||
nix-direnv.enable = true;
|
||||
};
|
||||
|
||||
# zoxide (directory jumping)
|
||||
programs.zoxide = {
|
||||
enable = true;
|
||||
enableBashIntegration = true;
|
||||
};
|
||||
}
|
||||
60
home/programs/desktop/ghostty.nix
Normal file
60
home/programs/desktop/ghostty.nix
Normal file
@@ -0,0 +1,60 @@
|
||||
# ABOUTME: Ghostty terminal emulator configuration with nix-colors theming
|
||||
# ABOUTME: Creates a custom color theme from the nix-colors palette
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
cfg = import ./config.nix;
|
||||
palette = config.colorScheme.palette;
|
||||
in
|
||||
{
|
||||
programs.ghostty = {
|
||||
enable = true;
|
||||
settings = {
|
||||
window-padding-x = 14;
|
||||
window-padding-y = 14;
|
||||
background-opacity = 0.95;
|
||||
window-decoration = "none";
|
||||
|
||||
font-family = cfg.monoFont;
|
||||
font-size = 12;
|
||||
|
||||
theme = "desktop-theme";
|
||||
keybind = [
|
||||
"ctrl+k=reset"
|
||||
];
|
||||
};
|
||||
themes = {
|
||||
desktop-theme = {
|
||||
background = "#${palette.base00}";
|
||||
foreground = "#${palette.base05}";
|
||||
|
||||
selection-background = "#${palette.base02}";
|
||||
selection-foreground = "#${palette.base00}";
|
||||
palette = [
|
||||
"0=#${palette.base00}"
|
||||
"1=#${palette.base08}"
|
||||
"2=#${palette.base0B}"
|
||||
"3=#${palette.base0A}"
|
||||
"4=#${palette.base0D}"
|
||||
"5=#${palette.base0E}"
|
||||
"6=#${palette.base0C}"
|
||||
"7=#${palette.base05}"
|
||||
"8=#${palette.base03}"
|
||||
"9=#${palette.base08}"
|
||||
"10=#${palette.base0B}"
|
||||
"11=#${palette.base0A}"
|
||||
"12=#${palette.base0D}"
|
||||
"13=#${palette.base0E}"
|
||||
"14=#${palette.base0C}"
|
||||
"15=#${palette.base07}"
|
||||
"16=#${palette.base09}"
|
||||
"17=#${palette.base0F}"
|
||||
"18=#${palette.base01}"
|
||||
"19=#${palette.base02}"
|
||||
"20=#${palette.base04}"
|
||||
"21=#${palette.base06}"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
24
home/programs/desktop/git.nix
Normal file
24
home/programs/desktop/git.nix
Normal file
@@ -0,0 +1,24 @@
|
||||
# ABOUTME: Git and GitHub CLI configuration
|
||||
# ABOUTME: Sets up git with user info and gh CLI integration
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
cfg = import ./config.nix;
|
||||
in
|
||||
{
|
||||
programs.git = {
|
||||
enable = true;
|
||||
settings = {
|
||||
user.name = cfg.user.fullName;
|
||||
user.email = cfg.user.email;
|
||||
credential.helper = "store";
|
||||
};
|
||||
};
|
||||
|
||||
programs.gh = {
|
||||
enable = true;
|
||||
gitCredentialHelper = {
|
||||
enable = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
27
home/programs/desktop/hypridle.nix
Normal file
27
home/programs/desktop/hypridle.nix
Normal file
@@ -0,0 +1,27 @@
|
||||
# ABOUTME: Hypridle idle daemon configuration
|
||||
# ABOUTME: Handles screen locking and DPMS after idle timeout
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
services.hypridle = {
|
||||
enable = true;
|
||||
settings = {
|
||||
general = {
|
||||
lock_cmd = "pidof hyprlock || hyprlock";
|
||||
before_sleep_cmd = "loginctl lock-session";
|
||||
after_sleep_cmd = "hyprctl dispatch dpms on";
|
||||
};
|
||||
listener = [
|
||||
{
|
||||
timeout = 300;
|
||||
on-timeout = "loginctl lock-session";
|
||||
}
|
||||
{
|
||||
timeout = 330;
|
||||
on-timeout = "hyprctl dispatch dpms off";
|
||||
on-resume = "hyprctl dispatch dpms on && brightnessctl -r";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
17
home/programs/desktop/hyprland/autostart.nix
Normal file
17
home/programs/desktop/hyprland/autostart.nix
Normal file
@@ -0,0 +1,17 @@
|
||||
# ABOUTME: Hyprland autostart configuration
|
||||
# ABOUTME: Defines programs to run at Hyprland startup
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
wayland.windowManager.hyprland.settings = {
|
||||
exec-once = [
|
||||
"hyprsunset"
|
||||
"systemctl --user start hyprpolkitagent"
|
||||
"wl-clip-persist --clipboard regular & clipse -listen"
|
||||
];
|
||||
|
||||
exec = [
|
||||
"pkill -SIGUSR2 waybar || waybar"
|
||||
];
|
||||
};
|
||||
}
|
||||
99
home/programs/desktop/hyprland/bindings.nix
Normal file
99
home/programs/desktop/hyprland/bindings.nix
Normal file
@@ -0,0 +1,99 @@
|
||||
# ABOUTME: Hyprland keybindings configuration
|
||||
# ABOUTME: Defines keyboard and mouse shortcuts for window management
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
wayland.windowManager.hyprland.settings = {
|
||||
bind = [
|
||||
# Application launchers
|
||||
"$mod, Space, exec, $menu"
|
||||
"$mod, Return, exec, $terminal"
|
||||
"$mod, E, exec, $fileManager"
|
||||
"$mod, B, exec, $browser"
|
||||
|
||||
# Window management
|
||||
"$mod, W, killactive,"
|
||||
"$mod, BackSpace, killactive,"
|
||||
"$mod, V, togglefloating,"
|
||||
"$mod SHIFT, equal, fullscreen,"
|
||||
"$mod, J, togglesplit,"
|
||||
"$mod, P, pseudo,"
|
||||
|
||||
# Focus navigation
|
||||
"$mod, left, movefocus, l"
|
||||
"$mod, right, movefocus, r"
|
||||
"$mod, up, movefocus, u"
|
||||
"$mod, down, movefocus, d"
|
||||
|
||||
# Workspace switching
|
||||
"$mod, 1, workspace, 1"
|
||||
"$mod, 2, workspace, 2"
|
||||
"$mod, 3, workspace, 3"
|
||||
"$mod, 4, workspace, 4"
|
||||
"$mod, 5, workspace, 5"
|
||||
"$mod, 6, workspace, 6"
|
||||
"$mod, 7, workspace, 7"
|
||||
"$mod, 8, workspace, 8"
|
||||
"$mod, 9, workspace, 9"
|
||||
"$mod, 0, workspace, 10"
|
||||
|
||||
# Move window to workspace
|
||||
"$mod SHIFT, 1, movetoworkspace, 1"
|
||||
"$mod SHIFT, 2, movetoworkspace, 2"
|
||||
"$mod SHIFT, 3, movetoworkspace, 3"
|
||||
"$mod SHIFT, 4, movetoworkspace, 4"
|
||||
"$mod SHIFT, 5, movetoworkspace, 5"
|
||||
"$mod SHIFT, 6, movetoworkspace, 6"
|
||||
"$mod SHIFT, 7, movetoworkspace, 7"
|
||||
"$mod SHIFT, 8, movetoworkspace, 8"
|
||||
"$mod SHIFT, 9, movetoworkspace, 9"
|
||||
"$mod SHIFT, 0, movetoworkspace, 10"
|
||||
|
||||
# Workspace navigation
|
||||
"$mod, comma, workspace, m-1"
|
||||
"$mod, period, workspace, m+1"
|
||||
|
||||
# Window resize
|
||||
"$mod, minus, splitratio, -0.1"
|
||||
"$mod, equal, splitratio, +0.1"
|
||||
|
||||
# Lock screen
|
||||
"$mod, Escape, exec, loginctl lock-session"
|
||||
|
||||
# Screenshots
|
||||
", Print, exec, hyprshot -m region"
|
||||
"SHIFT, Print, exec, hyprshot -m window"
|
||||
"CTRL, Print, exec, hyprshot -m output"
|
||||
|
||||
# Color picker
|
||||
"$mod SHIFT, C, exec, hyprpicker -a"
|
||||
|
||||
# Clipboard manager
|
||||
"$mod SHIFT, V, exec, ghostty --class=clipse -e clipse"
|
||||
];
|
||||
|
||||
bindm = [
|
||||
# Mouse bindings for window management
|
||||
"$mod, mouse:272, movewindow"
|
||||
"$mod, mouse:273, resizewindow"
|
||||
];
|
||||
|
||||
binde = [
|
||||
# Repeatable bindings for media controls
|
||||
", XF86AudioRaiseVolume, exec, wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+"
|
||||
", XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
|
||||
", XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
|
||||
|
||||
# Brightness controls
|
||||
", XF86MonBrightnessUp, exec, brightnessctl s +5%"
|
||||
", XF86MonBrightnessDown, exec, brightnessctl s 5%-"
|
||||
];
|
||||
|
||||
bindl = [
|
||||
# Media player controls
|
||||
", XF86AudioNext, exec, playerctl next"
|
||||
", XF86AudioPrev, exec, playerctl previous"
|
||||
", XF86AudioPlay, exec, playerctl play-pause"
|
||||
];
|
||||
};
|
||||
}
|
||||
39
home/programs/desktop/hyprland/default.nix
Normal file
39
home/programs/desktop/hyprland/default.nix
Normal file
@@ -0,0 +1,39 @@
|
||||
# ABOUTME: Hyprland window manager home-manager configuration
|
||||
# ABOUTME: Imports all hyprland submodules for complete WM setup
|
||||
|
||||
{ config, pkgs, lib, ... }:
|
||||
let
|
||||
cfg = import ../config.nix;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./bindings.nix
|
||||
./autostart.nix
|
||||
./input.nix
|
||||
./looknfeel.nix
|
||||
./windows.nix
|
||||
./envs.nix
|
||||
];
|
||||
|
||||
wayland.windowManager.hyprland = {
|
||||
enable = true;
|
||||
systemd.enable = true;
|
||||
|
||||
settings = {
|
||||
# Monitor configuration
|
||||
monitor = cfg.monitors;
|
||||
|
||||
# Default applications
|
||||
"$terminal" = "ghostty";
|
||||
"$fileManager" = "nautilus";
|
||||
"$browser" = "google-chrome-stable --new-window --ozone-platform=wayland";
|
||||
"$menu" = "wofi --show drun";
|
||||
|
||||
# Mod key
|
||||
"$mod" = "SUPER";
|
||||
};
|
||||
};
|
||||
|
||||
# Hyprland polkit agent for privilege escalation
|
||||
services.hyprpolkitagent.enable = true;
|
||||
}
|
||||
56
home/programs/desktop/hyprland/envs.nix
Normal file
56
home/programs/desktop/hyprland/envs.nix
Normal file
@@ -0,0 +1,56 @@
|
||||
# ABOUTME: Hyprland environment variables configuration
|
||||
# ABOUTME: Sets up Wayland, cursor, and application environment variables
|
||||
|
||||
{ config, lib, pkgs, osConfig ? { }, ... }:
|
||||
let
|
||||
cfg = import ../config.nix;
|
||||
hasNvidiaDrivers = builtins.elem "nvidia" (osConfig.services.xserver.videoDrivers or []);
|
||||
nvidiaEnv = [
|
||||
"NVD_BACKEND,direct"
|
||||
"LIBVA_DRIVER_NAME,nvidia"
|
||||
"__GLX_VENDOR_LIBRARY_NAME,nvidia"
|
||||
];
|
||||
in
|
||||
{
|
||||
wayland.windowManager.hyprland.settings = {
|
||||
env = (lib.optionals hasNvidiaDrivers nvidiaEnv) ++ [
|
||||
"GDK_SCALE,${toString cfg.scale}"
|
||||
|
||||
# Cursor size and theme
|
||||
"XCURSOR_SIZE,24"
|
||||
"HYPRCURSOR_SIZE,24"
|
||||
"XCURSOR_THEME,Adwaita"
|
||||
"HYPRCURSOR_THEME,Adwaita"
|
||||
|
||||
# Force Wayland for applications
|
||||
"GDK_BACKEND,wayland"
|
||||
"QT_QPA_PLATFORM,wayland"
|
||||
"QT_STYLE_OVERRIDE,kvantum"
|
||||
"SDL_VIDEODRIVER,wayland"
|
||||
"MOZ_ENABLE_WAYLAND,1"
|
||||
"ELECTRON_OZONE_PLATFORM_HINT,wayland"
|
||||
"OZONE_PLATFORM,wayland"
|
||||
|
||||
# Chromium Wayland support
|
||||
"CHROMIUM_FLAGS,\"--enable-features=UseOzonePlatform --ozone-platform=wayland --gtk-version=4\""
|
||||
|
||||
# Make .desktop files available for wofi
|
||||
"XDG_DATA_DIRS,$XDG_DATA_DIRS:$HOME/.nix-profile/share:/nix/var/nix/profiles/default/share"
|
||||
|
||||
# XCompose support
|
||||
"XCOMPOSEFILE,~/.XCompose"
|
||||
"EDITOR,nvim"
|
||||
|
||||
# GTK dark theme
|
||||
"GTK_THEME,Adwaita:dark"
|
||||
];
|
||||
|
||||
xwayland = {
|
||||
force_zero_scaling = true;
|
||||
};
|
||||
|
||||
ecosystem = {
|
||||
no_update_news = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
19
home/programs/desktop/hyprland/input.nix
Normal file
19
home/programs/desktop/hyprland/input.nix
Normal file
@@ -0,0 +1,19 @@
|
||||
# ABOUTME: Hyprland input and gesture configuration
|
||||
# ABOUTME: Keyboard layout, mouse settings, and touchpad behavior
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
wayland.windowManager.hyprland.settings = {
|
||||
input = lib.mkDefault {
|
||||
kb_layout = "us";
|
||||
kb_options = "caps:super,compose:ralt";
|
||||
|
||||
follow_mouse = 1;
|
||||
sensitivity = 0;
|
||||
|
||||
touchpad = {
|
||||
natural_scroll = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
89
home/programs/desktop/hyprland/looknfeel.nix
Normal file
89
home/programs/desktop/hyprland/looknfeel.nix
Normal file
@@ -0,0 +1,89 @@
|
||||
# ABOUTME: Hyprland visual appearance configuration
|
||||
# ABOUTME: Window gaps, borders, animations, and decorations with nix-colors theming
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
palette = config.colorScheme.palette;
|
||||
hexToRgba = hex: alpha: "rgba(${hex}${alpha})";
|
||||
inactiveBorder = hexToRgba palette.base09 "aa";
|
||||
activeBorder = hexToRgba palette.base0D "aa";
|
||||
in
|
||||
{
|
||||
wayland.windowManager.hyprland.settings = {
|
||||
general = {
|
||||
gaps_in = 5;
|
||||
gaps_out = 10;
|
||||
border_size = 2;
|
||||
|
||||
"col.active_border" = activeBorder;
|
||||
"col.inactive_border" = inactiveBorder;
|
||||
|
||||
resize_on_border = false;
|
||||
allow_tearing = false;
|
||||
layout = "dwindle";
|
||||
};
|
||||
|
||||
decoration = {
|
||||
rounding = 4;
|
||||
|
||||
shadow = {
|
||||
enabled = false;
|
||||
range = 30;
|
||||
render_power = 3;
|
||||
ignore_window = true;
|
||||
color = "rgba(00000045)";
|
||||
};
|
||||
|
||||
blur = {
|
||||
enabled = true;
|
||||
size = 5;
|
||||
passes = 2;
|
||||
vibrancy = 0.1696;
|
||||
};
|
||||
};
|
||||
|
||||
animations = {
|
||||
enabled = true;
|
||||
|
||||
bezier = [
|
||||
"easeOutQuint,0.23,1,0.32,1"
|
||||
"easeInOutCubic,0.65,0.05,0.36,1"
|
||||
"linear,0,0,1,1"
|
||||
"almostLinear,0.5,0.5,0.75,1.0"
|
||||
"quick,0.15,0,0.1,1"
|
||||
];
|
||||
|
||||
animation = [
|
||||
"global, 1, 10, default"
|
||||
"border, 1, 5.39, easeOutQuint"
|
||||
"windows, 1, 4.79, easeOutQuint"
|
||||
"windowsIn, 1, 4.1, easeOutQuint, popin 87%"
|
||||
"windowsOut, 1, 1.49, linear, popin 87%"
|
||||
"fadeIn, 1, 1.73, almostLinear"
|
||||
"fadeOut, 1, 1.46, almostLinear"
|
||||
"fade, 1, 3.03, quick"
|
||||
"layers, 1, 3.81, easeOutQuint"
|
||||
"layersIn, 1, 4, easeOutQuint, fade"
|
||||
"layersOut, 1, 1.5, linear, fade"
|
||||
"fadeLayersIn, 1, 1.79, almostLinear"
|
||||
"fadeLayersOut, 1, 1.39, almostLinear"
|
||||
"workspaces, 0, 0, ease"
|
||||
];
|
||||
};
|
||||
|
||||
dwindle = {
|
||||
pseudotile = true;
|
||||
preserve_split = true;
|
||||
force_split = 2;
|
||||
};
|
||||
|
||||
master = {
|
||||
new_status = "master";
|
||||
};
|
||||
|
||||
misc = {
|
||||
disable_hyprland_logo = true;
|
||||
disable_splash_rendering = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
31
home/programs/desktop/hyprland/windows.nix
Normal file
31
home/programs/desktop/hyprland/windows.nix
Normal file
@@ -0,0 +1,31 @@
|
||||
# ABOUTME: Hyprland window rules configuration
|
||||
# ABOUTME: Defines per-application window behavior and layer rules
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
wayland.windowManager.hyprland.settings = {
|
||||
windowrule = [
|
||||
"suppressevent maximize, class:.*"
|
||||
"tile, class:^(chromium)$"
|
||||
"float, class:^(org.pulseaudio.pavucontrol|blueberry.py)$"
|
||||
"float, class:^(steam)$"
|
||||
"fullscreen, class:^(com.libretro.RetroArch)$"
|
||||
"opacity 0.97 0.9, class:.*"
|
||||
"opacity 1 1, class:^(chromium|google-chrome|google-chrome-unstable)$, title:.*Youtube.*"
|
||||
"opacity 1 0.97, class:^(chromium|google-chrome|google-chrome-unstable)$"
|
||||
"opacity 0.97 0.9, initialClass:^(chrome-.*-Default)$"
|
||||
"opacity 1 1, initialClass:^(chrome-youtube.*-Default)$"
|
||||
"opacity 1 1, class:^(zoom|vlc|org.kde.kdenlive|com.obsproject.Studio)$"
|
||||
"opacity 1 1, class:^(com.libretro.RetroArch|steam)$"
|
||||
"nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0"
|
||||
"float, class:(clipse)"
|
||||
"size 622 652, class:(clipse)"
|
||||
"stayfocused, class:(clipse)"
|
||||
];
|
||||
|
||||
layerrule = [
|
||||
"blur,wofi"
|
||||
"blur,waybar"
|
||||
];
|
||||
};
|
||||
}
|
||||
70
home/programs/desktop/hyprlock.nix
Normal file
70
home/programs/desktop/hyprlock.nix
Normal file
@@ -0,0 +1,70 @@
|
||||
# ABOUTME: Hyprlock screen locker configuration with nix-colors theming
|
||||
# ABOUTME: Configures lock screen appearance with fingerprint support
|
||||
|
||||
{ config, pkgs, nix-colors, ... }:
|
||||
let
|
||||
cfg = import ./config.nix;
|
||||
palette = config.colorScheme.palette;
|
||||
convert = nix-colors.lib.conversions.hexToRGBString;
|
||||
wallpaperPath = "~/Pictures/Wallpapers/${cfg.wallpaper}";
|
||||
|
||||
backgroundRgb = "rgba(${convert ", " palette.base00}, 0.8)";
|
||||
surfaceRgb = "rgb(${convert ", " palette.base02})";
|
||||
foregroundRgb = "rgb(${convert ", " palette.base05})";
|
||||
foregroundMutedRgb = "rgb(${convert ", " palette.base04})";
|
||||
in
|
||||
{
|
||||
programs.hyprlock = {
|
||||
enable = true;
|
||||
settings = {
|
||||
general = {
|
||||
disable_loading_bar = true;
|
||||
no_fade_in = false;
|
||||
};
|
||||
auth = {
|
||||
fingerprint.enabled = true;
|
||||
};
|
||||
background = {
|
||||
monitor = "";
|
||||
path = wallpaperPath;
|
||||
};
|
||||
|
||||
input-field = {
|
||||
monitor = "";
|
||||
size = "600, 100";
|
||||
position = "0, 0";
|
||||
halign = "center";
|
||||
valign = "center";
|
||||
|
||||
inner_color = surfaceRgb;
|
||||
outer_color = foregroundRgb;
|
||||
outline_thickness = 4;
|
||||
|
||||
font_family = cfg.monoFont;
|
||||
font_size = 32;
|
||||
font_color = foregroundRgb;
|
||||
|
||||
placeholder_color = foregroundMutedRgb;
|
||||
placeholder_text = " Enter Password ";
|
||||
check_color = "rgba(131, 192, 146, 1.0)";
|
||||
fail_text = "Wrong";
|
||||
|
||||
rounding = 0;
|
||||
shadow_passes = 0;
|
||||
fade_on_empty = false;
|
||||
};
|
||||
|
||||
label = {
|
||||
monitor = "";
|
||||
text = "$FPRINTPROMPT";
|
||||
text_align = "center";
|
||||
color = "rgb(211, 198, 170)";
|
||||
font_size = 24;
|
||||
font_family = cfg.monoFont;
|
||||
position = "0, -100";
|
||||
halign = "center";
|
||||
valign = "center";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
23
home/programs/desktop/hyprpaper.nix
Normal file
23
home/programs/desktop/hyprpaper.nix
Normal file
@@ -0,0 +1,23 @@
|
||||
# ABOUTME: Hyprpaper wallpaper service configuration
|
||||
# ABOUTME: Sets up wallpaper based on theme selection
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
cfg = import ./config.nix;
|
||||
wallpaperPath = "~/Pictures/Wallpapers/${cfg.wallpaper}";
|
||||
in
|
||||
{
|
||||
# Copy wallpapers to Pictures directory
|
||||
home.file."Pictures/Wallpapers" = {
|
||||
source = ../../../common/desktop/assets/wallpapers;
|
||||
recursive = true;
|
||||
};
|
||||
|
||||
services.hyprpaper = {
|
||||
enable = true;
|
||||
settings = {
|
||||
preload = [ wallpaperPath ];
|
||||
wallpaper = [ ",${wallpaperPath}" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
41
home/programs/desktop/mako.nix
Normal file
41
home/programs/desktop/mako.nix
Normal file
@@ -0,0 +1,41 @@
|
||||
# ABOUTME: Mako notification daemon configuration with nix-colors theming
|
||||
# ABOUTME: Configures notification appearance and behavior
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
palette = config.colorScheme.palette;
|
||||
in
|
||||
{
|
||||
services.mako = {
|
||||
enable = true;
|
||||
|
||||
settings = {
|
||||
background-color = "#${palette.base00}";
|
||||
text-color = "#${palette.base05}";
|
||||
border-color = "#${palette.base04}";
|
||||
progress-color = "#${palette.base0D}";
|
||||
|
||||
width = 420;
|
||||
height = 110;
|
||||
padding = "10";
|
||||
margin = "10";
|
||||
border-size = 2;
|
||||
border-radius = 0;
|
||||
|
||||
anchor = "top-right";
|
||||
layer = "overlay";
|
||||
|
||||
default-timeout = 5000;
|
||||
ignore-timeout = false;
|
||||
max-visible = 5;
|
||||
sort = "-time";
|
||||
|
||||
group-by = "app-name";
|
||||
|
||||
actions = true;
|
||||
|
||||
format = "<b>%s</b>\\n%b";
|
||||
markup = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
7
home/programs/desktop/starship.nix
Normal file
7
home/programs/desktop/starship.nix
Normal file
@@ -0,0 +1,7 @@
|
||||
# ABOUTME: Starship prompt configuration
|
||||
# ABOUTME: Enables the cross-shell prompt with default settings
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
programs.starship.enable = true;
|
||||
}
|
||||
32
home/programs/desktop/themes.nix
Normal file
32
home/programs/desktop/themes.nix
Normal file
@@ -0,0 +1,32 @@
|
||||
# ABOUTME: Theme definitions mapping theme names to base16 and VSCode themes
|
||||
# ABOUTME: Used by vscode and other apps that need theme name mapping
|
||||
|
||||
{
|
||||
"tokyo-night" = {
|
||||
base16Theme = "tokyo-night-dark";
|
||||
vscodeTheme = "Tokyo Night";
|
||||
};
|
||||
"catppuccin-macchiato" = {
|
||||
vscodeTheme = "Catppuccin Macchiato";
|
||||
};
|
||||
"kanagawa" = {
|
||||
base16Theme = "kanagawa";
|
||||
vscodeTheme = "Kanagawa";
|
||||
};
|
||||
"everforest" = {
|
||||
base16Theme = "everforest";
|
||||
vscodeTheme = "Everforest Dark";
|
||||
};
|
||||
"nord" = {
|
||||
base16Theme = "nord";
|
||||
vscodeTheme = "Nord";
|
||||
};
|
||||
"gruvbox" = {
|
||||
base16Theme = "gruvbox-dark-hard";
|
||||
vscodeTheme = "Gruvbox Dark Hard";
|
||||
};
|
||||
"gruvbox-light" = {
|
||||
base16Theme = "gruvbox-light-medium";
|
||||
vscodeTheme = "Gruvbox Light Medium";
|
||||
};
|
||||
}
|
||||
54
home/programs/desktop/vscode.nix
Normal file
54
home/programs/desktop/vscode.nix
Normal file
@@ -0,0 +1,54 @@
|
||||
# ABOUTME: VSCode configuration with theme extensions
|
||||
# ABOUTME: Installs vim keybindings and color scheme extensions
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
cfg = import ./config.nix;
|
||||
themes = import ./themes.nix;
|
||||
theme = themes.${cfg.theme};
|
||||
in
|
||||
{
|
||||
programs.vscode = {
|
||||
enable = true;
|
||||
profiles.default = {
|
||||
extensions =
|
||||
with pkgs.vscode-extensions;
|
||||
[
|
||||
bbenoist.nix
|
||||
vscodevim.vim
|
||||
]
|
||||
++ pkgs.vscode-utils.extensionsFromVscodeMarketplace [
|
||||
{
|
||||
name = "everforest";
|
||||
publisher = "sainnhe";
|
||||
version = "0.3.0";
|
||||
sha256 = "sha256-nZirzVvM160ZTpBLTimL2X35sIGy5j2LQOok7a2Yc7U=";
|
||||
}
|
||||
{
|
||||
name = "tokyo-night";
|
||||
publisher = "enkia";
|
||||
version = "1.1.2";
|
||||
sha256 = "sha256-oW0bkLKimpcjzxTb/yjShagjyVTUFEg198oPbY5J2hM=";
|
||||
}
|
||||
{
|
||||
name = "kanagawa";
|
||||
publisher = "qufiwefefwoyn";
|
||||
version = "1.5.1";
|
||||
sha256 = "sha256-AGGioXcK/fjPaFaWk2jqLxovUNR59gwpotcSpGNbj1c=";
|
||||
}
|
||||
{
|
||||
name = "nord-visual-studio-code";
|
||||
publisher = "arcticicestudio";
|
||||
version = "0.19.0";
|
||||
sha256 = "sha256-awbqFv6YuYI0tzM/QbHRTUl4B2vNUdy52F4nPmv+dRU=";
|
||||
}
|
||||
{
|
||||
name = "gruvbox";
|
||||
publisher = "jdinhlife";
|
||||
version = "1.28.0";
|
||||
sha256 = "sha256-XwQzbbZU6MfYcT50/0YgQp8UaOeQskEvEQPZXG72lLk=";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
182
home/programs/desktop/waybar.nix
Normal file
182
home/programs/desktop/waybar.nix
Normal file
@@ -0,0 +1,182 @@
|
||||
# ABOUTME: Waybar status bar configuration with nix-colors theming
|
||||
# ABOUTME: Configures system tray, workspaces, and status indicators
|
||||
|
||||
{ config, pkgs, nix-colors, ... }:
|
||||
let
|
||||
palette = config.colorScheme.palette;
|
||||
convert = nix-colors.lib.conversions.hexToRGBString;
|
||||
backgroundRgb = "rgb(${convert ", " palette.base00})";
|
||||
foregroundRgb = "rgb(${convert ", " palette.base05})";
|
||||
in
|
||||
{
|
||||
home.file.".config/waybar/theme.css".text = ''
|
||||
@define-color background ${backgroundRgb};
|
||||
* {
|
||||
color: ${foregroundRgb};
|
||||
}
|
||||
|
||||
window#waybar {
|
||||
background-color: ${backgroundRgb};
|
||||
}
|
||||
'';
|
||||
|
||||
home.file.".config/waybar/style.css".text = ''
|
||||
@import "./theme.css";
|
||||
* {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
min-height: 0;
|
||||
font-family: CaskaydiaMono Nerd Font;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#workspaces {
|
||||
margin-left: 7px;
|
||||
}
|
||||
|
||||
#workspaces button {
|
||||
all: initial;
|
||||
padding: 2px 6px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
#custom-dropbox,
|
||||
#cpu,
|
||||
#power-profiles-daemon,
|
||||
#battery,
|
||||
#network,
|
||||
#bluetooth,
|
||||
#wireplumber,
|
||||
#tray,
|
||||
#clock {
|
||||
background-color: transparent;
|
||||
min-width: 12px;
|
||||
margin-right: 13px;
|
||||
}
|
||||
|
||||
tooltip {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
tooltip label {
|
||||
padding: 2px;
|
||||
}
|
||||
'';
|
||||
|
||||
programs.waybar = {
|
||||
enable = true;
|
||||
settings = [
|
||||
{
|
||||
layer = "top";
|
||||
position = "top";
|
||||
spacing = 0;
|
||||
height = 26;
|
||||
modules-left = [ "hyprland/workspaces" ];
|
||||
modules-center = [ "clock" ];
|
||||
modules-right = [
|
||||
"tray"
|
||||
"bluetooth"
|
||||
"network"
|
||||
"wireplumber"
|
||||
"cpu"
|
||||
"power-profiles-daemon"
|
||||
"battery"
|
||||
];
|
||||
"hyprland/workspaces" = {
|
||||
on-click = "activate";
|
||||
format = "{icon}";
|
||||
format-icons = {
|
||||
default = "";
|
||||
"1" = "1";
|
||||
"2" = "2";
|
||||
"3" = "3";
|
||||
"4" = "4";
|
||||
"5" = "5";
|
||||
"6" = "6";
|
||||
"7" = "7";
|
||||
"8" = "8";
|
||||
"9" = "9";
|
||||
active = "";
|
||||
};
|
||||
persistent-workspaces = {
|
||||
"1" = [ ];
|
||||
"2" = [ ];
|
||||
"3" = [ ];
|
||||
"4" = [ ];
|
||||
"5" = [ ];
|
||||
};
|
||||
};
|
||||
cpu = {
|
||||
interval = 5;
|
||||
format = "";
|
||||
on-click = "ghostty -e btop";
|
||||
};
|
||||
clock = {
|
||||
format = "{:%A %I:%M %p}";
|
||||
format-alt = "{:%d %B W%V %Y}";
|
||||
tooltip = false;
|
||||
};
|
||||
network = {
|
||||
format-icons = [ "" "" "" "" "" ];
|
||||
format = "{icon}";
|
||||
format-wifi = "{icon}";
|
||||
format-ethernet = "";
|
||||
format-disconnected = "";
|
||||
tooltip-format-wifi = "{essid} ({frequency} GHz)\n⇣{bandwidthDownBytes} ⇡{bandwidthUpBytes}";
|
||||
tooltip-format-ethernet = "⇣{bandwidthDownBytes} ⇡{bandwidthUpBytes}";
|
||||
tooltip-format-disconnected = "Disconnected";
|
||||
interval = 3;
|
||||
nospacing = 1;
|
||||
on-click = "ghostty -e nmcli";
|
||||
};
|
||||
battery = {
|
||||
interval = 5;
|
||||
format = "{capacity}% {icon}";
|
||||
format-discharging = "{icon}";
|
||||
format-charging = "{icon}";
|
||||
format-plugged = "";
|
||||
format-icons = {
|
||||
charging = [ "" "" "" "" "" "" "" "" "" "" ];
|
||||
default = [ "" "" "" "" "" "" "" "" "" "" ];
|
||||
};
|
||||
format-full = "Charged ";
|
||||
tooltip-format-discharging = "{power:>1.0f}W↓ {capacity}%";
|
||||
tooltip-format-charging = "{power:>1.0f}W↑ {capacity}%";
|
||||
states = {
|
||||
warning = 20;
|
||||
critical = 10;
|
||||
};
|
||||
};
|
||||
bluetooth = {
|
||||
format = "";
|
||||
format-disabled = "";
|
||||
format-connected = "";
|
||||
tooltip-format = "Devices connected: {num_connections}";
|
||||
on-click = "blueberry";
|
||||
};
|
||||
wireplumber = {
|
||||
format = "";
|
||||
format-muted = "";
|
||||
scroll-step = 5;
|
||||
on-click = "pavucontrol";
|
||||
tooltip-format = "Playing at {volume}%";
|
||||
on-click-right = "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle";
|
||||
max-volume = 150;
|
||||
};
|
||||
tray = {
|
||||
spacing = 13;
|
||||
};
|
||||
power-profiles-daemon = {
|
||||
format = "{icon}";
|
||||
tooltip-format = "Power profile: {profile}";
|
||||
tooltip = true;
|
||||
format-icons = {
|
||||
power-saver = "";
|
||||
balanced = "";
|
||||
performance = "";
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
102
home/programs/desktop/wofi.nix
Normal file
102
home/programs/desktop/wofi.nix
Normal file
@@ -0,0 +1,102 @@
|
||||
# ABOUTME: Wofi application launcher configuration with nix-colors theming
|
||||
# ABOUTME: Configures the drun launcher appearance and behavior
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
cfg = import ./config.nix;
|
||||
palette = config.colorScheme.palette;
|
||||
in
|
||||
{
|
||||
home.file.".config/wofi/style.css".text = ''
|
||||
* {
|
||||
font-family: '${cfg.monoFont}', monospace;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
window {
|
||||
margin: 0px;
|
||||
padding: 20px;
|
||||
background-color: #${palette.base00};
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
#inner-box {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background-color: #${palette.base00};
|
||||
}
|
||||
|
||||
#outer-box {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
border: none;
|
||||
background-color: #${palette.base00};
|
||||
}
|
||||
|
||||
#scroll {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background-color: #${palette.base00};
|
||||
}
|
||||
|
||||
#input {
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
border: none;
|
||||
background-color: #${palette.base00};
|
||||
color: @text;
|
||||
}
|
||||
|
||||
#input:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#text {
|
||||
margin: 5px;
|
||||
border: none;
|
||||
color: #${palette.base06};
|
||||
}
|
||||
|
||||
#entry {
|
||||
background-color: #${palette.base00};
|
||||
}
|
||||
|
||||
#entry:selected {
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#entry:selected #text {
|
||||
color: #${palette.base02};
|
||||
}
|
||||
|
||||
#entry image {
|
||||
-gtk-icon-transform: scale(0.7);
|
||||
}
|
||||
'';
|
||||
|
||||
programs.wofi = {
|
||||
enable = true;
|
||||
settings = {
|
||||
width = 600;
|
||||
height = 350;
|
||||
location = "center";
|
||||
show = "drun";
|
||||
prompt = "Search...";
|
||||
filter_rate = 100;
|
||||
allow_markup = true;
|
||||
no_actions = true;
|
||||
halign = "fill";
|
||||
orientation = "vertical";
|
||||
content_halign = "fill";
|
||||
insensitive = true;
|
||||
allow_images = true;
|
||||
image_size = 40;
|
||||
gtk_dark = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -347,8 +347,12 @@
|
||||
|
||||
git = {
|
||||
enable = true;
|
||||
userEmail = "petru@paler.net";
|
||||
userName = "Petru Paler";
|
||||
settings = {
|
||||
user = {
|
||||
email = "petru@paler.net";
|
||||
name = "Petru Paler";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
home-manager = {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
imports = [
|
||||
../../common/encrypted-btrfs-layout.nix
|
||||
../../common/global
|
||||
../../common/desktop-node.nix # Hyprland + GUI environment
|
||||
# Desktop environment is imported via flake.nix for desktop profile
|
||||
../../common/cluster-member.nix # Consul + storage clients
|
||||
../../common/cluster-tools.nix # Nomad CLI (no service)
|
||||
./hardware.nix
|
||||
@@ -24,10 +24,32 @@
|
||||
boot.kernelParams = [ "consoleblank=300" "nmi_watchdog=1" ];
|
||||
|
||||
# Netconsole - stream kernel messages to zippy (192.168.1.2)
|
||||
# Must configure via configfs after network is up (interface doesn't exist at module load)
|
||||
boot.kernelModules = [ "netconsole" ];
|
||||
boot.extraModprobeConfig = ''
|
||||
options netconsole netconsole=@/enp1s0,6666@192.168.1.2/c0:3f:d5:62:55:bb
|
||||
'';
|
||||
boot.kernel.sysctl."kernel.printk" = "8 4 1 7"; # Raise console_loglevel to send all messages
|
||||
systemd.services.netconsole-sender = {
|
||||
description = "Configure netconsole to send kernel messages to zippy";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script = ''
|
||||
TARGET=/sys/kernel/config/netconsole/target1
|
||||
mkdir -p $TARGET
|
||||
# Disable first if already enabled (can't modify params while enabled)
|
||||
if [ -f $TARGET/enabled ] && [ "$(cat $TARGET/enabled)" = "1" ]; then
|
||||
echo 0 > $TARGET/enabled
|
||||
fi
|
||||
echo enp1s0 > $TARGET/dev_name
|
||||
echo 192.168.1.2 > $TARGET/remote_ip
|
||||
echo 6666 > $TARGET/remote_port
|
||||
echo c0:3f:d5:62:55:bb > $TARGET/remote_mac
|
||||
echo 1 > $TARGET/enabled
|
||||
'';
|
||||
};
|
||||
|
||||
# Kdump for kernel crash analysis
|
||||
boot.crashDump = {
|
||||
|
||||
100
nix-runner/README.md
Normal file
100
nix-runner/README.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Nix Runner for Gitea Actions
|
||||
|
||||
Custom Docker image for running Nix builds in CI.
|
||||
|
||||
## What's Included
|
||||
|
||||
- **Nix** with flakes enabled (`experimental-features = nix-command flakes`)
|
||||
- **Node.js 20** for JavaScript-based GitHub Actions
|
||||
- **Tools**: git, curl, jq, skopeo, bash, coreutils
|
||||
- **Binary caches**:
|
||||
- `c3.mule-stork.ts.net:8501` (local cache proxy)
|
||||
- `cache.nixos.org` (official)
|
||||
|
||||
## Usage
|
||||
|
||||
In your workflow:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
build:
|
||||
runs-on: nix
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: nix build .#myPackage
|
||||
```
|
||||
|
||||
The `nix` label is configured in `services/act-runner.hcl`.
|
||||
|
||||
## Current Version
|
||||
|
||||
**Tag**: `v4`
|
||||
**Image**: `gitea.v.paler.net/alo/nix-runner:v4`
|
||||
|
||||
## Updating the Runner
|
||||
|
||||
### 1. Edit `flake.nix`
|
||||
|
||||
Make your changes, then bump the tag:
|
||||
|
||||
```nix
|
||||
tag = "v5"; # was v4
|
||||
```
|
||||
|
||||
### 2. Build
|
||||
|
||||
```bash
|
||||
cd nix-runner
|
||||
nix build
|
||||
```
|
||||
|
||||
### 3. Push to Registry
|
||||
|
||||
```bash
|
||||
skopeo copy --dest-authfile ~/.docker/config.json \
|
||||
docker-archive:result \
|
||||
docker://gitea.v.paler.net/alo/nix-runner:v5
|
||||
```
|
||||
|
||||
### 4. Update act-runner
|
||||
|
||||
Edit `services/act-runner.hcl`:
|
||||
|
||||
```hcl
|
||||
GITEA_RUNNER_LABELS = "ubuntu-latest:docker://node:20-bookworm,nix:docker://gitea.v.paler.net/alo/nix-runner:v5"
|
||||
```
|
||||
|
||||
### 5. Re-register Runner
|
||||
|
||||
```bash
|
||||
sudo rm /data/services/act-runner/.runner
|
||||
nomad run services/act-runner.hcl
|
||||
```
|
||||
|
||||
The runner will re-register with the new labels.
|
||||
|
||||
## Configuration
|
||||
|
||||
The image uses `NIX_CONFIG` environment variable for Nix settings:
|
||||
|
||||
```
|
||||
experimental-features = nix-command flakes
|
||||
sandbox = false
|
||||
build-users-group =
|
||||
substituters = http://c3.mule-stork.ts.net:8501 https://cache.nixos.org
|
||||
trusted-public-keys = cache.nixos.org-1:... c3:...
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Build fails with `build-users-group` error
|
||||
|
||||
The image runs as root without the nixbld group. This is handled by `build-users-group =` in NIX_CONFIG.
|
||||
|
||||
### Can't fetch from cache
|
||||
|
||||
Check that the runner container can reach `c3.mule-stork.ts.net:8501` (Tailscale network).
|
||||
|
||||
### Missing tool
|
||||
|
||||
Add it to `paths` in `flake.nix` and rebuild/push a new version.
|
||||
61
nix-runner/flake.lock
generated
Normal file
61
nix-runner/flake.lock
generated
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1767379071,
|
||||
"narHash": "sha256-EgE0pxsrW9jp9YFMkHL9JMXxcqi/OoumPJYwf+Okucw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "fb7944c166a3b630f177938e478f0378e64ce108",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
58
nix-runner/flake.nix
Normal file
58
nix-runner/flake.nix
Normal file
@@ -0,0 +1,58 @@
|
||||
# ABOUTME: Flake to build a custom Nix Docker image for Gitea Actions.
|
||||
# ABOUTME: Includes coreutils (/bin/sleep), modern Nix with flakes, and CI tools.
|
||||
{
|
||||
description = "Nix runner image for Gitea Actions";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
in {
|
||||
packages.default = pkgs.dockerTools.buildImage {
|
||||
name = "gitea.v.paler.net/alo/nix-runner";
|
||||
tag = "v4";
|
||||
|
||||
copyToRoot = pkgs.buildEnv {
|
||||
name = "image-root";
|
||||
paths = with pkgs; [
|
||||
# Core utilities (provides /bin/sleep that act_runner needs)
|
||||
coreutils-full
|
||||
bash
|
||||
# Nix itself
|
||||
nix
|
||||
# For actions that need node
|
||||
nodejs_20
|
||||
# Common CI tools
|
||||
git
|
||||
curl
|
||||
jq
|
||||
skopeo
|
||||
# CA certificates for HTTPS
|
||||
cacert
|
||||
];
|
||||
pathsToLink = [ "/bin" "/etc" ];
|
||||
};
|
||||
|
||||
# Create temp directories without runAsRoot (which needs KVM)
|
||||
extraCommands = ''
|
||||
mkdir -p -m 1777 tmp
|
||||
mkdir -p -m 1777 var/tmp
|
||||
'';
|
||||
|
||||
config = {
|
||||
Env = [
|
||||
"NIX_PAGER=cat"
|
||||
"USER=root"
|
||||
"SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
|
||||
"NIX_SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
|
||||
"NIX_CONFIG=experimental-features = nix-command flakes\nsandbox = false\nbuild-users-group =\nsubstituters = http://c3.mule-stork.ts.net:8501 https://cache.nixos.org\ntrusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= c3:sI3l1RN80xdehzXLA8u2P6352B0SyRPs2XiYy/YWYro="
|
||||
];
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
75
services/act-runner.hcl
Normal file
75
services/act-runner.hcl
Normal file
@@ -0,0 +1,75 @@
|
||||
# ABOUTME: Gitea Actions runner for CI/CD pipelines.
|
||||
# ABOUTME: Runs containerized actions with Docker-in-Docker support.
|
||||
|
||||
# Setup required before running:
|
||||
# sudo mkdir -p /data/services/act-runner
|
||||
# nomad var put secrets/act-runner registration_token="<token-from-gitea-ui>"
|
||||
|
||||
job "act-runner" {
|
||||
datacenters = ["alo"]
|
||||
type = "service"
|
||||
|
||||
group "runner" {
|
||||
network {
|
||||
mode = "host"
|
||||
}
|
||||
|
||||
task "runner" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "gitea/act_runner:latest"
|
||||
network_mode = "host"
|
||||
privileged = true
|
||||
volumes = [
|
||||
"/var/run/docker.sock:/var/run/docker.sock",
|
||||
"/data/services/act-runner:/data",
|
||||
"local/config.yaml:/.runner/config.yaml",
|
||||
]
|
||||
}
|
||||
|
||||
template {
|
||||
destination = "local/config.yaml"
|
||||
data = <<EOH
|
||||
log:
|
||||
level: info
|
||||
runner:
|
||||
file: /data/.runner
|
||||
capacity: 2
|
||||
timeout: 3h
|
||||
labels:
|
||||
- "ubuntu-latest:docker://node:20-bookworm"
|
||||
- "nix:docker://nixos/nix:latest"
|
||||
cache:
|
||||
enabled: true
|
||||
dir: /data/cache
|
||||
container:
|
||||
network: "host"
|
||||
privileged: true
|
||||
valid_volumes:
|
||||
- /data/services/**
|
||||
EOH
|
||||
}
|
||||
|
||||
env {
|
||||
GITEA_INSTANCE_URL = "https://gitea.v.paler.net"
|
||||
GITEA_RUNNER_LABELS = "ubuntu-latest:docker://node:20-bookworm,nix:docker://gitea.v.paler.net/alo/nix-runner:v4"
|
||||
}
|
||||
|
||||
# Template needed for nomadVar interpolation (secrets) and Nomad runtime vars
|
||||
template {
|
||||
destination = "secrets/env.env"
|
||||
env = true
|
||||
data = <<EOH
|
||||
GITEA_RUNNER_REGISTRATION_TOKEN={{ with nomadVar "secrets/act-runner" }}{{ .registration_token }}{{ end }}
|
||||
GITEA_RUNNER_NAME={{ env "NOMAD_ALLOC_ID" }}
|
||||
EOH
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 2000
|
||||
memory = 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
services/animaltrack.hcl
Normal file
88
services/animaltrack.hcl
Normal file
@@ -0,0 +1,88 @@
|
||||
# ABOUTME: Nomad job for AnimalTrack - poultry farm management app.
|
||||
# ABOUTME: Runs FastHTML Python app with SQLite, behind Traefik with OIDC auth.
|
||||
|
||||
# Setup required before running:
|
||||
# sudo mkdir -p /data/services/animaltrack && sudo chown 1000:1000 /data/services/animaltrack
|
||||
# nomad var put secrets/animaltrack csrf_secret="$(nix shell nixpkgs#openssl -c openssl rand -base64 32)"
|
||||
|
||||
job "animaltrack" {
|
||||
datacenters = ["alo"]
|
||||
|
||||
# Force re-pull of :latest images on each nomad run
|
||||
meta {
|
||||
uuid = uuidv4()
|
||||
}
|
||||
|
||||
update {
|
||||
max_parallel = 1
|
||||
health_check = "checks"
|
||||
min_healthy_time = "30s"
|
||||
healthy_deadline = "5m"
|
||||
progress_deadline = "10m"
|
||||
auto_revert = true
|
||||
}
|
||||
|
||||
group "web" {
|
||||
network {
|
||||
port "http" {
|
||||
to = 3366
|
||||
}
|
||||
}
|
||||
|
||||
task "app" {
|
||||
driver = "docker"
|
||||
user = "1000"
|
||||
|
||||
config {
|
||||
image = "gitea.v.paler.net/alo/animaltrack:latest"
|
||||
ports = ["http"]
|
||||
force_pull = true
|
||||
volumes = ["/data/services/animaltrack:/var/lib/animaltrack"]
|
||||
}
|
||||
|
||||
env {
|
||||
DB_PATH = "/var/lib/animaltrack/animaltrack.db"
|
||||
AUTH_HEADER_NAME = "X-Oidc-Username"
|
||||
SEED_ON_START = "true"
|
||||
TRUSTED_PROXY_IPS = "192.168.1.0/24"
|
||||
}
|
||||
|
||||
# Template needed for nomadVar interpolation (secrets)
|
||||
template {
|
||||
destination = "secrets/env.env"
|
||||
env = true
|
||||
data = <<EOH
|
||||
CSRF_SECRET={{ with nomadVar "secrets/animaltrack" }}{{ .csrf_secret }}{{ end }}
|
||||
EOH
|
||||
}
|
||||
|
||||
resources {
|
||||
memory = 512
|
||||
}
|
||||
|
||||
service {
|
||||
name = "animaltrack"
|
||||
port = "http"
|
||||
|
||||
tags = [
|
||||
"traefik.enable=true",
|
||||
"traefik.http.routers.animaltrack.entryPoints=websecure",
|
||||
"traefik.http.routers.animaltrack.middlewares=oidc-auth@file",
|
||||
"traefik.http.routers.animaltrack.rule=Host(`farm.alo.land`)",
|
||||
]
|
||||
|
||||
check {
|
||||
type = "http"
|
||||
path = "/healthz"
|
||||
interval = "10s"
|
||||
timeout = "5s"
|
||||
|
||||
check_restart {
|
||||
limit = 3
|
||||
grace = "60s"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ job "beancount" {
|
||||
user = "1000"
|
||||
|
||||
config {
|
||||
image = "gitea.v.paler.net/ppetru/fava:latest"
|
||||
image = "gitea.v.paler.net/alo/fava:latest"
|
||||
ports = ["http"]
|
||||
volumes = [
|
||||
"/data/services/beancount:/beancount",
|
||||
@@ -42,7 +42,7 @@ job "beancount" {
|
||||
}
|
||||
|
||||
resources {
|
||||
memory = 400
|
||||
memory = 600
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
job "farmos" {
|
||||
datacenters = ["alo"]
|
||||
|
||||
meta {
|
||||
uuid = uuidv4()
|
||||
}
|
||||
|
||||
group "os" {
|
||||
network {
|
||||
port "http" {
|
||||
to = 80
|
||||
}
|
||||
}
|
||||
|
||||
task "server" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "gitea.v.paler.net/ppetru/farmos:latest"
|
||||
ports = ["http"]
|
||||
volumes = [
|
||||
"/data/services/farmos/sites:/opt/drupal/web/sites",
|
||||
"/data/services/farmos/keys:/opt/drupal/keys",
|
||||
]
|
||||
}
|
||||
|
||||
service {
|
||||
name = "farmos"
|
||||
port = "http"
|
||||
check {
|
||||
type = "http"
|
||||
port = "http"
|
||||
path = "/health"
|
||||
interval = "30s"
|
||||
timeout = "2s"
|
||||
}
|
||||
|
||||
tags = [
|
||||
"traefik.enable=true",
|
||||
"traefik.http.routers.farmos.entryPoints=websecure",
|
||||
"traefik.http.routers.farmos.rule=Host(`farm.alo.land`)",
|
||||
]
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 2000
|
||||
memory = 1024
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,9 @@ job "gitea" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "gitea/gitea:latest-rootless"
|
||||
# TODO: revert to latest once 1.25.1+ is released
|
||||
#image = "gitea/gitea:latest-rootless"
|
||||
image = "gitea/gitea:1.25-nightly-rootless"
|
||||
ports = [
|
||||
"http",
|
||||
"ssh",
|
||||
@@ -42,6 +44,8 @@ job "gitea" {
|
||||
GITEA__mailer__FROM = "gitea@paler.net"
|
||||
GITEA__mailer__PROTOCOL = "smtp"
|
||||
GITEA__mailer__SMTP_ADDR = "192.168.1.1"
|
||||
GITEA__actions__ENABLED = "true"
|
||||
GITEA__actions__DEFAULT_ACTIONS_URL = "https://gitea.com"
|
||||
}
|
||||
|
||||
service {
|
||||
|
||||
@@ -9,42 +9,19 @@ job "igsync" {
|
||||
}
|
||||
|
||||
group "cron" {
|
||||
volume "nix-store" {
|
||||
type = "host"
|
||||
read_only = true
|
||||
source = "nix-store"
|
||||
}
|
||||
volume "sw" {
|
||||
type = "host"
|
||||
read_only = true
|
||||
source = "sw"
|
||||
}
|
||||
volume "services" {
|
||||
type = "host"
|
||||
read_only = false
|
||||
source = "services"
|
||||
}
|
||||
|
||||
task "sync" {
|
||||
driver = "exec"
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
command = "/data/services/igsync/run.sh"
|
||||
}
|
||||
image = "gitea.v.paler.net/alo/igsync:latest"
|
||||
|
||||
user = "ppetru"
|
||||
# Mount the data directory for .env, database, and media files
|
||||
volumes = [
|
||||
"/data/services/igsync:/data/services/igsync"
|
||||
]
|
||||
|
||||
volume_mount {
|
||||
volume = "services"
|
||||
destination = "/data/services"
|
||||
}
|
||||
volume_mount {
|
||||
volume = "nix-store"
|
||||
destination = "/nix/store"
|
||||
}
|
||||
volume_mount {
|
||||
volume = "sw"
|
||||
destination = "/sw"
|
||||
# Force pull to always get latest image
|
||||
force_pull = true
|
||||
}
|
||||
|
||||
resources {
|
||||
|
||||
138
services/phaseflow.hcl
Normal file
138
services/phaseflow.hcl
Normal file
@@ -0,0 +1,138 @@
|
||||
# ABOUTME: Nomad job for PhaseFlow - menstrual cycle tracking and training app.
|
||||
# ABOUTME: Runs Next.js app with PocketBase sidecar for data persistence.
|
||||
|
||||
# Setup required before running:
|
||||
# sudo mkdir -p /data/services/phaseflow/pb_data && sudo chown 1000:1000 /data/services/phaseflow /data/services/phaseflow/pb_data
|
||||
# nomad var put secrets/phaseflow \
|
||||
# mailgun_api_key="key-xxxx" \
|
||||
# mailgun_domain="paler.net" \
|
||||
# encryption_key="$(openssl rand -hex 16)" \
|
||||
# cron_secret="$(openssl rand -hex 32)" \
|
||||
# pocketbase_admin_email="admin@example.com" \
|
||||
# pocketbase_admin_password="your-admin-password"
|
||||
|
||||
job "phaseflow" {
|
||||
datacenters = ["alo"]
|
||||
|
||||
# Force re-pull of :latest images on each nomad run
|
||||
meta {
|
||||
uuid = uuidv4()
|
||||
}
|
||||
|
||||
update {
|
||||
max_parallel = 1
|
||||
health_check = "checks"
|
||||
min_healthy_time = "30s"
|
||||
healthy_deadline = "5m"
|
||||
progress_deadline = "10m"
|
||||
auto_revert = true
|
||||
}
|
||||
|
||||
group "app" {
|
||||
network {
|
||||
port "http" {
|
||||
to = 3000
|
||||
}
|
||||
port "pocketbase" {
|
||||
to = 8090
|
||||
}
|
||||
}
|
||||
|
||||
# PocketBase sidecar - starts first, provides database
|
||||
task "pocketbase" {
|
||||
driver = "docker"
|
||||
user = "1000"
|
||||
|
||||
lifecycle {
|
||||
hook = "prestart"
|
||||
sidecar = true
|
||||
}
|
||||
|
||||
config {
|
||||
image = "ghcr.io/muchobien/pocketbase:latest"
|
||||
ports = ["pocketbase"]
|
||||
volumes = ["/data/services/phaseflow/pb_data:/pb_data"]
|
||||
}
|
||||
|
||||
env {
|
||||
PB_DATA_DIR = "/pb_data"
|
||||
}
|
||||
|
||||
resources {
|
||||
memory = 256
|
||||
}
|
||||
|
||||
service {
|
||||
name = "pocketbase-phaseflow"
|
||||
port = "pocketbase"
|
||||
|
||||
tags = [
|
||||
"traefik.enable=true",
|
||||
"traefik.http.routers.pocketbase-phaseflow.entryPoints=websecure",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# Main Next.js application
|
||||
task "app" {
|
||||
driver = "docker"
|
||||
user = "1000"
|
||||
|
||||
config {
|
||||
image = "gitea.v.paler.net/alo/phaseflow:latest"
|
||||
ports = ["http"]
|
||||
force_pull = true
|
||||
}
|
||||
|
||||
env {
|
||||
NODE_ENV = "production"
|
||||
POCKETBASE_URL = "http://${NOMAD_ADDR_pocketbase}"
|
||||
NEXT_PUBLIC_POCKETBASE_URL = "https://pocketbase-phaseflow.v.paler.net"
|
||||
APP_URL = "https://phaseflow.v.paler.net"
|
||||
EMAIL_FROM = "phaseflow@paler.net"
|
||||
MAILGUN_URL = "https://api.eu.mailgun.net"
|
||||
}
|
||||
|
||||
# Secrets from Nomad variables
|
||||
template {
|
||||
destination = "secrets/env.env"
|
||||
env = true
|
||||
data = <<EOH
|
||||
MAILGUN_API_KEY={{ with nomadVar "secrets/phaseflow" }}{{ .mailgun_api_key }}{{ end }}
|
||||
MAILGUN_DOMAIN={{ with nomadVar "secrets/phaseflow" }}{{ .mailgun_domain }}{{ end }}
|
||||
ENCRYPTION_KEY={{ with nomadVar "secrets/phaseflow" }}{{ .encryption_key }}{{ end }}
|
||||
CRON_SECRET={{ with nomadVar "secrets/phaseflow" }}{{ .cron_secret }}{{ end }}
|
||||
POCKETBASE_ADMIN_EMAIL={{ with nomadVar "secrets/phaseflow" }}{{ .pocketbase_admin_email }}{{ end }}
|
||||
POCKETBASE_ADMIN_PASSWORD={{ with nomadVar "secrets/phaseflow" }}{{ .pocketbase_admin_password }}{{ end }}
|
||||
EOH
|
||||
}
|
||||
|
||||
resources {
|
||||
memory = 512
|
||||
}
|
||||
|
||||
service {
|
||||
name = "phaseflow"
|
||||
port = "http"
|
||||
|
||||
tags = [
|
||||
"traefik.enable=true",
|
||||
"traefik.http.routers.phaseflow.entryPoints=websecure",
|
||||
"metrics",
|
||||
]
|
||||
|
||||
check {
|
||||
type = "http"
|
||||
path = "/api/health"
|
||||
interval = "10s"
|
||||
timeout = "5s"
|
||||
|
||||
check_restart {
|
||||
limit = 3
|
||||
grace = "60s"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,18 @@
|
||||
job "tiddlywiki-mcp" {
|
||||
datacenters = ["alo"]
|
||||
|
||||
group "captainslog" {
|
||||
meta {
|
||||
uuid = uuidv4()
|
||||
}
|
||||
|
||||
group "servers" {
|
||||
network {
|
||||
port "http" {
|
||||
port "captainslog" {
|
||||
static = 3500
|
||||
}
|
||||
port "alowiki" {
|
||||
static = 3501
|
||||
}
|
||||
}
|
||||
|
||||
volume "services" {
|
||||
@@ -26,7 +33,7 @@ job "tiddlywiki-mcp" {
|
||||
read_only = true
|
||||
}
|
||||
|
||||
task "mcp-server" {
|
||||
task "captainslog" {
|
||||
driver = "exec"
|
||||
|
||||
config {
|
||||
@@ -36,10 +43,12 @@ job "tiddlywiki-mcp" {
|
||||
|
||||
env {
|
||||
MCP_TRANSPORT = "http"
|
||||
MCP_PORT = "${NOMAD_PORT_http}"
|
||||
CONSUL_SERVICE = "captainslog.service.consul"
|
||||
MCP_PORT = "${NOMAD_PORT_captainslog}"
|
||||
TIDDLYWIKI_URL = "captainslog.service.consul"
|
||||
OLLAMA_URL = "ollama.service.consul"
|
||||
AUTH_HEADER = "X-Oidc-Username"
|
||||
AUTH_USER = "claude-code"
|
||||
EMBEDDINGS_DB_PATH = "/data/services/tiddlywiki-mcp/embeddings-captainslog.db"
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
@@ -62,7 +71,62 @@ job "tiddlywiki-mcp" {
|
||||
|
||||
service {
|
||||
name = "tiddlywiki-mcp-captainslog"
|
||||
port = "http"
|
||||
port = "captainslog"
|
||||
|
||||
check {
|
||||
type = "http"
|
||||
path = "/health"
|
||||
interval = "10s"
|
||||
timeout = "2s"
|
||||
}
|
||||
}
|
||||
|
||||
resources {
|
||||
memory = 256
|
||||
}
|
||||
|
||||
user = "ppetru"
|
||||
}
|
||||
|
||||
task "alowiki" {
|
||||
driver = "exec"
|
||||
|
||||
config {
|
||||
command = "/sw/bin/node"
|
||||
args = ["/data/services/tiddlywiki-mcp/dist/index.js"]
|
||||
}
|
||||
|
||||
env {
|
||||
MCP_TRANSPORT = "http"
|
||||
MCP_PORT = "${NOMAD_PORT_alowiki}"
|
||||
TIDDLYWIKI_URL = "alowiki.service.consul"
|
||||
OLLAMA_URL = "ollama.service.consul"
|
||||
AUTH_HEADER = "X-Oidc-Username"
|
||||
AUTH_USER = "claude-code"
|
||||
EMBEDDINGS_DB_PATH = "/data/services/tiddlywiki-mcp/embeddings-alowiki.db"
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
volume = "services"
|
||||
destination = "/data/services"
|
||||
read_only = false
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
volume = "nix-store"
|
||||
destination = "/nix/store"
|
||||
read_only = true
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
volume = "sw"
|
||||
destination = "/sw"
|
||||
read_only = true
|
||||
}
|
||||
|
||||
service {
|
||||
name = "tiddlywiki-mcp-alowiki"
|
||||
port = "alowiki"
|
||||
|
||||
check {
|
||||
type = "http"
|
||||
|
||||
@@ -80,7 +80,7 @@ experimental:
|
||||
plugins:
|
||||
traefik-oidc-auth:
|
||||
moduleName: "github.com/sevensolutions/traefik-oidc-auth"
|
||||
version: "v0.16.0"
|
||||
version: "v0.17.0"
|
||||
|
||||
api:
|
||||
dashboard: true
|
||||
@@ -133,6 +133,8 @@ entryPoints:
|
||||
websecure:
|
||||
address: ":{{{ env "NOMAD_PORT_https" }}}"
|
||||
http:
|
||||
encodedCharacters:
|
||||
allowEncodedSlash: true
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
job "urbit" {
|
||||
datacenters = ["alo"]
|
||||
|
||||
meta {
|
||||
uuid = uuidv4()
|
||||
}
|
||||
|
||||
group "os" {
|
||||
network {
|
||||
port "http" {
|
||||
@@ -25,7 +29,7 @@ job "urbit" {
|
||||
# You can also set a variable loom size (Urbit memory size) using
|
||||
# --loom=$LOOM_SIZE. Passing /bin/start-urbit --loom=32 for example, would set up
|
||||
# a 4GiB loom (2^32 bytes = 4GiB). The default loom size is 31 (2GiB).
|
||||
"--loom=31",
|
||||
"--loom=32",
|
||||
]
|
||||
volumes = [
|
||||
"/data/services/urbit:/urbit",
|
||||
@@ -45,7 +49,7 @@ job "urbit" {
|
||||
|
||||
resources {
|
||||
# dependent on --loom setting + some buffer
|
||||
memory = 2100
|
||||
memory = 4200
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ job "weewx" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "gitea.v.paler.net/ppetru/weewx:latest"
|
||||
image = "gitea.v.paler.net/alo/weewx:latest"
|
||||
# to be able to receive UDP broadcast packets from the weatherlink
|
||||
network_mode = "host"
|
||||
volumes = [
|
||||
@@ -29,7 +29,7 @@ job "weewx" {
|
||||
}
|
||||
|
||||
resources {
|
||||
memory = 1024
|
||||
memory = 1500
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ job "weewx" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "gitea.v.paler.net/ppetru/opensprinkler-weather:latest"
|
||||
image = "gitea.v.paler.net/alo/opensprinkler-weather:latest"
|
||||
|
||||
ports = [ "osweather" ]
|
||||
}
|
||||
|
||||
@@ -86,8 +86,8 @@ job "wiki" {
|
||||
"host=0.0.0.0",
|
||||
"port=${NOMAD_PORT_alo}",
|
||||
"authenticated-user-header=X-Oidc-Username",
|
||||
"readers=ppetru,ines",
|
||||
"writers=ppetru,ines",
|
||||
"readers=ppetru,ines,claude-code",
|
||||
"writers=ppetru,ines,claude-code",
|
||||
"admin=ppetru",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ job "wordpress" {
|
||||
user = "237"
|
||||
|
||||
config {
|
||||
image = "gitea.v.paler.net/ppetru/wordpress"
|
||||
image = "gitea.v.paler.net/alo/wordpress"
|
||||
ports = ["http"]
|
||||
volumes = [
|
||||
"/data/services/wordpress:/var/www/html",
|
||||
|
||||
Reference in New Issue
Block a user