Compare commits
111 Commits
163b9e4c22
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 3016301729 | |||
| 027a9c675d | |||
| 14d267e12d | |||
| 2e8e11ecec | |||
| f90fa5c23b | |||
| caa6d0aafd | |||
| 29043896c8 | |||
| 1af9053cd5 | |||
| 2dcd03cbb0 | |||
| b5f0cdb429 | |||
| 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 | |||
| a8b63e71c8 | |||
| 58c851004d | |||
| bd889902be | |||
| 7fd79c9911 | |||
| 41eacfec02 | |||
| 0a0748b920 | |||
| d6e0e09e87 | |||
| 61c3020a5e | |||
| 972b973f58 | |||
| 8c5a7b78c6 | |||
| 675204816a | |||
| 3bb82dbc6b | |||
| 0f6233c3ec | |||
| 43fa56bf35 | |||
| 50c930eeaf | |||
| 8dde15b8ef | |||
| 6100d8dc69 | |||
| a92f0fcb28 | |||
| bd4604cdcc | |||
| 31db372b43 | |||
| 360e776745 | |||
| 5a819f70bb | |||
| b2c055ffb2 | |||
| 6e0b34843b | |||
| e8485e3bb7 | |||
| e8cd970960 | |||
| 78b59cec4f | |||
| e6d40a9f7e | |||
| 7733a1be46 | |||
| a5df98bc5a | |||
| fb9b0dd2f5 | |||
| 0dc214069c | |||
| a6c4be9530 | |||
| 6e338e6d65 | |||
| 41f16fa0b8 | |||
| 1b05728817 | |||
| 520a417316 | |||
| 88ed5360ca | |||
| 392d40def3 | |||
| 5ef4d832fb | |||
| 49afc0c084 | |||
| b2c82ceaa8 | |||
| b9286d7243 | |||
| 22931e6747 | |||
| ac030018c6 | |||
| 7386d3a5ee | |||
| 2a5a9f2ee9 | |||
| 963a7c10fa | |||
| 283cf9d614 | |||
| 5b3b4ea2ed | |||
| 5a9d5de5c4 | |||
| a5e3f613c2 | |||
| 8b8fac2d89 | |||
| 31d79ba75b | |||
| 6faf148fde | |||
| e88f1c93c5 | |||
| 51375db1e4 | |||
| 9415a8ece2 | |||
| da85ee776d | |||
| e23dc7df5b |
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
|
||||||
@@ -2,8 +2,9 @@ keys:
|
|||||||
- &admin_ppetru age1df9ukkmg9yn9cjeheq9m6wspa420su8qarmq570rdvf2de3rl38saqauwn
|
- &admin_ppetru age1df9ukkmg9yn9cjeheq9m6wspa420su8qarmq570rdvf2de3rl38saqauwn
|
||||||
- &server_zippy age1gtyw202hd07hddac9886as2cs8pm07e4exlnrgfm72lync75ng9qc5fjac
|
- &server_zippy age1gtyw202hd07hddac9886as2cs8pm07e4exlnrgfm72lync75ng9qc5fjac
|
||||||
- &server_chilly age16yqffw4yl5jqvsr7tyd883vn98zw0attuv9g5snc329juff6dy3qw2w5wp
|
- &server_chilly age16yqffw4yl5jqvsr7tyd883vn98zw0attuv9g5snc329juff6dy3qw2w5wp
|
||||||
- &server_sparky age10zxwwufrf5uu9cv9p9znse2ftfm74q9ce893us6cnvxjc7e3ypcqy709dy
|
- &server_sparky age14aml5s3sxksa8qthnt6apl3pu6egxyn0cz7pdzzvp2yl6wncad0q56udyj
|
||||||
- &server_stinky age1me78u46409q9ez6fj0qanrfffc5e9kuq7n7uuvlljfwwc2mdaezqmyzxhx
|
- &server_stinky age1me78u46409q9ez6fj0qanrfffc5e9kuq7n7uuvlljfwwc2mdaezqmyzxhx
|
||||||
|
- &server_beefy age1cs8uqj243lspyp042ueu5aes4t3azgyuaxl9au70ggrl2meulq4sgqpc7y
|
||||||
- &server_alo_cloud_1 age1w5w4wfvtul3sge9mt205zvrkjaeh3qs9gsxhmq7df2g4dztnvv6qylup8z
|
- &server_alo_cloud_1 age1w5w4wfvtul3sge9mt205zvrkjaeh3qs9gsxhmq7df2g4dztnvv6qylup8z
|
||||||
- &server_c1 age1wwufz86tm3auxn6pn27c47s8rvu7en58rk00nghtaxsdpw0gya6qj6qxdt
|
- &server_c1 age1wwufz86tm3auxn6pn27c47s8rvu7en58rk00nghtaxsdpw0gya6qj6qxdt
|
||||||
- &server_c2 age1jy7pe4530s8w904wtvrmpxvteztqy5ewdt92a7y3lq87sg9jce5qxxuydt
|
- &server_c2 age1jy7pe4530s8w904wtvrmpxvteztqy5ewdt92a7y3lq87sg9jce5qxxuydt
|
||||||
@@ -17,6 +18,7 @@ creation_rules:
|
|||||||
- *server_chilly
|
- *server_chilly
|
||||||
- *server_sparky
|
- *server_sparky
|
||||||
- *server_stinky
|
- *server_stinky
|
||||||
|
- *server_beefy
|
||||||
- *server_alo_cloud_1
|
- *server_alo_cloud_1
|
||||||
- *server_c1
|
- *server_c1
|
||||||
- *server_c2
|
- *server_c2
|
||||||
@@ -41,6 +43,11 @@ creation_rules:
|
|||||||
- age:
|
- age:
|
||||||
- *admin_ppetru
|
- *admin_ppetru
|
||||||
- *server_stinky
|
- *server_stinky
|
||||||
|
- path_regex: secrets/beefy\.yaml
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin_ppetru
|
||||||
|
- *server_beefy
|
||||||
- path_regex: secrets/wifi\.yaml
|
- path_regex: secrets/wifi\.yaml
|
||||||
key_groups:
|
key_groups:
|
||||||
- age:
|
- age:
|
||||||
|
|||||||
64
CLAUDE.md
64
CLAUDE.md
@@ -8,42 +8,35 @@ NixOS cluster configuration using flakes. Homelab infrastructure with Nomad/Cons
|
|||||||
├── common/
|
├── common/
|
||||||
│ ├── global/ # Applied to all hosts (backup, sops, users, etc.)
|
│ ├── global/ # Applied to all hosts (backup, sops, users, etc.)
|
||||||
│ ├── minimal-node.nix # Base (ssh, user, boot, impermanence)
|
│ ├── minimal-node.nix # Base (ssh, user, boot, impermanence)
|
||||||
│ ├── cluster-member.nix # Consul + storage clients (NFS/CIFS/GlusterFS)
|
│ ├── cluster-member.nix # Consul agent + storage mounts (NFS/CIFS)
|
||||||
│ ├── nomad-worker.nix # Nomad client (runs jobs) + Docker + NFS deps
|
│ ├── nomad-worker.nix # Nomad client (runs jobs) + Docker + NFS deps
|
||||||
│ ├── nomad-server.nix # Enables Consul + Nomad server mode
|
│ ├── nomad-server.nix # Enables Consul + Nomad server mode
|
||||||
│ ├── cluster-tools.nix # Just CLI tools (nomad, wander, damon)
|
│ ├── cluster-tools.nix # Just CLI tools (nomad, wander, damon)
|
||||||
│ ├── workstation-node.nix # Dev tools (wget, deploy-rs, docker, nix-ld)
|
│ ├── workstation-node.nix # Dev tools (wget, deploy-rs, docker, nix-ld)
|
||||||
│ ├── desktop-node.nix # Hyprland + GUI environment
|
│ ├── desktop-node.nix # Hyprland + GUI environment
|
||||||
│ ├── nfs-services-server.nix # NFS server + btrfs replication (zippy)
|
│ ├── nfs-services-server.nix # NFS server + btrfs replication
|
||||||
│ └── nfs-services-standby.nix # NFS standby + receive replication (c1)
|
│ └── nfs-services-standby.nix # NFS standby + receive replication
|
||||||
├── hosts/
|
├── hosts/ # Host configs - check imports for roles
|
||||||
│ ├── c1/, c2/, c3/ # Cattle nodes (quorum + workers)
|
|
||||||
│ ├── zippy/ # Primary storage + NFS server + worker (not quorum)
|
|
||||||
│ ├── chilly/ # Home Assistant VM + cluster member (Consul only)
|
|
||||||
│ ├── sparky/ # Desktop + cluster member (Consul only)
|
|
||||||
│ ├── fractal/ # (Proxmox, will become NixOS storage node)
|
|
||||||
│ └── sunny/ # (Standalone ethereum node, not in cluster)
|
|
||||||
├── docs/
|
├── docs/
|
||||||
│ ├── CLUSTER_REVAMP.md # Master plan for architecture changes
|
│ ├── CLUSTER_REVAMP.md # Master plan for architecture changes
|
||||||
│ ├── MIGRATION_TODO.md # Tracking checklist for migration
|
│ ├── MIGRATION_TODO.md # Tracking checklist for migration
|
||||||
│ └── NFS_FAILOVER.md # NFS failover procedures
|
│ ├── NFS_FAILOVER.md # NFS failover procedures
|
||||||
|
│ └── AUTH_SETUP.md # Authentication (Pocket ID + Traefik OIDC)
|
||||||
└── services/ # Nomad job specs (.hcl files)
|
└── services/ # Nomad job specs (.hcl files)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Current Architecture
|
## Current Architecture
|
||||||
|
|
||||||
### Storage Mounts
|
### Storage Mounts
|
||||||
- `/data/services` - NFS from `data-services.service.consul` (zippy primary, c1 standby)
|
- `/data/services` - NFS from `data-services.service.consul` (check nfs-services-server.nix for primary)
|
||||||
- `/data/media` - CIFS from fractal (existing, unchanged)
|
- `/data/media` - CIFS from fractal
|
||||||
- `/data/shared` - CIFS from fractal (existing, unchanged)
|
- `/data/shared` - CIFS from fractal
|
||||||
|
|
||||||
### Hosts
|
### Cluster Roles (check hosts/*/default.nix for each host's imports)
|
||||||
- **c1, c2, c3**: Cattle nodes, run most workloads, Nomad/Consul quorum members
|
- **Quorum**: hosts importing `nomad-server.nix` (3 expected for consensus)
|
||||||
- **zippy**: Primary NFS server, runs workloads (affinity), NOT quorum, replicates to c1 every 5min
|
- **Workers**: hosts importing `nomad-worker.nix` (run Nomad jobs)
|
||||||
- **chilly**: Home Assistant VM, cluster member (Consul agent + CLI tools), no workloads
|
- **NFS server**: host importing `nfs-services-server.nix` (affinity for direct disk access like DBs)
|
||||||
- **sparky**: Desktop/laptop, cluster member (Consul agent + CLI tools), no workloads
|
- **Standby**: hosts importing `nfs-services-standby.nix` (receive replication)
|
||||||
- **fractal**: Storage node (Proxmox/ZFS), will join quorum after GlusterFS removed
|
|
||||||
- **sunny**: Standalone ethereum staking node (not in cluster)
|
|
||||||
|
|
||||||
## Config Architecture
|
## Config Architecture
|
||||||
|
|
||||||
@@ -58,19 +51,22 @@ NixOS cluster configuration using flakes. Homelab infrastructure with Nomad/Cons
|
|||||||
- `workstation-node.nix` - Dev tools (deploy-rs, docker, nix-ld, emulation)
|
- `workstation-node.nix` - Dev tools (deploy-rs, docker, nix-ld, emulation)
|
||||||
- `desktop-node.nix` - Extends workstation + Hyprland/GUI
|
- `desktop-node.nix` - Extends workstation + Hyprland/GUI
|
||||||
|
|
||||||
**Host composition examples**:
|
**Composition patterns**:
|
||||||
- c1/c2/c3: `cluster-member + nomad-worker + nomad-server` (quorum + runs jobs)
|
- Quorum member: `cluster-member + nomad-worker + nomad-server`
|
||||||
- zippy: `cluster-member + nomad-worker` (runs jobs, not quorum)
|
- Worker only: `cluster-member + nomad-worker`
|
||||||
- chilly/sparky: `cluster-member + cluster-tools` (Consul + CLI only)
|
- CLI only: `cluster-member + cluster-tools` (Consul agent, no Nomad service)
|
||||||
|
- NFS primary: `cluster-member + nomad-worker + nfs-services-server`
|
||||||
|
- Standalone: `minimal-node` only (no cluster membership)
|
||||||
|
|
||||||
**Key insight**: Profiles (workstation/desktop) no longer imply cluster membership. Hosts explicitly declare roles via imports.
|
**Key insight**: Profiles (workstation/desktop) don't imply cluster roles. Check imports for actual roles.
|
||||||
|
|
||||||
## Key Patterns
|
## Key Patterns
|
||||||
|
|
||||||
**NFS Server/Standby**:
|
**NFS Server/Standby**:
|
||||||
- Primary (zippy): imports `nfs-services-server.nix`, sets `standbys = ["c1"]`
|
- Primary: imports `nfs-services-server.nix`, sets `standbys = [...]`
|
||||||
- Standby (c1): imports `nfs-services-standby.nix`, sets `replicationKeys = [...]`
|
- Standby: imports `nfs-services-standby.nix`, sets `replicationKeys = [...]`
|
||||||
- Replication: btrfs send/receive every 5min, incremental with fallback to full
|
- Replication: btrfs send/receive every 5min, incremental with fallback to full
|
||||||
|
- Check host configs for current primary/standby assignments
|
||||||
|
|
||||||
**Backups**:
|
**Backups**:
|
||||||
- Kopia client on all nodes → Kopia server on fractal
|
- Kopia client on all nodes → Kopia server on fractal
|
||||||
@@ -81,6 +77,12 @@ NixOS cluster configuration using flakes. Homelab infrastructure with Nomad/Cons
|
|||||||
- SOPS for secrets, files in `secrets/`
|
- SOPS for secrets, files in `secrets/`
|
||||||
- Keys managed per-host
|
- Keys managed per-host
|
||||||
|
|
||||||
|
**Authentication**:
|
||||||
|
- Pocket ID (OIDC provider) at `pocket-id.v.paler.net`
|
||||||
|
- Traefik uses `traefik-oidc-auth` plugin for SSO
|
||||||
|
- Services add `middlewares=oidc-auth@file` tag to protect
|
||||||
|
- See `docs/AUTH_SETUP.md` for details
|
||||||
|
|
||||||
## Migration Status
|
## Migration Status
|
||||||
|
|
||||||
**Phase 3 & 4**: COMPLETE! GlusterFS removed, all services on NFS
|
**Phase 3 & 4**: COMPLETE! GlusterFS removed, all services on NFS
|
||||||
@@ -92,7 +94,7 @@ See `docs/MIGRATION_TODO.md` for detailed checklist.
|
|||||||
|
|
||||||
**Deploy a host**: `deploy -s '.#hostname'`
|
**Deploy a host**: `deploy -s '.#hostname'`
|
||||||
**Deploy all**: `deploy`
|
**Deploy all**: `deploy`
|
||||||
**Check replication**: `ssh zippy journalctl -u replicate-services-to-c1.service -f`
|
**Check replication**: Check NFS primary host, then `ssh <primary> journalctl -u replicate-services-to-*.service -f`
|
||||||
**NFS failover**: See `docs/NFS_FAILOVER.md`
|
**NFS failover**: See `docs/NFS_FAILOVER.md`
|
||||||
**Nomad jobs**: `services/*.hcl` - service data stored at `/data/services/<service-name>`
|
**Nomad jobs**: `services/*.hcl` - service data stored at `/data/services/<service-name>`
|
||||||
|
|
||||||
@@ -106,8 +108,8 @@ See `docs/MIGRATION_TODO.md` for detailed checklist.
|
|||||||
## Important Files
|
## Important Files
|
||||||
|
|
||||||
- `common/global/backup.nix` - Kopia backup configuration
|
- `common/global/backup.nix` - Kopia backup configuration
|
||||||
- `hosts/zippy/default.nix` - NFS server config, replication targets
|
- `common/nfs-services-server.nix` - NFS server role (check hosts for which imports this)
|
||||||
- `hosts/c1/default.nix` - NFS standby config, authorized replication keys
|
- `common/nfs-services-standby.nix` - NFS standby role (check hosts for which imports this)
|
||||||
- `flake.nix` - Host definitions, nixpkgs inputs
|
- `flake.nix` - Host definitions, nixpkgs inputs
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ Each layer extends the previous one, inheriting all configurations. Hosts select
|
|||||||
|
|
||||||
### Special Node Types
|
### Special Node Types
|
||||||
|
|
||||||
- **cloud-node**: Minimal + Consul only (cloud VPS deployments)
|
|
||||||
- **compute-node**: Cluster + Nomad worker (container orchestration)
|
- **compute-node**: Cluster + Nomad worker (container orchestration)
|
||||||
|
|
||||||
## Directory Structure
|
## Directory Structure
|
||||||
@@ -40,7 +39,6 @@ Each layer extends the previous one, inheriting all configurations. Hosts select
|
|||||||
│ ├── server-node.nix # Server layer: bare metal services (future)
|
│ ├── server-node.nix # Server layer: bare metal services (future)
|
||||||
│ ├── workstation-node.nix # Workstation layer: dev tools
|
│ ├── workstation-node.nix # Workstation layer: dev tools
|
||||||
│ ├── desktop-node.nix # Desktop layer: GUI environment
|
│ ├── desktop-node.nix # Desktop layer: GUI environment
|
||||||
│ ├── cloud-node.nix # Cloud VPS profile
|
|
||||||
│ ├── compute-node.nix # Nomad worker profile
|
│ ├── compute-node.nix # Nomad worker profile
|
||||||
│ └── [feature modules] # Individual feature configs
|
│ └── [feature modules] # Individual feature configs
|
||||||
├── hosts/
|
├── hosts/
|
||||||
@@ -101,7 +99,7 @@ This ensures system and user configurations stay synchronized.
|
|||||||
| Host | Profile | Role | Hardware |
|
| Host | Profile | Role | Hardware |
|
||||||
|------|---------|------|----------|
|
|------|---------|------|----------|
|
||||||
| **c1, c2, c3** | compute-node | Nomad workers | Bare metal servers |
|
| **c1, c2, c3** | compute-node | Nomad workers | Bare metal servers |
|
||||||
| **alo-cloud-1** | cloud-node | Reverse proxy | Cloud VPS |
|
| **alo-cloud-1** | minimal | Reverse proxy (Traefik) | Cloud VPS |
|
||||||
| **chilly** | server | Home Assistant in a VM | Bare metal server |
|
| **chilly** | server | Home Assistant in a VM | Bare metal server |
|
||||||
| **zippy** | workstation | Development machine, server | Bare metal server |
|
| **zippy** | workstation | Development machine, server | Bare metal server |
|
||||||
| **sparky** | desktop | Desktop environment | Bare metal desktop |
|
| **sparky** | desktop | Desktop environment | Bare metal desktop |
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
let
|
let
|
||||||
# this line prevents hanging on network split
|
# this line prevents hanging on network split
|
||||||
automount_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.mount-timeout=5s";
|
automount_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.mount-timeout=5s,nobrl";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
environment.systemPackages = [ pkgs.cifs-utils ];
|
environment.systemPackages = [ pkgs.cifs-utils ];
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
{ pkgs, ... }:
|
|
||||||
{
|
|
||||||
# Cloud node: Minimal system with Consul for cloud deployments
|
|
||||||
imports = [
|
|
||||||
./minimal-node.nix
|
|
||||||
./consul.nix
|
|
||||||
];
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{ pkgs, ... }:
|
{ pkgs, lib, config, ... }:
|
||||||
{
|
{
|
||||||
# Cluster node configuration
|
# Cluster node configuration
|
||||||
# Extends minimal-node with cluster-specific services (Consul, GlusterFS, CIFS, NFS)
|
# Extends minimal-node with cluster-specific services (Consul, GlusterFS, CIFS, NFS)
|
||||||
@@ -11,7 +11,14 @@
|
|||||||
./nfs-services-client.nix # New: NFS client for /data/services
|
./nfs-services-client.nix # New: NFS client for /data/services
|
||||||
];
|
];
|
||||||
|
|
||||||
# Wait for eno1 to be routable before considering network online
|
options.networking.cluster.primaryInterface = lib.mkOption {
|
||||||
# (hosts with different primary interfaces should override this)
|
type = lib.types.str;
|
||||||
systemd.network.wait-online.extraArgs = [ "--interface=eno1:routable" ];
|
default = "eno1";
|
||||||
|
description = "Primary network interface for cluster communication (Consul, NFS, etc.)";
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
# Wait for primary interface to be routable before considering network online
|
||||||
|
systemd.network.wait-online.extraArgs = [ "--interface=${config.networking.cluster.primaryInterface}:routable" ];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ in
|
|||||||
services.consul = {
|
services.consul = {
|
||||||
enable = true;
|
enable = true;
|
||||||
webUi = true;
|
webUi = true;
|
||||||
interface.advertise = "eno1";
|
interface.advertise = config.networking.cluster.primaryInterface;
|
||||||
extraConfig = {
|
extraConfig = {
|
||||||
client_addr = "0.0.0.0";
|
client_addr = "0.0.0.0";
|
||||||
datacenter = "alo";
|
datacenter = "alo";
|
||||||
|
|||||||
@@ -1,47 +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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
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
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -51,7 +51,7 @@ in
|
|||||||
services."backup-persist" = {
|
services."backup-persist" = {
|
||||||
description = "Backup persistent data with Kopia";
|
description = "Backup persistent data with Kopia";
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
type = "oneshot";
|
Type = "oneshot";
|
||||||
User = "root";
|
User = "root";
|
||||||
ExecStart = "${backupScript}";
|
ExecStart = "${backupScript}";
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
# sometimes the impermanence bind mount is stopped when sops needs these
|
# sometimes the impermanence bind mount is stopped when sops needs these
|
||||||
age.sshKeyPaths = [
|
age.sshKeyPaths = [
|
||||||
"${config.custom.impermanence.persistPath}/etc/ssh/ssh_host_ed25519_key"
|
"${config.custom.impermanence.persistPath}/etc/ssh/ssh_host_ed25519_key"
|
||||||
"${config.custom.impermanence.persistPath}/etc/ssh/ssh_host_rsa_key"
|
|
||||||
];
|
];
|
||||||
defaultSopsFile = ./../../secrets/common.yaml;
|
defaultSopsFile = ./../../secrets/common.yaml;
|
||||||
secrets = {
|
secrets = {
|
||||||
|
|||||||
8
common/ham-radio.nix
Normal file
8
common/ham-radio.nix
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# ABOUTME: Ham radio tools profile for amateur radio operators.
|
||||||
|
# ABOUTME: Provides CLI tools for logging and processing ham radio contacts.
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
environment.systemPackages = [
|
||||||
|
pkgs.custom.flecli
|
||||||
|
];
|
||||||
|
}
|
||||||
32
common/netconsole-receiver.nix
Normal file
32
common/netconsole-receiver.nix
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
options.services.netconsoleReceiver = {
|
||||||
|
enable = lib.mkEnableOption "netconsole UDP receiver";
|
||||||
|
port = lib.mkOption {
|
||||||
|
type = lib.types.port;
|
||||||
|
default = 6666;
|
||||||
|
description = "UDP port to listen on for netconsole messages";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf config.services.netconsoleReceiver.enable {
|
||||||
|
systemd.services.netconsole-receiver = {
|
||||||
|
description = "Netconsole UDP receiver";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "network.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = "${pkgs.socat}/bin/socat -u UDP-LISTEN:${toString config.services.netconsoleReceiver.port},fork STDOUT";
|
||||||
|
StandardOutput = "journal";
|
||||||
|
StandardError = "journal";
|
||||||
|
SyslogIdentifier = "netconsole";
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = "5s";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -103,11 +103,14 @@ in
|
|||||||
] ++ (lib.forEach cfg.standbys (standby: {
|
] ++ (lib.forEach cfg.standbys (standby: {
|
||||||
"replicate-services-to-${standby}" = {
|
"replicate-services-to-${standby}" = {
|
||||||
description = "Replicate /persist/services to ${standby}";
|
description = "Replicate /persist/services to ${standby}";
|
||||||
path = [ pkgs.btrfs-progs pkgs.openssh pkgs.coreutils pkgs.findutils pkgs.gnugrep ];
|
path = [ pkgs.btrfs-progs pkgs.openssh pkgs.coreutils pkgs.findutils pkgs.gnugrep pkgs.curl ];
|
||||||
|
|
||||||
script = ''
|
script = ''
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
START_TIME=$(date +%s)
|
||||||
|
REPLICATION_SUCCESS=0
|
||||||
|
|
||||||
SSH_KEY="/persist/root/.ssh/btrfs-replication"
|
SSH_KEY="/persist/root/.ssh/btrfs-replication"
|
||||||
if [ ! -f "$SSH_KEY" ]; then
|
if [ ! -f "$SSH_KEY" ]; then
|
||||||
echo "ERROR: SSH key not found at $SSH_KEY"
|
echo "ERROR: SSH key not found at $SSH_KEY"
|
||||||
@@ -130,15 +133,19 @@ in
|
|||||||
echo "Attempting incremental send from $(basename $PREV_LOCAL) to ${standby}"
|
echo "Attempting incremental send from $(basename $PREV_LOCAL) to ${standby}"
|
||||||
|
|
||||||
# Try incremental send, if it fails (e.g., parent missing on receiver), fall back to full
|
# Try incremental send, if it fails (e.g., parent missing on receiver), fall back to full
|
||||||
if btrfs send -p "$PREV_LOCAL" "$SNAPSHOT_PATH" | \
|
# Use -c to help with broken Received UUID chains
|
||||||
|
if btrfs send -p "$PREV_LOCAL" -c "$PREV_LOCAL" "$SNAPSHOT_PATH" | \
|
||||||
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=accept-new root@${standby} \
|
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=accept-new root@${standby} \
|
||||||
"btrfs receive /persist/services-standby"; then
|
"btrfs receive /persist/services-standby"; then
|
||||||
echo "Incremental send completed successfully"
|
echo "Incremental send completed successfully"
|
||||||
|
REPLICATION_SUCCESS=1
|
||||||
else
|
else
|
||||||
echo "Incremental send failed (likely missing parent on receiver), falling back to full send"
|
echo "Incremental send failed (likely missing parent on receiver), falling back to full send"
|
||||||
|
# Plain full send without clone source (receiver may have no snapshots)
|
||||||
btrfs send "$SNAPSHOT_PATH" | \
|
btrfs send "$SNAPSHOT_PATH" | \
|
||||||
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=accept-new root@${standby} \
|
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=accept-new root@${standby} \
|
||||||
"btrfs receive /persist/services-standby"
|
"btrfs receive /persist/services-standby"
|
||||||
|
REPLICATION_SUCCESS=1
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# First snapshot, do full send
|
# First snapshot, do full send
|
||||||
@@ -146,10 +153,28 @@ in
|
|||||||
btrfs send "$SNAPSHOT_PATH" | \
|
btrfs send "$SNAPSHOT_PATH" | \
|
||||||
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=accept-new root@${standby} \
|
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=accept-new root@${standby} \
|
||||||
"btrfs receive /persist/services-standby"
|
"btrfs receive /persist/services-standby"
|
||||||
|
REPLICATION_SUCCESS=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Cleanup old snapshots on sender (keep last 24 hours = 288 snapshots at 5min intervals)
|
# Cleanup old snapshots on sender (keep last 10 snapshots, sorted by name/timestamp)
|
||||||
find /persist -maxdepth 1 -name 'services@*' -mmin +1440 -exec btrfs subvolume delete {} \;
|
ls -1d /persist/services@* 2>/dev/null | sort | head -n -10 | xargs -r btrfs subvolume delete
|
||||||
|
|
||||||
|
# Calculate metrics
|
||||||
|
END_TIME=$(date +%s)
|
||||||
|
DURATION=$((END_TIME - START_TIME))
|
||||||
|
SNAPSHOT_COUNT=$(ls -1d /persist/services@* 2>/dev/null | wc -l)
|
||||||
|
|
||||||
|
# Push metrics to Prometheus pushgateway
|
||||||
|
cat <<METRICS | curl -s --data-binary @- http://pushgateway.service.consul:9091/metrics/job/nfs_replication/instance/${standby} || true
|
||||||
|
# TYPE nfs_replication_last_success_timestamp gauge
|
||||||
|
nfs_replication_last_success_timestamp $END_TIME
|
||||||
|
# TYPE nfs_replication_duration_seconds gauge
|
||||||
|
nfs_replication_duration_seconds $DURATION
|
||||||
|
# TYPE nfs_replication_snapshot_count gauge
|
||||||
|
nfs_replication_snapshot_count $SNAPSHOT_COUNT
|
||||||
|
# TYPE nfs_replication_success gauge
|
||||||
|
nfs_replication_success $REPLICATION_SUCCESS
|
||||||
|
METRICS
|
||||||
'';
|
'';
|
||||||
|
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
|
|||||||
@@ -39,17 +39,28 @@ in
|
|||||||
noCheck = true;
|
noCheck = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
# Cleanup old snapshots on standby (keep last 4 hours for HA failover)
|
# Cleanup old snapshots on standby (keep last 10 snapshots)
|
||||||
systemd.services.cleanup-services-standby-snapshots = {
|
systemd.services.cleanup-services-standby-snapshots = {
|
||||||
description = "Cleanup old btrfs snapshots in services-standby";
|
description = "Cleanup old btrfs snapshots in services-standby";
|
||||||
path = [ pkgs.btrfs-progs pkgs.findutils ];
|
path = [ pkgs.btrfs-progs pkgs.findutils pkgs.coreutils pkgs.curl ];
|
||||||
|
|
||||||
script = ''
|
script = ''
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
# Keep last 4 hours of snapshots (48 snapshots at 5min intervals)
|
|
||||||
find /persist/services-standby -maxdepth 1 -name 'services@*' -mmin +240 -exec btrfs subvolume delete {} \; || true
|
|
||||||
'';
|
|
||||||
|
|
||||||
|
# Cleanup old snapshots on standby (keep last 10 snapshots, sorted by name/timestamp)
|
||||||
|
ls -1d /persist/services-standby/services@* 2>/dev/null | sort | head -n -10 | xargs -r btrfs subvolume delete || true
|
||||||
|
|
||||||
|
# Calculate metrics
|
||||||
|
CLEANUP_TIME=$(date +%s)
|
||||||
|
SNAPSHOT_COUNT=$(ls -1d /persist/services-standby/services@* 2>/dev/null | wc -l)
|
||||||
|
|
||||||
|
# Push metrics to Prometheus pushgateway
|
||||||
|
cat <<METRICS | curl -s --data-binary @- http://pushgateway.service.consul:9091/metrics/job/nfs_standby_cleanup/instance/$(hostname) || true
|
||||||
|
# TYPE nfs_standby_snapshot_count gauge
|
||||||
|
nfs_standby_snapshot_count $SNAPSHOT_COUNT
|
||||||
|
# TYPE nfs_standby_cleanup_last_run_timestamp gauge
|
||||||
|
nfs_standby_cleanup_last_run_timestamp $CLEANUP_TIME
|
||||||
|
METRICS
|
||||||
|
'';
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
User = "root";
|
User = "root";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
boot.loader.systemd-boot = {
|
boot.loader.systemd-boot = {
|
||||||
enable = true;
|
enable = true;
|
||||||
configurationLimit = 5;
|
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;
|
boot.loader.efi.canTouchEfiVariables = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH+QbeQG/gTPJ2sIMPgZ3ZPEirVo5qX/carbZMKt50YN petru@happy"
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH+QbeQG/gTPJ2sIMPgZ3ZPEirVo5qX/carbZMKt50YN petru@happy"
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOOQ2EcJ+T+7BItZl89oDYhq7ZW4B9KuQVCy2DuQaPKR ppetru@sparky"
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOOQ2EcJ+T+7BItZl89oDYhq7ZW4B9KuQVCy2DuQaPKR ppetru@sparky"
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFRYVOfrqk2nFSyiu7TzU23ql8D6TfXICFpMIEvPbNsc JuiceSSH"
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFRYVOfrqk2nFSyiu7TzU23ql8D6TfXICFpMIEvPbNsc JuiceSSH"
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINBIqK6+aPIbmviJPWP8PI/k8GmaC7RO8v2ENnsK8sJx ppetru@beefy"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
55
docs/AUTH_SETUP.md
Normal file
55
docs/AUTH_SETUP.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# Authentication Setup
|
||||||
|
|
||||||
|
SSO for homelab services using OIDC.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
**Pocket ID** (`pocket-id.v.paler.net`) - Lightweight OIDC provider, data in `/data/services/pocket-id`
|
||||||
|
|
||||||
|
**Traefik** - Uses `traefik-oidc-auth` plugin (v0.16.0) to protect services
|
||||||
|
- Plugin downloaded from GitHub at startup, cached in `/data/services/traefik/plugins-storage`
|
||||||
|
- Middleware config in `/data/services/traefik/rules/middlewares.yml`
|
||||||
|
- Protected services add tag: `traefik.http.routers.<name>.middlewares=oidc-auth@file`
|
||||||
|
|
||||||
|
## Flow
|
||||||
|
|
||||||
|
1. User hits protected service → Traefik intercepts
|
||||||
|
2. Redirects to Pocket ID for login
|
||||||
|
3. Pocket ID returns OIDC token
|
||||||
|
4. Traefik validates and forwards with `X-Oidc-Username` header
|
||||||
|
|
||||||
|
## Protected Services
|
||||||
|
|
||||||
|
Use `oidc-auth@file` middleware (grep codebase for full list):
|
||||||
|
- Wikis (TiddlyWiki instances)
|
||||||
|
- Media stack (Radarr, Sonarr, Plex, etc.)
|
||||||
|
- Infrastructure (Traefik dashboard, Loki, Jupyter, Unifi)
|
||||||
|
|
||||||
|
## Key Files
|
||||||
|
|
||||||
|
- `services/pocket-id.hcl` - OIDC provider
|
||||||
|
- `services/traefik.hcl` - Plugin declaration
|
||||||
|
- `/data/services/traefik/rules/middlewares.yml` - Middleware definitions (oidc-auth, simple-auth fallback)
|
||||||
|
|
||||||
|
## Cold Start Notes
|
||||||
|
|
||||||
|
- Traefik needs internet to download plugin on first start
|
||||||
|
- Pocket ID needs `/data/services` NFS mounted
|
||||||
|
- Pocket ID down = all protected services inaccessible
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**Infinite redirects**: Check `TRUST_PROXY=true` on Pocket ID
|
||||||
|
|
||||||
|
**Plugin not loading**: Clear cache in `/data/services/traefik/plugins-storage/`, restart Traefik
|
||||||
|
|
||||||
|
**401 after login**: Verify client ID/secret in middlewares.yml matches Pocket ID client config
|
||||||
|
|
||||||
|
## Migration History
|
||||||
|
|
||||||
|
- Previous: Authentik with forwardAuth (removed Nov 2024)
|
||||||
|
- Current: Pocket ID + traefik-oidc-auth (simpler, lighter)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Manage users/clients via Pocket ID UI. Basic auth fallback available via `simple-auth` middleware.*
|
||||||
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
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
* remote docker images used, can't come up if internet is down
|
* remote docker images used, can't come up if internet is down
|
||||||
* local docker images pulled from gitea, can't come up if gitea isn't up (yet)
|
* local docker images pulled from gitea, can't come up if gitea isn't up (yet)
|
||||||
|
* traefik-oidc-auth plugin downloaded from GitHub at startup (cached in /data/services/traefik/plugins-storage)
|
||||||
* renovate system of some kind
|
* 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
|
||||||
|
|||||||
710
flake.lock
generated
710
flake.lock
generated
@@ -1,42 +1,5 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"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": {
|
"base16-schemes": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
@@ -62,11 +25,11 @@
|
|||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1761420385,
|
"lastModified": 1769196967,
|
||||||
"narHash": "sha256-bfBSmKNHry6L/NGBmdymmEA5P/XLzuLDRw2kqaHDsLc=",
|
"narHash": "sha256-js2jXLzaZbXNFkYTszQntIS8QUJYJumSFK+3bR5nhlo=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "browser-previews",
|
"repo": "browser-previews",
|
||||||
"rev": "c5eae237f38310ed4c9bea0f6c19e0fe04ad61ef",
|
"rev": "edc3b1c0455abc74bfe2d6e029abe5fc778b0d62",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -84,11 +47,11 @@
|
|||||||
"utils": "utils"
|
"utils": "utils"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1756719547,
|
"lastModified": 1766051518,
|
||||||
"narHash": "sha256-N9gBKUmjwRKPxAafXEk1EGadfk2qDZPBQp4vXWPHINQ=",
|
"narHash": "sha256-znKOwPXQnt3o7lDb3hdf19oDo0BLP4MfBOYiWkEHoik=",
|
||||||
"owner": "serokell",
|
"owner": "serokell",
|
||||||
"repo": "deploy-rs",
|
"repo": "deploy-rs",
|
||||||
"rev": "125ae9e3ecf62fb2c0fd4f2d894eb971f1ecaed2",
|
"rev": "d5eff7f948535b9c723d60cd8239f8f11ddc90fa",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -105,11 +68,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1741473158,
|
"lastModified": 1768818222,
|
||||||
"narHash": "sha256-kWNaq6wQUbUMlPgw8Y+9/9wP0F8SHkjy24/mN3UAppg=",
|
"narHash": "sha256-460jc0+CZfyaO8+w8JNtlClB2n4ui1RbHfPTLkpwhU8=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "devshell",
|
"repo": "devshell",
|
||||||
"rev": "7c9e793ebe66bcba8292989a68c0419b737a22a0",
|
"rev": "255a2b1725a20d060f566e4755dbf571bbbb5f76",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -125,11 +88,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1760701190,
|
"lastModified": 1768923567,
|
||||||
"narHash": "sha256-y7UhnWlER8r776JsySqsbTUh2Txf7K30smfHlqdaIQw=",
|
"narHash": "sha256-GVJ0jKsyXLuBzRMXCDY6D5J8wVdwP1DuQmmvYL/Vw/Q=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "disko",
|
"repo": "disko",
|
||||||
"rev": "3a9450b26e69dcb6f8de6e2b07b3fc1c288d85f5",
|
"rev": "00395d188e3594a1507f214a2f15d4ce5c07cb28",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -141,7 +104,6 @@
|
|||||||
"ethereum-nix": {
|
"ethereum-nix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"devshell": "devshell",
|
"devshell": "devshell",
|
||||||
"flake-compat": "flake-compat_2",
|
|
||||||
"flake-parts": "flake-parts",
|
"flake-parts": "flake-parts",
|
||||||
"flake-utils": "flake-utils_2",
|
"flake-utils": "flake-utils_2",
|
||||||
"foundry-nix": "foundry-nix",
|
"foundry-nix": "foundry-nix",
|
||||||
@@ -153,11 +115,11 @@
|
|||||||
"treefmt-nix": "treefmt-nix"
|
"treefmt-nix": "treefmt-nix"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1761647152,
|
"lastModified": 1769298686,
|
||||||
"narHash": "sha256-zuciw00Auu3tNquWiznfIVxgVZepLsaIqC7cwC1+GQ4=",
|
"narHash": "sha256-ZwsxXeLyrb5VinFsdjrjt/J7Tp5O2A9yy7lxWaw/h78=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "ethereum.nix",
|
"repo": "ethereum.nix",
|
||||||
"rev": "7046ba564c47d7cb298493175ea3c3e9b1186c2e",
|
"rev": "d52663e0592ced611098f80224b45e57d7223453",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -182,47 +144,16 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-compat_2": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1746162366,
|
|
||||||
"narHash": "sha256-5SSSZ/oQkwfcAz/o/6TlejlVGqeK08wyREBQ5qFFPhM=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "flake-compat",
|
|
||||||
"rev": "0f158086a2ecdbb138cd0429410e44994f1b7e4b",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "flake-compat",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-compat_3": {
|
|
||||||
"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": {
|
"flake-parts": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs-lib": "nixpkgs-lib"
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1760948891,
|
"lastModified": 1768135262,
|
||||||
"narHash": "sha256-TmWcdiUUaWk8J4lpjzu4gCGxWY6/Ok7mOK4fIFfBuU4=",
|
"narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "864599284fc7c0ba6357ed89ed5e2cd5040f0c04",
|
"rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -239,11 +170,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1760948891,
|
"lastModified": 1768135262,
|
||||||
"narHash": "sha256-TmWcdiUUaWk8J4lpjzu4gCGxWY6/Ok7mOK4fIFfBuU4=",
|
"narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "864599284fc7c0ba6357ed89ed5e2cd5040f0c04",
|
"rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -294,24 +225,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-utils_3": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems_4"
|
|
||||||
},
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"foundry-nix": {
|
"foundry-nix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": [
|
"flake-utils": [
|
||||||
@@ -324,11 +237,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1759569036,
|
"lastModified": 1767517855,
|
||||||
"narHash": "sha256-FuxbXLDArxD1NeRR8zNnsb8Xww5/+qdMwzN1m8Kow/M=",
|
"narHash": "sha256-LnZosb07bahYAyFw07JFzSXslx9j1dCe+npWDZdPFZg=",
|
||||||
"owner": "shazow",
|
"owner": "shazow",
|
||||||
"repo": "foundry.nix",
|
"repo": "foundry.nix",
|
||||||
"rev": "47ba6d3b02bf3faaa857d3572df82ff186d5279a",
|
"rev": "ee376e8a93f537c2865dda9811e748e4567a7aaf",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -338,29 +251,6 @@
|
|||||||
"type": "github"
|
"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": {
|
"home-manager": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -368,341 +258,57 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1758463745,
|
"lastModified": 1768949235,
|
||||||
"narHash": "sha256-uhzsV0Q0I9j2y/rfweWeGif5AWe0MGrgZ/3TjpDYdGA=",
|
"narHash": "sha256-TtjKgXyg1lMfh374w5uxutd6Vx2P/hU81aEhTxrO2cg=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "3b955f5f0a942f9f60cdc9cacb7844335d0f21c3",
|
"rev": "75ed713570ca17427119e7e204ab3590cc3bf2a5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"ref": "release-25.05",
|
"ref": "release-25.11",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hyprcursor": {
|
"home-manager_2": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"hyprlang": [
|
|
||||||
"omarchy-nix",
|
|
||||||
"hyprland",
|
|
||||||
"hyprlang"
|
|
||||||
],
|
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"omarchy-nix",
|
"impermanence",
|
||||||
"hyprland",
|
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
|
||||||
"systems": [
|
|
||||||
"omarchy-nix",
|
|
||||||
"hyprland",
|
|
||||||
"systems"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1753964049,
|
"lastModified": 1768598210,
|
||||||
"narHash": "sha256-lIqabfBY7z/OANxHoPeIrDJrFyYy9jAM4GQLzZ2feCM=",
|
"narHash": "sha256-kkgA32s/f4jaa4UG+2f8C225Qvclxnqs76mf8zvTVPg=",
|
||||||
"owner": "hyprwm",
|
"owner": "nix-community",
|
||||||
"repo": "hyprcursor",
|
"repo": "home-manager",
|
||||||
"rev": "44e91d467bdad8dcf8bbd2ac7cf49972540980a5",
|
"rev": "c47b2cc64a629f8e075de52e4742de688f930dc6",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "hyprwm",
|
"owner": "nix-community",
|
||||||
"repo": "hyprcursor",
|
"repo": "home-manager",
|
||||||
"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_6",
|
|
||||||
"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",
|
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"impermanence": {
|
"impermanence": {
|
||||||
"locked": {
|
|
||||||
"lastModified": 1737831083,
|
|
||||||
"narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "impermanence",
|
|
||||||
"rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "impermanence",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ixx": {
|
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": [
|
"home-manager": "home-manager_2",
|
||||||
"nixvim",
|
"nixpkgs": "nixpkgs"
|
||||||
"nuschtosSearch",
|
|
||||||
"flake-utils"
|
|
||||||
],
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixvim",
|
|
||||||
"nuschtosSearch",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1754860581,
|
"lastModified": 1768941735,
|
||||||
"narHash": "sha256-EM0IE63OHxXCOpDHXaTyHIOk2cNvMCGPqLt/IdtVxgk=",
|
"narHash": "sha256-OyxsfXNcOkt06/kM+4bnuC8moDx+t7Qr+RB0BBa83Ig=",
|
||||||
"owner": "NuschtOS",
|
"owner": "nix-community",
|
||||||
"repo": "ixx",
|
"repo": "impermanence",
|
||||||
"rev": "babfe85a876162c4acc9ab6fb4483df88fa1f281",
|
"rev": "69ecf31e8fddc9354a4b418f3a517445d486bb54",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NuschtOS",
|
"owner": "nix-community",
|
||||||
"ref": "v0.1.1",
|
"repo": "impermanence",
|
||||||
"repo": "ixx",
|
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -732,11 +338,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1761451000,
|
"lastModified": 1765267181,
|
||||||
"narHash": "sha256-qBJL6xEIjqYq9zOcG2vf2nPTeVBppNJzvO0LuQWMwMo=",
|
"narHash": "sha256-d3NBA9zEtBu2JFMnTBqWj7Tmi7R5OikoU2ycrdhQEws=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nix-index-database",
|
"repo": "nix-index-database",
|
||||||
"rev": "ed6b293161b378a7368cda38659eb8d3d9a0dac4",
|
"rev": "82befcf7dc77c909b0f2a09f5da910ec95c5b78f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -747,11 +353,11 @@
|
|||||||
},
|
},
|
||||||
"nixos-hardware": {
|
"nixos-hardware": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1760958188,
|
"lastModified": 1769302137,
|
||||||
"narHash": "sha256-2m1S4jl+GEDtlt2QqeHil8Ny456dcGSKJAM7q3j/BFU=",
|
"narHash": "sha256-QEDtctEkOsbx8nlFh4yqPEOtr4tif6KTqWwJ37IM2ds=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixos-hardware",
|
"repo": "nixos-hardware",
|
||||||
"rev": "d6645c340ef7d821602fd2cd199e8d1eed10afbc",
|
"rev": "a351494b0e35fd7c0b7a1aae82f0afddf4907aa8",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -763,27 +369,27 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1761468971,
|
"lastModified": 1768564909,
|
||||||
"narHash": "sha256-vY2OLVg5ZTobdroQKQQSipSIkHlxOTrIF1fsMzPh8w8=",
|
"narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=",
|
||||||
"owner": "NixOS",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "78e34d1667d32d8a0ffc3eba4591ff256e80576e",
|
"rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "nixos",
|
||||||
"ref": "nixos-25.05",
|
"ref": "nixos-unstable",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-lib": {
|
"nixpkgs-lib": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1754788789,
|
"lastModified": 1765674936,
|
||||||
"narHash": "sha256-x2rJ+Ovzq0sCMpgfgGaaqgBSwY+LST+WbZ6TytnT9Rk=",
|
"narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nixpkgs.lib",
|
"repo": "nixpkgs.lib",
|
||||||
"rev": "a73b9c743612e4244d865a2fdee11865283c04e6",
|
"rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -809,11 +415,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unstable": {
|
"nixpkgs-unstable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1761349956,
|
"lastModified": 1769092226,
|
||||||
"narHash": "sha256-tH3wHnOJms+U4k/rK2Nn1RfBrhffX92jLP/2VndSn0w=",
|
"narHash": "sha256-6h5sROT/3CTHvzPy9koKBmoCa2eJKh4fzQK8eYFEgl8=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "02f2cb8e0feb4596d20cc52fda73ccee960e3538",
|
"rev": "b579d443b37c9c5373044201ea77604e37e748c8",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -825,11 +431,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unstable_2": {
|
"nixpkgs-unstable_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1761373498,
|
"lastModified": 1769170682,
|
||||||
"narHash": "sha256-Q/uhWNvd7V7k1H1ZPMy/vkx3F8C13ZcdrKjO7Jv7v0c=",
|
"narHash": "sha256-oMmN1lVQU0F0W2k6OI3bgdzp2YOHWYUAw79qzDSjenU=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "6a08e6bb4e46ff7fcbb53d409b253f6bad8a28ce",
|
"rev": "c5296fdd05cfa2c187990dd909864da9658df755",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -841,16 +447,16 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1754725699,
|
"lastModified": 1769089682,
|
||||||
"narHash": "sha256-iAcj9T/Y+3DBy2J0N+yF9XQQQ8IEb5swLFzs23CdP88=",
|
"narHash": "sha256-9yA/LIuAVQq0lXelrZPjLuLVuZdm03p8tfmHhnDIkms=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "85dbfc7aaf52ecb755f87e577ddbe6dbbdbc1054",
|
"rev": "078d69f03934859a181e81ba987c2bb033eebfc5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-unstable",
|
"ref": "nixos-25.11",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@@ -861,15 +467,14 @@
|
|||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs-unstable"
|
"nixpkgs-unstable"
|
||||||
],
|
],
|
||||||
"nuschtosSearch": "nuschtosSearch",
|
"systems": "systems_4"
|
||||||
"systems": "systems_5"
|
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1761657569,
|
"lastModified": 1769247851,
|
||||||
"narHash": "sha256-2D4Tw5Vp52RU5amnBvq0/z+zgZqafwl4bhg8dJBBjXI=",
|
"narHash": "sha256-fbsopU0qWfqq1WRKjWYpYCMxmEYyq+Cmw++VXVke5Ns=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nixvim",
|
"repo": "nixvim",
|
||||||
"rev": "03c0dabb9a63f52bc2ebf571f3755720df1ca81e",
|
"rev": "34a7d94cdcd2b034eb06202992bed1345aa046c9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -878,78 +483,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nuschtosSearch": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-utils": "flake-utils_3",
|
|
||||||
"ixx": "ixx",
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixvim",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1760652422,
|
|
||||||
"narHash": "sha256-C88Pgz38QIl9JxQceexqL2G7sw9vodHWx1Uaq+NRJrw=",
|
|
||||||
"owner": "NuschtOS",
|
|
||||||
"repo": "search",
|
|
||||||
"rev": "3ebeebe8b6a49dfb11f771f761e0310f7c48d726",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NuschtOS",
|
|
||||||
"repo": "search",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"omarchy-nix": {
|
|
||||||
"inputs": {
|
|
||||||
"home-manager": [
|
|
||||||
"home-manager"
|
|
||||||
],
|
|
||||||
"hyprland": "hyprland",
|
|
||||||
"nix-colors": "nix-colors",
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1760558991,
|
|
||||||
"narHash": "sha256-E8MMVwy7QNURBtCLiCjFXfv7uZUEg6QVSZLu4q9YGpk=",
|
|
||||||
"owner": "henrysipp",
|
|
||||||
"repo": "omarchy-nix",
|
|
||||||
"rev": "fba993c589920fbe68d9f7918e52903c476adad2",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "henrysipp",
|
|
||||||
"repo": "omarchy-nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pre-commit-hooks": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-compat": "flake-compat_3",
|
|
||||||
"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": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"browser-previews": "browser-previews",
|
"browser-previews": "browser-previews",
|
||||||
@@ -958,12 +491,12 @@
|
|||||||
"ethereum-nix": "ethereum-nix",
|
"ethereum-nix": "ethereum-nix",
|
||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
"impermanence": "impermanence",
|
"impermanence": "impermanence",
|
||||||
|
"nix-colors": "nix-colors",
|
||||||
"nix-index-database": "nix-index-database",
|
"nix-index-database": "nix-index-database",
|
||||||
"nixos-hardware": "nixos-hardware",
|
"nixos-hardware": "nixos-hardware",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs_2",
|
||||||
"nixpkgs-unstable": "nixpkgs-unstable_2",
|
"nixpkgs-unstable": "nixpkgs-unstable_2",
|
||||||
"nixvim": "nixvim",
|
"nixvim": "nixvim",
|
||||||
"omarchy-nix": "omarchy-nix",
|
|
||||||
"sops-nix": "sops-nix"
|
"sops-nix": "sops-nix"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -974,11 +507,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1760998189,
|
"lastModified": 1769314333,
|
||||||
"narHash": "sha256-ee2e1/AeGL5X8oy/HXsZQvZnae6XfEVdstGopKucYLY=",
|
"narHash": "sha256-+Uvq9h2eGsbhacXpuS7irYO7fFlz514nrhPCSTkASlw=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "5a7d18b5c55642df5c432aadb757140edfeb70b3",
|
"rev": "2eb9eed7ef48908e0f02985919f7eb9d33fa758f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1047,36 +580,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"systems_5": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems_6": {
|
|
||||||
"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": {
|
"treefmt-nix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -1085,11 +588,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1761311587,
|
"lastModified": 1768158989,
|
||||||
"narHash": "sha256-Msq86cR5SjozQGCnC6H8C+0cD4rnx91BPltZ9KK613Y=",
|
"narHash": "sha256-67vyT1+xClLldnumAzCTBvU0jLZ1YBcf4vANRWP3+Ak=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "treefmt-nix",
|
"repo": "treefmt-nix",
|
||||||
"rev": "2eddae033e4e74bf581c2d1dfa101f9033dbd2dc",
|
"rev": "e96d59dff5c0d7fddb9d113ba108f03c3ef99eca",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1115,53 +618,6 @@
|
|||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"type": "github"
|
"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",
|
"root": "root",
|
||||||
|
|||||||
84
flake.nix
84
flake.nix
@@ -5,7 +5,7 @@
|
|||||||
deploy-rs.url = "github:serokell/deploy-rs";
|
deploy-rs.url = "github:serokell/deploy-rs";
|
||||||
deploy-rs.inputs.nixpkgs.follows = "nixpkgs";
|
deploy-rs.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
impermanence.url = "github:nix-community/impermanence";
|
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";
|
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
disko.url = "github:nix-community/disko";
|
disko.url = "github:nix-community/disko";
|
||||||
disko.inputs.nixpkgs.follows = "nixpkgs";
|
disko.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
inputs.nixpkgs.follows = "nixpkgs-unstable";
|
inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
};
|
};
|
||||||
home-manager = {
|
home-manager = {
|
||||||
url = "github:nix-community/home-manager/release-25.05";
|
url = "github:nix-community/home-manager/release-25.11";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
nix-index-database = {
|
nix-index-database = {
|
||||||
@@ -33,11 +33,7 @@
|
|||||||
url = "github:nix-community/browser-previews";
|
url = "github:nix-community/browser-previews";
|
||||||
inputs.nixpkgs.follows = "nixpkgs-unstable";
|
inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
};
|
};
|
||||||
omarchy-nix = {
|
nix-colors.url = "github:misterio77/nix-colors";
|
||||||
url = "github:henrysipp/omarchy-nix";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
inputs.home-manager.follows = "home-manager";
|
|
||||||
};
|
|
||||||
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
|
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -53,7 +49,7 @@
|
|||||||
impermanence,
|
impermanence,
|
||||||
sops-nix,
|
sops-nix,
|
||||||
browser-previews,
|
browser-previews,
|
||||||
omarchy-nix,
|
nix-colors,
|
||||||
nixos-hardware,
|
nixos-hardware,
|
||||||
...
|
...
|
||||||
}@inputs:
|
}@inputs:
|
||||||
@@ -62,15 +58,17 @@
|
|||||||
|
|
||||||
overlay-unstable = final: prev: {
|
overlay-unstable = final: prev: {
|
||||||
unstable = import nixpkgs-unstable {
|
unstable = import nixpkgs-unstable {
|
||||||
inherit (prev) system;
|
system = prev.stdenv.hostPlatform.system;
|
||||||
config.allowUnfree = true;
|
config.allowUnfree = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
overlay-browser-previews = final: prev: {
|
overlay-browser-previews = final: prev: {
|
||||||
browser-previews = browser-previews.packages.${prev.system};
|
browser-previews = browser-previews.packages.${prev.stdenv.hostPlatform.system};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
overlay-custom = import ./pkgs;
|
||||||
|
|
||||||
mkHost =
|
mkHost =
|
||||||
system: profile: modules:
|
system: profile: modules:
|
||||||
let
|
let
|
||||||
@@ -83,7 +81,7 @@
|
|||||||
(
|
(
|
||||||
{ config, pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
{
|
{
|
||||||
nixpkgs.overlays = [ overlay-unstable overlay-browser-previews ];
|
nixpkgs.overlays = [ overlay-unstable overlay-browser-previews overlay-custom ];
|
||||||
nixpkgs.config.allowUnfree = true;
|
nixpkgs.config.allowUnfree = true;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -93,37 +91,27 @@
|
|||||||
home-manager.nixosModules.home-manager
|
home-manager.nixosModules.home-manager
|
||||||
(
|
(
|
||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
lib.mkMerge [
|
{
|
||||||
{
|
home-manager = {
|
||||||
home-manager = {
|
useGlobalPkgs = true;
|
||||||
useGlobalPkgs = true;
|
useUserPackages = true;
|
||||||
useUserPackages = true;
|
users.ppetru = {
|
||||||
users.ppetru = {
|
imports = [
|
||||||
imports = [
|
inputs.nix-index-database.homeModules.nix-index
|
||||||
inputs.nix-index-database.homeModules.nix-index
|
inputs.nixvim.homeModules.nixvim
|
||||||
inputs.nixvim.homeModules.nixvim
|
./home
|
||||||
./home
|
] ++ lib.optionals (profile == "desktop") [
|
||||||
] ++ lib.optionals (profile == "desktop") [
|
nix-colors.homeManagerModules.default
|
||||||
omarchy-nix.homeManagerModules.default
|
];
|
||||||
];
|
|
||||||
};
|
|
||||||
extraSpecialArgs = {
|
|
||||||
inherit profile;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
extraSpecialArgs = {
|
||||||
(lib.optionalAttrs (profile == "desktop") {
|
inherit profile nix-colors;
|
||||||
omarchy = {
|
|
||||||
full_name = "Petru Paler";
|
|
||||||
email_address = "petru@paler.net";
|
|
||||||
theme = "tokyo-night";
|
|
||||||
monitors = [ "DP-1,preferred,auto,1.5" ];
|
|
||||||
};
|
};
|
||||||
})
|
};
|
||||||
]
|
}
|
||||||
)
|
)
|
||||||
] ++ nixpkgs.lib.optionals (profile == "desktop") [
|
] ++ nixpkgs.lib.optionals (profile == "desktop") [
|
||||||
omarchy-nix.nixosModules.default
|
./common/desktop
|
||||||
] ++ modules;
|
] ++ modules;
|
||||||
specialArgs = {
|
specialArgs = {
|
||||||
inherit inputs self;
|
inherit inputs self;
|
||||||
@@ -159,12 +147,10 @@
|
|||||||
c2 = mkHost "x86_64-linux" "minimal" [ ./hosts/c2 ];
|
c2 = mkHost "x86_64-linux" "minimal" [ ./hosts/c2 ];
|
||||||
c3 = mkHost "x86_64-linux" "minimal" [ ./hosts/c3 ];
|
c3 = mkHost "x86_64-linux" "minimal" [ ./hosts/c3 ];
|
||||||
alo-cloud-1 = mkHost "aarch64-linux" "cloud" [ ./hosts/alo-cloud-1 ];
|
alo-cloud-1 = mkHost "aarch64-linux" "cloud" [ ./hosts/alo-cloud-1 ];
|
||||||
zippy = mkHost "x86_64-linux" "minimal" [
|
zippy = mkHost "x86_64-linux" "minimal" [ ./hosts/zippy ];
|
||||||
ethereum-nix.nixosModules.default
|
|
||||||
./hosts/zippy
|
|
||||||
];
|
|
||||||
chilly = mkHost "x86_64-linux" "workstation" [ ./hosts/chilly ];
|
chilly = mkHost "x86_64-linux" "workstation" [ ./hosts/chilly ];
|
||||||
sparky = mkHost "x86_64-linux" "desktop" [ ./hosts/sparky ];
|
sparky = mkHost "x86_64-linux" "minimal" [ ./hosts/sparky ];
|
||||||
|
beefy = mkHost "x86_64-linux" "desktop" [ ./hosts/beefy ];
|
||||||
stinky = mkHost "aarch64-linux" "minimal" [
|
stinky = mkHost "aarch64-linux" "minimal" [
|
||||||
nixos-hardware.nixosModules.raspberry-pi-4
|
nixos-hardware.nixosModules.raspberry-pi-4
|
||||||
./hosts/stinky
|
./hosts/stinky
|
||||||
@@ -195,7 +181,8 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
alo-cloud-1 = {
|
alo-cloud-1 = {
|
||||||
hostname = "49.13.163.72";
|
hostname = "alo-cloud-1";
|
||||||
|
#hostname = "49.13.163.72";
|
||||||
profiles = {
|
profiles = {
|
||||||
system = {
|
system = {
|
||||||
user = "root";
|
user = "root";
|
||||||
@@ -230,6 +217,15 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
beefy = {
|
||||||
|
hostname = "beefy";
|
||||||
|
profiles = {
|
||||||
|
system = {
|
||||||
|
user = "root";
|
||||||
|
path = (deployPkgsFor "x86_64-linux").deploy-rs.lib.activate.nixos self.nixosConfigurations.beefy;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
stinky = {
|
stinky = {
|
||||||
hostname = "stinky";
|
hostname = "stinky";
|
||||||
profiles = {
|
profiles = {
|
||||||
|
|||||||
@@ -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 = {
|
home = {
|
||||||
packages = (import ./packages.nix { inherit pkgs profile; }).packages;
|
packages = (import ./packages.nix { inherit pkgs profile; }).packages;
|
||||||
@@ -12,7 +20,7 @@
|
|||||||
MOSH_SERVER_NETWORK_TMOUT = 604800;
|
MOSH_SERVER_NETWORK_TMOUT = 604800;
|
||||||
NOMAD_ADDR = "http://nomad.service.consul:4646";
|
NOMAD_ADDR = "http://nomad.service.consul:4646";
|
||||||
LESS = "-F -i -M -+S -R -w -X -z-4";
|
LESS = "-F -i -M -+S -R -w -X -z-4";
|
||||||
SYSTEMD_LESS = "FiM+SRwXz-4";
|
SYSTEMD_LESS = "FiM+SRwX";
|
||||||
NIX_LD = "${pkgs.glibc}/lib/ld-linux-x86-64.so.2";
|
NIX_LD = "${pkgs.glibc}/lib/ld-linux-x86-64.so.2";
|
||||||
NIX_LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
|
NIX_LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
|
||||||
pkgs.stdenv.cc.cc
|
pkgs.stdenv.cc.cc
|
||||||
@@ -26,5 +34,13 @@
|
|||||||
shellAliases = {
|
shellAliases = {
|
||||||
reload-home-manager-config = "home-manager switch --flake ${builtins.toString ./.}";
|
reload-home-manager-config = "home-manager switch --flake ${builtins.toString ./.}";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
file.".ssh/rc".text = ''
|
||||||
|
#!/bin/sh
|
||||||
|
if test "$SSH_AUTH_SOCK"; then
|
||||||
|
ln -sf "$SSH_AUTH_SOCK" "$HOME/.ssh/ssh_auth_sock"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
file.".ssh/rc".executable = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,31 @@
|
|||||||
|
# ABOUTME: Desktop profile package list
|
||||||
|
# ABOUTME: Extends workstation with GUI and Wayland tools
|
||||||
{ pkgs }:
|
{ pkgs }:
|
||||||
let
|
let
|
||||||
workstationProfile = import ./workstation.nix { inherit pkgs; };
|
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; [
|
desktopPkgs = with pkgs; [
|
||||||
browser-previews.google-chrome
|
browser-previews.google-chrome
|
||||||
foot # Wayland-native terminal emulator
|
nautilus
|
||||||
wofi # Application launcher for Wayland
|
blueberry
|
||||||
|
libnotify
|
||||||
];
|
];
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
packages = workstationProfile.packages ++ desktopPkgs;
|
packages = workstationProfile.packages ++ hyprlandPkgs ++ desktopPkgs;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ let
|
|||||||
serverProfile = import ./server.nix { inherit pkgs; };
|
serverProfile = import ./server.nix { inherit pkgs; };
|
||||||
|
|
||||||
cliPkgs = with pkgs; [
|
cliPkgs = with pkgs; [
|
||||||
|
ast-grep
|
||||||
|
yq
|
||||||
|
unstable.amp-cli
|
||||||
|
unstable.beads
|
||||||
unstable.claude-code
|
unstable.claude-code
|
||||||
unstable.codex
|
unstable.codex
|
||||||
unstable.gemini-cli
|
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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -327,7 +327,6 @@
|
|||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
shellAbbrs = {
|
shellAbbrs = {
|
||||||
fix-ssh = "eval $(tmux show-env | grep ^SSH_AUTH_SOCK | sed 's/=/ /;s/^/set /')";
|
|
||||||
diff-persist = "sudo rsync -amvxx --dry-run --no-links --exclude '/tmp/*' --exclude '/root/*' / /persist/ | rg -v '^skipping|/$'";
|
diff-persist = "sudo rsync -amvxx --dry-run --no-links --exclude '/tmp/*' --exclude '/root/*' / /persist/ | rg -v '^skipping|/$'";
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -340,6 +339,16 @@
|
|||||||
set pure_show_prefix_root_prompt true
|
set pure_show_prefix_root_prompt true
|
||||||
set sponge_regex_patterns 'password|passwd'
|
set sponge_regex_patterns 'password|passwd'
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
functions = {
|
||||||
|
brain = ''
|
||||||
|
echo "🧠 Brain session starting..."
|
||||||
|
echo " • wrap - end session with notes"
|
||||||
|
echo " • inbox: <thought> - quick capture"
|
||||||
|
echo ""
|
||||||
|
cd ~/brain && claude
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fzf = {
|
fzf = {
|
||||||
@@ -348,8 +357,12 @@
|
|||||||
|
|
||||||
git = {
|
git = {
|
||||||
enable = true;
|
enable = true;
|
||||||
userEmail = "petru@paler.net";
|
settings = {
|
||||||
userName = "Petru Paler";
|
user = {
|
||||||
|
email = "petru@paler.net";
|
||||||
|
name = "Petru Paler";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
home-manager = {
|
home-manager = {
|
||||||
@@ -398,6 +411,12 @@
|
|||||||
|
|
||||||
setw -g automatic-rename on
|
setw -g automatic-rename on
|
||||||
set -g set-titles on
|
set -g set-titles on
|
||||||
|
|
||||||
|
# first, unset update-environment[SSH_AUTH_SOCK] (idx 3), to prevent
|
||||||
|
# the client overriding the global value
|
||||||
|
set-option -g -u update-environment[3]
|
||||||
|
# And set the global value to our static symlink'd path:
|
||||||
|
set-environment -g SSH_AUTH_SOCK $HOME/.ssh/ssh_auth_sock
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{ pkgs, inputs, ... }:
|
{ pkgs, lib, inputs, ... }:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
../../common/global
|
../../common/global
|
||||||
../../common/cloud-node.nix # Minimal system with Consul
|
../../common/minimal-node.nix
|
||||||
./hardware.nix
|
./hardware.nix
|
||||||
./reverse-proxy.nix
|
./reverse-proxy.nix
|
||||||
];
|
];
|
||||||
@@ -12,4 +12,27 @@
|
|||||||
|
|
||||||
networking.hostName = "alo-cloud-1";
|
networking.hostName = "alo-cloud-1";
|
||||||
services.tailscaleAutoconnect.authkey = "tskey-auth-kbdARC7CNTRL-pNQddmWV9q5C2sRV3WGep5ehjJ1qvcfD";
|
services.tailscaleAutoconnect.authkey = "tskey-auth-kbdARC7CNTRL-pNQddmWV9q5C2sRV3WGep5ehjJ1qvcfD";
|
||||||
|
|
||||||
|
services.tailscale = {
|
||||||
|
enable = true;
|
||||||
|
useRoutingFeatures = lib.mkForce "server"; # enables IPv4/IPv6 forwarding + loose rp_filter
|
||||||
|
extraUpFlags = [ "--advertise-exit-node" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.nat = {
|
||||||
|
enable = true;
|
||||||
|
externalInterface = "enp1s0";
|
||||||
|
internalInterfaces = [ "tailscale0" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall = {
|
||||||
|
enable = lib.mkForce true;
|
||||||
|
allowedTCPPorts = [ 80 443 ]; # Public web traffic only
|
||||||
|
allowedUDPPorts = [ 41641 ]; # Tailscale
|
||||||
|
trustedInterfaces = [ "tailscale0" ]; # Full access via VPN
|
||||||
|
};
|
||||||
|
|
||||||
|
services.openssh = {
|
||||||
|
settings.PasswordAuthentication = false; # Keys only
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
77
hosts/beefy/default.nix
Normal file
77
hosts/beefy/default.nix
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
{ pkgs, inputs, config, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
../../common/encrypted-btrfs-layout.nix
|
||||||
|
../../common/global
|
||||||
|
# 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)
|
||||||
|
../../common/docker.nix # Docker daemon
|
||||||
|
../../common/ham-radio.nix # Ham radio tools (FLEcli)
|
||||||
|
./hardware.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
diskLayout = {
|
||||||
|
mainDiskDevice = "/dev/disk/by-id/nvme-CT1000P3PSSD8_25164F81F31D";
|
||||||
|
#keyDiskDevice = "/dev/disk/by-id/usb-Intenso_Micro_Line_22080777650797-0:0";
|
||||||
|
keyDiskDevice = "/dev/sda";
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.hostName = "beefy";
|
||||||
|
networking.cluster.primaryInterface = "enp1s0";
|
||||||
|
services.tailscaleAutoconnect.authkey = "tskey-auth-k79UsDTw2v11CNTRL-oYqji35BE9c7CqM89Dzs9cBF14PmqYsi";
|
||||||
|
|
||||||
|
# Console blanking after 5 minutes (for greeter display sleep)
|
||||||
|
# NMI watchdog for hardlockup detection
|
||||||
|
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.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 = {
|
||||||
|
enable = true;
|
||||||
|
reservedMemory = "256M";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Lockup detectors - panic on detection so kdump captures state
|
||||||
|
boot.kernel.sysctl = {
|
||||||
|
# Enable all SysRq functions for debugging hangs
|
||||||
|
"kernel.sysrq" = 1;
|
||||||
|
# Panic on soft lockup (CPU not scheduling for >20s)
|
||||||
|
"kernel.softlockup_panic" = 1;
|
||||||
|
# Panic on hung tasks (blocked >120s)
|
||||||
|
"kernel.hung_task_panic" = 1;
|
||||||
|
"kernel.hung_task_timeout_secs" = 120;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Persist crash dumps
|
||||||
|
environment.persistence.${config.custom.impermanence.persistPath}.directories = [
|
||||||
|
"/var/crash"
|
||||||
|
];
|
||||||
|
}
|
||||||
19
hosts/beefy/hardware.nix
Normal file
19
hosts/beefy/hardware.nix
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
modulesPath,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
|
||||||
|
|
||||||
|
boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "usbhid" "usb_storage" "sd_mod" ];
|
||||||
|
boot.initrd.kernelModules = [ ];
|
||||||
|
boot.kernelModules = [ "kvm-amd" ];
|
||||||
|
boot.extraModulePackages = [ ];
|
||||||
|
|
||||||
|
nixpkgs.hostPlatform = "x86_64-linux";
|
||||||
|
hardware.cpu.amd.updateMicrocode = true; # Uncomment for AMD
|
||||||
|
}
|
||||||
BIN
hosts/beefy/key.bin
Normal file
BIN
hosts/beefy/key.bin
Normal file
Binary file not shown.
@@ -23,8 +23,8 @@
|
|||||||
networking.hostName = "c1";
|
networking.hostName = "c1";
|
||||||
services.tailscaleAutoconnect.authkey = "tskey-auth-k2nQ771YHM11CNTRL-YVpoumL2mgR6nLPG51vNhRpEKMDN7gLAi";
|
services.tailscaleAutoconnect.authkey = "tskey-auth-k2nQ771YHM11CNTRL-YVpoumL2mgR6nLPG51vNhRpEKMDN7gLAi";
|
||||||
|
|
||||||
# NFS standby configuration: accept replication from zippy
|
|
||||||
nfsServicesStandby.replicationKeys = [
|
nfsServicesStandby.replicationKeys = [
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHyTKsMCbwCIlMcC/aopgz5Yfx/Q9QdlWC9jzMLgYFAV root@zippy-replication"
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHyTKsMCbwCIlMcC/aopgz5Yfx/Q9QdlWC9jzMLgYFAV root@zippy-replication"
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO5s73FSUiysHijWRGYCJY8lCtZkX1DGKAqp2671REDq root@sparky-replication"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,11 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
networking.hostName = "chilly";
|
networking.hostName = "chilly";
|
||||||
|
networking.cluster.primaryInterface = "br0";
|
||||||
services.tailscaleAutoconnect.authkey = "tskey-auth-kRXS9oPyPm11CNTRL-BE6YnbP9J6ZZuV9dHkX17ZMnm1JGdu93";
|
services.tailscaleAutoconnect.authkey = "tskey-auth-kRXS9oPyPm11CNTRL-BE6YnbP9J6ZZuV9dHkX17ZMnm1JGdu93";
|
||||||
services.consul.interface.advertise = lib.mkForce "br0";
|
|
||||||
|
|
||||||
networking.useNetworkd = true;
|
networking.useNetworkd = true;
|
||||||
systemd.network.enable = true;
|
systemd.network.enable = true;
|
||||||
# Wait for br0 to be routable before considering network online
|
|
||||||
systemd.network.wait-online.extraArgs = [ "--interface=br0:routable" ];
|
|
||||||
# not useful and potentially a security loophole
|
# not useful and potentially a security loophole
|
||||||
services.resolved.llmnr = "false";
|
services.resolved.llmnr = "false";
|
||||||
|
|
||||||
|
|||||||
@@ -3,18 +3,24 @@
|
|||||||
imports = [
|
imports = [
|
||||||
../../common/encrypted-btrfs-layout.nix
|
../../common/encrypted-btrfs-layout.nix
|
||||||
../../common/global
|
../../common/global
|
||||||
../../common/desktop-node.nix # Hyprland + GUI environment
|
../../common/cluster-member.nix
|
||||||
../../common/cluster-member.nix # Consul + storage clients
|
../../common/nomad-worker.nix
|
||||||
../../common/cluster-tools.nix # Nomad CLI (no service)
|
../../common/nfs-services-server.nix
|
||||||
|
# To move NFS server role to another host:
|
||||||
|
# 1. Follow procedure in docs/NFS_FAILOVER.md
|
||||||
|
# 2. Replace above line with: ../../common/nfs-services-standby.nix
|
||||||
|
# 3. Add nfsServicesStandby.replicationKeys with the new server's public key
|
||||||
./hardware.nix
|
./hardware.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
diskLayout = {
|
diskLayout = {
|
||||||
mainDiskDevice = "/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_Plus_250GB_S4EUNF0MA33640P";
|
mainDiskDevice = "/dev/disk/by-id/nvme-KIOXIA-EXCERIA_with_Heatsink_SSD_84GF7016FA4S";
|
||||||
#keyDiskDevice = "/dev/disk/by-id/usb-Intenso_Micro_Line_22080777660468-0:0";
|
#keyDiskDevice = "/dev/disk/by-id/usb-Intenso_Micro_Line_22080777660468-0:0";
|
||||||
keyDiskDevice = "/dev/sda";
|
keyDiskDevice = "/dev/sda";
|
||||||
};
|
};
|
||||||
|
|
||||||
networking.hostName = "sparky";
|
networking.hostName = "sparky";
|
||||||
services.tailscaleAutoconnect.authkey = "tskey-auth-kBCKN7QNv411CNTRL-n5Td7Jw7h3TAjubEeLmy1THy33JvD9JnM";
|
services.tailscaleAutoconnect.authkey = "tskey-auth-k6VC79UrzN11CNTRL-rvPmd4viyrQ261ifCrfTrQve7c2FesxrG";
|
||||||
|
|
||||||
|
nfsServicesServer.standbys = [ "c1" ];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,16 +5,13 @@
|
|||||||
../../common/global
|
../../common/global
|
||||||
../../common/cluster-member.nix # Consul + storage clients
|
../../common/cluster-member.nix # Consul + storage clients
|
||||||
../../common/nomad-worker.nix # Nomad client (runs jobs)
|
../../common/nomad-worker.nix # Nomad client (runs jobs)
|
||||||
# NOTE: zippy is NOT a server - no nomad-server.nix import
|
../../common/netconsole-receiver.nix
|
||||||
# ../../common/ethereum.nix
|
|
||||||
../../common/nfs-services-server.nix # NFS server for /data/services
|
|
||||||
# To move NFS server role to another host:
|
|
||||||
# 1. Follow procedure in docs/NFS_FAILOVER.md
|
|
||||||
# 2. Replace above line with: ../../common/nfs-services-standby.nix
|
|
||||||
# 3. Add nfsServicesStandby.replicationKeys with the new server's public key
|
|
||||||
./hardware.nix
|
./hardware.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Receive kernel messages from beefy via netconsole
|
||||||
|
services.netconsoleReceiver.enable = true;
|
||||||
|
|
||||||
diskLayout = {
|
diskLayout = {
|
||||||
mainDiskDevice = "/dev/disk/by-id/ata-KINGSTON_SKC600MS1024G_50026B7785AE0A92";
|
mainDiskDevice = "/dev/disk/by-id/ata-KINGSTON_SKC600MS1024G_50026B7785AE0A92";
|
||||||
#keyDiskDevice = "/dev/disk/by-id/usb-Intenso_Micro_Line_22080777660702-0:0";
|
#keyDiskDevice = "/dev/disk/by-id/usb-Intenso_Micro_Line_22080777660702-0:0";
|
||||||
@@ -23,7 +20,4 @@
|
|||||||
|
|
||||||
networking.hostName = "zippy";
|
networking.hostName = "zippy";
|
||||||
services.tailscaleAutoconnect.authkey = "tskey-auth-ktKyQ59f2p11CNTRL-ut8E71dLWPXsVtb92hevNX9RTjmk4owBf";
|
services.tailscaleAutoconnect.authkey = "tskey-auth-ktKyQ59f2p11CNTRL-ut8E71dLWPXsVtb92hevNX9RTjmk4owBf";
|
||||||
|
|
||||||
# NFS server configuration: replicate to c1 as standby
|
|
||||||
nfsServicesServer.standbys = [ "c1" ];
|
|
||||||
}
|
}
|
||||||
|
|||||||
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="
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
7
pkgs/default.nix
Normal file
7
pkgs/default.nix
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# ABOUTME: Custom packages overlay for packages not in nixpkgs.
|
||||||
|
# ABOUTME: Adds packages under pkgs.custom.* namespace.
|
||||||
|
final: prev: {
|
||||||
|
custom = {
|
||||||
|
flecli = final.callPackage ./flecli.nix { };
|
||||||
|
};
|
||||||
|
}
|
||||||
28
pkgs/flecli.nix
Normal file
28
pkgs/flecli.nix
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# ABOUTME: FLEcli - Fast Log Entry CLI for amateur radio logging.
|
||||||
|
# ABOUTME: Processes FLE-formatted files into ADIF for SOTA, POTA, WWFF, etc.
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
buildGoModule,
|
||||||
|
fetchFromGitHub,
|
||||||
|
}:
|
||||||
|
|
||||||
|
buildGoModule rec {
|
||||||
|
pname = "flecli";
|
||||||
|
version = "0.1.7";
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "on4kjm";
|
||||||
|
repo = "FLEcli";
|
||||||
|
rev = "v${version}";
|
||||||
|
hash = "sha256-6OFcShgUaK9RwonP6cl8eOD6Cu+F5LHZEUFPjCfWNV0=";
|
||||||
|
};
|
||||||
|
|
||||||
|
vendorHash = "sha256-6m01rcewPyy8pUXnIMwjyW+7I08pyJaTDliwTNp3fOM=";
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
description = "Fast Log Entry CLI - process amateur radio logs";
|
||||||
|
homepage = "https://github.com/on4kjm/FLEcli";
|
||||||
|
license = licenses.mit;
|
||||||
|
mainProgram = "FLEcli";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -14,6 +14,102 @@ BLUE='\033[0;34m'
|
|||||||
YELLOW='\033[0;33m'
|
YELLOW='\033[0;33m'
|
||||||
NC='\033[0m' # No Color
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Normalize nix store paths by replacing 32-char hashes with placeholder
|
||||||
|
normalize_nix_paths() {
|
||||||
|
sed -E 's|/nix/store/[a-z0-9]{32}-|/nix/store/HASH-|g'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Filter diff output to remove hunks where only nix store hashes differ
|
||||||
|
# Returns: filtered diff (empty if only hash changes), exit code 0 if real changes found
|
||||||
|
filter_hash_only_diffs() {
|
||||||
|
local diff_output="$1"
|
||||||
|
local current_hunk=""
|
||||||
|
local output=""
|
||||||
|
local has_real_changes=false
|
||||||
|
|
||||||
|
# Process line by line
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
if [[ "$line" =~ ^@@ ]]; then
|
||||||
|
# New hunk starts - process previous one if it exists
|
||||||
|
if [ -n "$current_hunk" ]; then
|
||||||
|
if hunk_has_real_changes "$current_hunk"; then
|
||||||
|
output+="$current_hunk"$'\n'
|
||||||
|
has_real_changes=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# Start new hunk
|
||||||
|
current_hunk="$line"$'\n'
|
||||||
|
else
|
||||||
|
# Add line to current hunk
|
||||||
|
current_hunk+="$line"$'\n'
|
||||||
|
fi
|
||||||
|
done <<< "$diff_output"
|
||||||
|
|
||||||
|
# Process last hunk
|
||||||
|
if [ -n "$current_hunk" ]; then
|
||||||
|
if hunk_has_real_changes "$current_hunk"; then
|
||||||
|
output+="$current_hunk"
|
||||||
|
has_real_changes=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove trailing newline
|
||||||
|
output="${output%$'\n'}"
|
||||||
|
|
||||||
|
if [ "$has_real_changes" = true ]; then
|
||||||
|
echo "$output"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if a diff hunk has real changes (not just hash changes)
|
||||||
|
hunk_has_real_changes() {
|
||||||
|
local hunk="$1"
|
||||||
|
|
||||||
|
# Use temp file to avoid bash here-string issues
|
||||||
|
local temp_hunk=$(mktemp)
|
||||||
|
printf '%s' "$hunk" > "$temp_hunk"
|
||||||
|
|
||||||
|
local minus_lines=()
|
||||||
|
local plus_lines=()
|
||||||
|
|
||||||
|
# Extract - and + lines (skip @@ and context lines)
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
if [[ "$line" =~ ^- && ! "$line" =~ ^--- ]]; then
|
||||||
|
minus_lines+=("${line:1}") # Remove the - prefix
|
||||||
|
elif [[ "$line" =~ ^\+ && ! "$line" =~ ^\+\+\+ ]]; then
|
||||||
|
plus_lines+=("${line:1}") # Remove the + prefix
|
||||||
|
fi
|
||||||
|
done < "$temp_hunk"
|
||||||
|
|
||||||
|
rm -f "$temp_hunk"
|
||||||
|
|
||||||
|
# If counts don't match, there are structural changes
|
||||||
|
if [ ${#minus_lines[@]} -ne ${#plus_lines[@]} ]; then
|
||||||
|
return 0 # Has real changes
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If no changes at all, skip
|
||||||
|
if [ ${#minus_lines[@]} -eq 0 ]; then
|
||||||
|
return 1 # No real changes
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Compare each pair of lines after normalization
|
||||||
|
for i in "${!minus_lines[@]}"; do
|
||||||
|
local minus_norm=$(echo "${minus_lines[$i]}" | normalize_nix_paths)
|
||||||
|
local plus_norm=$(echo "${plus_lines[$i]}" | normalize_nix_paths)
|
||||||
|
|
||||||
|
if [ "$minus_norm" != "$plus_norm" ]; then
|
||||||
|
return 0 # Has real changes
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# All changes are hash-only
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
# Check for nvd
|
# Check for nvd
|
||||||
if ! command -v nvd &> /dev/null; then
|
if ! command -v nvd &> /dev/null; then
|
||||||
echo "Error: nvd not found in PATH"
|
echo "Error: nvd not found in PATH"
|
||||||
@@ -205,11 +301,17 @@ for host in $hosts; do
|
|||||||
echo
|
echo
|
||||||
echo -e " ${BLUE}▸ $basename${NC}"
|
echo -e " ${BLUE}▸ $basename${NC}"
|
||||||
|
|
||||||
# If it's a directory, diff key files within it
|
# If it's a directory, diff all files within it
|
||||||
if [ -d "$old_path" ] && [ -d "$new_path" ]; then
|
if [ -d "$old_path" ] && [ -d "$new_path" ]; then
|
||||||
# Focus on important files
|
# Count files to avoid processing huge directories
|
||||||
for pattern in "activate" "etc/*" "*.conf" "*.fish" "*.service" "*.nix"; do
|
file_count=$(find "$new_path" -maxdepth 3 -type f 2>/dev/null | wc -l)
|
||||||
while IFS= read -r file; do
|
|
||||||
|
# Skip very large directories (e.g., system-path with 900+ files)
|
||||||
|
if [ "$file_count" -gt 100 ]; then
|
||||||
|
echo " (skipping directory with $file_count files - too large)"
|
||||||
|
else
|
||||||
|
# Diff all files in the directory
|
||||||
|
for file in $(find "$new_path" -maxdepth 3 -type f 2>/dev/null); do
|
||||||
[ -z "$file" ] && continue
|
[ -z "$file" ] && continue
|
||||||
relpath="${file#$new_path/}"
|
relpath="${file#$new_path/}"
|
||||||
old_file="$old_path/$relpath"
|
old_file="$old_path/$relpath"
|
||||||
@@ -217,16 +319,38 @@ for host in $hosts; do
|
|||||||
if [ -f "$old_file" ] && [ -f "$file" ]; then
|
if [ -f "$old_file" ] && [ -f "$file" ]; then
|
||||||
# Check if file is text
|
# Check if file is text
|
||||||
if file "$file" | grep -q "text"; then
|
if file "$file" | grep -q "text"; then
|
||||||
echo -e " ${YELLOW}$relpath:${NC}"
|
# Get diff output
|
||||||
diff -u "$old_file" "$file" 2>/dev/null | head -50 | tail -n +3 | sed 's/^/ /' || true
|
diff_output=$(diff -u "$old_file" "$file" 2>/dev/null | head -50 | tail -n +3 || true)
|
||||||
|
|
||||||
|
# Filter hash-only changes
|
||||||
|
if [ -n "$diff_output" ]; then
|
||||||
|
filtered_diff=$(filter_hash_only_diffs "$diff_output" || true)
|
||||||
|
|
||||||
|
if [ -n "$filtered_diff" ]; then
|
||||||
|
echo -e " ${YELLOW}$relpath:${NC}"
|
||||||
|
echo "$filtered_diff" | sed 's/^/ /'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done < <(find "$new_path" -type f -name "$pattern" 2>/dev/null | head -20)
|
done
|
||||||
done
|
fi
|
||||||
# If it's a file, diff it directly
|
# If it's a file, diff it directly
|
||||||
elif [ -f "$old_path" ] && [ -f "$new_path" ]; then
|
elif [ -f "$old_path" ] && [ -f "$new_path" ]; then
|
||||||
if file "$new_path" | grep -q "text"; then
|
if file "$new_path" | grep -q "text"; then
|
||||||
diff -u "$old_path" "$new_path" 2>/dev/null | head -50 | tail -n +3 | sed 's/^/ /' || true
|
# Get diff output
|
||||||
|
diff_output=$(diff -u "$old_path" "$new_path" 2>/dev/null | head -50 | tail -n +3 || true)
|
||||||
|
|
||||||
|
# Filter hash-only changes
|
||||||
|
if [ -n "$diff_output" ]; then
|
||||||
|
filtered_diff=$(filter_hash_only_diffs "$diff_output" || true)
|
||||||
|
|
||||||
|
if [ -n "$filtered_diff" ]; then
|
||||||
|
echo "$filtered_diff" | sed 's/^/ /'
|
||||||
|
else
|
||||||
|
echo " (only hash changes)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo " (binary file)"
|
echo " (binary file)"
|
||||||
fi
|
fi
|
||||||
|
|||||||
25
secrets/beefy.yaml
Normal file
25
secrets/beefy.yaml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
kopia: ENC[AES256_GCM,data:/6jqArNgeBoGnEdJ1eshrsG8RJs=,iv:2nNdrKczus70QDdvO/MC2wJubGnAf3M8PtzSe1aoBF4=,tag:aOoktsqhQLXr0YkjYZq4OQ==,type:str]
|
||||||
|
sops:
|
||||||
|
age:
|
||||||
|
- recipient: age1df9ukkmg9yn9cjeheq9m6wspa420su8qarmq570rdvf2de3rl38saqauwn
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBODczb2FsMis0cVIvN2FK
|
||||||
|
UG1QVWt6U1MvaU1Rd0dXWDhmK2RpZ2dSUXlRClZ5ZGRZRk1vUFp0eVVwVzA5R0Ni
|
||||||
|
RUdjVFh5T3o5ZllFaHVFS1pCWjNzVkEKLS0tIGoxNWhhZUhVSms5cEJEa3lZQWlz
|
||||||
|
aXNBMWhUNFBHTDJUZEtDeU85Z1pPU1kKWNm6Wk+Mbc9QIXMXiwleIvP4hlGLvmpI
|
||||||
|
u+udOAinxTxmB9LOXG+y3iPuS9n0B6Y+4WbTjKm9jEqaqNoW8JypJA==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age1cs8uqj243lspyp042ueu5aes4t3azgyuaxl9au70ggrl2meulq4sgqpc7y
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjTG1MZjM1bVNqeFNqeTgy
|
||||||
|
UXZBWVVacVVsaHJKMkJ1ZWdCbG4zS2tBWDFjCnpSbUw0ZFZMVENNNmYyTWZFdndL
|
||||||
|
RmxUajdsU1l1cmlZa2NQQjJublVsMmMKLS0tIHNpZmRpY2hIbVVZSUdGNHM2WnN6
|
||||||
|
R21jYU96SGVHOUxmZjlldS96K2VqbWcKC28wLdT/zx6yHluCLqB/cFRmc0Alq6AH
|
||||||
|
DqmAaxRhOg/SI5ljCX1gE5BB9rNIJ1Gq8+li7wCpsdfLMr5Yy/HAsw==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
lastmodified: "2025-11-04T20:25:17Z"
|
||||||
|
mac: ENC[AES256_GCM,data:llS+R5Pj51ZUkU8FkJx2KqqE4D42Uno3Btn31FadIl4kFamnrL6uJjbiNEJpFFO+SchXD3l7VCatbBhMSoxsPYd+rdDRT2klq+iIcZU/k413GC87xdmHIwWE+L2pujv36iBjtM+HJTSvXI0xOxjUmzH4FPdEa1r3Z5yGNnCI+Q4=,iv:ld6pSEzvKTSZtBb+QjHyqqj2VT05YegxBrUR2yxhjKY=,tag:7/vYBh8lDOcVXJL3esTIZQ==,type:str]
|
||||||
|
unencrypted_suffix: _unencrypted
|
||||||
|
version: 3.11.0
|
||||||
@@ -4,83 +4,92 @@ sops:
|
|||||||
- recipient: age1df9ukkmg9yn9cjeheq9m6wspa420su8qarmq570rdvf2de3rl38saqauwn
|
- recipient: age1df9ukkmg9yn9cjeheq9m6wspa420su8qarmq570rdvf2de3rl38saqauwn
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwandOMnBCNUtFYnRWVjRl
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4WExPaEtTdEljYkF1ZUQw
|
||||||
U2RNREZ6d1JiYXdDcTRoWGJmaHNrQXBYdzBBCklFcGxxTjlMT3VRMGtqZGRFMzZH
|
UHhRNDJZb2wydWVUaXFmR213SjJsNDFKU0FjCnJ3Tk1yZDZkU3orcHZ2UDY3elRi
|
||||||
bm1qN1pXdDhERGZkeW9QeTNOSUNQUTgKLS0tIHEzQTk3Sm1ncUFaS0lYVFI1S2lp
|
WW9FMXU0cDNjV3QrOWo3MVB0UzMwakUKLS0tIEhQVldBVWhmR0k0WW9jTE0xc2ZW
|
||||||
NkdoZ0dMdWRkdGJlSE5QSjFxVnE3VUUK4I2euCBACjjoexxa7lPM9vkkNSK/YbyW
|
RWp4ZjlVN0FWaURlRHNONDhXdmJpS1EKZVXYyFRFD9KdyWuMoQytkQk4VxpBRyAV
|
||||||
OoENUPsbvRkcNLDsi2sLnrEHSxBA/YJkfPDWxNrxZVZfUqoX+NrzOw==
|
lF4FA99wjGMhHFNQExnqYYLYtFkA18/SB6pkneOjdhIvEr0IFLJEqg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1gtyw202hd07hddac9886as2cs8pm07e4exlnrgfm72lync75ng9qc5fjac
|
- recipient: age1gtyw202hd07hddac9886as2cs8pm07e4exlnrgfm72lync75ng9qc5fjac
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJRGNMaUIrbzlPSlIzSDZ2
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjZ29wdk1aOHZJYWFjaG9v
|
||||||
MVZFV0lUa2xtLzdJc3ZCWjRxK0MwQzZJeEVRCkNKUzVXV2pRVkd4MG1KWWNPUmJ2
|
RGxsek95QmtrZS9xRWdKMFdLSHZ3NmlZRGxzCnBvRXZkYnkxdkhJWkY0Ukg1M0dE
|
||||||
OUZ3NFJOT1NZSVlNNlJTcUFCSFV5ZWcKLS0tIFBZaTQvd2NHMmdNc1dON1puZmll
|
dWc3QWtCdkV5Ymd4MkxhZWl0ZDNCZXcKLS0tIGMrVWtNNWtscm9STUN1aHVZc2Ny
|
||||||
ZG4yLytpUi8zenNQc1VySm1QeGUzbmsKBtAX+v4NSX0yiAtXxhq9i6eoJTyXFuE1
|
Vm1oaFFTbTBpRWxuR3gxbUZ0YkZieVkKdaSSXrDzAUGkj3w8/YcFZaJTiUUEbJdw
|
||||||
61kd7hFrCKJDMI0VOBxkqzsDI+/cH9sVV0Us4C5njcxckCWTCA6Uog==
|
GjuLz7bxX8+HQvhSbu6/KCwG6R4j1eO5Zg1w0wYtyeUOV1HfZEGQog==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age16yqffw4yl5jqvsr7tyd883vn98zw0attuv9g5snc329juff6dy3qw2w5wp
|
- recipient: age16yqffw4yl5jqvsr7tyd883vn98zw0attuv9g5snc329juff6dy3qw2w5wp
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByTENXMjBCcmladHRIQlZJ
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIMGpibmNRUDRFaFVOTDB4
|
||||||
MDZxSHpkRzJHN2c0ZmpvN0xid0l1UlI0ZW5nCkJIb25qUUNjdUx6dFFvZHlYLzZ0
|
RVdTc1RrTmRPb0dlZGlpcGxuRlJ1L2w5MVVBCi9HdXNGZmdSaVZsQWRoa2RpVDNV
|
||||||
N1RpZGtyL3laSldqdXBoN0NjSFhSWUkKLS0tIGlhdU0xanlsV2ZLYm9yeEYzaGF3
|
OXBtS0pwYnhjS2hCUk10UUtwam4zMWcKLS0tIFV0dVpQNGpSOEVoZnE5OGpCZkxa
|
||||||
ZzJmSnZWVTViRVpZUWJNQ1Y5OGFDNDQK68nH8bQgBef3afCywEt+1mPzcPPF0sml
|
MFMxSG95dmJncGJzR29mQkVzNjFIQUEKrJ0MDTBmiwiAaLt7CJ1pjlxuFvZJuRkR
|
||||||
k3jraFAbL//4ZhQuRQcWbja5Qj1fqyc30EH0a/cIxUhE7MQ74gP5rA==
|
EuLYOYLdVaxgZ442io5OE7wme0P4LLcxSAreDG84GVs67JHvsFE89g==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age10zxwwufrf5uu9cv9p9znse2ftfm74q9ce893us6cnvxjc7e3ypcqy709dy
|
- recipient: age14aml5s3sxksa8qthnt6apl3pu6egxyn0cz7pdzzvp2yl6wncad0q56udyj
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByaXNZaHlGbnB1TGYyeXJh
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBucHdSNGNyRkVITmNDVkpx
|
||||||
ck5Sd1pWdXZkcXBHbnMzZ0FwcHlUZjNRdjBBCnhKeEMzUnNZWmVsVlh5Mldhd0sx
|
QVFKK0VucFNSMnNqSGRFRmRoRWpsZ0srUUhrCkwwY2pDSkJ0aGlqc3U3ZXNJUVl0
|
||||||
Uld0djNlVk9MTDA3OHpOUHJ2ZkV3Q0kKLS0tIEh1MHdKZzMydDBBcjkyWU1wUVhH
|
bXZMSVg3bDhaK3d1MTBnL1BQVUhkMUkKLS0tIDdxSk1DMVpsbnI1QlFnNEFJYXRD
|
||||||
ZVJFbWNzUWV0dDlGa0FnUERwUHNHZTgK08zByyr84MCjDgfJxOUbZ4DwhXKzM63+
|
RTNxYUxlUGxsM1NvekZ4R1hQVE9KMk0KocfE75DTfQMj/RsznOdeF82aO8WwO4HD
|
||||||
/3rltIx/fjRFc+UAeR7OXU6nX2oauUzr5OAwYHdO2C5K+vF/kiLVLg==
|
1xakOM2FHoHi60Q5uOWzfGtz0i+R4ue9hafa5Esn01TOjc3qWSlW3A==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1me78u46409q9ez6fj0qanrfffc5e9kuq7n7uuvlljfwwc2mdaezqmyzxhx
|
- recipient: age1me78u46409q9ez6fj0qanrfffc5e9kuq7n7uuvlljfwwc2mdaezqmyzxhx
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDdnR1N3N2RGEwbWNJMXhZ
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYTEhiSDZvZTg3ZWxJRXlu
|
||||||
S0poOHNKZjd5akZxNTdzUDdqU1NIOG9hMlVjCmNzU252Uzc0NVRTRk1ab3R5cGxy
|
a0ozOXRVL2lia054SkNLc2tEYmlZUCt1NW1JCkorK0hub1pLQTE0QThEUDRDWXJV
|
||||||
Z2h1UjJCd0RGYzRxTVZFazRwV1oyQzAKLS0tIFoyakhGQ3pwR0pmNkVzcTVYSTU0
|
YWtGamNxMTFIYjVDT2RqTXh0Z2hVTjAKLS0tIGxoRTAwc3FKVVNSQndtbTZmc3BR
|
||||||
eWhpQ2I5VVlrcWh4ZEF4VmlLaDdBLzQKpI1F2w6yMh+cFor5vsBwVLvuKvY0RLDH
|
QnMrK2lMT25tR1ErV2xvS01JWWswVUEKtrGaLETMfY2D8qmgml/fgGxkvQLoiMTP
|
||||||
69svSp/l541YYAf/QKV1hJZ9ab0rK7uNBi8GIL/rGr5GhE3HiQ9r3w==
|
l3a7Y6kwutuzRnmW1tnWv7yoPbTn+BDwfOwBcnesl2x0aJ5iLUKruA==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age1cs8uqj243lspyp042ueu5aes4t3azgyuaxl9au70ggrl2meulq4sgqpc7y
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqMFJ1bzQxWjlZTmF6eEl0
|
||||||
|
d3VVd0VsbGZGdDBKRG9uNEhaMThmWmpuQ1hFClA1aDhwRU1Pb2pibGh6T0pqdmlq
|
||||||
|
S3cxM0wyWWlCL3U5TjV4Vkg4blRsUVkKLS0tIENnYk5GbmZWbFo4cElON1Z0ZVlv
|
||||||
|
ZDdsci9rcG5Wc2V0NlQ3MWx1cFF4dUkKumFT4xtjGDBGK+/SV27Dh/vyGMJAEZNo
|
||||||
|
9gTmVLfR9vXVAXUdOMcqgo7Nl4OJCS4HrDxvVCoER/bVoQVRiPzuXw==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1w5w4wfvtul3sge9mt205zvrkjaeh3qs9gsxhmq7df2g4dztnvv6qylup8z
|
- recipient: age1w5w4wfvtul3sge9mt205zvrkjaeh3qs9gsxhmq7df2g4dztnvv6qylup8z
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVQUxYSnJ5N0w0c0cyRk1y
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCM2E5a2lsZGJzRnk5N3Rr
|
||||||
ZHNmNTk1L0hjNWN4NW42dVBHQWVWTlYwSVhJCjk5bzlaMVM5VU56WmRPdUR1NkUw
|
bWRwRlI2c0c4NzBNdEVqMGZFWTZtNDlvSzJFCmFPM05XbndsazRGWEw3Zy83dldm
|
||||||
dEUwZ053M2xoQnVOZk15UVdGS2JPOFkKLS0tIEtJblhvMU1WZ09PWXJoQ3duQ0tC
|
eXhEZUZQZWk5bVNwaEk5SDRka0NOWjAKLS0tIHNvZ016Rjh5bmYwRUxyRWRydFpI
|
||||||
LzkyNmJya3dJTVpIT0dSMURLTXlsdkUKGrF6TMCDSDXPPfFANL3NuNZfvzxfPrLb
|
Z0NHYjFzem55bVNORGlVbVNxR2psc2cK6JpNZwznwgl61d/W9g+48/894TQRe4gJ
|
||||||
sgn8RZtgsezyRAjmysce3OclX9ZOGwsv+vAvt79NA6OLO26We0SxUw==
|
nl4oDwRPbZwJZgdAKQVfTTujB0QbWpJc24mDGD4I4CydqTKwy6FN3A==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1wwufz86tm3auxn6pn27c47s8rvu7en58rk00nghtaxsdpw0gya6qj6qxdt
|
- recipient: age1wwufz86tm3auxn6pn27c47s8rvu7en58rk00nghtaxsdpw0gya6qj6qxdt
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnY0N0SVVVNUVVUFAvUDR3
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlK1A1eVdRQThQUHdqbHdk
|
||||||
eU5WVW0yOUFuWmQ4T3BjWFlOVCt1RndHdFhrCkZLYTJmRzFUVERNcGRWcmVmWXQx
|
b1MyMlBJUFluTm13ZWwwc1RNbThFZUMrNXhzCnRPTVhPSzUzM0VtaUVJbFl5Wllj
|
||||||
WkRGYmdzMlA1MnNlWUNOWTMybHFtMG8KLS0tIDhBR1JXaEFYK3l0R1UxSWEvM1Iv
|
NUlndzc3Yzhjd1JSb3czajI3UmRDZ1kKLS0tIE03M1hab1MxU0I2VExBWlh2TnJC
|
||||||
SWFPTDIwSTF3MkhVMjQ4RkdVR1FUNmcKcqzqJIK8/mDyUR0hG0QcMrjJTk1EahJ6
|
eGRXRTlsWmlpenJrVkMxakJZVTV0cE0KMQCKscSLnCu3NsurFFiDaUGjJbyIAwd0
|
||||||
f6pdBA49ZrFjYS8ld1WbJ6A2zWQbV8T4NikfxTXM8AzGi9BTanpJww==
|
HTutCiuPYVI4zznQ3RZDBeO5L6a/twXxMRTePUCwOkRNWRWpzR9nxg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1jy7pe4530s8w904wtvrmpxvteztqy5ewdt92a7y3lq87sg9jce5qxxuydt
|
- recipient: age1jy7pe4530s8w904wtvrmpxvteztqy5ewdt92a7y3lq87sg9jce5qxxuydt
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFTUs5VVF5WEt3ZmRwRStp
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1VEJmMWlnemFGNExWYUI4
|
||||||
SUY2a1RsQXE4ejVxaW9vZlFpUTJ3R1NGZDBNCkV4MnNjN0hHMXVlZ3dlOFU1ZWQw
|
QWRwRktwODNvSmlEcGJORHNlQXNVeXVpbFNrCms0QUFNdDlrNjMxazU1UTcwc2JF
|
||||||
ZDI3UUVianplb3kwTXlJNEQwVzVlR28KLS0tIFNvem53ajl0TE9XUSt4R2x2Q1Zj
|
RC9JUnJsWmgyc01zZU9JQmxuM3V6STQKLS0tIGxQZGFsZ0pNTjQ3QW1sS0E2Y2RM
|
||||||
OUxTc0hmemJReldsUXI4VGxJeFN6eVEKa6rDLAd0LrECyoPFesYtTZfh1/qGAiUK
|
aVVrNW1BNXQ5UDk1UEtVVWJPNHpwUFUKcArFPFknBj8ss1lD38YtMaB06L/ASeu5
|
||||||
3rkBrjJuj5IGPeeeeRzLsNCs68InhIOB9aczB/RgDBSuAX4hPn1Z3g==
|
u4ff0rTDx237snaSFg5RIJ+6uxX16p5ODg3xOYGOMkDeuTLdl2bg3A==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1zjgqu3zks5kvlw6hvy6ytyygq7n25lu0uj2435zlf30smpxuy4hshpmfer
|
- recipient: age1zjgqu3zks5kvlw6hvy6ytyygq7n25lu0uj2435zlf30smpxuy4hshpmfer
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0UFJobWI4QzZUWXFVSExL
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArWTNkaFlrQkJHRnd4cTBw
|
||||||
RW1JVURFNDZldHN5RHc4TnhtclRjZmZJNkg0CkpMcjZLTXNFK3RuZHFUb2tPbFdB
|
N3dnTXk3SlJkQkZDdWpLcEpNQ2Z2RHZoVjBJCjBaK1MzbzdaaXluR1dFaFFNaGEx
|
||||||
clN4M3djZjlEZ2ptNHZERUN1Z2F4RG8KLS0tIC8vVnVwZVZrczZEUGkwOEdiSzFr
|
VTNrVU0yeG9KQkhqUkYxU3VBM0E0R1UKLS0tIDJHek9vVldSZGN0M0c0UHcySGhk
|
||||||
c3pnUXowWWRZd1R4czBFdk12WHVCV0kK+y9xAgtGTqQnf6WEa3cla9QfOvxASfkk
|
Z2RoZno4bmhidytlL2ZmNWUzNTcwcVEKXvgaO8Uo0R+Kc8lizLtVxmTi0W5XHjYw
|
||||||
NxHhH7rbtAzVEL/QrOMoIs5+R21jUps2kDSvqtUBO748UOfevCua0A==
|
7evdCHQHmFl0vg/bGOJBmcTUhioJv06D0LR3XMl9I6ufXDNaT/NHxw==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2025-04-04T09:34:06Z"
|
lastmodified: "2025-04-04T09:34:06Z"
|
||||||
mac: ENC[AES256_GCM,data:YIcRrsPparPfPaI2+MLlKsxu7M19H8nndOsrDLuh/5BXzIZNiuTIWyvxODyhI745rDwlibO+7Q0QctanhTl4+IzGaYtuY4i+rb+3dzBMpcdT2VAbtCHHxcltWeanRGFq2K3WM2tbnQCERst5kejfn0Razjq3UU5vNwfBsdJMwGc=,iv:izDxy0ufVnH8ImkZIngcYhGuj0PGpLqBD/ZDvQyE+5I=,tag:oYBUEQS52pr09h5OvOadNg==,type:str]
|
mac: ENC[AES256_GCM,data:YIcRrsPparPfPaI2+MLlKsxu7M19H8nndOsrDLuh/5BXzIZNiuTIWyvxODyhI745rDwlibO+7Q0QctanhTl4+IzGaYtuY4i+rb+3dzBMpcdT2VAbtCHHxcltWeanRGFq2K3WM2tbnQCERst5kejfn0Razjq3UU5vNwfBsdJMwGc=,iv:izDxy0ufVnH8ImkZIngcYhGuj0PGpLqBD/ZDvQyE+5I=,tag:oYBUEQS52pr09h5OvOadNg==,type:str]
|
||||||
|
|||||||
@@ -4,20 +4,20 @@ sops:
|
|||||||
- recipient: age1df9ukkmg9yn9cjeheq9m6wspa420su8qarmq570rdvf2de3rl38saqauwn
|
- recipient: age1df9ukkmg9yn9cjeheq9m6wspa420su8qarmq570rdvf2de3rl38saqauwn
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkdHVtQ3hHSjVuRkxRMklO
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtSjhXazlWd3YwNVFKVkw4
|
||||||
NEJjeERFUDBMRkVTTGVGTTJIR1lwM25kZWpRCitlUFIvUTVPUloreEFIWjIvT1dL
|
dDMydVFCN1lLeUJOWkxuSGJ1a0srNm9PaWswCm8yZ3hiOWFHUlAzNVRrck53OElD
|
||||||
S2hBY2NUWHdxOVhRejU0eUpZa1FMY00KLS0tIEZ3bDRIdlQzaVQ5U0kyRjYyeUor
|
b056YmV4S2NtNnEzRkpnRVNEblV5blkKLS0tIG1ramoya3RHV1FJZGlFU2ZSeUtS
|
||||||
c1M2V2J1Q2R3alI3b3NoYk5SK1AzKzgKbOhSxwTpLr7wwbN+nY4aK+6WmpofBxNX
|
KzJlbEsvYWlXaHhEQU5oOS9HaDdYSDAKvlhKgi4Pf8xVB5MnO33GWYg313mRdUGu
|
||||||
CEaEBz98KTTrSQ9Qvm1+/yep95l7i0HPQGdGwCRNKdvUoXzk1KalpQ==
|
kFCs5b1N96x9JOS7zgnM0AKDY8IPBSe33tmDqtYygwPdkOys1PmZkw==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age10zxwwufrf5uu9cv9p9znse2ftfm74q9ce893us6cnvxjc7e3ypcqy709dy
|
- recipient: age14aml5s3sxksa8qthnt6apl3pu6egxyn0cz7pdzzvp2yl6wncad0q56udyj
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZb2oyd1ZGR2FXVFlhT2d3
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYTVovQld2RkRxaW90b3lR
|
||||||
NFVlU3VlR1BEWFRyTDBYZFFlQjZxUXVyK240CkdLRmk5M01ZTloyREtwQ3hpRkxZ
|
NGFtbWVLZUNHdnlZVWkrL1RXUHBVeGdvSDJrClJmSmZRZmdjcy8rNnJBVmVUWDZq
|
||||||
RDRKYVlRVFVLYzVSenU3THhFc0ZrdjgKLS0tIG1iMWh1TTZIZDEwazVKY1g3NXg0
|
M2lPbDBhT0Y0NkJ5a1FNYnU3Zkl0TkEKLS0tIGxqM2h2TDB2akl4ODlYY042R1Z4
|
||||||
NFRKemkwcnBxR0NIbXBGcm8xejdUMjgKOAGxkrvtvf7Y9W5BteL12HuUWA/d5Bah
|
ZVJWN3pZelFJR0Jid3JseEZKVFZtYmsKmKXQRjnghuF/s9z2Xk98sFvxic91fGa2
|
||||||
wVoeBK21Zxz/GodBpVCuDnJ5DwM3c+7O3jnvtTShIW00evDhJIvcvA==
|
V7IGmpqAYQV3jJ1G4cjJxtpidQ6fLCqlnR+sq+y8+dT+LN7i+Zbnnw==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2025-10-19T17:33:13Z"
|
lastmodified: "2025-10-19T17:33:13Z"
|
||||||
mac: ENC[AES256_GCM,data:IwEyBr/I7BJa0gWZ494dCT0ogyP2PbnUg5fLOn15vZAHIyYtTB3dI3gV5Lx7oPdqOPlI61MsShIYBnk0uBChpNu6O4oiGUfwvBfegzlDyHHERLx+S7nZpcwmf/3JoNXwq0f2OtOu8nA6Q1V4gVjFFNWUCAh5cq106vG1awsQkn0=,iv:j+JcVtKz2RfyWu55dUeJJTRK6prB9DGLvcjiAAdVySM=,tag:Pg5sKiLzYUFoN9Duu+nF0w==,type:str]
|
mac: ENC[AES256_GCM,data:IwEyBr/I7BJa0gWZ494dCT0ogyP2PbnUg5fLOn15vZAHIyYtTB3dI3gV5Lx7oPdqOPlI61MsShIYBnk0uBChpNu6O4oiGUfwvBfegzlDyHHERLx+S7nZpcwmf/3JoNXwq0f2OtOu8nA6Q1V4gVjFFNWUCAh5cq106vG1awsQkn0=,iv:j+JcVtKz2RfyWu55dUeJJTRK6prB9DGLvcjiAAdVySM=,tag:Pg5sKiLzYUFoN9Duu+nF0w==,type:str]
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ job "adminer" {
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.adminer.entryPoints=websecure",
|
"traefik.http.routers.adminer.entryPoints=websecure",
|
||||||
"traefik.http.routers.adminer.middlewares=authentik@file",
|
"traefik.http.routers.adminer.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
job "authentik" {
|
|
||||||
datacenters = ["alo"]
|
|
||||||
|
|
||||||
group "auth" {
|
|
||||||
network {
|
|
||||||
port "http" {
|
|
||||||
# traefik forwardAuth hardcodes this port
|
|
||||||
static = 9000
|
|
||||||
}
|
|
||||||
port "https" {
|
|
||||||
to = 9443
|
|
||||||
}
|
|
||||||
port "metrics" {
|
|
||||||
to = 9300
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task "server" {
|
|
||||||
driver = "docker"
|
|
||||||
|
|
||||||
config {
|
|
||||||
image = "ghcr.io/goauthentik/server:${var.authentik_version}"
|
|
||||||
ports = [
|
|
||||||
"http",
|
|
||||||
"https",
|
|
||||||
"metrics"
|
|
||||||
]
|
|
||||||
command = "server"
|
|
||||||
}
|
|
||||||
|
|
||||||
env {
|
|
||||||
AUTHENTIK_REDIS__HOST = "redis.service.consul"
|
|
||||||
AUTHENTIK_POSTGRESQL__HOST = "postgres.service.consul"
|
|
||||||
AUTHENTIK_POSTGRESQL__NAME = "${var.pg_db}"
|
|
||||||
AUTHENTIK_POSTGRESQL__USER = "${var.pg_user}"
|
|
||||||
AUTHENTIK_POSTGRESQL__PASSWORD = "${var.pg_password}"
|
|
||||||
AUTHENTIK_SECRET_KEY = "${var.secret_key}"
|
|
||||||
AUTHENTIK_EMAIL__HOST = "192.168.1.1"
|
|
||||||
AUTHENTIK_EMAIL__FROM = "authentik@paler.net"
|
|
||||||
}
|
|
||||||
|
|
||||||
resources {
|
|
||||||
cpu = 2000
|
|
||||||
memory = 1024
|
|
||||||
}
|
|
||||||
|
|
||||||
service {
|
|
||||||
name = "authentik"
|
|
||||||
port = "http"
|
|
||||||
tags = [
|
|
||||||
"traefik.enable=true",
|
|
||||||
# Main UI
|
|
||||||
"traefik.http.routers.authentik.entryPoints=websecure",
|
|
||||||
"traefik.http.routers.authentik.rule=Host(`authentik.v.paler.net`) || Host(`authentik.alo.land`)",
|
|
||||||
# Embedded outpost for forward auth
|
|
||||||
"traefik.http.routers.authentik-palernet.entryPoints=websecure",
|
|
||||||
"traefik.http.routers.authentik-palernet.rule=HostRegexp(`{subdomain:[a-z0-9-]+}.v.paler.net`) && PathPrefix(`/outpost.goauthentik.io/`)",
|
|
||||||
"traefik.http.routers.authentik-aloland.entryPoints=websecure",
|
|
||||||
"traefik.http.routers.authentik-aloland.rule=HostRegexp(`{subdomain:[a-z0-9-]+}.alo.land`) && PathPrefix(`/outpost.goauthentik.io/`)",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
service {
|
|
||||||
name = "authentik-metrics"
|
|
||||||
port = "metrics"
|
|
||||||
tags = [ "metrics" ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task "worker" {
|
|
||||||
driver = "docker"
|
|
||||||
|
|
||||||
config {
|
|
||||||
image = "ghcr.io/goauthentik/server:${var.authentik_version}"
|
|
||||||
command = "worker"
|
|
||||||
}
|
|
||||||
|
|
||||||
env {
|
|
||||||
AUTHENTIK_REDIS__HOST = "redis.service.consul"
|
|
||||||
AUTHENTIK_POSTGRESQL__HOST = "postgres.service.consul"
|
|
||||||
AUTHENTIK_POSTGRESQL__NAME = "${var.pg_db}"
|
|
||||||
AUTHENTIK_POSTGRESQL__USER = "${var.pg_user}"
|
|
||||||
AUTHENTIK_POSTGRESQL__PASSWORD = "${var.pg_password}"
|
|
||||||
AUTHENTIK_SECRET_KEY = "${var.secret_key}"
|
|
||||||
AUTHENTIK_EMAIL__HOST = "192.168.1.1"
|
|
||||||
AUTHENTIK_EMAIL__FROM = "authentik@paler.net"
|
|
||||||
}
|
|
||||||
|
|
||||||
resources {
|
|
||||||
memory = 600
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "pg_user" {
|
|
||||||
type = string
|
|
||||||
default = "authentik"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "pg_password" {
|
|
||||||
type = string
|
|
||||||
default = "aQueiquuo6aiyah5eoch"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "pg_db" {
|
|
||||||
type = string
|
|
||||||
default = "authentik"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "secret_key" {
|
|
||||||
type = string
|
|
||||||
default = "uUzCYhGV93Z8wKLAScuGFqBskxyzSfG4cz6bnXq6McM67Ho7p9"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "authentik_version" {
|
|
||||||
type = string
|
|
||||||
default = "2025.6"
|
|
||||||
}
|
|
||||||
@@ -19,7 +19,7 @@ job "beancount" {
|
|||||||
user = "1000"
|
user = "1000"
|
||||||
|
|
||||||
config {
|
config {
|
||||||
image = "gitea.v.paler.net/ppetru/fava:latest"
|
image = "gitea.v.paler.net/alo/fava:latest"
|
||||||
ports = ["http"]
|
ports = ["http"]
|
||||||
volumes = [
|
volumes = [
|
||||||
"/data/services/beancount:/beancount",
|
"/data/services/beancount:/beancount",
|
||||||
@@ -37,12 +37,12 @@ job "beancount" {
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.finances.entryPoints=websecure",
|
"traefik.http.routers.finances.entryPoints=websecure",
|
||||||
"traefik.http.routers.finances.middlewares=authentik@file",
|
"traefik.http.routers.finances.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
resources {
|
resources {
|
||||||
memory = 400
|
memory = 600
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
50
services/brain.hcl
Normal file
50
services/brain.hcl
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# ABOUTME: Brain - Petre's externalized executive function system
|
||||||
|
# ABOUTME: SilverBullet for markdown web UI + ttyd for web terminal
|
||||||
|
|
||||||
|
job "brain" {
|
||||||
|
datacenters = ["alo"]
|
||||||
|
|
||||||
|
group "web" {
|
||||||
|
volume "services" {
|
||||||
|
type = "host"
|
||||||
|
read_only = false
|
||||||
|
source = "services"
|
||||||
|
}
|
||||||
|
|
||||||
|
network {
|
||||||
|
port "silverbullet" {
|
||||||
|
to = 3000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task "silverbullet" {
|
||||||
|
driver = "docker"
|
||||||
|
user = "1000"
|
||||||
|
|
||||||
|
config {
|
||||||
|
image = "zefhemel/silverbullet:latest"
|
||||||
|
ports = ["silverbullet"]
|
||||||
|
volumes = ["/data/services/brain:/space"]
|
||||||
|
}
|
||||||
|
|
||||||
|
resources {
|
||||||
|
memory = 512
|
||||||
|
}
|
||||||
|
|
||||||
|
service {
|
||||||
|
name = "brain"
|
||||||
|
port = "silverbullet"
|
||||||
|
|
||||||
|
tags = [
|
||||||
|
"traefik.enable=true",
|
||||||
|
"traefik.http.routers.brain.entryPoints=websecure",
|
||||||
|
"traefik.http.routers.brain.middlewares=oidc-auth@file",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO: terminal task with ttyd for web-based amp/claude access
|
||||||
|
# Needs custom image with tmux + amp + claude-code installed
|
||||||
|
# For now, use SSH or local terminal for amp sessions
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,7 +49,7 @@ job "evcc" {
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.evcc.entryPoints=websecure",
|
"traefik.http.routers.evcc.entryPoints=websecure",
|
||||||
"traefik.http.routers.evcc.middlewares=authentik@file",
|
"traefik.http.routers.evcc.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
75
services/fitdata.hcl
Normal file
75
services/fitdata.hcl
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# ABOUTME: Nomad job for Fitdata - fitness data analysis and MCP server.
|
||||||
|
# ABOUTME: Runs FastHTML Python app with SQLite, static port for MCP access.
|
||||||
|
|
||||||
|
# Setup required before running:
|
||||||
|
# sudo mkdir -p /data/services/fitdata && sudo chown 1000:1000 /data/services/fitdata
|
||||||
|
|
||||||
|
job "fitdata" {
|
||||||
|
datacenters = ["alo"]
|
||||||
|
|
||||||
|
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" {
|
||||||
|
static = 5311
|
||||||
|
to = 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task "app" {
|
||||||
|
driver = "docker"
|
||||||
|
user = "1000"
|
||||||
|
|
||||||
|
config {
|
||||||
|
image = "gitea.v.paler.net/alo/fitdata:latest"
|
||||||
|
ports = ["http"]
|
||||||
|
force_pull = true
|
||||||
|
volumes = ["/data/services/fitdata:/var/lib/fitdata"]
|
||||||
|
}
|
||||||
|
|
||||||
|
env {
|
||||||
|
FITDATA_DATA_DIR = "/var/lib/fitdata"
|
||||||
|
FITDATA_DB_PATH = "/var/lib/fitdata/fitdata.db"
|
||||||
|
}
|
||||||
|
|
||||||
|
resources {
|
||||||
|
memory = 512
|
||||||
|
}
|
||||||
|
|
||||||
|
service {
|
||||||
|
name = "fitdata"
|
||||||
|
port = "http"
|
||||||
|
|
||||||
|
tags = [
|
||||||
|
"traefik.enable=true",
|
||||||
|
"traefik.http.routers.fitdata.entryPoints=websecure",
|
||||||
|
"traefik.http.routers.fitdata.middlewares=oidc-auth@file",
|
||||||
|
]
|
||||||
|
|
||||||
|
check {
|
||||||
|
type = "http"
|
||||||
|
path = "/healthz"
|
||||||
|
interval = "10s"
|
||||||
|
timeout = "5s"
|
||||||
|
|
||||||
|
check_restart {
|
||||||
|
limit = 3
|
||||||
|
grace = "60s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,9 @@ job "gitea" {
|
|||||||
driver = "docker"
|
driver = "docker"
|
||||||
|
|
||||||
config {
|
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 = [
|
ports = [
|
||||||
"http",
|
"http",
|
||||||
"ssh",
|
"ssh",
|
||||||
@@ -42,6 +44,8 @@ job "gitea" {
|
|||||||
GITEA__mailer__FROM = "gitea@paler.net"
|
GITEA__mailer__FROM = "gitea@paler.net"
|
||||||
GITEA__mailer__PROTOCOL = "smtp"
|
GITEA__mailer__PROTOCOL = "smtp"
|
||||||
GITEA__mailer__SMTP_ADDR = "192.168.1.1"
|
GITEA__mailer__SMTP_ADDR = "192.168.1.1"
|
||||||
|
GITEA__actions__ENABLED = "true"
|
||||||
|
GITEA__actions__DEFAULT_ACTIONS_URL = "https://gitea.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
service {
|
service {
|
||||||
@@ -51,6 +55,7 @@ job "gitea" {
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.gitea.entryPoints=websecure",
|
"traefik.http.routers.gitea.entryPoints=websecure",
|
||||||
|
"traefik.http.services.gitea.loadBalancer.serversTransport=gitea-transport@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,19 +25,22 @@ job "grafana" {
|
|||||||
GF_SERVER_ROOT_URL = "https://grafana.v.paler.net"
|
GF_SERVER_ROOT_URL = "https://grafana.v.paler.net"
|
||||||
GF_AUTH_BASIC_ENABLED = "false"
|
GF_AUTH_BASIC_ENABLED = "false"
|
||||||
GF_AUTH_GENERIC_OAUTH_ENABLED = "true"
|
GF_AUTH_GENERIC_OAUTH_ENABLED = "true"
|
||||||
GF_AUTH_GENERIC_OAUTH_NAME = "authentik"
|
GF_AUTH_GENERIC_OAUTH_NAME = "Pocket ID"
|
||||||
GF_AUTH_GENERIC_OAUTH_CLIENT_ID = "E78NG1AZeW6FaAox0mUhaTSrHeqFgNkWG12My2zx"
|
GF_AUTH_GENERIC_OAUTH_CLIENT_ID = "99e44cf2-ecc6-4e82-8882-129c017f8a4a"
|
||||||
GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET = "N7u2RfFZ5KVLdEkhlpUTzymGxeK5rLo9SYZLSGGBXJDr46p5g5uv1qZ4Jm2d1rP4aJX4PSzauZlxHhkG2byiBFMbdo6K742KXcEimZsOBFiNKeWOHxofYerBnPuoECQW"
|
GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET = "NjJ9Uro4MK7siqLGSmkiQmjFuESulqQN"
|
||||||
GF_AUTH_GENERIC_OAUTH_SCOPES = "openid profile email offline_access"
|
GF_AUTH_GENERIC_OAUTH_SCOPES = "openid profile email groups"
|
||||||
GF_AUTH_GENERIC_OAUTH_AUTH_URL = "https://authentik.v.paler.net/application/o/authorize/"
|
GF_AUTH_GENERIC_OAUTH_AUTH_URL = "https://pocket-id.v.paler.net/authorize"
|
||||||
GF_AUTH_GENERIC_OAUTH_TOKEN_URL = "https://authentik.v.paler.net/application/o/token/"
|
GF_AUTH_GENERIC_OAUTH_TOKEN_URL = "https://pocket-id.v.paler.net/api/oidc/token"
|
||||||
GF_AUTH_GENERIC_OAUTH_API_URL = "https://authentik.v.paler.net/application/o/userinfo/"
|
GF_AUTH_GENERIC_OAUTH_API_URL = "https://pocket-id.v.paler.net/api/oidc/userinfo"
|
||||||
GF_AUTH_SIGNOUT_REDIRECT_URL = "https://authentik.v.paler.net/application/o/grafana/end-session/"
|
GF_AUTH_SIGNOUT_REDIRECT_URL = "https://pocket-id.v.paler.net/logout"
|
||||||
# Optionally enable auto-login (bypasses Grafana login screen)
|
# Optionally enable auto-login (bypasses Grafana login screen)
|
||||||
GF_AUTH_OAUTH_AUTO_LOGIN = "true"
|
GF_AUTH_OAUTH_AUTO_LOGIN = "true"
|
||||||
# Optionally map user groups to Grafana roles
|
# Optionally map user groups to Grafana roles
|
||||||
GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH = "contains(groups[*], 'Grafana Admins') && 'Admin' || contains(groups[*], 'Grafana Editors') && 'Editor' || 'Viewer'"
|
GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH = "contains(groups[*], 'admins') && 'Admin' || contains(groups[*], 'residents') && 'Editor' || 'Viewer'"
|
||||||
GF_AUTH_GENERIC_OAUTH_USE_REFRESH_TOKEN = "true"
|
GF_AUTH_GENERIC_OAUTH_USE_REFRESH_TOKEN = "true"
|
||||||
|
GF_AUTH_GENERIC_OAUTH_EMAIL_ATTRIBUTE_PATH = "email"
|
||||||
|
GF_AUTH_GENERIC_OAUTH_LOGIN_ATTRIBUTE_PATH = "preferred_username"
|
||||||
|
GF_AUTH_GENERIC_OAUTH_NAME_ATTRIBUTE_PATH = "name"
|
||||||
#GF_LOG_LEVEL = "debug"
|
#GF_LOG_LEVEL = "debug"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
50
services/homepage.hcl
Normal file
50
services/homepage.hcl
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
job "homepage" {
|
||||||
|
datacenters = ["alo"]
|
||||||
|
|
||||||
|
group "app" {
|
||||||
|
network {
|
||||||
|
port "http" { to = 3000 }
|
||||||
|
}
|
||||||
|
|
||||||
|
task "homepage" {
|
||||||
|
driver = "docker"
|
||||||
|
|
||||||
|
config {
|
||||||
|
image = "ghcr.io/gethomepage/homepage:latest"
|
||||||
|
ports = [ "http" ]
|
||||||
|
volumes = [
|
||||||
|
"/data/services/homepage:/app/config",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
env {
|
||||||
|
PUID = 1000
|
||||||
|
PGID = 1000
|
||||||
|
HOMEPAGE_ALLOWED_HOSTS = "homepage.v.paler.net"
|
||||||
|
}
|
||||||
|
|
||||||
|
resources {
|
||||||
|
cpu = 200
|
||||||
|
memory = 256
|
||||||
|
}
|
||||||
|
|
||||||
|
service {
|
||||||
|
name = "homepage"
|
||||||
|
port = "http"
|
||||||
|
|
||||||
|
tags = [
|
||||||
|
"traefik.enable=true",
|
||||||
|
"traefik.http.routers.homepage.entryPoints=websecure",
|
||||||
|
"traefik.http.routers.homepage.middlewares=oidc-auth@file",
|
||||||
|
]
|
||||||
|
|
||||||
|
check {
|
||||||
|
type = "http"
|
||||||
|
path = "/"
|
||||||
|
interval = "10s"
|
||||||
|
timeout = "5s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,42 +9,19 @@ job "igsync" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group "cron" {
|
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" {
|
task "sync" {
|
||||||
driver = "exec"
|
driver = "docker"
|
||||||
|
|
||||||
config {
|
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 {
|
# Force pull to always get latest image
|
||||||
volume = "services"
|
force_pull = true
|
||||||
destination = "/data/services"
|
|
||||||
}
|
|
||||||
volume_mount {
|
|
||||||
volume = "nix-store"
|
|
||||||
destination = "/nix/store"
|
|
||||||
}
|
|
||||||
volume_mount {
|
|
||||||
volume = "sw"
|
|
||||||
destination = "/sw"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resources {
|
resources {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ job "jupyter" {
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.jupyter.entryPoints=websecure",
|
"traefik.http.routers.jupyter.entryPoints=websecure",
|
||||||
"traefik.http.routers.jupyter.middlewares=authentik@file",
|
"traefik.http.routers.jupyter.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ EOH
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.loki.entryPoints=websecure",
|
"traefik.http.routers.loki.entryPoints=websecure",
|
||||||
"traefik.http.routers.loki.middlewares=authentik@file",
|
"traefik.http.routers.loki.middlewares=oidc-auth@file",
|
||||||
"metrics",
|
"metrics",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ job "maps" {
|
|||||||
to = 80
|
to = 80
|
||||||
}
|
}
|
||||||
port "php" {
|
port "php" {
|
||||||
static = 9001
|
static = 9000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,12 @@ job "media" {
|
|||||||
|
|
||||||
group "servers" {
|
group "servers" {
|
||||||
network {
|
network {
|
||||||
port "radarr" { to = 7878 }
|
port "radarr" { static = 7878 }
|
||||||
port "sonarr" { to = 8989 }
|
port "sonarr" { static = 8989 }
|
||||||
port "bazarr" { to = 6767 }
|
port "bazarr" { to = 6767 }
|
||||||
|
port "prowlarr" { static = 9696 }
|
||||||
|
port "jellyseerr" { static = 5055 }
|
||||||
|
port "flaresolverr" { static = 8191 }
|
||||||
port "pms" { static = 32400 }
|
port "pms" { static = 32400 }
|
||||||
port "qbt_ui" { static = 8080 }
|
port "qbt_ui" { static = 8080 }
|
||||||
port "qbt_torrent" { static = 51413 }
|
port "qbt_torrent" { static = 51413 }
|
||||||
@@ -34,7 +37,7 @@ job "media" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resources {
|
resources {
|
||||||
cpu = 200
|
cpu = 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
service {
|
service {
|
||||||
@@ -44,7 +47,7 @@ job "media" {
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.radarr.entryPoints=websecure",
|
"traefik.http.routers.radarr.entryPoints=websecure",
|
||||||
"traefik.http.routers.radarr.middlewares=authentik@file",
|
"traefik.http.routers.radarr.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,7 +71,8 @@ job "media" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resources {
|
resources {
|
||||||
cpu = 200
|
cpu = 1000
|
||||||
|
memory = 500
|
||||||
}
|
}
|
||||||
|
|
||||||
service {
|
service {
|
||||||
@@ -78,20 +82,54 @@ job "media" {
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.sonarr.entryPoints=websecure",
|
"traefik.http.routers.sonarr.entryPoints=websecure",
|
||||||
"traefik.http.routers.sonarr.middlewares=authentik@file",
|
"traefik.http.routers.sonarr.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task "bazarr" {
|
# task "bazarr" {
|
||||||
|
# driver = "docker"
|
||||||
|
#
|
||||||
|
# config {
|
||||||
|
# image = "ghcr.io/hotio/bazarr:latest"
|
||||||
|
# ports = [ "bazarr" ]
|
||||||
|
# volumes = [
|
||||||
|
# "/data/services/media/bazarr:/config",
|
||||||
|
# "/data/media/media:/data/media",
|
||||||
|
# ]
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# env {
|
||||||
|
# PUID = 1000
|
||||||
|
# PGID = 1000
|
||||||
|
# TZ = "Europe/Lisbon"
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# resources {
|
||||||
|
# cpu = 200
|
||||||
|
# memory = 500
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# service {
|
||||||
|
# name = "bazarr"
|
||||||
|
# port = "bazarr"
|
||||||
|
#
|
||||||
|
# tags = [
|
||||||
|
# "traefik.enable=true",
|
||||||
|
# "traefik.http.routers.bazarr.entryPoints=websecure",
|
||||||
|
# "traefik.http.routers.bazarr.middlewares=oidc-auth@file",
|
||||||
|
# ]
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
|
task "prowlarr" {
|
||||||
driver = "docker"
|
driver = "docker"
|
||||||
|
|
||||||
config {
|
config {
|
||||||
image = "ghcr.io/hotio/bazarr:latest"
|
image = "ghcr.io/hotio/prowlarr:latest"
|
||||||
ports = [ "bazarr" ]
|
ports = [ "prowlarr" ]
|
||||||
volumes = [
|
volumes = [
|
||||||
"/data/services/media/bazarr:/config",
|
"/data/services/media/prowlarr:/config",
|
||||||
"/data/media/media:/data/media",
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,17 +144,93 @@ job "media" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
service {
|
service {
|
||||||
name = "bazarr"
|
name = "prowlarr"
|
||||||
port = "bazarr"
|
port = "prowlarr"
|
||||||
|
|
||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.bazarr.entryPoints=websecure",
|
"traefik.http.routers.prowlarr.entryPoints=websecure",
|
||||||
"traefik.http.routers.bazarr.middlewares=authentik@file",
|
"traefik.http.routers.prowlarr.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task "jellyseerr" {
|
||||||
|
driver = "docker"
|
||||||
|
|
||||||
|
config {
|
||||||
|
image = "fallenbagel/jellyseerr:latest"
|
||||||
|
ports = [ "jellyseerr" ]
|
||||||
|
volumes = [
|
||||||
|
"/data/services/media/jellyseerr:/app/config",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
env {
|
||||||
|
TZ = "Europe/Lisbon"
|
||||||
|
}
|
||||||
|
|
||||||
|
resources {
|
||||||
|
cpu = 200
|
||||||
|
}
|
||||||
|
|
||||||
|
service {
|
||||||
|
name = "jellyseerr"
|
||||||
|
port = "jellyseerr"
|
||||||
|
|
||||||
|
tags = [
|
||||||
|
"traefik.enable=true",
|
||||||
|
"traefik.http.routers.jellyseerr.entryPoints=websecure",
|
||||||
|
"traefik.http.routers.jellyseerr.middlewares=oidc-auth@file",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task "flaresolverr" {
|
||||||
|
driver = "docker"
|
||||||
|
|
||||||
|
config {
|
||||||
|
image = "ghcr.io/flaresolverr/flaresolverr:latest"
|
||||||
|
ports = [ "flaresolverr" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
env {
|
||||||
|
LOG_LEVEL = "info"
|
||||||
|
TZ = "Europe/Lisbon"
|
||||||
|
}
|
||||||
|
|
||||||
|
resources {
|
||||||
|
cpu = 500
|
||||||
|
memory = 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
service {
|
||||||
|
name = "flaresolverr"
|
||||||
|
port = "flaresolverr"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task "recyclarr" {
|
||||||
|
driver = "docker"
|
||||||
|
|
||||||
|
config {
|
||||||
|
image = "ghcr.io/recyclarr/recyclarr:latest"
|
||||||
|
volumes = [
|
||||||
|
"/data/services/media/recyclarr:/config",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
env {
|
||||||
|
TZ = "Europe/Lisbon"
|
||||||
|
CRON_SCHEDULE = "0 0 * * *" # Daily at midnight
|
||||||
|
}
|
||||||
|
|
||||||
|
resources {
|
||||||
|
cpu = 100
|
||||||
|
memory = 256
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
task "plex" {
|
task "plex" {
|
||||||
driver = "docker"
|
driver = "docker"
|
||||||
|
|
||||||
@@ -138,7 +252,7 @@ job "media" {
|
|||||||
|
|
||||||
resources {
|
resources {
|
||||||
cpu = 2000
|
cpu = 2000
|
||||||
memory = 1000
|
memory = 2000
|
||||||
}
|
}
|
||||||
|
|
||||||
service {
|
service {
|
||||||
@@ -148,7 +262,7 @@ job "media" {
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.plex.entryPoints=websecure",
|
"traefik.http.routers.plex.entryPoints=websecure",
|
||||||
"traefik.http.routers.plex.middlewares=authentik@file",
|
"traefik.http.routers.plex.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,7 +291,7 @@ job "media" {
|
|||||||
|
|
||||||
resources {
|
resources {
|
||||||
cpu = 2000
|
cpu = 2000
|
||||||
memory = 1000
|
memory = 1500
|
||||||
}
|
}
|
||||||
|
|
||||||
service {
|
service {
|
||||||
@@ -187,7 +301,7 @@ job "media" {
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.torrent.entryPoints=websecure",
|
"traefik.http.routers.torrent.entryPoints=websecure",
|
||||||
"traefik.http.routers.torrent.middlewares=authentik@file",
|
"traefik.http.routers.torrent.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,10 +39,10 @@ job "netbox" {
|
|||||||
REMOTE_AUTH_ENABLED = "true"
|
REMOTE_AUTH_ENABLED = "true"
|
||||||
REMOTE_AUTH_BACKEND = "social_core.backends.open_id_connect.OpenIdConnectAuth"
|
REMOTE_AUTH_BACKEND = "social_core.backends.open_id_connect.OpenIdConnectAuth"
|
||||||
|
|
||||||
SOCIAL_AUTH_OIDC_ENDPOINT = "https://authentik.v.paler.net/application/o/netbox/"
|
SOCIAL_AUTH_OIDC_ENDPOINT = "https://pocket-id.v.paler.net/"
|
||||||
SOCIAL_AUTH_OIDC_KEY = "XiPhZmWy2mp8hQyHLXCwk7njRNPSLTp2vSHhvWYI"
|
SOCIAL_AUTH_OIDC_KEY = "6ce1f1bb-d5e8-4ba5-b136-2643dc8bcbcf"
|
||||||
SOCIAL_AUTH_OIDC_SECRET = "Kkop2dStx0gN52V1LfPnoxcaemuur6zMsvRnqpWSDe2qSngJVcqWfvFXaNeTbdURRB6TPwjlaNJ5BXR2ChcSmokWGTGargu84Ox1D6M2zXTsfLFj9B149Mhblos4mJL1"
|
SOCIAL_AUTH_OIDC_SECRET = "Af7sJvCn9BuijoJXrB5aWv6fTmEqLCAf"
|
||||||
LOGOUT_REDIRECT_URL = "https://authentik.v.paler.net/application/o/netbox/end-session/"
|
LOGOUT_REDIRECT_URL = "https://pocket-id.v.paler.net/logout"
|
||||||
}
|
}
|
||||||
|
|
||||||
resources {
|
resources {
|
||||||
|
|||||||
39
services/ollama.hcl
Normal file
39
services/ollama.hcl
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
job "ollama" {
|
||||||
|
datacenters = ["alo"]
|
||||||
|
type = "service"
|
||||||
|
|
||||||
|
group "ollama" {
|
||||||
|
network {
|
||||||
|
port "http" {
|
||||||
|
static = 11434
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task "server" {
|
||||||
|
driver = "docker"
|
||||||
|
|
||||||
|
config {
|
||||||
|
image = "ollama/ollama:latest"
|
||||||
|
ports = ["http"]
|
||||||
|
volumes = ["/data/services/ollama:/root/.ollama"]
|
||||||
|
}
|
||||||
|
|
||||||
|
service {
|
||||||
|
name = "ollama"
|
||||||
|
port = "http"
|
||||||
|
|
||||||
|
check {
|
||||||
|
type = "http"
|
||||||
|
path = "/"
|
||||||
|
interval = "30s"
|
||||||
|
timeout = "5s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resources {
|
||||||
|
cpu = 8000
|
||||||
|
memory = 2048
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
51
services/pocket-id.hcl
Normal file
51
services/pocket-id.hcl
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
job "pocket-id" {
|
||||||
|
datacenters = ["alo"]
|
||||||
|
|
||||||
|
group "app" {
|
||||||
|
network {
|
||||||
|
port "http" {
|
||||||
|
to = 1411
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task "server" {
|
||||||
|
driver = "docker"
|
||||||
|
|
||||||
|
config {
|
||||||
|
image = "ghcr.io/pocket-id/pocket-id:v1"
|
||||||
|
ports = ["http"]
|
||||||
|
volumes = [
|
||||||
|
"/data/services/pocket-id:/app/data",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
env {
|
||||||
|
APP_URL = "https://pocket-id.v.paler.net"
|
||||||
|
TRUST_PROXY = "true"
|
||||||
|
MAXMIND_LICENSE_KEY = "${var.maxmind_license_key}"
|
||||||
|
PUID = "1000"
|
||||||
|
PGID = "1000"
|
||||||
|
}
|
||||||
|
|
||||||
|
resources {
|
||||||
|
cpu = 500
|
||||||
|
memory = 512
|
||||||
|
}
|
||||||
|
|
||||||
|
service {
|
||||||
|
name = "pocket-id"
|
||||||
|
port = "http"
|
||||||
|
|
||||||
|
tags = [
|
||||||
|
"traefik.enable=true",
|
||||||
|
"traefik.http.routers.pocket-id.entryPoints=websecure",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "maxmind_license_key" {
|
||||||
|
type = string
|
||||||
|
default = "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"
|
||||||
|
}
|
||||||
@@ -91,15 +91,15 @@ job "postgres" {
|
|||||||
PGADMIN_CONFIG_OAUTH2_AUTO_CREATE_USER = "True"
|
PGADMIN_CONFIG_OAUTH2_AUTO_CREATE_USER = "True"
|
||||||
PGADMIN_CONFIG_OAUTH2_CONFIG = <<EOH
|
PGADMIN_CONFIG_OAUTH2_CONFIG = <<EOH
|
||||||
[{
|
[{
|
||||||
'OAUTH2_NAME' : 'authentik',
|
'OAUTH2_NAME' : 'pocket-id',
|
||||||
'OAUTH2_DISPLAY_NAME' : 'SSO',
|
'OAUTH2_DISPLAY_NAME' : 'SSO',
|
||||||
'OAUTH2_CLIENT_ID' : 'o4p3B03ayTQ2kpwmM7GswbcfO78JHCTdoZqKJEut',
|
'OAUTH2_CLIENT_ID' : '180133da-1bd7-4cde-9c18-2f277e962dab',
|
||||||
'OAUTH2_CLIENT_SECRET' : '7UYHONOCVdjpRMK9Ojwds0qPPpxCiztbIRhK7FJ2IFBpUgN6tnmpEjlkPYimiGKfaHLhy4XE7kQm7Et1Jm0hgyia0iB1VIlp623ckppbwkM6IfpTE1LfEmTMtPrxSngx',
|
'OAUTH2_CLIENT_SECRET' : 'ELYNAfiWSGYJQUXUDOdpm7tTtyLbrs4E',
|
||||||
'OAUTH2_TOKEN_URL' : 'https://authentik.v.paler.net/application/o/token/',
|
'OAUTH2_TOKEN_URL' : 'https://pocket-id.v.paler.net/api/oidc/token',
|
||||||
'OAUTH2_AUTHORIZATION_URL' : 'https://authentik.v.paler.net/application/o/authorize/',
|
'OAUTH2_AUTHORIZATION_URL' : 'https://pocket-id.v.paler.net/authorize',
|
||||||
'OAUTH2_API_BASE_URL' : 'https://authentik.v.paler.net/',
|
'OAUTH2_API_BASE_URL' : 'https://pocket-id.v.paler.net/',
|
||||||
'OAUTH2_USERINFO_ENDPOINT' : 'https://authentik.v.paler.net/application/o/userinfo/',
|
'OAUTH2_USERINFO_ENDPOINT' : 'https://pocket-id.v.paler.net/api/oidc/userinfo',
|
||||||
'OAUTH2_SERVER_METADATA_URL' : 'https://authentik.v.paler.net/application/o/pgadmin/.well-known/openid-configuration',
|
'OAUTH2_SERVER_METADATA_URL' : 'https://pocket-id.v.paler.net/.well-known/openid-configuration',
|
||||||
'OAUTH2_SCOPE' : 'openid email profile',
|
'OAUTH2_SCOPE' : 'openid email profile',
|
||||||
'OAUTH2_ICON' : 'fa-database',
|
'OAUTH2_ICON' : 'fa-database',
|
||||||
'OAUTH2_BUTTON_COLOR' : '#00ff00'
|
'OAUTH2_BUTTON_COLOR' : '#00ff00'
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ job "prometheus" {
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.prometheus.entryPoints=websecure",
|
"traefik.http.routers.prometheus.entryPoints=websecure",
|
||||||
"traefik.http.routers.prometheus.middlewares=authentik@file",
|
"traefik.http.routers.prometheus.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
|
|
||||||
check {
|
check {
|
||||||
|
|||||||
146
services/tiddlywiki-mcp.hcl
Normal file
146
services/tiddlywiki-mcp.hcl
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
job "tiddlywiki-mcp" {
|
||||||
|
datacenters = ["alo"]
|
||||||
|
|
||||||
|
meta {
|
||||||
|
uuid = uuidv4()
|
||||||
|
}
|
||||||
|
|
||||||
|
group "servers" {
|
||||||
|
network {
|
||||||
|
port "captainslog" {
|
||||||
|
static = 3500
|
||||||
|
}
|
||||||
|
port "alowiki" {
|
||||||
|
static = 3501
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
volume "services" {
|
||||||
|
type = "host"
|
||||||
|
source = "services"
|
||||||
|
read_only = false
|
||||||
|
}
|
||||||
|
|
||||||
|
volume "nix-store" {
|
||||||
|
type = "host"
|
||||||
|
source = "nix-store"
|
||||||
|
read_only = true
|
||||||
|
}
|
||||||
|
|
||||||
|
volume "sw" {
|
||||||
|
type = "host"
|
||||||
|
source = "sw"
|
||||||
|
read_only = true
|
||||||
|
}
|
||||||
|
|
||||||
|
task "captainslog" {
|
||||||
|
driver = "exec"
|
||||||
|
|
||||||
|
config {
|
||||||
|
command = "/sw/bin/node"
|
||||||
|
args = ["/data/services/tiddlywiki-mcp/dist/index.js"]
|
||||||
|
}
|
||||||
|
|
||||||
|
env {
|
||||||
|
MCP_TRANSPORT = "http"
|
||||||
|
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 {
|
||||||
|
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-captainslog"
|
||||||
|
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"
|
||||||
|
path = "/health"
|
||||||
|
interval = "10s"
|
||||||
|
timeout = "2s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resources {
|
||||||
|
memory = 256
|
||||||
|
}
|
||||||
|
|
||||||
|
user = "ppetru"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,7 +34,7 @@ job "traefik" {
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.api.entryPoints=websecure",
|
"traefik.http.routers.api.entryPoints=websecure",
|
||||||
"traefik.http.routers.api.middlewares=authentik@file",
|
"traefik.http.routers.api.middlewares=oidc-auth@file",
|
||||||
"traefik.http.routers.api.rule=Host(`traefik.v.paler.net`)",
|
"traefik.http.routers.api.rule=Host(`traefik.v.paler.net`)",
|
||||||
"traefik.http.routers.api.service=api@internal",
|
"traefik.http.routers.api.service=api@internal",
|
||||||
]
|
]
|
||||||
@@ -63,6 +63,7 @@ job "traefik" {
|
|||||||
volumes = [
|
volumes = [
|
||||||
"local/traefik.yml:/etc/traefik/traefik.yml",
|
"local/traefik.yml:/etc/traefik/traefik.yml",
|
||||||
"/data/services/traefik:/config",
|
"/data/services/traefik:/config",
|
||||||
|
"/data/services/traefik/plugins-storage:/plugins-storage",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +76,12 @@ global:
|
|||||||
#log:
|
#log:
|
||||||
# level: debug
|
# level: debug
|
||||||
|
|
||||||
|
experimental:
|
||||||
|
plugins:
|
||||||
|
traefik-oidc-auth:
|
||||||
|
moduleName: "github.com/sevensolutions/traefik-oidc-auth"
|
||||||
|
version: "v0.17.0"
|
||||||
|
|
||||||
api:
|
api:
|
||||||
dashboard: true
|
dashboard: true
|
||||||
|
|
||||||
@@ -126,6 +133,8 @@ entryPoints:
|
|||||||
websecure:
|
websecure:
|
||||||
address: ":{{{ env "NOMAD_PORT_https" }}}"
|
address: ":{{{ env "NOMAD_PORT_https" }}}"
|
||||||
http:
|
http:
|
||||||
|
encodedCharacters:
|
||||||
|
allowEncodedSlash: true
|
||||||
tls:
|
tls:
|
||||||
certResolver: letsencrypt
|
certResolver: letsencrypt
|
||||||
|
|
||||||
@@ -145,7 +154,7 @@ EOH
|
|||||||
}
|
}
|
||||||
|
|
||||||
resources {
|
resources {
|
||||||
cpu = 100
|
cpu = 200
|
||||||
memory = 512
|
memory = 512
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ job "unifi" {
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.unifi.entryPoints=websecure",
|
"traefik.http.routers.unifi.entryPoints=websecure",
|
||||||
"traefik.http.routers.unifi.middlewares=authentik@file",
|
"traefik.http.routers.unifi.middlewares=oidc-auth@file",
|
||||||
"traefik.http.services.unifi.loadbalancer.server.scheme=https",
|
"traefik.http.services.unifi.loadbalancer.server.scheme=https",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
job "urbit" {
|
job "urbit" {
|
||||||
datacenters = ["alo"]
|
datacenters = ["alo"]
|
||||||
|
|
||||||
|
meta {
|
||||||
|
uuid = uuidv4()
|
||||||
|
}
|
||||||
|
|
||||||
group "os" {
|
group "os" {
|
||||||
network {
|
network {
|
||||||
port "http" {
|
port "http" {
|
||||||
@@ -25,7 +29,7 @@ job "urbit" {
|
|||||||
# You can also set a variable loom size (Urbit memory size) using
|
# 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
|
# --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).
|
# a 4GiB loom (2^32 bytes = 4GiB). The default loom size is 31 (2GiB).
|
||||||
"--loom=31",
|
"--loom=32",
|
||||||
]
|
]
|
||||||
volumes = [
|
volumes = [
|
||||||
"/data/services/urbit:/urbit",
|
"/data/services/urbit:/urbit",
|
||||||
@@ -39,13 +43,13 @@ job "urbit" {
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.urbit.entryPoints=websecure",
|
"traefik.http.routers.urbit.entryPoints=websecure",
|
||||||
"traefik.http.routers.urbit.middlewares=authentik@file",
|
"traefik.http.routers.urbit.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
resources {
|
resources {
|
||||||
# dependent on --loom setting + some buffer
|
# dependent on --loom setting + some buffer
|
||||||
memory = 2100
|
memory = 4200
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
48
services/wavelog.hcl
Normal file
48
services/wavelog.hcl
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# ABOUTME: Nomad job spec for Wavelog amateur radio logging application
|
||||||
|
# ABOUTME: Uses MySQL database and OIDC auth via Traefik middleware
|
||||||
|
|
||||||
|
job "wavelog" {
|
||||||
|
datacenters = ["alo"]
|
||||||
|
|
||||||
|
group "wavelog" {
|
||||||
|
network {
|
||||||
|
port "http" {
|
||||||
|
to = 80
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task "wavelog" {
|
||||||
|
driver = "docker"
|
||||||
|
|
||||||
|
config {
|
||||||
|
image = "ghcr.io/wavelog/wavelog:latest"
|
||||||
|
ports = ["http"]
|
||||||
|
volumes = [
|
||||||
|
"/data/services/wavelog/config:/var/www/html/application/config/docker",
|
||||||
|
"/data/services/wavelog/uploads:/var/www/html/uploads",
|
||||||
|
"/data/services/wavelog/userdata:/var/www/html/userdata",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
env {
|
||||||
|
CI_ENV = "docker"
|
||||||
|
}
|
||||||
|
|
||||||
|
resources {
|
||||||
|
cpu = 300
|
||||||
|
memory = 512
|
||||||
|
}
|
||||||
|
|
||||||
|
service {
|
||||||
|
name = "wavelog"
|
||||||
|
port = "http"
|
||||||
|
|
||||||
|
tags = [
|
||||||
|
"traefik.enable=true",
|
||||||
|
"traefik.http.routers.wavelog.entryPoints=websecure",
|
||||||
|
"traefik.http.routers.wavelog.middlewares=oidc-auth@file",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -73,7 +73,7 @@ EOH
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.webodm.entryPoints=websecure",
|
"traefik.http.routers.webodm.entryPoints=websecure",
|
||||||
"traefik.http.routers.webodm.middlewares=authentik@file",
|
"traefik.http.routers.webodm.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,7 +97,7 @@ EOH
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.clusterodm.entryPoints=websecure",
|
"traefik.http.routers.clusterodm.entryPoints=websecure",
|
||||||
"traefik.http.routers.clusterodm.middlewares=authentik@file",
|
"traefik.http.routers.clusterodm.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ job "weewx" {
|
|||||||
driver = "docker"
|
driver = "docker"
|
||||||
|
|
||||||
config {
|
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
|
# to be able to receive UDP broadcast packets from the weatherlink
|
||||||
network_mode = "host"
|
network_mode = "host"
|
||||||
volumes = [
|
volumes = [
|
||||||
@@ -29,7 +29,7 @@ job "weewx" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resources {
|
resources {
|
||||||
memory = 1024
|
memory = 1500
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ job "weewx" {
|
|||||||
driver = "docker"
|
driver = "docker"
|
||||||
|
|
||||||
config {
|
config {
|
||||||
image = "gitea.v.paler.net/ppetru/opensprinkler-weather:latest"
|
image = "gitea.v.paler.net/alo/opensprinkler-weather:latest"
|
||||||
|
|
||||||
ports = [ "osweather" ]
|
ports = [ "osweather" ]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ job "whoami" {
|
|||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.whoami.rule=Host(`test.alo.land`)",
|
"traefik.http.routers.whoami.rule=Host(`test.alo.land`)",
|
||||||
"traefik.http.routers.whoami.entryPoints=websecure",
|
"traefik.http.routers.whoami.entryPoints=websecure",
|
||||||
"traefik.http.routers.whoami.middlewares=authentik@file",
|
"traefik.http.routers.whoami.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ job "wiki" {
|
|||||||
"--listen",
|
"--listen",
|
||||||
"host=0.0.0.0",
|
"host=0.0.0.0",
|
||||||
"port=${NOMAD_PORT_captainslog}",
|
"port=${NOMAD_PORT_captainslog}",
|
||||||
"authenticated-user-header=X-authentik-username",
|
"authenticated-user-header=X-Oidc-Username",
|
||||||
"readers=ppetru",
|
"readers=ppetru,claude-code",
|
||||||
"writers=ppetru",
|
"writers=ppetru,claude-code",
|
||||||
"admin=ppetru",
|
"admin=ppetru",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ job "wiki" {
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.captainslog.entryPoints=websecure",
|
"traefik.http.routers.captainslog.entryPoints=websecure",
|
||||||
"traefik.http.routers.captainslog.middlewares=authentik@file",
|
"traefik.http.routers.captainslog.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,9 +85,9 @@ job "wiki" {
|
|||||||
"--listen",
|
"--listen",
|
||||||
"host=0.0.0.0",
|
"host=0.0.0.0",
|
||||||
"port=${NOMAD_PORT_alo}",
|
"port=${NOMAD_PORT_alo}",
|
||||||
"authenticated-user-header=X-authentik-username",
|
"authenticated-user-header=X-Oidc-Username",
|
||||||
"readers=ppetru,ines",
|
"readers=ppetru,ines,claude-code",
|
||||||
"writers=ppetru,ines",
|
"writers=ppetru,ines,claude-code",
|
||||||
"admin=ppetru",
|
"admin=ppetru",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -112,7 +112,7 @@ job "wiki" {
|
|||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.alowiki.rule=Host(`wiki.alo.land`)",
|
"traefik.http.routers.alowiki.rule=Host(`wiki.alo.land`)",
|
||||||
"traefik.http.routers.alowiki.entryPoints=websecure",
|
"traefik.http.routers.alowiki.entryPoints=websecure",
|
||||||
"traefik.http.routers.alowiki.middlewares=authentik@file",
|
"traefik.http.routers.alowiki.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ job "wiki" {
|
|||||||
"--listen",
|
"--listen",
|
||||||
"host=0.0.0.0",
|
"host=0.0.0.0",
|
||||||
"port=${NOMAD_PORT_pispace}",
|
"port=${NOMAD_PORT_pispace}",
|
||||||
"authenticated-user-header=X-authentik-username",
|
"authenticated-user-header=X-Oidc-Username",
|
||||||
"readers=ppetru,ines",
|
"readers=ppetru,ines",
|
||||||
"writers=ppetru,ines",
|
"writers=ppetru,ines",
|
||||||
"admin=ppetru",
|
"admin=ppetru",
|
||||||
@@ -160,7 +160,7 @@ job "wiki" {
|
|||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.pispace.rule=Host(`pi.paler.net`)",
|
"traefik.http.routers.pispace.rule=Host(`pi.paler.net`)",
|
||||||
"traefik.http.routers.pispace.entryPoints=websecure",
|
"traefik.http.routers.pispace.entryPoints=websecure",
|
||||||
"traefik.http.routers.pispace.middlewares=authentik@file",
|
"traefik.http.routers.pispace.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ job "wiki" {
|
|||||||
"--listen",
|
"--listen",
|
||||||
"host=0.0.0.0",
|
"host=0.0.0.0",
|
||||||
"port=${NOMAD_PORT_grok}",
|
"port=${NOMAD_PORT_grok}",
|
||||||
"authenticated-user-header=X-authentik-username",
|
"authenticated-user-header=X-Oidc-Username",
|
||||||
"readers=ppetru",
|
"readers=ppetru",
|
||||||
"writers=ppetru",
|
"writers=ppetru",
|
||||||
"admin=ppetru",
|
"admin=ppetru",
|
||||||
@@ -207,7 +207,7 @@ job "wiki" {
|
|||||||
tags = [
|
tags = [
|
||||||
"traefik.enable=true",
|
"traefik.enable=true",
|
||||||
"traefik.http.routers.groktw.entryPoints=websecure",
|
"traefik.http.routers.groktw.entryPoints=websecure",
|
||||||
"traefik.http.routers.groktw.middlewares=authentik@file",
|
"traefik.http.routers.groktw.middlewares=oidc-auth@file",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user