diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
 # Changelog
 
+## 0.2.2.0 — 2026-02-23
+
+- Gate `NovaCache.Compression` and `lzma` dependency behind a `compression`
+  cabal flag (default on, backwards compatible)
+- Consumers that only need hashing/NAR/narinfo can build with `-compression`
+  to avoid the system `liblzma` C dependency
+- Compression tests moved to separate test suite (`nova-cache-compression-test`)
+- No changes to any library source modules
+
 ## 0.2.1.0 — 2026-02-22
 
 - Server: `validateNarInfo` wired into PUT handler — rejects malformed uploads
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@
 - **NAR** — Binary serialization and deserialization of the Nix ARchive format using `Builder` monoid composition
 - **NarInfo** — Parse and render the key-value narinfo text format
 - **Signing** — Ed25519 fingerprint signing and verification for binary cache trust
-- **Compression** — xz compress/decompress for NAR transport
+- **Compression** — xz compress/decompress for NAR transport (behind `compression` cabal flag, default on)
 - **Store** — Filesystem storage backend for narinfo and NAR files
 - **Validate** — Pure protocol validation: field semantics, content hashes, Ed25519 signatures — all errors collected, not short-circuited
 - **Server** — Optional WAI/Warp HTTP server implementing the cache protocol (behind `server` cabal flag)
@@ -279,13 +279,13 @@
   └──────────────────────────────────────────┘
                        │
               IO Boundary (thin)
-  ┌──────────────────────────────────────────┐
-  │  Compression    Store    Server (flag)   │
-  └──────────────────────────────────────────┘
+  ┌──────────────────────────────────────────────────────┐
+  │  Compression (flag)    Store    Server (flag)       │
+  └──────────────────────────────────────────────────────┘
 ```
 
-- **9 modules**, 7 pure + 2 at the IO boundary
-- **74 tests**, hand-rolled harness, no framework dependencies
+- **9 modules**, 7 pure + 2 at the IO boundary (Compression optional via flag)
+- **54 core tests + 3 compression tests**, hand-rolled harness, no framework dependencies
 - **Zero partial functions** — total by construction
 - **Strict by default** — bang patterns on all data fields
 
@@ -400,11 +400,18 @@
 ## Build & Test
 
 ```bash
-cabal build                              # Build library
-cabal test                               # Run all tests (74 tests, 9 groups)
+cabal build                              # Build library (compression on by default)
+cabal build -f-compression               # Build without lzma C dependency
+cabal test                               # Run all tests
 cabal build --ghc-options="-Werror"      # Warnings as errors
 cabal build --flag server                # Build with WAI server
 cabal haddock                            # Generate docs
+```
+
+Consumers that only need hashing, NAR, or narinfo can disable the `compression` flag to avoid the system `liblzma` dependency:
+
+```cabal
+constraints: nova-cache -compression
 ```
 
 ---
diff --git a/nova-cache.cabal b/nova-cache.cabal
--- a/nova-cache.cabal
+++ b/nova-cache.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               nova-cache
-version:            0.2.1.0
+version:            0.2.2.0
 synopsis:           Pure Nix binary cache protocol library
 description:
   A focused, minimal, pure-first library implementing the full Nix binary
@@ -27,10 +27,14 @@
   default:     False
   manual:      True
 
+flag compression
+  description: Enable LZMA/XZ compression (requires system liblzma)
+  default:     True
+  manual:      True
+
 library
   exposed-modules:
     NovaCache.Base32
-    NovaCache.Compression
     NovaCache.Hash
     NovaCache.NAR
     NovaCache.NarInfo
@@ -39,6 +43,10 @@
     NovaCache.StorePath
     NovaCache.Validate
 
+  if flag(compression)
+    exposed-modules: NovaCache.Compression
+    build-depends:   lzma >= 0.0.1 && < 0.1
+
   build-depends:
       base                >= 4.16 && < 5
     , base64-bytestring   >= 1.2 && < 1.3
@@ -47,7 +55,6 @@
     , crypton             >= 1.0 && < 2
     , directory           >= 1.3 && < 1.4
     , filepath            >= 1.4 && < 1.6
-    , lzma                >= 0.0.1 && < 0.1
     , memory              >= 0.18 && < 1
     , text                >= 2.0 && < 2.2
     , unix                >= 2.7 && < 2.9
@@ -102,6 +109,23 @@
     , nova-cache
     , memory              >= 0.18 && < 1
     , text                >= 2.0 && < 2.2
+
+test-suite nova-cache-compression-test
+  if !flag(compression)
+    buildable: False
+
+  type:             exitcode-stdio-1.0
+  main-is:          CompressionTest.hs
+  hs-source-dirs:   test
+  default-language: Haskell2010
+  default-extensions:
+    OverloadedStrings
+  ghc-options:      -Wall -Wcompat
+
+  build-depends:
+      base                >= 4.16 && < 5
+    , bytestring          >= 0.11 && < 0.13
+    , nova-cache
 
 source-repository head
   type:     git
diff --git a/test/CompressionTest.hs b/test/CompressionTest.hs
new file mode 100644
--- /dev/null
+++ b/test/CompressionTest.hs
@@ -0,0 +1,66 @@
+module Main (main) where
+
+import qualified Data.ByteString as BS
+import qualified NovaCache.Compression as Compression
+import System.Exit (exitFailure, exitSuccess)
+import System.IO (hFlush, stdout)
+
+-- | Run a named test, short-circuit on first failure.
+test :: String -> IO Bool -> IO Bool
+test name action = do
+  putStr ("  " ++ name ++ "... ")
+  hFlush stdout
+  result <- action
+  if result
+    then do
+      putStrLn "OK"
+      pure True
+    else do
+      putStrLn "FAILED"
+      pure False
+
+-- | Assert equality.
+assertEqual :: (Eq a, Show a) => String -> a -> a -> IO Bool
+assertEqual label expected actual
+  | expected == actual = pure True
+  | otherwise = do
+      putStrLn ""
+      putStrLn ("    " ++ label)
+      putStrLn ("    expected: " ++ show expected)
+      putStrLn ("    actual:   " ++ show actual)
+      pure False
+
+-- | Assert a Bool is True.
+assertTrue :: String -> Bool -> IO Bool
+assertTrue _ True = pure True
+assertTrue label False = do
+  putStrLn ""
+  putStrLn ("    " ++ label ++ ": expected True")
+  pure False
+
+main :: IO ()
+main = do
+  putStrLn "nova-cache compression tests"
+  putStrLn "=============================="
+  putStrLn ""
+  putStrLn "Compression:"
+  ok1 <-
+    test "compress/decompress roundtrip" $
+      let input = BS.pack [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
+          compressed = Compression.compressXz input
+          decompressed = Compression.decompressXz compressed
+       in assertEqual "roundtrip" input decompressed
+  ok2 <-
+    test "compress/decompress roundtrip (empty)" $
+      let compressed = Compression.compressXz BS.empty
+          decompressed = Compression.decompressXz compressed
+       in assertEqual "roundtrip empty" BS.empty decompressed
+  ok3 <-
+    test "compressed is smaller for repetitive data" $
+      let input = BS.replicate 10000 0x42
+          compressed = Compression.compressXz input
+       in assertTrue "smaller" (BS.length compressed < BS.length input)
+  putStrLn ""
+  if ok1 && ok2 && ok3
+    then putStrLn "All compression tests passed." >> exitSuccess
+    else putStrLn "Some compression tests failed." >> exitFailure
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -9,7 +9,6 @@
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as TE
 import qualified NovaCache.Base32 as Base32
-import qualified NovaCache.Compression as Compression
 import qualified NovaCache.Hash as Hash
 import qualified NovaCache.NAR as NAR
 import qualified NovaCache.NarInfo as NarInfo
@@ -122,7 +121,6 @@
       testNAR,
       testNarInfo,
       testSigning,
-      testCompression,
       testFileStore,
       testValidate
     ]
@@ -490,29 +488,6 @@
       NarInfo.niSigs = [],
       NarInfo.niCA = Nothing
     }
-
--- ---------------------------------------------------------------------------
--- Compression tests
--- ---------------------------------------------------------------------------
-
-testCompression :: IO Bool
-testCompression =
-  runGroup
-    "Compression"
-    [ test "compress/decompress roundtrip" $
-        let input = BS.pack [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
-            compressed = Compression.compressXz input
-            decompressed = Compression.decompressXz compressed
-         in assertEqual "roundtrip" input decompressed,
-      test "compress/decompress roundtrip (empty)" $
-        let compressed = Compression.compressXz BS.empty
-            decompressed = Compression.decompressXz compressed
-         in assertEqual "roundtrip empty" BS.empty decompressed,
-      test "compressed is smaller for repetitive data" $
-        let input = BS.replicate 10000 0x42
-            compressed = Compression.compressXz input
-         in assertTrue "smaller" (BS.length compressed < BS.length input)
-    ]
 
 -- ---------------------------------------------------------------------------
 -- FileStore tests
