diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # niv
 
-![GitHub Actions](https://github.com/nmattia/niv/workflows/Test/badge.svg?branch=master)
+[![Test](https://github.com/nmattia/niv/actions/workflows/test.yml/badge.svg)](https://github.com/nmattia/niv/actions/workflows/test.yml)
 [![Netlify Status](https://api.netlify.com/api/v1/badges/48532eaa-259f-4ca2-aadf-67f7c6b957fd/deploy-status)](https://niv.nmattia.com)
 
 Painless dependencies for [Nix] projects. Read more in the [Getting started](#getting-started) section below.
@@ -106,52 +106,43 @@
 ```
 
 The file `nix/sources.json` is the file used by niv to store versions and is
-initialized with niv and nixpkgs:
+initialized with nixpkgs:
 
 ``` json
 {
     "nixpkgs": {
-        "url": "https://github.com/NixOS/nixpkgs/archive/109a28ab954a0ad129f7621d468f829981b8b96c.tar.gz",
+        "branch": "release-21.05",
+        "description": "Nix Packages collection",
+        "homepage": "",
         "owner": "NixOS",
-        "branch": "nixos-19.09",
-        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz",
         "repo": "nixpkgs",
-        "sha256": "12wnxla7ld4cgpdndaipdh3j4zdalifk287ihxhnmrzrghjahs3q",
-        "description": "Nix Packages collection",
-        "rev": "109a28ab954a0ad129f7621d468f829981b8b96c"
-    },
-    "niv": {
-        "homepage": "https://github.com/nmattia/niv",
-        "url": "https://github.com/nmattia/niv/archive/72e77204544527279e8f1e2d982d29503482b8f4.tar.gz",
-        "owner": "nmattia",
-        "branch": "master",
-        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz",
-        "repo": "niv",
-        "sha256": "1zjcyzxhq9iwxh58j5d7sx1vz5s3r1f6gpmnfgj2a3rxmclwvn3c",
-        "description": "Easy dependency management for Nix projects",
-        "rev": "72e77204544527279e8f1e2d982d29503482b8f4"
+        "rev": "5f244caea76105b63d826911b2a1563d33ff1cdc",
+        "sha256": "1xlgynfw9svy7nvh9nkxsxdzncv9hg99gbvbwv3gmrhmzc3sar75",
+        "type": "tarball",
+        "url": "https://github.com/NixOS/nixpkgs/archive/5f244caea76105b63d826911b2a1563d33ff1cdc.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
     }
 }
 ```
 
-To use those dependencies `import` the file `nix/sources.nix`, e.g.:
+To use this dependency, `import` the file `nix/sources.nix`, e.g.:
 
 ``` nix
 { sources ? import ./sources.nix }:     # import the sources
-with
-  { overlay = _: pkgs:
-      { niv = import sources.niv {};    # use the sources :)
-      };
-  };
 import sources.nixpkgs                  # and use them again!
-  { overlays = [ overlay ] ; config = {}; }
+  { overlays = [] ; config = {}; }
 ```
 
+For more information about importing sources to your nix files, check
+the [frequently asked questions](#Frequently-asked-questions).
+
 #### Tracking a nixpkgs branch
 
-The `init` command sets the `nix/sources.json` file to track the latest commit
-present on nixpkgs 19.09 when the command was run. Run the following command to
-update it:
+The `init` command sets the `nix/sources.json` to the content of the file
+[data/nixpkgs.json](data/nixpkgs.json). Currently, you would be tracking the
+`release-21.05` branch.
+Run the following command to
+update it to the last commit of the configured branch:
 
 ``` shell
 $ niv update nixpkgs
@@ -160,7 +151,7 @@
 To change the branch being tracked run this command:
 
 ``` shell
-$ niv update nixpkgs -b nixos-19.09     # equivalent to --branch nixos-19.09
+$ niv update nixpkgs -b master     # equivalent to --branch master
 ```
 
 #### Importing packages from GitHub
@@ -219,7 +210,7 @@
 ```
 niv - dependency manager for Nix projects
 
-version: 0.2.19
+version: 0.2.20
 
 Usage: niv [-s|--sources-file FILE] [--no-colors] COMMAND
 
@@ -249,10 +240,11 @@
   niv add NixOS/nixpkgs -n nixpkgs -b nixpkgs-unstable
   niv add my-package -v alpha-0.1 -t http://example.com/archive/<version>.zip
 
-Usage: niv add PACKAGE [-n|--name NAME] ([-a|--attribute KEY=VAL] |
-               [-s|--string-attribute KEY=VAL] | [-b|--branch BRANCH] |
-               [-o|--owner OWNER] | [-r|--rev REV] | [-v|--version VERSION] |
-               [-t|--template URL] | [-T|--type TYPE])
+Usage: niv add PACKAGE [-n|--name NAME] 
+                 [(-a|--attribute KEY=VAL) | (-s|--string-attribute KEY=VAL) | 
+                   (-b|--branch BRANCH) | (-o|--owner OWNER) | (-r|--rev REV) | 
+                   (-v|--version VERSION) | (-t|--template URL) | 
+                   (-T|--type TYPE)]
   Add a GitHub dependency
 
 Available options:
@@ -287,10 +279,12 @@
   niv update nixpkgs             # update nixpkgs
   niv update my-package -v beta-0.2 # update my-package to version "beta-0.2"
 
-Usage: niv update [PACKAGE] ([-a|--attribute KEY=VAL] |
-                  [-s|--string-attribute KEY=VAL] | [-b|--branch BRANCH] |
-                  [-o|--owner OWNER] | [-r|--rev REV] | [-v|--version VERSION] |
-                  [-t|--template URL] | [-T|--type TYPE])
+Usage: niv update [PACKAGE 
+                    [(-a|--attribute KEY=VAL) | 
+                      (-s|--string-attribute KEY=VAL) | (-b|--branch BRANCH) | 
+                      (-o|--owner OWNER) | (-r|--rev REV) | 
+                      (-v|--version VERSION) | (-t|--template URL) | 
+                      (-T|--type TYPE)]]
   Update dependencies
 
 Available options:
@@ -318,10 +312,11 @@
   niv modify nixpkgs -v beta-0.2
   niv modify nixpkgs -a branch=nixpkgs-unstable
 
-Usage: niv modify PACKAGE [-n|--name NAME] ([-a|--attribute KEY=VAL] |
-                  [-s|--string-attribute KEY=VAL] | [-b|--branch BRANCH] |
-                  [-o|--owner OWNER] | [-r|--rev REV] | [-v|--version VERSION] |
-                  [-t|--template URL] | [-T|--type TYPE])
+Usage: niv modify PACKAGE [-n|--name NAME] 
+                  [(-a|--attribute KEY=VAL) | (-s|--string-attribute KEY=VAL) | 
+                    (-b|--branch BRANCH) | (-o|--owner OWNER) | (-r|--rev REV) |
+                    (-v|--version VERSION) | (-t|--template URL) | 
+                    (-T|--type TYPE)]
   Modify dependency attributes without performing an update
 
 Available options:
@@ -360,15 +355,19 @@
 #### Init
 
 ```
-Usage: niv init ([--no-nixpkgs] | [-b|--nixpkgs-branch ARG]
-                [--nixpkgs OWNER/REPO])
+Usage: niv init [--fast | --latest | --nixpkgs OWNER/REPO
+                  (-b|--nixpkgs-branch ARG) |
+                  --no-nixpkgs]
   Initialize a Nix project. Existing files won't be modified.
 
 Available options:
+  --fast                   Use the latest nixpkgs cached at
+                           'https://github.com/nmattia/niv/blob/master/data/nixpkgs.json'.
+                           This is the default.
+  --latest                 Pull the latest unstable nixpkgs from NixOS/nixpkgs.
+  --nixpkgs OWNER/REPO     Use a custom nixpkgs repository from GitHub.
+  -b,--nixpkgs-branch ARG  The nixpkgs branch when using --nixpkgs ....
   --no-nixpkgs             Don't add a nixpkgs entry to sources.json.
-  -b,--nixpkgs-branch ARG  The nixpkgs branch to use. (default: "release-20.03")
-  --nixpkgs OWNER/REPO     Use a custom nixpkgs repository from
-                           GitHub. (default: NixOS/nixpkgs)
   -h,--help                Show this help text
 ```
 
@@ -389,6 +388,7 @@
 ## Frequently Asked Questions
 
 * [Can I use private GitHub repositories?](#can-i-use-private-github-repositories)
+* [How do I import and use the content of a source?](#how-do-i-import-and-use-the-content-of-a-source)
 * [How do I import a subpath of a source?](#how-do-i-import-a-subpath-of-a-source)
 * [How do I import NixOS modules](#how-do-i-import-nixos-modules)
 * [Can I use local packages?](#can-i-use-local-packages)
@@ -426,6 +426,74 @@
 ```
 GITHUB_TOKEN=$YOUR_GITHUB_TOKEN niv add ...
 ```
+
+### How do I import and use the content of a source?
+
+The way to import a source depend mainly on the content targetted by this
+source. A source could be a file, a repository with no knowledge of nix
+or a repository already in the nix ecosystem.
+
+#### 1. Direct import of a nix based source
+
+In the case of a nix based source, you'll often find a `default.nix`  at the
+root. Let's take this repository as example. We can add it to our `sources.json`
+with the following command.
+
+``` shell
+$ niv add nmattia/niv
+```
+
+We can now import niv to use it a nix expression, e.g.:
+
+``` nix
+{ sources ? import nix/sources.nix }:
+let niv = import sources.niv {};
+in { inherit niv; } # A glorious expression using the reference to niv
+```
+
+#### 2. Import of a nix based source via an overlay
+
+Rather than use the resulting derivation directly, you can add it to your custom 
+nixpkgs via the overlay system.
+
+``` nix
+{ sources ? import nix/sources.nix}:
+let overlay = _: pkgs: {
+      niv = (import sources.niv {}).niv;
+    };
+    nixpkgs = import sources.nixpkgs { overlays = [ overlay ]; config = {}; };
+in { inherit (nixpkgs) niv; } # A glorious expression where nixpkgs.niv is referenced
+```
+
+#### 3. Reference to the source's files in the nix store
+
+You can also reference a simple file, a folder or a repo without nix knowledge
+with niv. In these cases, you can use the source in your nix expression without
+importing it.
+
+The following exemple will compile gnu hello while using this technique to retrieve
+the source. First, we need to add the new source.
+
+``` shell
+$ niv add hello-src -v 2.10 -t 'https://ftp.gnu.org/gnu/hello/hello-<version>.tar.gz'
+```
+
+Then, we can use it inside a nix expression.
+
+``` nix
+{ sources ? import nix/sources.nix }:
+let hello_src = sources.hello-src;
+    nixpkgs =  import sources.nixpkgs {};
+in nixpkgs.stdenv.mkDerivation {
+    pname = "hello";
+    version = "custom";
+    src = hello_src;
+}
+```
+
+:warning: If you have problems, consider using the outPath of the source
+(e.g. `sources.hello-src.outPath`) instead of the source directly. See 
+[this issue](https://github.com/nmattia/niv/issues/325) for more details.
 
 ### How do I import a subpath of a source?
 
diff --git a/niv.cabal b/niv.cabal
--- a/niv.cabal
+++ b/niv.cabal
@@ -1,13 +1,11 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.33.0.
+-- This file has been generated from package.yaml by hpack version 0.34.6.
 --
 -- see: https://github.com/sol/hpack
---
--- hash: 6551d56f4a527a8b959ff8a47f1bf12e96efb5c6f62bb82d4c61cfb92ff1544f
 
 name:           niv
-version:        0.2.19
+version:        0.2.20
 synopsis:       Easy dependency management for Nix projects
 description:    Easy dependency management for Nix projects.
 category:       Development
@@ -86,7 +84,7 @@
       app
   ghc-options: -Wall -optP-Wno-nonportable-include-path -main-is Niv.main
   build-depends:
-      aeson
+      aeson >=2
     , aeson-pretty
     , ansi-terminal
     , base <5
@@ -118,7 +116,7 @@
       app
   ghc-options: -Wall -optP-Wno-nonportable-include-path -main-is NivTest.main
   build-depends:
-      aeson
+      aeson >=2
     , aeson-pretty
     , ansi-terminal
     , base <5
diff --git a/nix/sources.nix b/nix/sources.nix
--- a/nix/sources.nix
+++ b/nix/sources.nix
@@ -31,8 +31,10 @@
           if spec ? branch then "refs/heads/${spec.branch}" else
             if spec ? tag then "refs/tags/${spec.tag}" else
               abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!";
+      submodules = if spec ? submodules then spec.submodules else false;
     in
-      builtins.fetchGit { url = spec.repo; inherit (spec) rev; inherit ref; };
+      builtins.fetchGit { url = spec.repo; inherit (spec) rev; inherit ref; }
+      // (if builtins.compareVersions builtins.nixVersion "2.4" >= 0 then { inherit submodules; } else {});
 
   fetch_local = spec: spec.path;
 
diff --git a/src/Data/HashMap/Strict/Extended.hs b/src/Data/HashMap/Strict/Extended.hs
--- a/src/Data/HashMap/Strict/Extended.hs
+++ b/src/Data/HashMap/Strict/Extended.hs
@@ -26,8 +26,9 @@
   HMS.HashMap k v1 ->
   m (HMS.HashMap k v2)
 mapWithKeyM f m = do
-  fmap mconcat $ forM (HMS.toList m) $ \(k, v) ->
-    HMS.singleton k <$> f k v
+  fmap mconcat $
+    forM (HMS.toList m) $ \(k, v) ->
+      HMS.singleton k <$> f k v
 
 mapWithKeyM_ ::
   (Eq k, Hashable k, Monad m) =>
diff --git a/src/Niv/Cli.hs b/src/Niv/Cli.hs
--- a/src/Niv/Cli.hs
+++ b/src/Niv/Cli.hs
@@ -13,6 +13,8 @@
 import Control.Monad.Reader
 import Data.Aeson ((.=))
 import qualified Data.Aeson as Aeson
+import qualified Data.Aeson.Key as K
+import qualified Data.Aeson.KeyMap as KM
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Char8 as B8
 import Data.Char (isSpace)
@@ -22,9 +24,9 @@
 import qualified Data.Text as T
 import Data.Text.Extended
 import Data.Version (showVersion)
+import qualified Network.HTTP.Simple as HTTP
 import Niv.Cmd
 import Niv.Git.Cmd
-import Niv.GitHub.API (warnGitHubEnvVars)
 import Niv.GitHub.Cmd
 import Niv.Local.Cmd
 import Niv.Logger
@@ -53,7 +55,6 @@
 
 cli :: IO ()
 cli = do
-  warnGitHubEnvVars
   ((fsj, colors), nio) <-
     execParserPure' Opts.defaultPrefs opts <$> getArgs
       >>= Opts.handleParseResult
@@ -62,15 +63,16 @@
   where
     execParserPure' pprefs pinfo [] =
       Opts.Failure $
-        Opts.parserFailure pprefs pinfo Opts.ShowHelpText mempty
+        Opts.parserFailure pprefs pinfo (Opts.ShowHelpText Nothing) mempty
     execParserPure' pprefs pinfo args = Opts.execParserPure pprefs pinfo args
     opts = Opts.info ((,) <$> ((,) <$> parseFindSourcesJson <*> parseColors) <*> (parseCommand <**> Opts.helper <**> versionflag)) $ mconcat desc
     desc =
       [ Opts.fullDesc,
-        Opts.headerDoc $ Just $
-          "niv - dependency manager for Nix projects"
-            Opts.<$$> ""
-            Opts.<$$> "version:" Opts.<+> Opts.text (showVersion version)
+        Opts.headerDoc $
+          Just $
+            "niv - dependency manager for Nix projects"
+              Opts.<$$> ""
+              Opts.<$$> "version:" Opts.<+> Opts.text (showVersion version)
       ]
     parseFindSourcesJson =
       AtPath
@@ -119,55 +121,66 @@
 -- | Whether or not to fetch nixpkgs
 data FetchNixpkgs
   = NoNixpkgs
-  | YesNixpkgs T.Text Nixpkgs -- branch, nixpkgs
+  | NixpkgsFast -- Pull latest known nixpkgs
+  | NixpkgsCustom T.Text Nixpkgs -- branch, nixpkgs
+  deriving (Show)
 
 data Nixpkgs = Nixpkgs T.Text T.Text -- owner, repo
 
 instance Show Nixpkgs where
   show (Nixpkgs o r) = T.unpack o <> "/" <> T.unpack r
 
--- | The default nixpkgs
-defaultNixpkgsRepo, defaultNixpkgsUser, defaultNixpkgsBranch :: T.Text
-defaultNixpkgsRepo = "nixpkgs"
-defaultNixpkgsUser = "NixOS"
-defaultNixpkgsBranch = "release-20.03"
-
 parseCmdInit :: Opts.ParserInfo (NIO ())
 parseCmdInit = Opts.info (cmdInit <$> parseNixpkgs <**> Opts.helper) $ mconcat desc
   where
-    customNixpkgsReader = Opts.maybeReader $ \(T.pack -> repo) -> case T.splitOn "/" repo of
-      [owner, reponame] -> Just (Nixpkgs owner reponame)
-      _ -> Nothing
-    parseNixpkgs =
-      Opts.flag'
-        NoNixpkgs
-        ( Opts.long "no-nixpkgs"
-            <> Opts.help "Don't add a nixpkgs entry to sources.json."
-        )
-        <|> ( YesNixpkgs
-                <$> ( Opts.strOption
-                        ( Opts.long "nixpkgs-branch"
-                            <> Opts.short 'b'
-                            <> Opts.help "The nixpkgs branch to use."
-                            <> Opts.showDefault
-                            <> Opts.value defaultNixpkgsBranch
-                        )
-                    )
-                  <*> Opts.option
-                    customNixpkgsReader
-                    ( Opts.long "nixpkgs"
-                        <> Opts.showDefault
-                        <> Opts.help "Use a custom nixpkgs repository from GitHub."
-                        <> Opts.metavar "OWNER/REPO"
-                        <> Opts.value (Nixpkgs defaultNixpkgsUser defaultNixpkgsRepo)
-                    )
-            )
     desc =
       [ Opts.fullDesc,
         Opts.progDesc
           "Initialize a Nix project. Existing files won't be modified."
       ]
 
+parseNixpkgs :: Opts.Parser FetchNixpkgs
+parseNixpkgs = parseNixpkgsFast <|> parseNixpkgsLatest <|> parseNixpkgsCustom <|> parseNoNixpkgs <|> pure NixpkgsFast
+  where
+    parseNixpkgsFast =
+      Opts.flag'
+        NixpkgsFast
+        ( Opts.long "fast"
+            <> Opts.help "Use the latest nixpkgs cached at 'https://github.com/nmattia/niv/blob/master/data/nixpkgs.json'. This is the default."
+        )
+    parseNixpkgsLatest =
+      Opts.flag'
+        (NixpkgsCustom "master" (Nixpkgs "NixOS" "nixpkgs"))
+        ( Opts.long "latest"
+            <> Opts.help "Pull the latest unstable nixpkgs from NixOS/nixpkgs."
+        )
+    parseNixpkgsCustom =
+      (flip NixpkgsCustom)
+        <$> ( Opts.option
+                customNixpkgsReader
+                ( Opts.long "nixpkgs"
+                    <> Opts.showDefault
+                    <> Opts.help "Use a custom nixpkgs repository from GitHub."
+                    <> Opts.metavar "OWNER/REPO"
+                )
+            )
+          <*> ( Opts.strOption
+                  ( Opts.long "nixpkgs-branch"
+                      <> Opts.short 'b'
+                      <> Opts.help "The nixpkgs branch when using --nixpkgs ...."
+                      <> Opts.showDefault
+                  )
+              )
+    parseNoNixpkgs =
+      Opts.flag'
+        NoNixpkgs
+        ( Opts.long "no-nixpkgs"
+            <> Opts.help "Don't add a nixpkgs entry to sources.json."
+        )
+    customNixpkgsReader = Opts.maybeReader $ \(T.pack -> repo) -> case T.splitOn "/" repo of
+      [owner, reponame] -> Just (Nixpkgs owner reponame)
+      _ -> Nothing
+
 cmdInit :: FetchNixpkgs -> NIO ()
 cmdInit nixpkgs = do
   job "Initializing" $ do
@@ -187,33 +200,9 @@
         ( pathNixSourcesJson fsj,
           \path -> do
             createFile path initNixSourcesJsonContent
-            -- Imports @niv@ and @nixpkgs@
-            say "Importing 'niv' ..."
-            cmdAdd
-              githubCmd
-              (PackageName "niv")
-              ( specToFreeAttrs $ PackageSpec $
-                  HMS.fromList
-                    [ "owner" .= ("nmattia" :: T.Text),
-                      "repo" .= ("niv" :: T.Text)
-                    ]
-              )
-            case nixpkgs of
-              NoNixpkgs -> say "Not importing 'nixpkgs'."
-              YesNixpkgs branch nixpkgs' -> do
-                say "Importing 'nixpkgs' ..."
-                let (owner, repo) = case nixpkgs' of
-                      Nixpkgs o r -> (o, r)
-                cmdAdd
-                  githubCmd
-                  (PackageName "nixpkgs")
-                  ( specToFreeAttrs $ PackageSpec $
-                      HMS.fromList
-                        [ "owner" .= owner,
-                          "repo" .= repo,
-                          "branch" .= branch
-                        ]
-                  ),
+
+            -- Import nixpkgs, if necessary
+            initNixpkgs nixpkgs,
           \path _content -> dontCreateFile path
         )
       ]
@@ -248,6 +237,34 @@
     dontCreateFile :: FilePath -> NIO ()
     dontCreateFile path = say $ "Not creating " <> path
 
+initNixpkgs :: FetchNixpkgs -> NIO ()
+initNixpkgs nixpkgs =
+  case nixpkgs of
+    NoNixpkgs -> say "Not importing 'nixpkgs'."
+    NixpkgsFast -> do
+      say "Using known 'nixpkgs' ..."
+      packageSpec <- HTTP.getResponseBody <$> HTTP.httpJSON "https://raw.githubusercontent.com/nmattia/niv/master/data/nixpkgs.json"
+      cmdAdd
+        githubCmd
+        (PackageName "nixpkgs")
+        (specToLockedAttrs packageSpec)
+      pure ()
+    NixpkgsCustom branch nixpkgs' -> do
+      say "Importing 'nixpkgs' ..."
+      let (owner, repo) = case nixpkgs' of
+            Nixpkgs o r -> (o, r)
+      cmdAdd
+        githubCmd
+        (PackageName "nixpkgs")
+        ( specToFreeAttrs $
+            PackageSpec $
+              KM.fromList
+                [ "owner" .= owner,
+                  "repo" .= repo,
+                  "branch" .= branch
+                ]
+        )
+
 -------------------------------------------------------------------------------
 -- ADD
 -------------------------------------------------------------------------------
@@ -315,8 +332,8 @@
         (pname, baseSpec) = case specAndName of
           (Just (_, spec), Just pname') -> (pname', PackageSpec spec)
           (Just (pname', spec), Nothing) -> (pname', PackageSpec spec)
-          (Nothing, Just pname') -> (pname', PackageSpec HMS.empty)
-          (Nothing, Nothing) -> (PackageName "unnamed", PackageSpec HMS.empty)
+          (Nothing, Just pname') -> (pname', PackageSpec KM.empty)
+          (Nothing, Nothing) -> (PackageName "unnamed", PackageSpec KM.empty)
     parseNameAndShortcut =
       (,)
         <$> Opts.optional
@@ -340,16 +357,18 @@
   job ("Adding package " <> T.unpack (unPackageName packageName)) $ do
     fsj <- getFindSourcesJson
     sources <- unSources <$> li (getSources fsj)
-    when (HMS.member packageName sources)
-      $ li
-      $ abortCannotAddPackageExists packageName
+    when (HMS.member packageName sources) $
+      li $
+        abortCannotAddPackageExists packageName
     eFinalSpec <- fmap attrsToSpec <$> li (doUpdate attrs cmd)
     case eFinalSpec of
       Left e -> li (abortUpdateFailed [(packageName, e)])
       Right finalSpec -> do
         say $ "Writing new sources file"
-        li $ setSources fsj $ Sources $
-          HMS.insert packageName finalSpec sources
+        li $
+          setSources fsj $
+            Sources $
+              HMS.insert packageName finalSpec sources
 
 -------------------------------------------------------------------------------
 -- SHOW
@@ -378,11 +397,11 @@
 showPackage :: MonadIO io => PackageName -> PackageSpec -> io ()
 showPackage (PackageName pname) (PackageSpec spec) = do
   tsay $ tbold pname
-  forM_ (HMS.toList spec) $ \(attrName, attrValValue) -> do
+  forM_ (KM.toList spec) $ \(attrName, attrValValue) -> do
     let attrValue = case attrValValue of
           Aeson.String str -> str
           _ -> tfaint "<barabajagal>"
-    tsay $ "  " <> attrName <> ": " <> attrValue
+    tsay $ "  " <> K.toText attrName <> ": " <> attrValue
 
 -------------------------------------------------------------------------------
 -- UPDATE
@@ -397,21 +416,23 @@
     desc =
       [ Opts.fullDesc,
         Opts.progDesc "Update dependencies",
-        Opts.headerDoc $ Just $ Opts.nest 2 $
-          "Examples:"
-            Opts.<$$> ""
-            Opts.<$$> Opts.vcat
-              [ Opts.fill 30 "niv update" Opts.<+> "# update all packages",
-                Opts.fill 30 "niv update nixpkgs" Opts.<+> "# update nixpkgs",
-                Opts.fill 30 "niv update my-package -v beta-0.2" Opts.<+> "# update my-package to version \"beta-0.2\""
-              ]
+        Opts.headerDoc $
+          Just $
+            Opts.nest 2 $
+              "Examples:"
+                Opts.<$$> ""
+                Opts.<$$> Opts.vcat
+                  [ Opts.fill 30 "niv update" Opts.<+> "# update all packages",
+                    Opts.fill 30 "niv update nixpkgs" Opts.<+> "# update nixpkgs",
+                    Opts.fill 30 "niv update my-package -v beta-0.2" Opts.<+> "# update my-package to version \"beta-0.2\""
+                  ]
       ]
 
 specToFreeAttrs :: PackageSpec -> Attrs
-specToFreeAttrs = fmap (Free,) . unPackageSpec
+specToFreeAttrs = KM.toHashMapText . fmap (Free,) . unPackageSpec
 
 specToLockedAttrs :: PackageSpec -> Attrs
-specToLockedAttrs = fmap (Locked,) . unPackageSpec
+specToLockedAttrs = KM.toHashMapText . fmap (Locked,) . unPackageSpec
 
 cmdUpdate :: Maybe (PackageName, PackageSpec) -> NIO ()
 cmdUpdate = \case
@@ -423,7 +444,7 @@
         Just defaultSpec -> do
           -- lookup the "type" to find a Cmd to run, defaulting to legacy
           -- github
-          let cmd = case HMS.lookup "type" (unPackageSpec defaultSpec) of
+          let cmd = case KM.lookup "type" (unPackageSpec defaultSpec) of
                 Just "git" -> gitCmd
                 Just "local" -> localCmd
                 _ -> githubCmd
@@ -433,8 +454,10 @@
       case eFinalSpec of
         Left e -> li $ abortUpdateFailed [(packageName, e)]
         Right finalSpec ->
-          li $ setSources fsj $ Sources $
-            HMS.insert packageName finalSpec sources
+          li $
+            setSources fsj $
+              Sources $
+                HMS.insert packageName finalSpec sources
   Nothing -> job "Updating all packages" $ do
     fsj <- getFindSourcesJson
     sources <- unSources <$> li (getSources fsj)
@@ -444,16 +467,16 @@
         let initialSpec = specToFreeAttrs defaultSpec
         -- lookup the "type" to find a Cmd to run, defaulting to legacy
         -- github
-        let cmd = case HMS.lookup "type" (unPackageSpec defaultSpec) of
+        let cmd = case KM.lookup "type" (unPackageSpec defaultSpec) of
               Just "git" -> gitCmd
               Just "local" -> localCmd
               _ -> githubCmd
         finalSpec <- fmap attrsToSpec <$> li (doUpdate initialSpec cmd)
         pure finalSpec
     let (failed, sources') = partitionEithersHMS esources'
-    unless (HMS.null failed)
-      $ li
-      $ abortUpdateFailed (HMS.toList failed)
+    unless (HMS.null failed) $
+      li $
+        abortUpdateFailed (HMS.toList failed)
     li $ setSources fsj $ Sources sources'
 
 -- | pretty much tryEvalUpdate but we might issue some warnings first
@@ -484,11 +507,12 @@
     desc =
       [ Opts.fullDesc,
         Opts.progDesc "Modify dependency attributes without performing an update",
-        Opts.headerDoc $ Just $
-          "Examples:"
-            Opts.<$$> ""
-            Opts.<$$> "  niv modify nixpkgs -v beta-0.2"
-            Opts.<$$> "  niv modify nixpkgs -a branch=nixpkgs-unstable"
+        Opts.headerDoc $
+          Just $
+            "Examples:"
+              Opts.<$$> ""
+              Opts.<$$> "  niv modify nixpkgs -v beta-0.2"
+              Opts.<$$> "  niv modify nixpkgs -a branch=nixpkgs-unstable"
       ]
     optName =
       Opts.optional $
@@ -510,9 +534,9 @@
     Nothing -> li $ abortCannotModifyNoSuchPackage packageName
   case mNewName of
     Just newName -> do
-      when (HMS.member newName sources)
-        $ li
-        $ abortCannotAddPackageExists newName
+      when (HMS.member newName sources) $
+        li $
+          abortCannotAddPackageExists newName
       li $ setSources fsj $ Sources $ HMS.insert newName finalSpec $ HMS.delete packageName sources
     Nothing ->
       li $ setSources fsj $ Sources $ HMS.insert packageName finalSpec sources
@@ -532,11 +556,12 @@
     desc =
       [ Opts.fullDesc,
         Opts.progDesc "Drop dependency",
-        Opts.headerDoc $ Just $
-          "Examples:"
-            Opts.<$$> ""
-            Opts.<$$> "  niv drop jq"
-            Opts.<$$> "  niv drop my-package version"
+        Opts.headerDoc $
+          Just $
+            "Examples:"
+              Opts.<$$> ""
+              Opts.<$$> "  niv drop jq"
+              Opts.<$$> "  niv drop my-package version"
       ]
     parseDropAttributes :: Opts.Parser [T.Text]
     parseDropAttributes =
@@ -549,11 +574,13 @@
     tsay $ "Dropping package: " <> unPackageName packageName
     fsj <- getFindSourcesJson
     sources <- unSources <$> li (getSources fsj)
-    when (not $ HMS.member packageName sources)
-      $ li
-      $ abortCannotDropNoSuchPackage packageName
-    li $ setSources fsj $ Sources $
-      HMS.delete packageName sources
+    when (not $ HMS.member packageName sources) $
+      li $
+        abortCannotDropNoSuchPackage packageName
+    li $
+      setSources fsj $
+        Sources $
+          HMS.delete packageName sources
   attrs -> do
     tsay $ "Dropping attributes: " <> T.intercalate " " attrs
     tsay $ "In package: " <> unPackageName packageName
@@ -563,12 +590,15 @@
       Nothing ->
         li $ abortCannotAttributesDropNoSuchPackage packageName
       Just (PackageSpec packageSpec) ->
-        pure $ PackageSpec $
-          HMS.mapMaybeWithKey
-            (\k v -> if k `elem` attrs then Nothing else Just v)
-            packageSpec
-    li $ setSources fsj $ Sources $
-      HMS.insert packageName packageSpec sources
+        pure $
+          PackageSpec $
+            KM.mapMaybeWithKey
+              (\k v -> if K.toText k `elem` attrs then Nothing else Just v)
+              packageSpec
+    li $
+      setSources fsj $
+        Sources $
+          HMS.insert packageName packageSpec sources
 
 -------------------------------------------------------------------------------
 -- Files and their content
@@ -653,10 +683,11 @@
 
 abortUpdateFailed :: [(PackageName, SomeException)] -> IO a
 abortUpdateFailed errs =
-  abort $ T.unlines $
-    ["One or more packages failed to update:"]
-      <> map
-        ( \(PackageName pname, e) ->
-            pname <> ": " <> tshow e
-        )
-        errs
+  abort $
+    T.unlines $
+      ["One or more packages failed to update:"]
+        <> map
+          ( \(PackageName pname, e) ->
+              pname <> ": " <> tshow e
+          )
+          errs
diff --git a/src/Niv/Cmd.hs b/src/Niv/Cmd.hs
--- a/src/Niv/Cmd.hs
+++ b/src/Niv/Cmd.hs
@@ -9,13 +9,12 @@
 import qualified Options.Applicative as Opts
 
 -- TODO: add filter
-data Cmd
-  = Cmd
-      { description :: forall a. Opts.InfoMod a,
-        parseCmdShortcut :: T.Text -> Maybe (PackageName, Aeson.Object),
-        parsePackageSpec :: Opts.Parser PackageSpec,
-        updateCmd :: Update () (),
-        name :: T.Text,
-        -- | Some notes to print
-        extraLogs :: Attrs -> [T.Text]
-      }
+data Cmd = Cmd
+  { description :: forall a. Opts.InfoMod a,
+    parseCmdShortcut :: T.Text -> Maybe (PackageName, Aeson.Object),
+    parsePackageSpec :: Opts.Parser PackageSpec,
+    updateCmd :: Update () (),
+    name :: T.Text,
+    -- | Some notes to print
+    extraLogs :: Attrs -> [T.Text]
+  }
diff --git a/src/Niv/Git/Cmd.hs b/src/Niv/Git/Cmd.hs
--- a/src/Niv/Git/Cmd.hs
+++ b/src/Niv/Git/Cmd.hs
@@ -10,6 +10,8 @@
 import Control.Applicative
 import Control.Arrow
 import qualified Data.Aeson as Aeson
+import qualified Data.Aeson.Key as K
+import qualified Data.Aeson.KeyMap as KM
 import qualified Data.ByteString.Char8 as B8
 import qualified Data.HashMap.Strict as HMS
 import Data.Maybe
@@ -60,8 +62,8 @@
     then case T.splitOn "/" txt of
       [] -> Nothing
       (last -> w) -> case T.stripSuffix ".git" w of
-        Nothing -> Just (PackageName w, HMS.singleton "repo" (Aeson.String txt'))
-        Just w' -> Just (PackageName w', HMS.singleton "repo" (Aeson.String txt'))
+        Nothing -> Just (PackageName w, KM.singleton "repo" (Aeson.String txt'))
+        Just w' -> Just (PackageName w', KM.singleton "repo" (Aeson.String txt'))
     else Nothing
   where
     isGitURL =
@@ -71,7 +73,7 @@
 
 parseGitPackageSpec :: Opts.Parser PackageSpec
 parseGitPackageSpec =
-  (PackageSpec . HMS.fromList)
+  (PackageSpec . KM.fromList)
     <$> many (parseRepo <|> parseBranch <|> parseRev <|> parseAttr <|> parseSAttr)
   where
     parseRepo =
@@ -116,9 +118,9 @@
       -- | how to convert to JSON
       (String -> Aeson.Value) ->
       String ->
-      Maybe (T.Text, Aeson.Value)
+      Maybe (K.Key, Aeson.Value)
     parseKeyVal toJSON str = case span (/= '=') str of
-      (key, '=' : val) -> Just (T.pack key, toJSON val)
+      (key, '=' : val) -> Just (K.fromString key, toJSON val)
       _ -> Nothing
 
 describeGit :: Opts.InfoMod a
@@ -126,13 +128,14 @@
   mconcat
     [ Opts.fullDesc,
       Opts.progDesc "Add a git dependency. Experimental.",
-      Opts.headerDoc $ Just $
-        "Examples:"
-          Opts.<$$> ""
-          Opts.<$$> "  niv add git git@github.com:stedolan/jq"
-          Opts.<$$> "  niv add git ssh://git@github.com/stedolan/jq --rev deadb33f"
-          Opts.<$$> "  niv add git https://github.com/stedolan/jq.git"
-          Opts.<$$> "  niv add git --repo /my/custom/repo --name custom --branch development"
+      Opts.headerDoc $
+        Just $
+          "Examples:"
+            Opts.<$$> ""
+            Opts.<$$> "  niv add git git@github.com:stedolan/jq"
+            Opts.<$$> "  niv add git ssh://git@github.com/stedolan/jq --rev deadb33f"
+            Opts.<$$> "  niv add git https://github.com/stedolan/jq.git"
+            Opts.<$$> "  niv add git --repo /my/custom/repo --name custom --branch development"
     ]
 
 gitUpdate ::
@@ -183,8 +186,9 @@
         args
         $ "Git didn't produce any output. Does the branch '" <> branch <> "' exist?"
     abortTooMuchOutput args ls =
-      abortGitBug args $ T.unlines $
-        ["Git produced too much output:"] <> map ("  " <>) ls
+      abortGitBug args $
+        T.unlines $
+          ["Git produced too much output:"] <> map ("  " <>) ls
 
 defaultBranchAndRev ::
   -- | the repository
@@ -195,10 +199,11 @@
   case sout of
     (l1 : l2 : _) -> (,) <$> parseBranch l1 <*> parseRev l2
     _ ->
-      abortGitBug args $ T.unlines $
-        [ "Could not read reference and revision from stdout:"
-        ]
-          <> sout
+      abortGitBug args $
+        T.unlines $
+          [ "Could not read reference and revision from stdout:"
+          ]
+            <> sout
   where
     args = ["ls-remote", "--symref", repo, "HEAD"]
     parseBranch l = maybe (abortNoRef args l) pure $ do
@@ -248,9 +253,10 @@
 
 abortGitBug :: [T.Text] -> T.Text -> IO a
 abortGitBug args msg =
-  abort $ bug $
-    T.unlines
-      [ "Could not read the output of 'git'.",
-        T.unwords ("command:" : "git" : args),
-        msg
-      ]
+  abort $
+    bug $
+      T.unlines
+        [ "Could not read the output of 'git'.",
+          T.unwords ("command:" : "git" : args),
+          msg
+        ]
diff --git a/src/Niv/Git/Test.hs b/src/Niv/Git/Test.hs
--- a/src/Niv/Git/Test.hs
+++ b/src/Niv/Git/Test.hs
@@ -7,6 +7,7 @@
 where
 
 import Control.Monad
+import qualified Data.Aeson.KeyMap as KM
 import Data.Bifunctor
 import qualified Data.HashMap.Strict as HMS
 import Data.IORef
@@ -29,21 +30,21 @@
       Tasty.testCase "git@github.com:nmattia/niv" $
         parseGitShortcut "git@github.com:nmattia/niv"
           @=? Just
-            (PackageName "niv", HMS.singleton "repo" "git@github.com:nmattia/niv"),
+            (PackageName "niv", KM.singleton "repo" "git@github.com:nmattia/niv"),
       Tasty.testCase "ssh://git@github.com/stedolan/jq" $
         parseGitShortcut "ssh://git@github.com/stedolan/jq"
           @=? Just
-            (PackageName "jq", HMS.singleton "repo" "ssh://git@github.com/stedolan/jq"),
+            (PackageName "jq", KM.singleton "repo" "ssh://git@github.com/stedolan/jq"),
       Tasty.testCase "https://github.com/stedolan/jq.git" $
         parseGitShortcut "https://github.com/stedolan/jq.git"
           @=? Just
-            (PackageName "jq", HMS.singleton "repo" "https://github.com/stedolan/jq.git"),
+            (PackageName "jq", KM.singleton "repo" "https://github.com/stedolan/jq.git"),
       Tasty.testCase "https://github.com/stedolan/jq" $
         parseGitShortcut "https://github.com/stedolan/jq" @=? Nothing,
       Tasty.testCase "~/path/to/repo.git" $
         parseGitShortcut "~/path/to/repo.git"
           @=? Just
-            (PackageName "repo", HMS.singleton "repo" "~/path/to/repo.git")
+            (PackageName "repo", KM.singleton "repo" "~/path/to/repo.git")
     ]
 
 test_gitUpdates :: Tasty.TestTree
@@ -61,9 +62,9 @@
   let interState' = HMS.map (first (\_ -> Free)) interState
   actualState <- evalUpdate interState' $ proc () ->
     gitUpdate latestRev' (error "should update") -< ()
-  unless ((snd <$> actualState) == expectedState)
-    $ error
-    $ "State mismatch: " <> show actualState
+  unless ((snd <$> actualState) == expectedState) $
+    error $
+      "State mismatch: " <> show actualState
   where
     latestRev' _ _ = pure "some-other-rev"
     defaultBranchAndHEAD' _ = pure ("some-branch", "some-rev")
@@ -111,9 +112,9 @@
   let interState' = HMS.map (first (\_ -> Free)) interState
   actualState <- evalUpdate interState' $ proc () ->
     gitUpdate latestRev'' (error "should update") -< ()
-  unless ((snd <$> actualState) == expectedState)
-    $ error
-    $ "State mismatch: " <> show actualState
+  unless ((snd <$> actualState) == expectedState) $
+    error $
+      "State mismatch: " <> show actualState
   where
     latestRev' _ _ = pure "some-other-rev"
     defaultBranchAndHEAD' _ = pure ("some-branch", "some-rev")
diff --git a/src/Niv/GitHub.hs b/src/Niv/GitHub.hs
--- a/src/Niv/GitHub.hs
+++ b/src/Niv/GitHub.hs
@@ -31,8 +31,9 @@
 githubUpdate prefetch latestRev ghRepo = proc () -> do
   urlTemplate <-
     template
-      <<< (useOrSet "url_template" <<< completeSpec) <+> (load "url_template") -<
-      ()
+      <<< (useOrSet "url_template" <<< completeSpec) <+> (load "url_template")
+      -<
+        ()
   url <- update "url" -< urlTemplate
   let isTarGuess = (\u -> "tar.gz" `T.isSuffixOf` u || ".tgz" `T.isSuffixOf` u) <$> url
   type' <- useOrSet "type" -< bool "file" "tarball" <$> isTarGuess :: Box T.Text
@@ -46,13 +47,15 @@
       repo <- load "repo" -< ()
       repoInfo <- run (\(a, b) -> ghRepo a b) -< (,) <$> owner <*> repo
       branch <-
-        useOrSet "branch" <<< arr (fmap $ fromMaybe "master") -<
-          repoDefaultBranch <$> repoInfo
+        useOrSet "branch" <<< arr (fmap $ fromMaybe "master")
+          -<
+            repoDefaultBranch <$> repoInfo
       _description <- useOrSet "description" -< repoDescription <$> repoInfo
       _homepage <- useOrSet "homepage" -< repoHomepage <$> repoInfo
       _ <-
-        update "rev" <<< run' (\(a, b, c) -> latestRev a b c) -<
-          (,,) <$> owner <*> repo <*> branch
+        update "rev" <<< run' (\(a, b, c) -> latestRev a b c)
+          -<
+            (,,) <$> owner <*> repo <*> branch
       returnA -< pure githubURLTemplate
 
 githubURLTemplate :: T.Text
diff --git a/src/Niv/GitHub/API.hs b/src/Niv/GitHub/API.hs
--- a/src/Niv/GitHub/API.hs
+++ b/src/Niv/GitHub/API.hs
@@ -6,16 +6,15 @@
 module Niv.GitHub.API where
 
 import qualified Data.Aeson as Aeson
+import qualified Data.Aeson.KeyMap as KM
 import qualified Data.ByteString.Char8 as BS8
 import Data.Functor
-import qualified Data.HashMap.Strict as HMS
 import Data.Maybe
 import Data.String.QQ (s)
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as T
 import Data.Text.Extended
 import qualified Network.HTTP.Simple as HTTP
-import Niv.Logger
 import System.Environment (lookupEnv)
 import System.Exit (exitFailure)
 import System.IO.Unsafe (unsafePerformIO)
@@ -23,12 +22,11 @@
 
 -- Bunch of GitHub helpers
 
-data GithubRepo
-  = GithubRepo
-      { repoDescription :: Maybe T.Text,
-        repoHomepage :: Maybe T.Text,
-        repoDefaultBranch :: Maybe T.Text
-      }
+data GithubRepo = GithubRepo
+  { repoDescription :: Maybe T.Text,
+    repoHomepage :: Maybe T.Text,
+    repoDefaultBranch :: Maybe T.Text
+  }
 
 githubRepo :: T.Text -> T.Text -> IO GithubRepo
 githubRepo owner repo = do
@@ -39,7 +37,7 @@
   let resp = fmap Aeson.eitherDecodeStrict resp0
   case (HTTP.getResponseStatusCode resp, HTTP.getResponseBody resp) of
     (200, Right (Aeson.Object m)) -> do
-      let lookupText k = case HMS.lookup k m of
+      let lookupText k = case KM.lookup k m of
             Just (Aeson.String t) -> Just t
             _ -> Nothing
       pure
@@ -82,17 +80,17 @@
 defaultRequest (map T.encodeUtf8 -> parts) = do
   let path = T.encodeUtf8 githubPath <> BS8.intercalate "/" (parts)
   mtoken <- lookupEnv' "GITHUB_TOKEN"
-  pure
-    $ ( flip (maybe id) mtoken $ \token ->
-          HTTP.addRequestHeader "authorization" ("token " <> BS8.pack token)
-      )
-    $ HTTP.setRequestPath path
-    $ HTTP.addRequestHeader "user-agent" "niv"
-    $ HTTP.addRequestHeader "accept" "application/vnd.github.v3+json"
-    $ HTTP.setRequestSecure githubSecure
-    $ HTTP.setRequestHost (T.encodeUtf8 githubApiHost)
-    $ HTTP.setRequestPort githubApiPort
-    $ HTTP.defaultRequest
+  pure $
+    ( flip (maybe id) mtoken $ \token ->
+        HTTP.addRequestHeader "authorization" ("token " <> BS8.pack token)
+    )
+      $ HTTP.setRequestPath path $
+        HTTP.addRequestHeader "user-agent" "niv" $
+          HTTP.addRequestHeader "accept" "application/vnd.github.v3+json" $
+            HTTP.setRequestSecure githubSecure $
+              HTTP.setRequestHost (T.encodeUtf8 githubApiHost) $
+                HTTP.setRequestPort githubApiPort $
+                  HTTP.defaultRequest
 
 -- | Get the latest revision for owner, repo and branch.
 -- TODO: explain no error handling
@@ -135,34 +133,12 @@
 
 |]
 
--- Some environment variables may have different meanings (see for instance
--- https://github.com/nmattia/niv/issues/280)
--- For ambiguous ones, we prepend NIV_.
---
-warnGitHubEnvVars :: IO ()
-warnGitHubEnvVars =
-  mapM_
-    warnEnvVar
-    [ "GITHUB_INSECURE",
-      "GITHUB_PATH"
-    ]
-  where
-    warnEnvVar vn = lookupEnv (T.unpack vn) >>= \case
-      Nothing -> pure ()
-      Just {} -> do
-        twarn $
-          T.unwords
-            [ "The environment variable",
-              vn,
-              "was renamed to",
-              "NIV_" <> vn
-            ]
-
 -- | Like lookupEnv "foo" but also looks up "NIV_foo"
 lookupEnv' :: String -> IO (Maybe String)
-lookupEnv' vn = lookupEnv vn >>= \case
-  Just x -> pure (Just x)
-  Nothing -> lookupEnv ("NIV_" <> vn)
+lookupEnv' vn =
+  lookupEnv vn >>= \case
+    Just x -> pure (Just x)
+    Nothing -> lookupEnv ("NIV_" <> vn)
 
 githubHost :: T.Text
 githubHost = unsafePerformIO $ do
diff --git a/src/Niv/GitHub/Cmd.hs b/src/Niv/GitHub/Cmd.hs
--- a/src/Niv/GitHub/Cmd.hs
+++ b/src/Niv/GitHub/Cmd.hs
@@ -13,10 +13,11 @@
 import Control.Applicative
 import Data.Aeson ((.=))
 import qualified Data.Aeson as Aeson
+import qualified Data.Aeson.Key as K
+import qualified Data.Aeson.KeyMap as KM
 import Data.Bifunctor
 import qualified Data.ByteString.Char8 as B8
 import Data.Char (isAlphaNum)
-import qualified Data.HashMap.Strict as HMS
 import Data.Maybe
 import Data.String.QQ (s)
 import qualified Data.Text as T
@@ -45,10 +46,10 @@
 
 parseGitHubPackageSpec :: Opts.Parser PackageSpec
 parseGitHubPackageSpec =
-  (PackageSpec . HMS.fromList)
+  (PackageSpec . KM.fromList)
     <$> many parseAttribute
   where
-    parseAttribute :: Opts.Parser (T.Text, Aeson.Value)
+    parseAttribute :: Opts.Parser (K.Key, Aeson.Value)
     parseAttribute =
       Opts.option
         (Opts.maybeReader parseKeyValJSON)
@@ -88,22 +89,22 @@
       -- | how to convert to JSON
       (String -> Aeson.Value) ->
       String ->
-      Maybe (T.Text, Aeson.Value)
+      Maybe (K.Key, Aeson.Value)
     parseKeyVal toJSON str = case span (/= '=') str of
-      (key, '=' : val) -> Just (T.pack key, toJSON val)
+      (key, '=' : val) -> Just (K.fromString key, toJSON val)
       _ -> Nothing
     -- Shortcuts for common attributes
-    shortcutAttributes :: Opts.Parser (T.Text, Aeson.Value)
+    shortcutAttributes :: Opts.Parser (K.Key, Aeson.Value)
     shortcutAttributes =
       foldr (<|>) empty $
         mkShortcutAttribute
           <$> ["branch", "owner", "rev", "version"]
     -- TODO: infer those shortcuts from 'Update' keys
-    mkShortcutAttribute :: T.Text -> Opts.Parser (T.Text, Aeson.Value)
+    mkShortcutAttribute :: T.Text -> Opts.Parser (K.Key, Aeson.Value)
     mkShortcutAttribute = \case
       attr@(T.uncons -> Just (c, _)) ->
         fmap (second Aeson.String) $
-          (attr,)
+          (K.fromText attr,)
             <$> Opts.strOption
               ( Opts.long (T.unpack attr)
                   <> Opts.short c
@@ -124,12 +125,13 @@
   mconcat
     [ Opts.fullDesc,
       Opts.progDesc "Add a GitHub dependency",
-      Opts.headerDoc $ Just $
-        "Examples:"
-          Opts.<$$> ""
-          Opts.<$$> "  niv add stedolan/jq"
-          Opts.<$$> "  niv add NixOS/nixpkgs -n nixpkgs -b nixpkgs-unstable"
-          Opts.<$$> "  niv add my-package -v alpha-0.1 -t http://example.com/archive/<version>.zip"
+      Opts.headerDoc $
+        Just $
+          "Examples:"
+            Opts.<$$> ""
+            Opts.<$$> "  niv add stedolan/jq"
+            Opts.<$$> "  niv add NixOS/nixpkgs -n nixpkgs -b nixpkgs-unstable"
+            Opts.<$$> "  niv add my-package -v alpha-0.1 -t http://example.com/archive/<version>.zip"
     ]
 
 -- parse a github shortcut of the form "owner/repo"
@@ -143,12 +145,12 @@
       ) ->
         Just
           ( PackageName repo,
-            HMS.fromList ["owner" .= owner, "repo" .= repo]
+            KM.fromList ["owner" .= owner, "repo" .= repo]
           )
     -- XXX: this should be "Nothing" but for the time being we keep
     -- backwards compatibility with "niv add foo" adding "foo" as a
     -- package name.
-    _ -> Just (PackageName str, HMS.empty)
+    _ -> Just (PackageName str, KM.empty)
 
 -- | The IO (real) github update
 githubUpdate' :: Update () ()
diff --git a/src/Niv/GitHub/Test.hs b/src/Niv/GitHub/Test.hs
--- a/src/Niv/GitHub/Test.hs
+++ b/src/Niv/GitHub/Test.hs
@@ -16,9 +16,9 @@
 test_githubInitsProperly = do
   actualState <- evalUpdate initialState $ proc () ->
     githubUpdate prefetch latestRev ghRepo -< ()
-  unless ((snd <$> actualState) == expectedState)
-    $ error
-    $ "State mismatch: " <> show actualState
+  unless ((snd <$> actualState) == expectedState) $
+    error $
+      "State mismatch: " <> show actualState
   where
     prefetch _ _ = pure "some-sha"
     latestRev _ _ _ = pure "some-rev"
@@ -52,9 +52,9 @@
 test_githubUpdates = do
   actualState <- evalUpdate initialState $ proc () ->
     githubUpdate prefetch latestRev ghRepo -< ()
-  unless ((snd <$> actualState) == expectedState)
-    $ error
-    $ "State mismatch: " <> show actualState
+  unless ((snd <$> actualState) == expectedState) $
+    error $
+      "State mismatch: " <> show actualState
   where
     prefetch _ _ = pure "new-sha"
     latestRev _ _ _ = pure "new-rev"
@@ -96,9 +96,9 @@
 test_githubDoesntOverrideRev = do
   actualState <- evalUpdate initialState $ proc () ->
     githubUpdate prefetch latestRev ghRepo -< ()
-  unless ((snd <$> actualState) == expectedState)
-    $ error
-    $ "State mismatch: " <> show actualState
+  unless ((snd <$> actualState) == expectedState) $
+    error $
+      "State mismatch: " <> show actualState
   where
     prefetch _ _ = pure "new-sha"
     latestRev _ _ _ = error "shouldn't fetch rev"
@@ -135,9 +135,9 @@
 test_githubURLFallback = do
   actualState <- evalUpdate initialState $ proc () ->
     githubUpdate prefetch latestRev ghRepo -< ()
-  unless ((snd <$> actualState) == expectedState)
-    $ error
-    $ "State mismatch: " <> show actualState
+  unless ((snd <$> actualState) == expectedState) $
+    error $
+      "State mismatch: " <> show actualState
   where
     prefetch _ _ = pure "some-sha"
     latestRev _ _ _ = error "shouldn't fetch rev"
@@ -161,16 +161,16 @@
   ioref <- newIORef False
   tmpState <- evalUpdate initialState $ proc () ->
     githubUpdate (prefetch ioref) latestRev ghRepo -< ()
-  unless ((snd <$> tmpState) == expectedState)
-    $ error
-    $ "State mismatch: " <> show tmpState
+  unless ((snd <$> tmpState) == expectedState) $
+    error $
+      "State mismatch: " <> show tmpState
   -- Set everything free
   let tmpState' = HMS.map (first (\_ -> Free)) tmpState
   actualState <- evalUpdate tmpState' $ proc () ->
     githubUpdate (prefetch ioref) latestRev ghRepo -< ()
-  unless ((snd <$> actualState) == expectedState)
-    $ error
-    $ "State mismatch: " <> show actualState
+  unless ((snd <$> actualState) == expectedState) $
+    error $
+      "State mismatch: " <> show actualState
   where
     prefetch ioref _ _ = do
       readIORef ioref >>= \case
diff --git a/src/Niv/Local/Cmd.hs b/src/Niv/Local/Cmd.hs
--- a/src/Niv/Local/Cmd.hs
+++ b/src/Niv/Local/Cmd.hs
@@ -10,7 +10,8 @@
 
 import Control.Arrow
 import qualified Data.Aeson as Aeson
-import qualified Data.HashMap.Strict as HMS
+import qualified Data.Aeson.Key as K
+import qualified Data.Aeson.KeyMap as KM
 import qualified Data.Text as T
 import Niv.Cmd
 import Niv.Sources
@@ -36,13 +37,13 @@
   if (T.isPrefixOf "./" txt || T.isPrefixOf "/" txt)
     then do
       let n = last $ T.splitOn "/" txt
-      Just (PackageName n, HMS.fromList [("path", Aeson.String txt)])
+      Just (PackageName n, KM.fromList [("path", Aeson.String txt)])
     else Nothing
 
 parseLocalPackageSpec :: Opts.Parser PackageSpec
-parseLocalPackageSpec = PackageSpec . HMS.fromList <$> parseParams
+parseLocalPackageSpec = PackageSpec . KM.fromList <$> parseParams
   where
-    parseParams :: Opts.Parser [(T.Text, Aeson.Value)]
+    parseParams :: Opts.Parser [(K.Key, Aeson.Value)]
     parseParams = maybe [] pure <$> Opts.optional parsePath
     parsePath =
       ("path",) . Aeson.String
@@ -56,8 +57,9 @@
   mconcat
     [ Opts.fullDesc,
       Opts.progDesc "Add a local dependency. Experimental.",
-      Opts.headerDoc $ Just $
-        "Examples:"
-          Opts.<$$> ""
-          Opts.<$$> "  niv add local ./foo/bar"
+      Opts.headerDoc $
+        Just $
+          "Examples:"
+            Opts.<$$> ""
+            Opts.<$$> "  niv add local ./foo/bar"
     ]
diff --git a/src/Niv/Sources.hs b/src/Niv/Sources.hs
--- a/src/Niv/Sources.hs
+++ b/src/Niv/Sources.hs
@@ -10,6 +10,7 @@
 import Data.Aeson (FromJSON, FromJSONKey, ToJSON, ToJSONKey)
 import qualified Data.Aeson as Aeson
 import qualified Data.Aeson.Extended as Aeson
+import qualified Data.Aeson.KeyMap as KM
 import Data.Bifunctor (first)
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy.Char8 as BL8
@@ -43,9 +44,8 @@
   | SourceIsntJSON
   | SpecIsntAMap
 
-newtype Sources
-  = Sources
-      {unSources :: HMS.HashMap PackageName PackageSpec}
+newtype Sources = Sources
+  {unSources :: HMS.HashMap PackageName PackageSpec}
   deriving newtype (FromJSON, ToJSON)
 
 getSourcesEither :: FindSourcesJson -> IO (Either SourcesError Sources)
@@ -62,7 +62,7 @@
     valueToSources :: Aeson.Value -> Maybe Sources
     valueToSources = \case
       Aeson.Object obj ->
-        fmap (Sources . mapKeys PackageName) $
+        fmap (Sources . mapKeys PackageName . KM.toHashMapText) $
           traverse
             ( \case
                 Aeson.Object obj' -> Just (PackageSpec obj')
@@ -96,7 +96,7 @@
 
 -- | Simply discards the 'Freedom'
 attrsToSpec :: Attrs -> PackageSpec
-attrsToSpec = PackageSpec . fmap snd
+attrsToSpec = PackageSpec . KM.fromHashMapText . fmap snd
 
 -- | @nix/sources.json@ or pointed at by 'FindSourcesJson'
 pathNixSourcesJson :: FindSourcesJson -> FilePath
@@ -172,6 +172,10 @@
     V23
   | -- Fix NIV_OVERRIDE_{name} for sandbox
     V24
+  | -- Add the ability to pass submodules to fetchGit
+    V25
+  | -- formatting fix
+    V26
   deriving stock (Bounded, Enum, Eq)
 
 -- | A user friendly version
@@ -201,6 +205,8 @@
   V22 -> "22"
   V23 -> "23"
   V24 -> "24"
+  V25 -> "25"
+  V26 -> "26"
 
 latestVersionMD5 :: T.Text
 latestVersionMD5 = sourcesVersionToMD5 maxBound
@@ -237,6 +243,8 @@
   V22 -> "935d1d2f0bf95fda977a6e3a7e548ed4"
   V23 -> "4111204b613ec688e2669516dd313440"
   V24 -> "116c2d936f1847112fef0013771dab28"
+  V25 -> "6612caee5814670e5e4d9dd1b71b5f70"
+  V26 -> "937bff93370a064c9000f13cec5867f9"
 
 -- | The MD5 sum of ./nix/sources.nix
 sourcesNixMD5 :: IO T.Text
diff --git a/src/Niv/Update.hs b/src/Niv/Update.hs
--- a/src/Niv/Update.hs
+++ b/src/Niv/Update.hs
@@ -120,13 +120,12 @@
 runBox :: Box a -> IO a
 runBox = boxOp
 
-data Box a
-  = Box
-      { -- | Whether the value is new or was retrieved (or derived) from old
-        -- attributes
-        boxNew :: Bool,
-        boxOp :: IO a
-      }
+data Box a = Box
+  { -- | Whether the value is new or was retrieved (or derived) from old
+    -- attributes
+    boxNew :: Bool,
+    boxOp :: IO a
+  }
   deriving (Functor)
 
 mkBox :: Box a -> IO (Box a)
@@ -188,28 +187,35 @@
   Id -> pure $ UpdateNeedMore $ pure . UpdateSuccess attrs
   Arr f -> pure $ UpdateNeedMore $ pure . UpdateSuccess attrs . f
   Zero -> pure $ UpdateReady (UpdateFailed FailZero)
-  Plus l r -> runUpdate' attrs l >>= \case
-    UpdateReady (UpdateFailed {}) -> runUpdate' attrs r
-    UpdateReady (UpdateSuccess f v) -> pure $ UpdateReady (UpdateSuccess f v)
-    UpdateNeedMore next -> pure $ UpdateNeedMore $ \v -> next v >>= \case
-      UpdateSuccess f res -> pure $ UpdateSuccess f res
-      UpdateFailed {} -> runUpdate' attrs r >>= \case
-        UpdateReady res -> pure res
-        UpdateNeedMore next' -> next' v
-  Load k -> pure $ UpdateReady $ do
-    case HMS.lookup k attrs of
-      Just (_, v') -> UpdateSuccess attrs v'
-      Nothing -> UpdateFailed $ FailNoSuchKey k
+  Plus l r ->
+    runUpdate' attrs l >>= \case
+      UpdateReady (UpdateFailed {}) -> runUpdate' attrs r
+      UpdateReady (UpdateSuccess f v) -> pure $ UpdateReady (UpdateSuccess f v)
+      UpdateNeedMore next -> pure $
+        UpdateNeedMore $ \v ->
+          next v >>= \case
+            UpdateSuccess f res -> pure $ UpdateSuccess f res
+            UpdateFailed {} ->
+              runUpdate' attrs r >>= \case
+                UpdateReady res -> pure res
+                UpdateNeedMore next' -> next' v
+  Load k -> pure $
+    UpdateReady $ do
+      case HMS.lookup k attrs of
+        Just (_, v') -> UpdateSuccess attrs v'
+        Nothing -> UpdateFailed $ FailNoSuchKey k
   First a -> do
     runUpdate' attrs a >>= \case
       UpdateReady (UpdateFailed e) -> pure $ UpdateReady $ UpdateFailed e
-      UpdateReady (UpdateSuccess fo ba) -> pure $ UpdateNeedMore $ \gtt -> do
-        pure $ UpdateSuccess fo (ba, snd gtt)
-      UpdateNeedMore next -> pure $ UpdateNeedMore $ \gtt -> do
-        next (fst gtt) >>= \case
-          UpdateFailed e -> pure $ UpdateFailed e
-          UpdateSuccess f res -> do
-            pure $ UpdateSuccess f (res, snd gtt)
+      UpdateReady (UpdateSuccess fo ba) -> pure $
+        UpdateNeedMore $ \gtt -> do
+          pure $ UpdateSuccess fo (ba, snd gtt)
+      UpdateNeedMore next -> pure $
+        UpdateNeedMore $ \gtt -> do
+          next (fst gtt) >>= \case
+            UpdateFailed e -> pure $ UpdateFailed e
+            UpdateSuccess f res -> do
+              pure $ UpdateSuccess f (res, snd gtt)
   Run act ->
     pure
       ( UpdateNeedMore $ \gtt -> do
@@ -249,28 +255,33 @@
           pure $ UpdateSuccess attrs v
     Nothing -> UpdateNeedMore $ \gtt -> do
       pure $ UpdateSuccess (HMS.insert k (Locked, gtt) attrs) gtt
-  Compose (Compose' f g) -> runUpdate' attrs g >>= \case
-    UpdateReady (UpdateFailed e) -> pure $ UpdateReady $ UpdateFailed e
-    UpdateReady (UpdateSuccess attrs' act) -> runUpdate' attrs' f >>= \case
+  Compose (Compose' f g) ->
+    runUpdate' attrs g >>= \case
       UpdateReady (UpdateFailed e) -> pure $ UpdateReady $ UpdateFailed e
-      UpdateReady (UpdateSuccess attrs'' act') -> pure $ UpdateReady $ UpdateSuccess attrs'' act'
-      UpdateNeedMore next -> UpdateReady <$> next act
-    UpdateNeedMore next -> pure $ UpdateNeedMore $ \gtt -> do
-      next gtt >>= \case
-        UpdateFailed e -> pure $ UpdateFailed e
-        UpdateSuccess attrs' act -> runUpdate' attrs' f >>= \case
-          UpdateReady ready -> pure ready
-          UpdateNeedMore next' -> next' act
-  Template -> pure $ UpdateNeedMore $ \v -> do
-    v' <- runBox v
-    case renderTemplate
-      ( \k ->
-          ((decodeBox $ "When rendering template " <> v') . snd)
-            <$> HMS.lookup k attrs
-      )
-      v' of
-      Nothing -> pure $ UpdateFailed $ FailTemplate v' (HMS.keys attrs)
-      Just v'' -> pure $ UpdateSuccess attrs (v'' <* v) -- carries over v's newness
+      UpdateReady (UpdateSuccess attrs' act) ->
+        runUpdate' attrs' f >>= \case
+          UpdateReady (UpdateFailed e) -> pure $ UpdateReady $ UpdateFailed e
+          UpdateReady (UpdateSuccess attrs'' act') -> pure $ UpdateReady $ UpdateSuccess attrs'' act'
+          UpdateNeedMore next -> UpdateReady <$> next act
+      UpdateNeedMore next -> pure $
+        UpdateNeedMore $ \gtt -> do
+          next gtt >>= \case
+            UpdateFailed e -> pure $ UpdateFailed e
+            UpdateSuccess attrs' act ->
+              runUpdate' attrs' f >>= \case
+                UpdateReady ready -> pure ready
+                UpdateNeedMore next' -> next' act
+  Template -> pure $
+    UpdateNeedMore $ \v -> do
+      v' <- runBox v
+      case renderTemplate
+        ( \k ->
+            ((decodeBox $ "When rendering template " <> v') . snd)
+              <$> HMS.lookup k attrs
+        )
+        v' of
+        Nothing -> pure $ UpdateFailed $ FailTemplate v' (HMS.keys attrs)
+        Just v'' -> pure $ UpdateSuccess attrs (v'' <* v) -- carries over v's newness
 
 decodeBox :: FromJSON a => T.Text -> Box Value -> Box a
 decodeBox msg v = v {boxOp = boxOp v >>= decodeValue msg}
@@ -286,17 +297,15 @@
 --  renderTemplate ("foo" -> "bar") "<foo>" -> pure (Just "bar")
 --  renderTemplate ("foo" -> "bar") "<baz>" -> pure Nothing
 renderTemplate :: (T.Text -> Maybe (Box T.Text)) -> T.Text -> Maybe (Box T.Text)
-renderTemplate vals = \case
-  (T.uncons -> Just ('<', str)) -> do
+renderTemplate vals tpl = case T.uncons tpl of
+  Just ('<', str) -> do
     case T.span (/= '>') str of
       (key, T.uncons -> Just ('>', rest)) -> do
         let v = vals key
         (liftA2 (<>) v) (renderTemplate vals rest)
       _ -> Nothing
-  (T.uncons -> Just (c, str)) -> fmap (T.cons c) <$> renderTemplate vals str
-  (T.uncons -> Nothing) -> Just $ pure T.empty
-  -- XXX: isn't this redundant?
-  _ -> Just $ pure T.empty
+  Just (c, str) -> fmap (T.cons c) <$> renderTemplate vals str
+  Nothing -> Just $ pure T.empty
 
 template :: Update (Box T.Text) (Box T.Text)
 template = Template
diff --git a/src/Niv/Update/Test.hs b/src/Niv/Update/Test.hs
--- a/src/Niv/Update/Test.hs
+++ b/src/Niv/Update/Test.hs
@@ -12,8 +12,9 @@
 
 simplyRuns :: IO ()
 simplyRuns =
-  void $ runUpdate attrs $ proc () -> do
-    returnA -< ()
+  void $
+    runUpdate attrs $ proc () -> do
+      returnA -< ()
   where
     attrs = HMS.empty
 
@@ -76,9 +77,9 @@
 dirtyForcesUpdate = do
   let f = constBox ("world" :: T.Text) >>> dirty >>> update "hello"
   attrs' <- evalUpdate attrs f
-  unless ((snd <$> HMS.lookup "hello" attrs') == Just "world")
-    $ error
-    $ "bad value for hello: " <> show attrs'
+  unless ((snd <$> HMS.lookup "hello" attrs') == Just "world") $
+    error $
+      "bad value for hello: " <> show attrs'
   where
     attrs = HMS.singleton "hello" (Free, "foo")
 
@@ -88,16 +89,16 @@
         update "hello" -< ("world" :: Box T.Text)
         run (\() -> error "io shouldn't be run") -< pure ()
   attrs <- evalUpdate HMS.empty f
-  unless ((snd <$> HMS.lookup "hello" attrs) == Just "world")
-    $ error
-    $ "bad value for hello: " <> show attrs
+  unless ((snd <$> HMS.lookup "hello" attrs) == Just "world") $
+    error $
+      "bad value for hello: " <> show attrs
   let f' = proc () -> do
         run (\() -> error "io shouldn't be run") -< pure ()
         update "hello" -< ("world" :: Box T.Text)
   attrs' <- evalUpdate HMS.empty f'
-  unless ((snd <$> HMS.lookup "hello" attrs') == Just "world")
-    $ error
-    $ "bad value for hello: " <> show attrs'
+  unless ((snd <$> HMS.lookup "hello" attrs') == Just "world") $
+    error $
+      "bad value for hello: " <> show attrs'
   v3 <- execUpdate
     (HMS.fromList [("hello", (Free, "world")), ("bar", (Free, "baz"))])
     $ proc () -> do
