diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@
 
 ```bash
 cabal update
-cabal install --overwrite-policy=always phino-0.0.95
+cabal install --overwrite-policy=always phino-0.0.105
 phino --version
 ```
 
@@ -132,6 +132,14 @@
 phino rewrite --normalize hello.phi
 ```
 
+Some rules mint fresh synthetic names via the `random-string` built-in. To
+keep the output reproducible across runs, `phino` seeds the random generator
+deterministically with `0` by default. Use `--seed` to pick a different seed:
+
+```bash
+phino rewrite --seed=42 --rule=my-rule.yml hello.phi
+```
+
 If no input file is provided, the 𝜑-expression is taken from `stdin`:
 
 ```bash
@@ -439,55 +447,55 @@
 === parse/phi ===
   warmup:     3 iterations
   batches:    10 x 1
-  total:      1249146.083 μs
-  avg:        124914.608 μs
-  min:        114445.668 μs
-  max:        153538.726 μs
-  std dev:    14836.651 μs
+  total:      1289912.679 μs
+  avg:        128991.268 μs
+  min:        117209.849 μs
+  max:        157967.879 μs
+  std dev:    16178.452 μs
 === parse/xmir ===
   warmup:     3 iterations
   batches:    10 x 1
-  total:      7756845.678 μs
-  avg:        775684.568 μs
-  min:        717096.328 μs
-  max:        814290.753 μs
-  std dev:    28717.270 μs
+  total:      7732390.781 μs
+  avg:        773239.078 μs
+  min:        704582.049 μs
+  max:        844495.931 μs
+  std dev:    54136.286 μs
 === rewrite/normalize ===
   warmup:     3 iterations
   batches:    10 x 1
-  total:      438127.137 μs
-  avg:        43812.714 μs
-  min:        42655.269 μs
-  max:        45113.109 μs
-  std dev:    720.868 μs
+  total:      665998.989 μs
+  avg:        66599.899 μs
+  min:        63509.721 μs
+  max:        72014.960 μs
+  std dev:    2455.894 μs
 === print/sweet/multiline ===
   warmup:     3 iterations
   batches:    10 x 1
-  total:      4445373.874 μs
-  avg:        444537.387 μs
-  min:        426945.439 μs
-  max:        466720.288 μs
-  std dev:    13809.207 μs
+  total:      4722211.658 μs
+  avg:        472221.166 μs
+  min:        463189.991 μs
+  max:        485006.422 μs
+  std dev:    6780.561 μs
 === print/sweet/flat ===
   warmup:     3 iterations
   batches:    10 x 1
-  total:      4535005.765 μs
-  avg:        453500.576 μs
-  min:        426572.785 μs
-  max:        478523.175 μs
-  std dev:    14154.997 μs
+  total:      4645473.928 μs
+  avg:        464547.393 μs
+  min:        431061.415 μs
+  max:        488222.918 μs
+  std dev:    21693.313 μs
 === print/salty/multiline ===
   warmup:     3 iterations
   batches:    10 x 1
-  total:      13746516.677 μs
-  avg:        1374651.668 μs
-  min:        1338899.143 μs
-  max:        1409557.147 μs
-  std dev:    24026.755 μs
+  total:      14501593.398 μs
+  avg:        1450159.340 μs
+  min:        1407041.861 μs
+  max:        1506159.801 μs
+  std dev:    33325.764 μs
 ```
 
 The results were calculated in [this GHA job][benchmark-gha]
-on 2026-06-19 at 10:57,
+on 2026-07-07 at 18:32,
 on Linux with 4 CPUs.
 
 <!-- benchmark_end -->
@@ -536,4 +544,4 @@
 [jna]: https://github.com/java-native-access/jna
 [jna-native]: https://github.com/java-native-access/jna/blob/master/src/com/sun/jna/Native.java
 [jeo]: https://github.com/objectionary/jeo-maven-plugin
-[benchmark-gha]: https://github.com/objectionary/phino/actions/runs/27821357485
+[benchmark-gha]: https://github.com/objectionary/phino/actions/runs/28889268073
diff --git a/phino.cabal b/phino.cabal
--- a/phino.cabal
+++ b/phino.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: phino
-version: 0.0.105
+version: 0.0.106
 license: MIT
 synopsis: Command-Line Manipulator of 𝜑-Calculus Expressions
 description: Please see the README on GitHub at <https://github.com/objectionary/phino#readme>
diff --git a/src/CLI/Parsers.hs b/src/CLI/Parsers.hs
--- a/src/CLI/Parsers.hs
+++ b/src/CLI/Parsers.hs
@@ -192,6 +192,17 @@
 optShuffle :: Parser Bool
 optShuffle = switch (long "shuffle" <> help "Shuffle rules before applying")
 
+optSeed :: Parser Int
+optSeed =
+  option
+    auto
+    ( long "seed"
+        <> metavar "SEED"
+        <> help "Seed for the random generator that mints fresh synthetic names, making output reproducible across runs"
+        <> value 0
+        <> showDefault
+    )
+
 optSugar :: Parser SugarType
 optSugar = flag SALTY SWEET (long "sweet" <> help (printf "Print result and intermediate (see %s option(s)) 𝜑-expressions using syntax sugar" _intermediateOptions))
 
@@ -259,6 +270,7 @@
             <*> optCanonize
             <*> optDepthSensitive
             <*> optShuffle
+            <*> optSeed
             <*> switch (long "quiet" <> help "Don't print the result of dataization")
             <*> optCompress
             <*> optMaxDepth
@@ -290,6 +302,7 @@
             <*> optMust
             <*> optNormalize
             <*> optShuffle
+            <*> optSeed
             <*> optOmitListing
             <*> optOmitComments
             <*> optDepthSensitive
@@ -343,6 +356,7 @@
             <*> optLogLines
             <*> optSugar
             <*> optLineFormat
+            <*> optSeed
             <*> optional (strOption (long "pattern" <> metavar "EXPRESSION" <> help "Pattern expression to match against"))
             <*> optional (strOption (long "when" <> metavar "CONDITION" <> help "Predicate for matched substitutions"))
             <*> argInputFile
diff --git a/src/CLI/Runners.hs b/src/CLI/Runners.hs
--- a/src/CLI/Runners.hs
+++ b/src/CLI/Runners.hs
@@ -31,6 +31,7 @@
 import Rule (RuleContext (..), matchExpressionWithRule)
 import System.Directory (doesFileExist, getModificationTime)
 import System.Exit (exitSuccess)
+import System.Random (mkStdGen, setStdGen)
 import Tau (seedTaus)
 import Text.Printf (printf)
 import XMIR
@@ -48,6 +49,7 @@
   validateBreakpoint _breakpoint rules
   input <- readInput _inputFile
   expr <- parseInput input _inputFormat
+  setStdGen (mkStdGen _seed)
   seedTaus expr
   logDebug (printf "Amount of rewriting cycles across all the rules: %d, per rule: %d" _maxCycles _maxDepth)
   let listing = case (rules, _inputFormat, _outputFormat) of
@@ -141,6 +143,7 @@
   [foc] <- validatedDispatches "focus" [_focus]
   input <- readInput _inputFile
   expr <- parseInput input _inputFormat
+  setStdGen (mkStdGen _seed)
   seedTaus expr
   let printCtx = toPrintCtx foc
       exclude = (`F.exclude` excluded)
@@ -235,6 +238,7 @@
 runMatch OptsMatch{..} = do
   input <- readInput _inputFile
   expr <- parseInput input PHI
+  setStdGen (mkStdGen _seed)
   seedTaus expr
   if isNothing _pattern
     then logDebug "The --pattern is not provided, no substitutions are built"
diff --git a/src/CLI/Types.hs b/src/CLI/Types.hs
--- a/src/CLI/Types.hs
+++ b/src/CLI/Types.hs
@@ -85,6 +85,7 @@
   , _canonize :: Bool
   , _depthSensitive :: Bool
   , _shuffle :: Bool
+  , _seed :: Int
   , _quiet :: Bool
   , _compress :: Bool
   , _maxDepth :: Int
@@ -125,6 +126,7 @@
   , _must :: Must
   , _normalize :: Bool
   , _shuffle :: Bool
+  , _seed :: Int
   , _omitListing :: Bool
   , _omitComments :: Bool
   , _depthSensitive :: Bool
@@ -172,6 +174,7 @@
   , _logLines :: Int
   , _sugarType :: SugarType
   , _flat :: LineFormat
+  , _seed :: Int
   , _pattern :: Maybe String
   , _when :: Maybe String
   , _inputFile :: Maybe FilePath
diff --git a/test/CLISpec.hs b/test/CLISpec.hs
--- a/test/CLISpec.hs
+++ b/test/CLISpec.hs
@@ -334,8 +334,25 @@
     it "prints help" $
       testCLISucceeded
         ["rewrite", "--help"]
-        ["Rewrite the 𝜑-expression"]
+        ["Rewrite the 𝜑-expression", "--seed SEED"]
 
+    it "accepts --seed flag" $
+      withStdin "[[ x -> 5 ]]" $
+        testCLISucceeded
+          ["rewrite", "--seed=42", "--sweet"]
+          ["⟦ x ↦ 5 ⟧"]
+
+    it "defaults --seed to 0 in help" $
+      testCLISucceeded
+        ["rewrite", "--help"]
+        ["default: 0"]
+
+    it "fails with a non-integer --seed" $
+      withStdin "[[ ]]" $
+        testCLIFailed
+          ["rewrite", "--seed=abc"]
+          ["[ERROR]"]
+
     it "saves steps to dir with --steps-dir" $ do
       let dir = "test-steps-temp"
       dirExists <- doesDirectoryExist dir
@@ -867,6 +884,10 @@
       withStdin "[[ D> 01- ]]" $
         testCLISucceeded ["dataize"] ["01-"]
 
+    it "accepts --seed flag" $
+      withStdin "[[ D> 01- ]]" $
+        testCLISucceeded ["dataize", "--seed=7"] ["01-"]
+
     it "fails to dataize an empty object, which dataizes the terminator ⊥" $
       withStdin "[[ ]]" $
         testCLIFailed ["dataize"] ["terminator ⊥"]
@@ -1314,6 +1335,10 @@
     it "prints one substitution" $
       withStdin "[[ x -> Q.x ]]" $
         testCLISucceeded ["match", "--pattern=Q.!t"] ["t >> x"]
+
+    it "accepts --seed flag" $
+      withStdin "[[ x -> Q.x ]]" $
+        testCLISucceeded ["match", "--seed=3", "--pattern=Q.!t"] ["t >> x"]
 
     it "prints many substitutions" $
       withStdin "[[ x -> Q.x, y -> Q.y ]]" $
