diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,4 @@
+## 0.1.0 (2026-09-14)
+Initial release.
+
+- `ShiftL`, `ShiftR`, `Xor`
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2026 Ben Orchard (@raehik) <thefirstmuffinman@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,17 @@
+# ghc-typenats-bits
+Do you feel sad at the lack of bitwise functions for Haskell type-level natural
+numbers? Me too. It's OK. Please enjoy this library. Includes such classics as:
+
+- left shift
+- logical right shift
+- XOR
+
+## Why?
+[hackage-type-level-prng]: https://hackage.haskell.org/package/type-level-prng
+
+I need these definitions in multiple libraries (e.g.
+[type-level-prng][hackage-type-level-prng], a WIP generics lib), and they appear
+to have gone undefined on Hackage.
+
+## License
+Provided under the MIT license. See `LICENSE` for license text.
diff --git a/ghc-typenats-bits.cabal b/ghc-typenats-bits.cabal
new file mode 100644
--- /dev/null
+++ b/ghc-typenats-bits.cabal
@@ -0,0 +1,48 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.38.2.
+--
+-- see: https://github.com/sol/hpack
+
+name:           ghc-typenats-bits
+version:        0.1.0
+synopsis:       Bitwise operations on type-level natural numbers
+description:    Provides extra bitwise operations on type-level 'GHC.TypeNats.Natural's, comparable to the functions in "Data.Bits".
+category:       Types, Data
+homepage:       https://github.com/raehik/ghc-typenats-bits#readme
+bug-reports:    https://github.com/raehik/ghc-typenats-bits/issues
+author:         Ben Orchard
+maintainer:     Ben Orchard <thefirstmuffinman@gmail.com>
+license:        MIT
+license-file:   LICENSE
+build-type:     Simple
+tested-with:
+    GHC==9.14
+  , GHC==9.12
+  , GHC==9.10
+extra-source-files:
+    README.md
+    CHANGELOG.md
+
+source-repository head
+  type: git
+  location: https://github.com/raehik/ghc-typenats-bits
+
+library
+  exposed-modules:
+      GHC.TypeNats.Bits
+  other-modules:
+      Paths_ghc_typenats_bits
+  hs-source-dirs:
+      src
+  default-extensions:
+      NoStarIsType
+      DerivingVia
+      DeriveAnyClass
+      DefaultSignatures
+      TypeFamilies
+      MagicHash
+  ghc-options: -fhide-source-paths -Wall -Winvalid-haddock
+  build-depends:
+      base >=4.20 && <5
+  default-language: GHC2024
diff --git a/src/GHC/TypeNats/Bits.hs b/src/GHC/TypeNats/Bits.hs
new file mode 100644
--- /dev/null
+++ b/src/GHC/TypeNats/Bits.hs
@@ -0,0 +1,43 @@
+{-# LANGUAGE UndecidableInstances #-}
+
+-- | Type-level "Data.Bits" definitions, over GHC opaque type-level 'Natural's.
+
+module GHC.TypeNats.Bits
+  ( type ShiftL
+  , type ShiftR
+  , type Xor
+  ) where
+
+import GHC.TypeNats
+import GHC.TypeError qualified as TE
+
+-- | Left shift.
+type ShiftL x i = x * 2^i
+
+-- | Logical right shift.
+type ShiftR x i = x `Div` 2^i
+
+-- | The exclusive or of the binary representation of two 'Natural's.
+type Xor n m = XorLoop 1 0 n m
+
+-- calculates one binary digit at a time, so should be O(log n) with value...
+-- but with a very high constant factor (8 ops, 2 pattern matches).
+-- I would love to compute this more efficiently!
+type family XorLoop factor acc n m where
+  XorLoop factor acc 0 0 = acc
+  XorLoop factor acc n m =
+    XorLoop
+      (factor*2)
+      (acc + factor * (((n `Mod` 2) `BitXor` (m `Mod` 2))))
+      (n `Div` 2)
+      (m `Div` 2)
+
+-- | Exclusive or on two "bit" 'Natural's.
+--
+-- Both 'Natural's must be either 0 or 1, or it emits a type error.
+type family BitXor n m where
+  BitXor 0 0 = 0
+  BitXor 0 1 = 1
+  BitXor 1 0 = 1
+  BitXor 1 1 = 0
+  BitXor n m = TE.TypeError (TE.Text "BitXor: got non-bit Naturals")
