This commit is contained in:
Dmitry Zuikov 2024-09-20 07:29:36 +03:00
parent 627a3e0911
commit 4e96b1f7f1
4 changed files with 234 additions and 74 deletions

View File

@ -39,6 +39,7 @@ import Data.ByteString.Lazy.Char8 qualified as LBS8
import Data.ByteString qualified as BS import Data.ByteString qualified as BS
import Data.Either import Data.Either
import Data.Map qualified as Map import Data.Map qualified as Map
import Data.Set qualified as Set
import Data.Maybe import Data.Maybe
import Data.HashSet qualified as HS import Data.HashSet qualified as HS
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
@ -592,6 +593,9 @@ refchanImport = do
here <- selectIsAlreadyScanned x here <- selectIsAlreadyScanned x
pure $ not here pure $ not here
fixmeGkSign <- putBlock sto "FIXMEGROUPKEYBLOCKv1" <&> fmap HashRef
>>= orThrowUser "hbs2 storage error. aborted"
walkRefChanTx @UNIX goodToGo chan $ \txh u -> do walkRefChanTx @UNIX goodToGo chan $ \txh u -> do
case u of case u of
@ -610,54 +614,42 @@ refchanImport = do
P orig (ProposeTran _ box) -> void $ runMaybeT do P orig (ProposeTran _ box) -> void $ runMaybeT do
(_, bs) <- unboxSignedBox0 box & toMPlus (_, bs) <- unboxSignedBox0 box & toMPlus
AnnotatedHashRef _ href <- deserialiseOrFail @AnnotatedHashRef (LBS.fromStrict bs) AnnotatedHashRef sn href <- deserialiseOrFail @AnnotatedHashRef (LBS.fromStrict bs)
& toMPlus . either (const Nothing) Just & toMPlus . either (const Nothing) Just
scanned <- lift $ selectIsAlreadyScanned href scanned <- lift $ selectIsAlreadyScanned href
when (not scanned || ignCached) do when (not scanned || ignCached) do
-- check if metadata tx let isGk = sn == Just fixmeGkSign
meta <- runExceptT (extractMetaData @'HBS2Basic (const $ pure Nothing) sto href)
<&> fromRight mempty
let parsed = parseTop meta & fromRight mempty
let isGk = not $ L.null [ True | ListVal [SymbolVal "GK:", _] <- parsed ]
debug $ "metadata:" <+> pretty isGk <+> pretty parsed
if isGk then do if isGk then do
-- TODO: check-error-type atomically $ writeTQueue tq (Left (txh, orig, href, href))
what <- liftIO (runExceptT $ getTreeContents sto href)
<&> either (const Nothing) Just
>>= toMPlus
gkz <- deserialiseOrFail @[GroupKey 'Symm 'HBS2Basic] what
& toMPlus
for_ gkz $ \gk -> do
atomically $ writeTQueue tq (Left (txh, orig, href, gk))
else do else do
what <- liftIO (runExceptT $ getTreeContents sto href) what <- liftIO (runExceptT $ getTreeContents sto href)
<&> either (const Nothing) Just <&> either (const Nothing) Just
>>= toMPlus >>= toMPlus
exported <- deserialiseOrFail @[FixmeExported] what let exported = deserialiseOrFail @[FixmeExported] what
& toMPlus & either (const Nothing) Just
for_ exported $ \e -> do case exported of
atomically $ writeTQueue tq (Right (txh, orig, href, e)) Just e -> do
for_ e $ \x -> do
atomically $ writeTQueue tq (Right (txh, orig, href, x))
Nothing -> do
lift $ withState $ insertScanned txh
imported <- atomically $ flushTQueue tq imported <- atomically $ flushTQueue tq
withState $ transactional do withState $ transactional do
for_ imported $ \case for_ imported $ \case
Left (txh, orig, href, gk) -> do Left (txh, orig, href, gk) -> do
hx <- writeAsMerkle sto (serialise gk) -- hx <- writeAsMerkle sto (serialise gk)
notice $ "import GK" <+> pretty hx <+> "from" <+> pretty href -- notice $ "import GK" <+> pretty hx <+> "from" <+> pretty href
-- let tx = AnnotatedHashRef _ href <- deserialiseOrFail @AnnotatedHashRef (LBS.fromStrict bs) -- let tx = AnnotatedHashRef _ href <- deserialiseOrFail @AnnotatedHashRef (LBS.fromStrict bs)
-- & toMPlus . either (const Nothing) Just -- & toMPlus . either (const Nothing) Just
insertScanned txh insertScanned txh
@ -844,7 +836,6 @@ fixmeRefChanInit = do
notice $ green "refchan added" <+> pretty (AsBase58 refchan) notice $ green "refchan added" <+> pretty (AsBase58 refchan)
refchanExportGroupKeys :: FixmePerks m => FixmeM m () refchanExportGroupKeys :: FixmePerks m => FixmeM m ()
refchanExportGroupKeys = do refchanExportGroupKeys = do
@ -868,6 +859,8 @@ refchanExportGroupKeys = do
skip <- newTVarIO HS.empty skip <- newTVarIO HS.empty
gkz <- newTVarIO HS.empty gkz <- newTVarIO HS.empty
fixmeSign <- putBlock sto "FIXMEGROUPKEYBLOCKv1" <&> fmap HashRef
walkRefChanTx @UNIX goodToGo chan $ \txh u -> do walkRefChanTx @UNIX goodToGo chan $ \txh u -> do
case u of case u of
@ -922,9 +915,16 @@ refchanExportGroupKeys = do
let (pk,sk) = (view peerSignPk creds, view peerSignSk creds) let (pk,sk) = (view peerSignPk creds, view peerSignSk creds)
keyz <- Map.fromList <$> S.toList_ do keyz <- Set.fromList <$> S.toList_ do
for_ r $ \gkh -> void $ runMaybeT do for_ r $ \gkh -> void $ runMaybeT do
debug $ red $ "FOR GK" <+> pretty gkh
gk <- loadGroupKeyMaybe @'HBS2Basic sto gkh >>= toMPlus gk <- loadGroupKeyMaybe @'HBS2Basic sto gkh >>= toMPlus
-- the original groupkey should be indexed as well
lift $ S.yield gkh
gks <- liftIO (runKeymanClientRO $ findMatchedGroupKeySecret sto gk) gks <- liftIO (runKeymanClientRO $ findMatchedGroupKeySecret sto gk)
when (isNothing gks) do when (isNothing gks) do
@ -934,30 +934,69 @@ refchanExportGroupKeys = do
gk1 <- generateGroupKey @'HBS2Basic gks (HS.toList $ view refChanHeadReaders rch) gk1 <- generateGroupKey @'HBS2Basic gks (HS.toList $ view refChanHeadReaders rch)
let lbs = serialise gk1 let lbs = serialise gk1
-- gkh1 <- writeAsMerkle sto lbs <&> HashRef gkh1 <- writeAsMerkle sto lbs <&> HashRef
debug $ red "prepare new gk0" <+> pretty (LBS.length lbs) <+> pretty gkh <+> pretty (groupKeyId gk) debug $ red "prepare new gk0" <+> pretty (LBS.length lbs) <+> pretty gkh <+> pretty (groupKeyId gk)
lift $ S.yield (groupKeyId gk, gk1) lift $ S.yield gkh1
notice $ yellow $ "new gk:" <+> pretty (Map.size keyz) notice $ yellow $ "new gk:" <+> pretty (Set.size keyz)
let nitems = 262144 `div` (125 * HS.size (view refChanHeadReaders rch) ) -- let nitems = 262144 `div` (125 * HS.size (view refChanHeadReaders rch) )
let chunks = Map.elems keyz & chunksOf nitems -- let chunks = Map.elems keyz & chunksOf nitems
for_ chunks $ \x -> do -- TODO: gk:performance-vs-reliability
-- ситуация такова: групповой ключ это меркл-дерево
-- для одного и того же блоба могут быть разные меркл-деревья,
-- так как могут быть разные настройки.
--
-- если распространять ключи по-одному, то хотя бы тот же ключ,
-- который мы создали изначально -- будет доступен по своему хэшу,
-- как отдельный артефакт.
--
-- Если писать их пачками, где каждый ключ представлен непосредственно,
-- то на принимающей стороне нет гарантии, что меркл дерево будет писаться
-- с таким же параметрами, хотя и может.
--
-- Решение: делать групповой ключ БЛОКОМ. тогда его размер будет ограничен,
-- но он хотя бы будет всегда однозначно определён хэшем.
--
-- Решение: ссылаться не на групповой ключ, а на хэш его секрета
-- что ломает текущую схему и обратная совместимость будет морокой.
--
-- Решение: добавить в hbs2-keyman возможно индексации единичного
-- ключа, и индексировать таким образом *исходные* ключи.
--
-- Тогда можно эти вот ключи писать пачками, их хэши не имеют особого значения,
-- если мы проиндексируем оригинальный ключ и будем знать, на какой секрет он
-- ссылается.
--
-- Заметим, что в один блок поместится аж >2000 читателей, что должно быть
-- более, чем достаточно => при таких группах вероятность утечки секрета
-- стремится к 1.0, так как большинство клало болт на меры безопасности.
--
-- Кстати говоря, проблема недостаточного количества авторов в ключе легко
-- решается полем ORIGIN, т.к мы можем эти самые ключи разделять.
--
-- Что бы не стоять перед такой проблемой, мы всегда можем распостранять эти ключи
-- по-одному, ЛИБО добавить в производный ключ поле
-- ORIGIN: где будет хэш изначального ключа.
--
-- Это нормально, так как мы сможем проверить, что у этих ключей
-- (текущий и ORIGIN) одинаковые хэши секретов.
--
-- Это всё равно оставляет возможность еще одной DoS атаки на сервис,
-- с распространением кривых ключей, но это хотя бы выяснимо, ну и атака
-- может быть только в рамках рефчана, т.е лечится выкидыванием пиров /
-- исключением зловредных авторов.
let gktreemeta = HM.fromList [ ("GK", Text.pack (show $ pretty $ L.length x)) ] for_ (Set.toList keyz) $ \href -> do
-- group keys are public (and already encrypted)
-- therefore, no encryption
href <- liftIO $ createTreeWithMetadata sto mzero gktreemeta (serialise x)
>>= orThrowPassIO
let tx = AnnotatedHashRef Nothing href let tx = AnnotatedHashRef fixmeSign href
let lbs = serialise tx let lbs = serialise tx
let box = makeSignedBox @'HBS2Basic @BS.ByteString pk sk (LBS.toStrict lbs) let box = makeSignedBox @'HBS2Basic @BS.ByteString pk sk (LBS.toStrict lbs)
warn $ "POST GK TX" <+> pretty (length x) <+> "tree" <+> pretty href warn $ "post gk tx" <+> "tree" <+> pretty href
result <- callRpcWaitMay @RpcRefChanPropose (TimeoutSec 1) api (chan, box) result <- callRpcWaitMay @RpcRefChanPropose (TimeoutSec 1) api (chan, box)

View File

@ -182,10 +182,7 @@ updateKeys = do
let gkz1 = deserialiseOrFail @(GroupKey 'Symm HBS2Basic) gkbs let gkz1 = deserialiseOrFail @(GroupKey 'Symm HBS2Basic) gkbs
& either mempty List.singleton & either mempty List.singleton
let gkz2 = deserialiseOrFail @[GroupKey 'Symm HBS2Basic] gkbs for_ gkz1 $ \gk -> do
& fromRight mempty
for_ (gkz1 <> gkz2) $ \gk -> do
gkId <- getGroupKeyId gk & toMPlus gkId <- getGroupKeyId gk & toMPlus

View File

@ -9,15 +9,16 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1708680396, "lastModified": 1713359411,
"narHash": "sha256-ZPwDreNdnyCS/hNdaE0OqVhytm+SzZGRfGRTRvBuSzE=", "narHash": "sha256-BzOZ6xU+Li5nIe71Wy4p+lOEQlYK/e94T0gBcP8IKgE=",
"ref": "refs/heads/master", "ref": "generic-sql",
"rev": "221fde04a00a9c38d2f6c0d05b1e1c3457d5a827", "rev": "03635c54b2e2bd809ec1196bc9082447279f6f24",
"revCount": 7, "revCount": 9,
"type": "git", "type": "git",
"url": "https://git.hbs2.net/5xrwbTzzweS9yeJQnrrUY9gQJfhJf84pbyHhF2MMmSft" "url": "https://git.hbs2.net/5xrwbTzzweS9yeJQnrrUY9gQJfhJf84pbyHhF2MMmSft"
}, },
"original": { "original": {
"ref": "generic-sql",
"type": "git", "type": "git",
"url": "https://git.hbs2.net/5xrwbTzzweS9yeJQnrrUY9gQJfhJf84pbyHhF2MMmSft" "url": "https://git.hbs2.net/5xrwbTzzweS9yeJQnrrUY9gQJfhJf84pbyHhF2MMmSft"
} }
@ -82,6 +83,21 @@
"type": "github" "type": "github"
} }
}, },
"flake-utils_10": {
"locked": {
"lastModified": 1644229661,
"narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": { "flake-utils_2": {
"locked": { "locked": {
"lastModified": 1644229661, "lastModified": 1644229661,
@ -187,6 +203,62 @@
"type": "github" "type": "github"
} }
}, },
"flake-utils_9": {
"locked": {
"lastModified": 1644229661,
"narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"fuzzy": {
"inputs": {
"haskell-flake-utils": "haskell-flake-utils_4",
"nixpkgs": [
"hbs2",
"nixpkgs"
]
},
"locked": {
"lastModified": 1715919110,
"narHash": "sha256-moioa3ixAZb0y/xxyxUVjSvXoSiDGXy/vAx6B70d2yM=",
"ref": "refs/heads/master",
"rev": "5a55c22750589b357e50b759d2a754df058446d6",
"revCount": 40,
"type": "git",
"url": "https://git.hbs2.net/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA"
},
"original": {
"type": "git",
"url": "https://git.hbs2.net/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA"
}
},
"fuzzy_2": {
"inputs": {
"haskell-flake-utils": "haskell-flake-utils_8",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1715918584,
"narHash": "sha256-moioa3ixAZb0y/xxyxUVjSvXoSiDGXy/vAx6B70d2yM=",
"rev": "831879978213a1aed15ac70aa116c33bcbe964b8",
"revCount": 63,
"type": "git",
"url": "http://git.hbs2.net/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA?tag=0.1.3.1"
},
"original": {
"rev": "831879978213a1aed15ac70aa116c33bcbe964b8",
"type": "git",
"url": "http://git.hbs2.net/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA?tag=0.1.3.1"
}
},
"haskell-flake-utils": { "haskell-flake-utils": {
"inputs": { "inputs": {
"flake-utils": "flake-utils_2" "flake-utils": "flake-utils_2"
@ -245,6 +317,24 @@
"inputs": { "inputs": {
"flake-utils": "flake-utils_5" "flake-utils": "flake-utils_5"
}, },
"locked": {
"lastModified": 1707809372,
"narHash": "sha256-wfTL9PlCSOqSSyU4eenFFI7pHrV21gba4GEILnI4nAU=",
"owner": "ivanovs-4",
"repo": "haskell-flake-utils",
"rev": "3cbdc5d6093e8b4464ae64097e0c8c61e4414ff2",
"type": "github"
},
"original": {
"owner": "ivanovs-4",
"repo": "haskell-flake-utils",
"type": "github"
}
},
"haskell-flake-utils_5": {
"inputs": {
"flake-utils": "flake-utils_6"
},
"locked": { "locked": {
"lastModified": 1698938553, "lastModified": 1698938553,
"narHash": "sha256-oXpTKXioqFbl2mhhvpJIAvgNd+wYyv4ekI+YnJHEJ6s=", "narHash": "sha256-oXpTKXioqFbl2mhhvpJIAvgNd+wYyv4ekI+YnJHEJ6s=",
@ -260,9 +350,9 @@
"type": "github" "type": "github"
} }
}, },
"haskell-flake-utils_5": { "haskell-flake-utils_6": {
"inputs": { "inputs": {
"flake-utils": "flake-utils_6" "flake-utils": "flake-utils_7"
}, },
"locked": { "locked": {
"lastModified": 1672412555, "lastModified": 1672412555,
@ -279,9 +369,9 @@
"type": "github" "type": "github"
} }
}, },
"haskell-flake-utils_6": { "haskell-flake-utils_7": {
"inputs": { "inputs": {
"flake-utils": "flake-utils_7" "flake-utils": "flake-utils_8"
}, },
"locked": { "locked": {
"lastModified": 1698938553, "lastModified": 1698938553,
@ -297,9 +387,27 @@
"type": "github" "type": "github"
} }
}, },
"haskell-flake-utils_7": { "haskell-flake-utils_8": {
"inputs": { "inputs": {
"flake-utils": "flake-utils_8" "flake-utils": "flake-utils_9"
},
"locked": {
"lastModified": 1707809372,
"narHash": "sha256-wfTL9PlCSOqSSyU4eenFFI7pHrV21gba4GEILnI4nAU=",
"owner": "ivanovs-4",
"repo": "haskell-flake-utils",
"rev": "3cbdc5d6093e8b4464ae64097e0c8c61e4414ff2",
"type": "github"
},
"original": {
"owner": "ivanovs-4",
"repo": "haskell-flake-utils",
"type": "github"
}
},
"haskell-flake-utils_9": {
"inputs": {
"flake-utils": "flake-utils_10"
}, },
"locked": { "locked": {
"lastModified": 1672412555, "lastModified": 1672412555,
@ -319,7 +427,8 @@
"inputs": { "inputs": {
"db-pipe": "db-pipe", "db-pipe": "db-pipe",
"fixme": "fixme", "fixme": "fixme",
"haskell-flake-utils": "haskell-flake-utils_4", "fuzzy": "fuzzy",
"haskell-flake-utils": "haskell-flake-utils_5",
"hspup": "hspup", "hspup": "hspup",
"lsm": "lsm", "lsm": "lsm",
"nixpkgs": [ "nixpkgs": [
@ -329,17 +438,15 @@
"suckless-conf": "suckless-conf_2" "suckless-conf": "suckless-conf_2"
}, },
"locked": { "locked": {
"lastModified": 1713159635, "lastModified": 1726739166,
"narHash": "sha256-iXf8qcJxePLM65E0fsAK2kj69/YIyQdNMrZ5yULzVGc=", "narHash": "sha256-8IXnyZnKZY2kaKNgdYHDzDcMOxxmtSvkQ9HRctSM4xk=",
"ref": "hbs2-git-index", "rev": "627a3e0911d470b0f06d986d8bc663f934269d0e",
"rev": "2289845078ba839bade83a1daf5234435e6e631e", "revCount": 1022,
"revCount": 997,
"type": "git", "type": "git",
"url": "http://git.hbs2/BTThPdHKF8XnEq4m6wzbKHKA6geLFK4ydYhBXAqBdHSP" "url": "http://git.hbs2/BTThPdHKF8XnEq4m6wzbKHKA6geLFK4ydYhBXAqBdHSP"
}, },
"original": { "original": {
"ref": "hbs2-git-index", "rev": "627a3e0911d470b0f06d986d8bc663f934269d0e",
"rev": "2289845078ba839bade83a1daf5234435e6e631e",
"type": "git", "type": "git",
"url": "http://git.hbs2/BTThPdHKF8XnEq4m6wzbKHKA6geLFK4ydYhBXAqBdHSP" "url": "http://git.hbs2/BTThPdHKF8XnEq4m6wzbKHKA6geLFK4ydYhBXAqBdHSP"
} }
@ -366,7 +473,7 @@
}, },
"hspup": { "hspup": {
"inputs": { "inputs": {
"haskell-flake-utils": "haskell-flake-utils_5", "haskell-flake-utils": "haskell-flake-utils_6",
"nixpkgs": [ "nixpkgs": [
"hbs2", "hbs2",
"nixpkgs" "nixpkgs"
@ -388,7 +495,7 @@
}, },
"lsm": { "lsm": {
"inputs": { "inputs": {
"haskell-flake-utils": "haskell-flake-utils_6", "haskell-flake-utils": "haskell-flake-utils_7",
"nixpkgs": [ "nixpkgs": [
"hbs2", "hbs2",
"nixpkgs" "nixpkgs"
@ -425,6 +532,22 @@
} }
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": {
"lastModified": 1707451808,
"narHash": "sha256-UwDBUNHNRsYKFJzyTMVMTF5qS4xeJlWoeyJf+6vvamU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "442d407992384ed9c0e6d352de75b69079904e4e",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "442d407992384ed9c0e6d352de75b69079904e4e",
"type": "github"
}
},
"nixpkgs_3": {
"locked": { "locked": {
"lastModified": 1709200309, "lastModified": 1709200309,
"narHash": "sha256-lKdtMbhnBNU1lr978T+wEYet3sfIXXgyiDZNEgx8CV8=", "narHash": "sha256-lKdtMbhnBNU1lr978T+wEYet3sfIXXgyiDZNEgx8CV8=",
@ -445,7 +568,7 @@
"extra-container": "extra-container", "extra-container": "extra-container",
"hbs2": "hbs2", "hbs2": "hbs2",
"home-manager": "home-manager", "home-manager": "home-manager",
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_3"
} }
}, },
"saltine": { "saltine": {
@ -490,22 +613,23 @@
}, },
"suckless-conf_2": { "suckless-conf_2": {
"inputs": { "inputs": {
"haskell-flake-utils": "haskell-flake-utils_7", "fuzzy": "fuzzy_2",
"haskell-flake-utils": "haskell-flake-utils_9",
"nixpkgs": [ "nixpkgs": [
"hbs2", "hbs2",
"nixpkgs" "nixpkgs"
] ]
}, },
"locked": { "locked": {
"lastModified": 1704001322, "lastModified": 1724740155,
"narHash": "sha256-D7T/8wAg5J4KkRw0uB90w3+adY11aQaX7rjmQPXkkQc=", "narHash": "sha256-dHAWLoQ0uZ2FckV/93qbXo6aYCTY+jARXiiTgUt6fcA=",
"ref": "refs/heads/master", "rev": "b6c5087312e6c09e5c27082da47846f377f73756",
"rev": "8cfc1272bb79ef6ad62ae6a625f21e239916d196", "revCount": 38,
"revCount": 28,
"type": "git", "type": "git",
"url": "https://git.hbs2.net/JAuk1UJzZfbDGKVazSQU5yYQ3NGfk4gVeZzBCduf5TgQ" "url": "https://git.hbs2.net/JAuk1UJzZfbDGKVazSQU5yYQ3NGfk4gVeZzBCduf5TgQ"
}, },
"original": { "original": {
"rev": "b6c5087312e6c09e5c27082da47846f377f73756",
"type": "git", "type": "git",
"url": "https://git.hbs2.net/JAuk1UJzZfbDGKVazSQU5yYQ3NGfk4gVeZzBCduf5TgQ" "url": "https://git.hbs2.net/JAuk1UJzZfbDGKVazSQU5yYQ3NGfk4gVeZzBCduf5TgQ"
} }

View File

@ -7,7 +7,7 @@
extra-container.url = "github:erikarvstedt/extra-container"; extra-container.url = "github:erikarvstedt/extra-container";
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
hbs2.url = hbs2.url =
"git+http://git.hbs2/BTThPdHKF8XnEq4m6wzbKHKA6geLFK4ydYhBXAqBdHSP?rev=3b8f3d48f486043c7fa2df5990e5ab96b71996e1"; "git+http://git.hbs2/BTThPdHKF8XnEq4m6wzbKHKA6geLFK4ydYhBXAqBdHSP?rev=627a3e0911d470b0f06d986d8bc663f934269d0e";
hbs2.inputs.nixpkgs.follows = "nixpkgs"; hbs2.inputs.nixpkgs.follows = "nixpkgs";
home-manager.url = "github:nix-community/home-manager"; home-manager.url = "github:nix-community/home-manager";