diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,19 @@
 The format is based on [Keep a Changelog](http://keepachangelog.com/)
 and this project adheres to [Semantic Versioning](http://semver.org/).
 
-## [0.5.0] - 2018-05-15
+## [0.5.0] - 2018-06-12
+
+### Changed
+
+- `PackageKey`s now correctly parse scoped npm names
+  - This means `Text` is now a sum of `SimplePackageKey`/`ScopedPackageKey`
+
+### Fixed
+
+- `PackageKey`s with versions containing `@` parse correctly
+  - Like for example `git+ssh:git@github.com` links for some git packages
+
+## [0.4.1] - 2018-05-15
 
 ### Changed
 
diff --git a/src/Yarn/Lock/File.hs b/src/Yarn/Lock/File.hs
--- a/src/Yarn/Lock/File.hs
+++ b/src/Yarn/Lock/File.hs
@@ -95,6 +95,11 @@
     text = FieldParser { parseField = either Just (const Nothing)
                        , parserName = "text" }
 
+    packageKey :: FieldParser T.PackageKeyName
+    packageKey = FieldParser
+      { parseField = parseField text >=> T.parsePackageKeyName
+      , parserName = "package key" }
+
     -- | Parse a field nested one level to a list of 'PackageKey's.
     keylist :: FieldParser [T.PackageKey]
     keylist = FieldParser
@@ -102,8 +107,9 @@
       , parseField = either (const Nothing)
              (\(Parse.PackageFields inner) ->
                   for (M.toList inner) $ \(k, v) -> do
+                    name <- parseField packageKey (Left k)
                     npmVersionSpec <- parseField text v
-                    pure $ T.PackageKey { T.name = k, ..}) }
+                    pure $ T.PackageKey { name, npmVersionSpec }) }
 
     -- | Appling heuristics to the field contents to find the
     -- correct remote type.
diff --git a/src/Yarn/Lock/Parse.hs b/src/Yarn/Lock/Parse.hs
--- a/src/Yarn/Lock/Parse.hs
+++ b/src/Yarn/Lock/Parse.hs
@@ -36,8 +36,9 @@
 
 import qualified Yarn.Lock.Types as YLT
 
-type Parser = Parsec Void Text
 
+-- | We use a simple (pure) @Megaparsec@ parser.
+type Parser = Parsec Void Text
 
 -- | The @yarn.lock@ format doesn’t specifically include a fixed scheme,
 -- it’s just an unnecessary custom version of a list of fields.
@@ -112,16 +113,37 @@
     pkgKey valueChars = label "package key" $ do
       key <- someTextOf (MP.noneOf valueChars)
       -- okay, here’s the rub:
-      -- `@` is used for separation, but package names containing `@`
-      -- are totally a thing. As first character, as well.
-      -- As are package keys without any semver whatsoever, thus ending
-      -- with `@`.
-      -- Did I mention this file format is big chunks of elephant shit?
-      case (\(n, v) -> (T.dropEnd 1 n, v)) $ T.breakOnEnd "@" key of
-        ("", _) -> fail "packageKey: package name can not be empty"
-        (n, "") -> pure $ YLT.PackageKey n ""
-        (n,  v) -> pure $ YLT.PackageKey n v
+      -- `@` is used for separation, but package names can also
+      -- start with the `@` character (so-called “scoped packages”).
+      -- Furthermore, versions can contain `@` as well.
+      -- This file format is a pile of elephant shit.
+      case breakDrop '@' key of
+        ("", rest) -> case breakDrop '@' rest of
+          -- scoped key with empty name
+          ("", _) -> emptyKeyErr key
+          -- scoped key ("@scope/package")
+          (scopedName, ver) -> YLT.PackageKey
+            <$> scoped (T.cons '@' scopedName) <*> pure ver
+        -- just a simple key
+        (name, ver) -> pure $ YLT.PackageKey (YLT.SimplePackageKey name) ver
 
+    emptyKeyErr :: Text -> Parser a
+    emptyKeyErr key = fail
+      ("packagekey: package name can not be empty (is: "
+      <> toS key <> ")")
+
+    -- | Like 'T.breakOn', but drops the separator char.
+    breakDrop :: Char -> Text -> (Text, Text)
+    breakDrop c str = case T.breakOn (T.singleton c) str of
+      (s, "") -> (s, "")
+      (s, s') -> (s, T.drop 1 s')
+
+    -- | Parses a (scoped) package key and throws an error if misformatted.
+    scoped n = maybe
+      (fail $ "packageKey: scoped variable must be of form @scope/package"
+           <> " (is: " <> toS n <> ")")
+      pure $ YLT.parsePackageKeyName n
+
 -- | Either a simple or a nested field.
 field :: Parser (Text, Either Text PackageFields)
 field = try nested <|> simple <?> "field"
@@ -175,6 +197,8 @@
 --
 -- Update: npm doesn’t specify the package name format, at all.
 -- Apart from the length.
+-- Update: According to https://docs.npmjs.com/misc/scope
+-- the package name format is “URL-safe characters, no leading dots or underscores” TODO
 symbolChars :: Parser Text
 symbolChars = label "key symbol" $ someTextOf $ MP.satisfy
   (\c -> Ch.isAscii c &&
diff --git a/src/Yarn/Lock/Types.hs b/src/Yarn/Lock/Types.hs
--- a/src/Yarn/Lock/Types.hs
+++ b/src/Yarn/Lock/Types.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DeriveFunctor, OverloadedStrings #-}
 {-|
 Module : Yarn.Lock.Types
 Description : Types for yarn.lock files
@@ -8,6 +8,7 @@
 module Yarn.Lock.Types where
 
 import Protolude hiding (try)
+import qualified Data.Text as T
 import qualified Data.MultiKeyedMap as MKM
 import qualified Data.List.NonEmpty as NE
 
@@ -18,8 +19,7 @@
 --
 -- Attention: Might be changed to a newtype in a future release.
 type Lockfile = MKM.MKMap PackageKey Package
--- TODO
--- newtype Lockfile = Lockfile (MKM.MKMap PackageKey Package)
+-- TODO newtype Lockfile = Lockfile (MKM.MKMap PackageKey Package)
 
 -- | Proxy type for our MKMap intermediate key
 lockfileIkProxy :: Proxy Int
@@ -27,11 +27,28 @@
 
 -- | Key that indexes package for a specific version.
 data PackageKey = PackageKey
-  { name           :: Text -- ^ package name
+  { name           :: PackageKeyName -- ^ package name
   , npmVersionSpec :: Text
   -- ^ tring that specifies the version of a package;
   -- sometimes a npm semver, sometimes an arbitrary string
   } deriving (Show, Eq, Ord)
+
+-- | The name of a package. They can be scoped, see
+-- | <https://docs.npmjs.com/misc/scope> for an explanation.
+data PackageKeyName
+  = SimplePackageKey Text
+  -- ^ just a package name
+  | ScopedPackageKey Text Text
+  -- ^ a scope and a package name (e.g. @types/foobar)
+  deriving (Show, Eq, Ord)
+
+-- | Try to parse a string into a package key name (scoped or not).
+parsePackageKeyName :: Text -> Maybe PackageKeyName
+parsePackageKeyName n = case T.stripPrefix "@" n of
+  Nothing -> Just $ SimplePackageKey n
+  Just sc -> case T.breakOn "/" sc of
+    (_, "") -> Nothing
+    (scope, pkg) -> Just $ ScopedPackageKey scope (T.drop 1 pkg)
 
 -- | Something with a list of 'PackageKey's pointing to it.
 data Keyed a = Keyed (NE.NonEmpty PackageKey) a
diff --git a/tests/TestParse.hs b/tests/TestParse.hs
--- a/tests/TestParse.hs
+++ b/tests/TestParse.hs
@@ -27,7 +27,7 @@
   parseSuccess packageList startComment
     >>= \((Keyed keys _) : _) -> do
       assertBool "only foo"
-        (keys == pure (PackageKey "dummy-package" "foo"))
+        (keys == pure (PackageKey (SimplePackageKey "dummy-package") "foo"))
 
 -- registryPackage :: Text
 -- registryPackage = [text|
@@ -56,8 +56,8 @@
       assertBool "field2 member" (Map.member "field2" fields)
       Map.lookup "field1" fields @=? (Just (Left "°§ℓ»«UAIERNT"))
 
-nestedPackage :: Text
-nestedPackage = [text|
+nestedPackageExample :: Text
+nestedPackageExample = [text|
   readable-stream@1.0, "readable-stream@>=1.0.33-1 <1.1.0-0":
     dependencies:
       core-util-is "~1.0.0"
@@ -81,8 +81,8 @@
 
 case_NestedPackage :: Assertion
 case_NestedPackage = do
-  assertBool "there is unicode" (all Ch.isAscii (toS nestedPackage :: [Char]))
-  parseSuccess packageEntry nestedPackage
+  assertBool "there is unicode" (all Ch.isAscii (toS nestedPackageExample :: [Char]))
+  parseSuccess packageEntry nestedPackageExample
     >>= \(Keyed _ (_, PackageFields fields)) -> do
       case Map.lookup "dependencies" fields of
         (Nothing) -> assertFailure "where’s the key"
@@ -92,7 +92,7 @@
           assertEqual "nested keys" 4 $ length nested
           assertEqual "dep exists" (Just (Left "2.3.4"))
             $ Map.lookup "johnny-dep" nested
-          assertEqual "there can be @" (Just (Left "~0.10.x"))
+          assertEqual "scoped packages start with @" (Just (Left "~0.10.x"))
             $ Map.lookup "@types/string_decoder" nested
 
 case_PackageField :: IO ()
@@ -112,17 +112,39 @@
 
 case_PackageKey :: Assertion
 case_PackageKey = do
-  let key = "foo@^1.3.4, bar@blafoo234, xnu@, @types/foo@@:\n"
+  let key = "foo@^1.3.4, bar@blafoo234, xnu@, @types/foo@:\n"
   parseSuccess packageKeys key
     >>= \keys -> do
       keys @?= NE.fromList
-               [ PackageKey "foo" "^1.3.4"
-               , PackageKey "bar" "blafoo234"
+               [ PackageKey (SimplePackageKey "foo") "^1.3.4"
+               , PackageKey (SimplePackageKey "bar") "blafoo234"
                -- yes, the version can be empty …
-               , PackageKey "xnu" ""
+               , PackageKey (SimplePackageKey "xnu") ""
                -- and yes, package names can contain `@`
-               , PackageKey "@types/foo@" ""
+               , PackageKey (ScopedPackageKey "types" "foo") ""
                ]
+
+
+-- | PackageKeys can contain arbitrary stuff apparently
+case_complexKey :: Assertion
+case_complexKey = do
+  parseSuccess packageKeys
+    "\"mango-components@git+ssh://git@github.com:stuff/#fe234\":"
+    >>= \((PackageKey name version) NE.:| []) -> do
+      assertEqual "complexKey name"
+        (SimplePackageKey "mango-components") name
+      assertEqual "complexKey version"
+        "git+ssh://git@github.com:stuff/#fe234"
+        version
+  parseSuccess packageKeys
+    "\"@types/mango-components@git@github\":"
+    >>= \((PackageKey name version) NE.:| []) -> do
+      assertEqual "complexKeyScoped name"
+        (ScopedPackageKey "types" "mango-components") name
+      assertEqual "complexKeyScoped version" "git@github" version
+
+
+-- HELPERS
 
 parseSuccess :: Parser a -> Text -> IO a
 parseSuccess parser string = do
diff --git a/yarn-lock.cabal b/yarn-lock.cabal
--- a/yarn-lock.cabal
+++ b/yarn-lock.cabal
@@ -2,10 +2,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: bfe08f55682f41944db8ec965c30c279fd7d244188df301b7d3d22a65d0c9df7
+-- hash: 9195f2d20c7a3cd78e9ef32e7e18b231b383f572887111142cd2df1d76891703
 
 name:           yarn-lock
-version:        0.4.1
+version:        0.5.0
 synopsis:       Represent and parse yarn.lock files
 description:    Types and parser for the lock file format of the npm successor yarn. All modules should be imported qualified.
 category:       Data
