diff --git a/.gitignore b/.gitignore
index 230ab66..c688ee2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,16 @@
 Cargo.lock
 target/
 **/*.rs.bk
+
+# python generated files
+__pycache__/
+*.py[oc]
+build/
+dist/
+wheels/
+*.egg-info
+# Rust
+target/
+
+# venv
+.venv
diff --git a/.python-version b/.python-version
new file mode 100644
index 0000000..871f80a
--- /dev/null
+++ b/.python-version
@@ -0,0 +1 @@
+3.12.3
diff --git a/Cargo.toml b/Cargo.toml
index 9bddb39..82c2fe3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,9 +6,9 @@ authors = ["Alex Carlin <alxcarln@gmail.com>"]
 description = "PDB reader and other protein modeling tools" 
 license = "MIT"
 
-# [lib]
-# name = "heme"
-# crate-type = ["cdylib"]
+[lib]
+name = "heme"
+crate-type = ["cdylib"]
 
 [dependencies]
 pyo3 = { version = "0.18.0", features = ["extension-module"] }
diff --git a/example_usage.py b/example_usage.py
new file mode 100644
index 0000000..e40dc8d
--- /dev/null
+++ b/example_usage.py
@@ -0,0 +1,5 @@
+import heme 
+
+
+#print(heme.hello()) 
+print(heme.parse_my_pdb("demo/1ee9.pdb")) 
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..3af92b1
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,25 @@
+[project]
+name = "heme"
+version = "0.1.0"
+description = "Add your description here"
+authors = [
+    { name = "Alex Carlin", email = "alxcarln@gmail.com" }
+]
+dependencies = [
+    "maturin>=1.7.0",
+]
+readme = "README.md"
+requires-python = ">= 3.8"
+
+[build-system]
+requires = ["maturin>=1.2,<2.0"]
+build-backend = "maturin"
+
+[tool.rye]
+managed = true
+dev-dependencies = []
+
+[tool.maturin]
+python-source = "python"
+module-name = "heme._lowlevel"
+features = ["pyo3/extension-module"]
diff --git a/python/heme/__init__.py b/python/heme/__init__.py
new file mode 100644
index 0000000..6da0ec1
--- /dev/null
+++ b/python/heme/__init__.py
@@ -0,0 +1,3 @@
+from heme._lowlevel import hello, parse_my_pdb 
+
+__all__ = ["hello", "parse_my_pdb"]
diff --git a/readme.md b/readme.md
index 26013da..2012872 100644
--- a/readme.md
+++ b/readme.md
@@ -62,6 +62,8 @@ modeling, see the OpenMM or Lumol projects.)
   - `Atom` and `Record` objects 
   
 
+## Development 
 
+Heme is a hybrid Python-Rust library with bindings provided by PyO3
 
 
diff --git a/requirements-dev.lock b/requirements-dev.lock
new file mode 100644
index 0000000..88fe4b4
--- /dev/null
+++ b/requirements-dev.lock
@@ -0,0 +1,14 @@
+# generated by rye
+# use `rye lock` or `rye sync` to update this lockfile
+#
+# last locked with the following flags:
+#   pre: false
+#   features: []
+#   all-features: false
+#   with-sources: false
+#   generate-hashes: false
+#   universal: false
+
+-e file:.
+maturin==1.7.0
+    # via heme
diff --git a/requirements.lock b/requirements.lock
new file mode 100644
index 0000000..88fe4b4
--- /dev/null
+++ b/requirements.lock
@@ -0,0 +1,14 @@
+# generated by rye
+# use `rye lock` or `rye sync` to update this lockfile
+#
+# last locked with the following flags:
+#   pre: false
+#   features: []
+#   all-features: false
+#   with-sources: false
+#   generate-hashes: false
+#   universal: false
+
+-e file:.
+maturin==1.7.0
+    # via heme
diff --git a/src/lib.rs b/src/lib.rs
index 8e726b5..f88035e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,46 @@
 pub mod conformation;
 pub mod sampling;
 pub mod transforms;
-pub mod io;
\ No newline at end of file
+pub mod io;
+
+// Python bindings 
+use std::fs; 
+use std::fs::File;
+use std::io::Read;
+use pyo3::prelude::*;
+
+/// Prints a message.
+#[pyfunction]
+fn hello() -> PyResult<String> {
+    Ok("Hello from Hemes!".into())
+}
+
+/// Reads a file and returns its contents with a greeting.
+#[pyfunction]
+fn parse_my_pdb(file_name: &str) -> PyResult<String> {
+
+    // read input files from Config object
+    let mut f = File::open(file_name)?;
+    let mut contents = String::new();
+    f.read_to_string(&mut contents)?;
+
+    // create pose object by parsing the PDB
+    let atoms = io::parse_pdb(&contents);
+    let mut pose = conformation::Pose::from_atoms(atoms);
+
+    println!("read pose"); 
+    // Apply a protocol to the Pose
+    //let protocol = sampling::get_protocol(&io::config.protocol);
+    //protocol.run(&mut pose);
+
+    Ok("read rule".into())
+}
+
+
+/// A Python module implemented in Rust.
+#[pymodule]
+fn _lowlevel(_py: Python, m: &PyModule) -> PyResult<()> {
+    m.add_function(wrap_pyfunction!(parse_my_pdb, m)?)?;
+    m.add_function(wrap_pyfunction!(hello, m)?)?;
+    Ok(())
+}
diff --git a/src/main.rs b/src/main.rs
index 35326c3..8bb51b9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,5 @@
 use std::env;
 use std::process;
-
 use heme::io::Config;
 
 fn main() {