Compare commits

...

3 Commits

Author SHA1 Message Date
17e3aa6841 Update inputs 2025-05-05 15:36:27 +01:00
795c8fab71 firmware: Add initial web server 2024-09-01 23:06:41 +01:00
8496ec73ad firmware: Add ability to load config from USB 2024-09-01 22:36:07 +01:00
6 changed files with 340 additions and 322 deletions

View File

@@ -1,12 +1,25 @@
{ lib, pkgs, config, ... }:
let
configurer = pkgs.substituteAll {
configurer = pkgs.replaceVarsWith {
src = ./configurer.py;
isExecutable = true;
python = pkgs.python3.withPackages (ps: with ps; [ pyyaml ]);
wireguardTools = pkgs.wireguard-tools;
systemd = config.systemd.package;
replacements = {
python = pkgs.python3.withPackages (ps: with ps; [ pyyaml ]);
utilLinux = pkgs.util-linux;
wireguardTools = pkgs.wireguard-tools;
systemd = config.systemd.package;
iwd = config.networking.wireless.iwd.package;
};
};
app = pkgs.replaceVarsWith {
src = ./app.py;
isExecutable = true;
replacements = {
python = pkgs.python3.withPackages (ps: with ps; [ pyyaml aiohttp ]);
};
};
in
{
@@ -14,18 +27,37 @@ in
systemd = {
services = {
qclk-configurer = {
description = "qclk dynamic configurer";
description = "qCLK dynamic configurer";
after = [ "network.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = "${configurer}";
Type = "notify-reload";
ExecStart = "${configurer} serve";
Restart = "on-failure";
};
wantedBy = [ "multi-user.target" ];
};
qclk-app = {
description = "qCLK app";
after = [ "qclk-configurer.service" ];
serviceConfig = {
Type = "simple";
ExecStart = "${app}";
Restart = "on-failure";
};
wantedBy = [ "multi-user.target" ];
};
};
};
services = {
udev.extraRules = ''
SUBSYSTEM=="block", ACTION=="add", ENV{ID_BUS}=="usb", ENV{ID_FS_TYPE}=="vfat", RUN+="${configurer} ext-load %E{ID_PATH_TAG} %N"
'';
};
};
}

29
firmware/app.py Executable file
View File

@@ -0,0 +1,29 @@
#! @python@/bin/python -B
import os
from aiohttp import web
import yaml
CONF_FILE = os.getenv('CONFIG', '/etc/qclk/config.yaml')
conf_k = web.AppKey('config', dict)
def log(m):
print(m, file=sys.stderr)
routes = web.RouteTableDef()
@routes.get('/')
async def index(req):
return web.Response(text='Hello, world!')
def main():
app = web.Application()
with open(CONF_FILE) as f:
app[conf_k] = yaml.safe_load(f)
app.add_routes(routes)
web.run_app(app, port=8080)
if __name__ == '__main__':
main()

View File

@@ -1,11 +1,15 @@
#! @python@/bin/python -B
import argparse
import base64
import json
import os
import shutil
import signal
import socket
import subprocess
import sys
import time
import tomllib
import yaml
@@ -37,18 +41,34 @@ def iwd_ssid_basename(ssid: str) -> str:
return f"={ssid.encode('utf-8').hex()}"
class Configurer:
tmpdir = '/tmp/qclk'
tmpdir = '/run/qclk'
def __init__(self, conf_file: str, wg_key_file: str):
with open(conf_file) as f:
self.config = yaml.safe_load(f)
self.conf_file = conf_file
self.load_config()
with open(wg_key_file, 'rb') as f:
output = subprocess.check_output(['@wireguardTools@/bin/wg', 'pubkey'], input=f.read())
pubkey = base64.b64decode(output)
self.id = pubkey[:4].hex()
self.load_dir = os.path.join(self.tmpdir, 'load')
self.sd_sock = None
os.makedirs(self.tmpdir, exist_ok=True)
os.makedirs(self.load_dir, exist_ok=True)
def load_config(self):
with open(self.conf_file) as f:
self.config = yaml.safe_load(f)
def write_config(self):
log(f'Updating config')
tmp = os.path.join(self.tmpdir, 'new-config.yaml')
with open(tmp, 'w') as f:
yaml.dump(self.config, f)
shutil.move(tmp, self.conf_file)
def _setup_hostname(self):
hostname = f'qclk-{self.id}'
@@ -78,6 +98,8 @@ class Configurer:
log(f"Cleaning up old IWD config '{f}'")
os.remove(os.path.join(IWD_PATH, f))
subprocess.call(['@iwd@/bin/iwctl', 'station', 'wifi', 'scan'])
def _setup_mgmt(self):
conf = self.config['management']
tmp = os.path.join(self.tmpdir, 'management.network')
@@ -90,13 +112,118 @@ class Configurer:
subprocess.check_call(['@systemd@/bin/networkctl', 'reload'])
def reconcile(self):
if self.sd_sock is not None:
self.sd_sock.sendall(b'STATUS=Reconciling configuration...')
self._setup_hostname()
self._setup_wifi()
self._setup_mgmt()
if self.sd_sock is not None:
self.sd_sock.sendall(b'STATUS=OK\nREADY=1')
def _ext_load(self, identifier: str, device: str):
mountpoint = os.path.join(self.tmpdir, 'mnt', identifier)
os.makedirs(mountpoint, exist_ok=True)
try:
log(f'Mounting {device} -> {mountpoint}')
subprocess.check_call(['@utilLinux@/bin/mount', '-t', 'vfat', '-o', 'ro', device, mountpoint])
try:
path = os.path.join(mountpoint, 'qclk.toml')
if not os.path.exists(path):
log(f'No config file found on {device}')
return
with open(path, 'rb') as f:
ext_conf = tomllib.load(f)
finally:
subprocess.check_call(['@utilLinux@/bin/umount', mountpoint])
finally:
os.rmdir(mountpoint)
if 'wifi' in ext_conf:
log(f'Loading WiFi settings from {device}')
self.config['wifi'] = {
'ssid': ext_conf['wifi']['ssid'],
'password': ext_conf['wifi'].get('password', ''),
'hidden': ext_conf['wifi'].get('hidden', False),
}
self._setup_wifi()
self.write_config()
def serve(self, args: argparse.Namespace):
os.makedirs(os.path.join(self.tmpdir, 'load'), exist_ok=True)
addr = os.getenv('NOTIFY_SOCKET')
if addr is not None:
self.sd_sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
if addr[0] == '@':
addr = '\0' + addr[1:]
self.sd_sock.connect(addr)
running = True
def sighandler(sig, frame):
nonlocal running
if sig in (signal.SIGINT, signal.SIGTERM):
running = False
return
if sig == signal.SIGHUP:
if self.sd_sock is not None:
t = time.clock_gettime_ns(time.CLOCK_MONOTONIC) // 1000
self.sd_sock.sendall(f'RELOADING=1\nMONOTONIC_USEC={t}'.encode('ascii'))
self.load_config()
self.reconcile()
elif sig == signal.SIGUSR1:
for identifier in os.listdir(self.load_dir):
fname = os.path.join(self.load_dir, identifier)
with open(fname) as f:
device = f.read().strip()
os.remove(fname)
try:
self._ext_load(identifier, device)
except Exception as ex:
log(f'Failed to load config from {device}: {ex}')
for s in [signal.SIGINT, signal.SIGTERM, signal.SIGHUP, signal.SIGUSR1]:
signal.signal(s, sighandler)
self.reconcile()
while running:
signal.pause()
if self.sd_sock is not None:
self.sd_sock.close()
def ext_load(self, args: argparse.Namespace):
with open(os.path.join(self.load_dir, args.identifier), 'w') as f:
print(args.device, file=f)
output = subprocess.check_output(
['@systemd@/bin/systemctl', 'show', '--property', 'MainPID', '--value', 'qclk-configurer.service'], encoding='ascii')
pid = int(output.strip())
os.kill(pid, signal.SIGUSR1)
def main():
parser = argparse.ArgumentParser(description='qclkOS configurer')
cmds = parser.add_subparsers(title='commands', help='reconcile')
rec_parser = cmds.add_parser('serve')
rec_parser.set_defaults(func=Configurer.serve)
eload_parser = cmds.add_parser('ext-load')
eload_parser.set_defaults(func=Configurer.ext_load)
eload_parser.add_argument('identifier', help='Unique device name')
eload_parser.add_argument('device', help='Block device path')
args = parser.parse_args()
c = Configurer(CONF_FILE, WG_KEY_FILE)
c.reconcile()
if not hasattr(args, 'func'):
c.serve(args)
else:
args.func(c, args)
if __name__ == '__main__':
main()

View File

@@ -7,8 +7,8 @@ let
in
{
trivial = prev.trivial // {
release = "24.08:u-${prev.trivial.release}";
codeName = "Alpha";
release = "25.05:u-${prev.trivial.release}";
codeName = "Beta";
revisionWithDefault = default: self.rev or default;
versionSuffix = ".${date}.${revCode self}:u-${revCode inputs.nixpkgs}";
};
@@ -40,6 +40,8 @@ in
perSystem = { lib, libMy, pkgs, ... }:
let
inherit (lib) concatMapStringsSep;
devPython = pkgs.python3.withPackages (ps: with ps; [ pyyaml aiohttp ]);
in
{
devenv.shells.firmware = libMy.withRootdir {
@@ -47,6 +49,7 @@ in
nixos-rebuild
nixVersions.latest
wireguard-tools
devPython
];
scripts =

View File

@@ -9,6 +9,11 @@
hostName = config.system.name;
useDHCP = false;
useNetworkd = true;
firewall = {
interfaces.management.allowedTCPPorts = [ 8080 ];
};
wireless.iwd = {
enable = true;
settings = {

438
flake.lock generated
View File

@@ -2,30 +2,28 @@
"nodes": {
"cachix": {
"inputs": {
"devenv": "devenv_2",
"devenv": [
"devenv"
],
"flake-compat": [
"devenv",
"flake-compat"
"devenv"
],
"nixpkgs": [
"devenv",
"nixpkgs"
"git-hooks": [
"devenv"
],
"pre-commit-hooks": [
"devenv",
"pre-commit-hooks"
]
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1712055811,
"narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=",
"lastModified": 1742042642,
"narHash": "sha256-D0gP8srrX0qj+wNYNPdtVJsQuFzIng3q43thnHXQ/es=",
"owner": "cachix",
"repo": "cachix",
"rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30",
"rev": "a624d3eaf4b1d225f918de8543ed739f2f574203",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "latest",
"repo": "cachix",
"type": "github"
}
@@ -33,54 +31,23 @@
"devenv": {
"inputs": {
"cachix": "cachix",
"flake-compat": "flake-compat_2",
"nix": "nix_2",
"flake-compat": "flake-compat",
"git-hooks": "git-hooks",
"nix": "nix",
"nixpkgs": [
"nixpkgs"
],
"pre-commit-hooks": "pre-commit-hooks"
},
"locked": {
"lastModified": 1724763216,
"narHash": "sha256-oW2bwCrJpIzibCNK6zfIDaIQw765yMAuMSG2gyZfGv0=",
"owner": "cachix",
"repo": "devenv",
"rev": "1e4ef61205b9aa20fe04bf1c468b6a316281c4f1",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"devenv_2": {
"inputs": {
"flake-compat": [
"devenv",
"cachix",
"flake-compat"
],
"nix": "nix",
"nixpkgs": "nixpkgs",
"poetry2nix": "poetry2nix",
"pre-commit-hooks": [
"devenv",
"cachix",
"pre-commit-hooks"
]
},
"locked": {
"lastModified": 1708704632,
"narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=",
"lastModified": 1746423062,
"narHash": "sha256-BgiRweL6nMjeO2BQgnOyIquuviybI4S8Nc8r9hYjcBc=",
"owner": "cachix",
"repo": "devenv",
"rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196",
"rev": "aba5cf8412827fdb637fceb2c305d10fcea907c6",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "python-rewrite",
"repo": "devenv",
"type": "github"
}
@@ -88,27 +55,11 @@
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"lastModified": 1733328505,
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
"type": "github"
},
"original": {
@@ -118,15 +69,37 @@
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"devenv",
"nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1712014858,
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1725024810,
"narHash": "sha256-ODYRm8zHfLTH3soTFWE452ydPYz2iTvr9T8ftDMUQ3E=",
"lastModified": 1743550720,
"narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "af510d4a62d071ea13925ce41c95e3dec816c01d",
"rev": "c621e8422220273271f52058f618c94e405bb0f5",
"type": "github"
},
"original": {
@@ -135,39 +108,28 @@
"type": "github"
}
},
"flake-utils": {
"git-hooks": {
"inputs": {
"systems": "systems"
"flake-compat": [
"devenv"
],
"gitignore": "gitignore",
"nixpkgs": [
"devenv",
"nixpkgs"
]
},
"locked": {
"lastModified": 1689068808,
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
"lastModified": 1742649964,
"narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
@@ -175,7 +137,7 @@
"inputs": {
"nixpkgs": [
"devenv",
"pre-commit-hooks",
"git-hooks",
"nixpkgs"
]
},
@@ -195,11 +157,11 @@
},
"impermanence": {
"locked": {
"lastModified": 1724489415,
"narHash": "sha256-ey8vhwY/6XCKoh7fyTn3aIQs7WeYSYtLbYEG87VCzX4=",
"lastModified": 1737831083,
"narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=",
"owner": "nix-community",
"repo": "impermanence",
"rev": "c7f5b394397398c023000cf843986ee2571a1fd7",
"rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170",
"type": "github"
},
"original": {
@@ -208,166 +170,109 @@
"type": "github"
}
},
"libgit2": {
"flake": false,
"locked": {
"lastModified": 1697646580,
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
"owner": "libgit2",
"repo": "libgit2",
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
"type": "github"
},
"original": {
"owner": "libgit2",
"repo": "libgit2",
"type": "github"
}
},
"nix": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": [
"devenv",
"cachix",
"devenv",
"nixpkgs"
"flake-compat": [
"devenv"
],
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1712911606,
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
"owner": "domenkozar",
"repo": "nix",
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "devenv-2.21",
"repo": "nix",
"type": "github"
}
},
"nix-github-actions": {
"inputs": {
"nixpkgs": [
"devenv",
"cachix",
"devenv",
"poetry2nix",
"nixpkgs"
"flake-parts": "flake-parts",
"libgit2": "libgit2",
"nixpkgs": "nixpkgs_2",
"nixpkgs-23-11": [
"devenv"
],
"nixpkgs-regression": [
"devenv"
],
"pre-commit-hooks": [
"devenv"
]
},
"locked": {
"lastModified": 1688870561,
"narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=",
"owner": "nix-community",
"repo": "nix-github-actions",
"rev": "165b1650b753316aa7f1787f3005a8d2da0f5301",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-github-actions",
"type": "github"
}
},
"nix_2": {
"inputs": {
"flake-compat": [
"devenv",
"flake-compat"
],
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-regression": "nixpkgs-regression_2"
},
"locked": {
"lastModified": 1712911606,
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
"lastModified": 1745930071,
"narHash": "sha256-bYyjarS3qSNqxfgc89IoVz8cAFDkF9yPE63EJr+h50s=",
"owner": "domenkozar",
"repo": "nix",
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
"rev": "b455edf3505f1bf0172b39a735caef94687d0d9c",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "devenv-2.21",
"ref": "devenv-2.24",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1692808169,
"narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=",
"lastModified": 1733212471,
"narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9201b5ff357e781bf014d0330d18555695df7ba8",
"rev": "55d15ad12a74eb7d4646254e13638ad0c4128776",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1722555339,
"narHash": "sha256-uFf2QeW7eAHlYXuDktm9c25OxOyCoUOQmh5SZ9amE5Q=",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz"
}
},
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"lastModified": 1743296961,
"narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-regression_2": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1710695816,
"narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "614b4613980a522ba49f0d194531beddbb7220d3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.11",
"repo": "nixpkgs",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1725067332,
"narHash": "sha256-bMi5zhDwR6jdmN5mBHEu9gQQf9CibIEasA/6mc34Iek=",
"lastModified": 1717432640,
"narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "192e7407cc66e2eccc3a6c5ad3834dd62fae3800",
"rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1746332716,
"narHash": "sha256-VBmKSkmw9PYBCEGhBKzORjx+nwNZkPZyHcUHE21A/ws=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6b1c028bce9c89e9824cde040d6986d428296055",
"type": "github"
},
"original": {
@@ -375,65 +280,12 @@
"type": "indirect"
}
},
"poetry2nix": {
"inputs": {
"flake-utils": "flake-utils",
"nix-github-actions": "nix-github-actions",
"nixpkgs": [
"devenv",
"cachix",
"devenv",
"nixpkgs"
]
},
"locked": {
"lastModified": 1692876271,
"narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=",
"owner": "nix-community",
"repo": "poetry2nix",
"rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "poetry2nix",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": [
"devenv",
"flake-compat"
],
"flake-utils": "flake-utils_2",
"gitignore": "gitignore",
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1713775815,
"narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"flake-parts": "flake-parts",
"flake-parts": "flake-parts_2",
"impermanence": "impermanence",
"nixpkgs": "nixpkgs_2",
"nixpkgs": "nixpkgs_3",
"rootdir": "rootdir"
}
},
@@ -448,36 +300,6 @@
"type": "file",
"url": "file:///dev/null"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"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",