diff --git a/secretspec.cabal b/secretspec.cabal
--- a/secretspec.cabal
+++ b/secretspec.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.4
 name:               secretspec
-version:            0.16.0
+version:            0.17.0
 synopsis:           Haskell SDK for SecretSpec, a declarative secrets manager
 description:
   A thin client over the @secretspec-ffi@ C ABI (linked at build time).
diff --git a/src/SecretSpec.hs b/src/SecretSpec.hs
--- a/src/SecretSpec.hs
+++ b/src/SecretSpec.hs
@@ -23,6 +23,7 @@
   , withPath
   , withProvider
   , withProfile
+  , withScope
   , withReason
   , withNoValues
     -- * Resolve (value-carrying)
@@ -125,6 +126,8 @@
 data Resolved = Resolved
   { resolvedProvider        :: Text
   , resolvedProfile         :: Text
+  -- | Selected manifest scope, or 'Nothing' for a full-profile resolve (0.17+).
+  , resolvedScope           :: Maybe Text
   , resolvedSecrets         :: Map Text ResolvedSecret
   , resolvedMissingOptional :: [Text]
   } deriving (Show, Eq)
@@ -157,6 +160,8 @@
 data Report = Report
   { reportProvider :: Text
   , reportProfile  :: Text
+  -- | Selected manifest scope, or 'Nothing' for a full-profile report (0.17+).
+  , reportScope    :: Maybe Text
   , reportSecrets  :: [SecretReport]
   } deriving (Show, Eq)
 
@@ -166,13 +171,14 @@
   { bPath     :: Maybe Text
   , bProvider :: Maybe Text
   , bProfile  :: Maybe Text
+  , bScope    :: Maybe Text
   , bReason   :: Maybe Text
   , bNoValues :: Bool
   }
 
 -- | A builder with no options set.
 builder :: Builder
-builder = Builder Nothing Nothing Nothing Nothing False
+builder = Builder Nothing Nothing Nothing Nothing Nothing False
 
 -- | Resolve from a manifest at this path instead of walking up from the working
 -- directory.
@@ -187,6 +193,10 @@
 withProfile :: Text -> Builder -> Builder
 withProfile v b = b { bProfile = Just v }
 
+-- | Limit resolution to a named manifest scope (SecretSpec 0.17+).
+withScope :: Text -> Builder -> Builder
+withScope v b = b { bScope = Just v }
+
 -- | Set a human-readable reason for this access (for audited providers).
 withReason :: Text -> Builder -> Builder
 withReason v b = b { bReason = Just v }
@@ -249,15 +259,16 @@
 load b = do
   resp <- callNative (requestBytes b Nothing)
   value <- responseValue resp resolveSchemaVersion "resolve"
-  (prov, prof, secs, mreq, mopt) <- fromResult (parseEither pResolve value)
+  (prov, prof, scope, secs, mreq, mopt) <- fromResult (parseEither pResolve value)
   case mreq of
-    [] -> pure (Resolved prov prof secs mopt)
+    [] -> pure (Resolved prov prof scope secs mopt)
     xs -> throwIO (MissingRequiredError xs)
   where
     pResolve = withObject "response" $ \o ->
-      (,,,,)
+      (,,,,,)
         <$> o .: "provider"
         <*> o .: "profile"
+        <*> o .:? "scope"
         <*> o .:? "secrets" .!= Map.empty
         <*> o .:? "missing_required" .!= []
         <*> o .:? "missing_optional" .!= []
@@ -270,13 +281,14 @@
 report b = do
   resp <- callNative (requestBytes b (Just "report"))
   value <- responseValue resp reportSchemaVersion "report"
-  (prov, prof, secs) <- fromResult (parseEither pReport value)
-  pure (Report prov prof secs)
+  (prov, prof, scope, secs) <- fromResult (parseEither pReport value)
+  pure (Report prov prof scope secs)
   where
     pReport = withObject "response" $ \o ->
-      (,,)
+      (,,,)
         <$> o .: "provider"
         <*> o .: "profile"
+        <*> o .:? "scope"
         <*> o .:? "secrets" .!= []
 
 -- Build the request JSON for a resolve (@mode = Nothing@) or report
@@ -288,6 +300,7 @@
       [ ("path" .=) <$> bPath b
       , ("provider" .=) <$> bProvider b
       , ("profile" .=) <$> bProfile b
+      , ("scope" .=) <$> bScope b
       , ("reason" .=) <$> bReason b
       ]
       ++ ["no_values" .= True | bNoValues b]
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -41,6 +41,7 @@
   let tests =
         [ ("abi_version_nonempty", testAbiVersion)
         , ("missing_required_throws", testMissingRequired)
+        , ("scoped_resolution", testScope)
         , ("codegen", testCodegen)
         ]
           ++ concatMap conformanceTests fixtures
@@ -92,6 +93,41 @@
   case r of
     Left _  -> pure ()
     Right _ -> ioError (userError "expected MissingRequiredError")
+
+testScope :: IO ()
+testScope = do
+  tmp <- getTemporaryDirectory
+  let dir = tmp </> "secretspec-hs-scope"
+  createDirectoryIfMissing True dir
+  writeFile (dir </> "secretspec.toml") $
+    unlines
+      [ "[project]"
+      , "name = \"hs-scope\""
+      , "revision = \"1.0\""
+      , ""
+      , "[profiles.default]"
+      , "DATABASE_URL = { description = \"DB\", required = true }"
+      , "SENTRY_DSN = { description = \"Sentry\", required = false }"
+      , ""
+      , "[scopes.database]"
+      , "secrets = [\"DATABASE_URL\"]"
+      ]
+  writeFile
+    (dir </> ".env")
+    "DATABASE_URL=postgres://db\nSENTRY_DSN=https://sentry\n"
+  let scoped = fixtureBuilder dir & S.withScope "database"
+
+  resolved <- S.load scoped
+  expect (S.resolvedScope resolved == Just "database") "resolve scope was not returned"
+  expect
+    (Map.keys (S.resolvedSecrets resolved) == ["DATABASE_URL"])
+    "resolve exposed an out-of-scope secret"
+
+  rep <- S.report scoped
+  expect (S.reportScope rep == Just "database") "report scope was not returned"
+  expect
+    (map S.srName (S.reportSecrets rep) == ["DATABASE_URL"])
+    "report exposed an out-of-scope secret"
 
 -- End-to-end codegen: secretspec schema -> quicktype --lang haskell -> compile
 -- the generated module and decode the SDK's own fieldsJson output with it, so
