diff --git a/Dust-tools.cabal b/Dust-tools.cabal
--- a/Dust-tools.cabal
+++ b/Dust-tools.cabal
@@ -1,5 +1,5 @@
 Name:                Dust-tools
-Version:             1.3
+Version:             1.3.2
 Description:         A set of tools for exploring network filtering
 Synopsis:            Network filtering exploration tools
 Category:            Network
@@ -18,7 +18,7 @@
   Build-Depends:
     base >= 3 && < 5,
     Dust-crypto,
-    Dust >= 2.3,
+    Dust >= 2.3.1,
     bytestring,
     entropy,
     network,
@@ -111,11 +111,31 @@
     directory,
     split
 
-Executable compile
-  Main-is:           Dust/Model/compile.hs
+Executable shaper-export
+  Main-is:           Dust/Model/export.hs
   Build-Depends:
     base >= 3 && < 5,
-    Dust >= 2.2.1,
+    Dust,
+    bytestring,
+    entropy,
+    network,
+    cereal,
+    ghc-prim,
+    binary,
+    random,
+    random-extras,
+    random-source,
+    random-fu,
+    containers,
+    directory,
+    split,
+    csv
+
+Executable shaper-show
+  Main-is:           Dust/Model/show.hs
+  Build-Depends:
+    base >= 3 && < 5,
+    Dust,
     bytestring,
     entropy,
     network,
diff --git a/Dust/Model/compile.hs b/Dust/Model/compile.hs
deleted file mode 100644
--- a/Dust/Model/compile.hs
+++ /dev/null
@@ -1,27 +0,0 @@
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Lazy as BL
-import Data.ByteString.Char8 (pack)
-import System.Environment (getArgs)
-import System.IO.Error
-import Data.Serialize
-import Text.CSV
-import Data.List as L
-
-import Dust.Model.Observations
-import Dust.Model.TrafficModel
-
-main = do
-    args <- getArgs
-
-    case args of
-        (obspath:modelpath:_) -> compile obspath modelpath
-        otherwise      -> putStrLn "Usage: compile [observation-file] [model-file]"
-
-compile :: FilePath -> FilePath -> IO()
-compile obspath modelpath  = do
-    eitherObs <- loadObservations obspath
-    case eitherObs of
-        Left error -> putStrLn "Error loading observations"
-        Right obs -> do
-            let model = makeModel obs
-            B.writeFile modelpath (encode model)
diff --git a/Dust/Model/export.hs b/Dust/Model/export.hs
new file mode 100644
--- /dev/null
+++ b/Dust/Model/export.hs
@@ -0,0 +1,40 @@
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as BL
+import Data.ByteString.Char8 (pack)
+import System.Environment (getArgs)
+import System.IO.Error
+import Data.Serialize
+import Text.CSV
+import Data.List as L
+import Data.Word
+import Foreign.Ptr
+import Foreign.ForeignPtr
+import Control.Monad
+import System.Directory
+
+import Dust.Model.TrafficModel
+import Dust.Model.PacketLength
+import Dust.Model.Content
+import Dust.Model.Observations
+
+main = do
+    args <- getArgs
+
+    case args of
+        (modelpath:csvpath:_) -> export modelpath csvpath
+        otherwise      -> putStrLn "Usage: export [observation-file] [csv-file]"
+
+export :: FilePath -> FilePath -> IO()
+export modelpath csvpath = do
+    eitherObs <- loadObservations modelpath
+    case eitherObs of
+      Left error -> putStrLn "Could not load observations"
+      Right obs -> do
+        let model = makeModel obs
+        let text = csvify modelpath model
+        writeFile csvpath text
+
+csvify :: String -> TrafficModel -> String
+csvify name (TrafficModel (PacketLengthModel ls) _ _) =
+  name ++ "\n" ++ (unlines $ map show ls)
+
diff --git a/Dust/Model/show.hs b/Dust/Model/show.hs
new file mode 100644
--- /dev/null
+++ b/Dust/Model/show.hs
@@ -0,0 +1,61 @@
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as BL
+import Data.ByteString.Char8 (pack)
+import System.Environment (getArgs)
+import System.IO.Error
+import Data.Serialize
+import Text.CSV
+import Data.List as L
+--import Network.Pcap
+import Data.Word
+import Foreign.Ptr
+import Foreign.ForeignPtr
+import Control.Monad
+import System.Directory
+import Data.Map
+
+import Dust.Model.TrafficModel
+import Dust.Model.PacketLength
+import Dust.Model.Content
+import Dust.Model.Observations
+
+main = do
+    args <- getArgs
+
+    case args of
+        (modelpath:_) -> showModel modelpath
+        otherwise      -> putStrLn "Usage: show [observation-file]"
+
+showModel :: FilePath -> IO()
+showModel modelpath = do
+    eitherObs <- loadObservations modelpath
+    case eitherObs of
+      Left error -> putStrLn "Could not load observations"
+      Right obs -> do
+--        putStrLn $ show obs
+        showSubstrings obs
+
+showSubstrings :: Observations -> IO()
+showSubstrings (Observations _ _ _ (SubstringObservations items)) = showSubstring 0 items
+
+showSubstring :: Int -> [(Map ByteString Int)] -> IO()
+showSubstring offset [] = return ()
+showSubstring offset (item:rest) = do
+  showMap offset item
+  showSubstring (offset+1) rest
+
+showMap :: Int -> (Map ByteString Int) -> IO()
+showMap offset item = showGoodItems offset (assocs item)
+
+showGoodItems :: Int -> [(ByteString,Int)] -> IO()
+showGoodItems offset [] = return ()
+showGoodItems offset ((bs,count):rest) = do
+  showGoodItem offset bs count
+  showGoodItems offset rest
+
+showGoodItem :: Int -> ByteString -> Int -> IO()
+showGoodItem offset bs count = do
+  if count > 1
+    then putStrLn $ (show offset) ++ " " ++ (show bs) ++ " " ++ (show count)
+    else return ()
