diff --git a/Cargo.toml b/Cargo.toml
index 348c03e..7f59c58 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,16 +1,17 @@
-# [package]
-# name = "protomask"
-# version = "0.2.0"
-# authors = ["Evan Pratten <ewpratten@gmail.com>"]
-# edition = "2021"
-# description = "A user space NAT64 implementation"
-# readme = "README.md"
-# homepage = "https://github.com/ewpratten/protomask"
-# documentation = "https://docs.rs/protomask"
-# repository = "https://github.com/ewpratten/protomask"
-# license = "GPL-3.0"
-# keywords = []
-# categories = []
+[package]
+name = "protomask"
+version = "0.2.0"
+authors = ["Evan Pratten <ewpratten@gmail.com>"]
+edition = "2021"
+description = "A user space NAT64 implementation"
+readme = "README.md"
+homepage = "https://github.com/ewpratten/protomask"
+documentation = "https://docs.rs/protomask"
+repository = "https://github.com/ewpratten/protomask"
+license = "GPL-3.0"
+keywords = []
+categories = []
+exclude = ["/.github/", "/.vscode/"]
 
 # # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 # [dependencies]
@@ -39,21 +40,56 @@
 # prometheus = "0.13.3"
 # lazy_static = "1.4.0"
 
-# [[bin]]
-# name = "protomask"
-# path = "src/cli/main.rs"
-
-# [package.metadata.deb]
-# section = "network"
-# assets = [
-#     ["target/release/protomask", "/usr/local/bin/protomask", "755"],
-#     ["./protomask.toml", "/etc/protomask.toml", "644"],
-#     ["README.md", "usr/share/doc/protomask/README.md", "644"]
-# ]
-# conf-files = ["/etc/protomask.toml"]
-# depends = []
-# maintainer-scripts = "./debian/"
-# systemd-units = { enable = false }
-
 [workspace]
 members = ["libs/easy-tun", "libs/fast-nat", "libs/interproto"]
+
+[[bin]]
+name = "protomask"
+path = "src/protomask.rs"
+
+[[bin]]
+name = "protomask-clat"
+path = "src/protomask-clat.rs"
+
+[[bin]]
+name = "protomask-6over4"
+path = "src/protomask-6over4.rs"
+
+[dependencies]
+# Internal dependencies
+easy-tun = { path = "libs/easy-tun" }
+fast-nat = { path = "libs/fast-nat" }
+interproto = { path = "libs/interproto" }
+
+# External Dependencies
+tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread"] }
+owo-colors = { version = "3.5.0", features = ["supports-colors"] }
+clap = { version = "4.3.11", features = ["derive"] }
+log = "0.4.19"
+fern = "0.6.2"
+ipnet = "2.8.0"
+nix = "0.26.2"
+
+[package.metadata.deb]
+section = "network"
+assets = [
+    [
+        "target/release/protomask",
+        "/usr/local/bin/protomask",
+        "755",
+    ],
+    [
+        "./protomask.toml",
+        "/etc/protomask.toml",
+        "644",
+    ],
+    [
+        "README.md",
+        "usr/share/doc/protomask/README.md",
+        "644",
+    ],
+]
+conf-files = ["/etc/protomask.toml"]
+depends = []
+maintainer-scripts = "./debian/"
+systemd-units = { enable = false }
diff --git a/src/cli/cli.rs b/src/cli/cli.rs
deleted file mode 100644
index 8ccd700..0000000
--- a/src/cli/cli.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-//! Command line argument definitions
-
-use std::path::PathBuf;
-
-use clap::Parser;
-
-#[derive(Parser, Debug)]
-#[command(author, version, about, long_about = None)]
-pub struct Args {
-    /// Path to the config file
-    pub config_file: PathBuf,
-
-    /// Enable verbose logging
-    #[clap(short, long)]
-    pub verbose: bool,
-}
diff --git a/src/cli/config.rs b/src/cli/config.rs
deleted file mode 100644
index 50e46f6..0000000
--- a/src/cli/config.rs
+++ /dev/null
@@ -1,94 +0,0 @@
-//! Serde definitions for the config file
-
-use std::{
-    net::{Ipv4Addr, Ipv6Addr, SocketAddr},
-    path::Path,
-    time::Duration,
-};
-
-use ipnet::{Ipv4Net, Ipv6Net};
-
-/// A static mapping rule
-#[derive(Debug, serde::Deserialize)]
-pub struct AddressMappingRule {
-    /// IPv4 address
-    pub v4: Ipv4Addr,
-    /// IPv6 address
-    pub v6: Ipv6Addr,
-}
-
-/// Used to generate the default reservation duration
-fn default_reservation_duration() -> u64 {
-    7200
-}
-
-/// Rules config
-#[derive(Debug, serde::Deserialize)]
-pub struct PoolConfig {
-    /// Pool prefixes
-    #[serde(rename = "Prefixes")]
-    pub prefixes: Vec<Ipv4Net>,
-    /// Static mapping rules
-    #[serde(rename = "Static", default = "Vec::new")]
-    pub static_map: Vec<AddressMappingRule>,
-    /// How long to hold a dynamic mapping for
-    #[serde(rename = "MaxIdleDuration", default = "default_reservation_duration")]
-    reservation_duration: u64,
-}
-
-impl PoolConfig {
-    /// Get the reservation duration
-    pub fn reservation_duration(&self) -> Duration {
-        Duration::from_secs(self.reservation_duration)
-    }
-}
-
-/// Representation of the `protomask.toml` config file
-#[derive(Debug, serde::Deserialize)]
-pub struct Config {
-    /// The NAT64 prefix
-    #[serde(rename = "Nat64Prefix")]
-    pub nat64_prefix: Ipv6Net,
-    /// Address to bind to for prometheus support
-    #[serde(rename = "Prometheus")]
-    pub prom_bind_addr: Option<SocketAddr>,
-    /// Pool configuration
-    #[serde(rename = "Pool")]
-    pub pool: PoolConfig,
-}
-
-impl Config {
-    /// Load the config from a file
-    pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, std::io::Error> {
-        // Load the file
-        let file_contents = std::fs::read_to_string(path)?;
-
-        // Build the deserializer
-        let deserializer = toml::Deserializer::new(&file_contents);
-
-        // Parse
-        match serde_path_to_error::deserialize(deserializer) {
-            Ok(config) => Ok(config),
-            // If there is a parsing error, display a reasonable error message
-            Err(e) => {
-                eprintln!(
-                    "Failed to parse config file due to:\n {}\n at {}",
-                    e.inner().message(),
-                    e.path()
-                );
-                std::process::exit(1);
-            }
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    /// Test that fails if the example file is not valid
-    #[test]
-    fn ensure_example_is_valid() {
-        let _ = Config::load("protomask.toml").unwrap();
-    }
-}
diff --git a/src/cli/main.rs b/src/cli/main.rs
deleted file mode 100644
index f2f2355..0000000
--- a/src/cli/main.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-//! This is the entrypoint for `protomask` from the command line.
-
-use clap::Parser;
-use config::Config;
-use logging::enable_logger;
-use protomask::nat::Nat64;
-
-mod cli;
-mod config;
-mod logging;
-
-#[tokio::main]
-pub async fn main() {
-    // Parse CLI args
-    let args = cli::Args::parse();
-
-    // Set up logging
-    enable_logger(args.verbose);
-
-    // Parse the config file
-    let config = Config::load(args.config_file).unwrap();
-
-    // Currently, only a /96 is supported
-    if config.nat64_prefix.prefix_len() != 96 {
-        log::error!("Only a /96 prefix is supported for the NAT64 prefix");
-        std::process::exit(1);
-    }
-
-    // Create the NAT64 instance
-    let mut nat64 = Nat64::new(
-        config.nat64_prefix,
-        config.pool.prefixes.clone(),
-        config
-            .pool
-            .static_map
-            .iter()
-            .map(|rule| (rule.v6, rule.v4))
-            .collect(),
-        config.pool.reservation_duration(),
-    )
-    .await
-    .unwrap();
-
-    // Handle metrics requests
-    if let Some(bind_addr) = config.prom_bind_addr {
-        log::info!("Enabling metrics server on {}", bind_addr);
-        tokio::spawn(protomask::metrics::serve_metrics(bind_addr));
-    }
-
-    // Handle packets
-    nat64.run().await.unwrap();
-}
diff --git a/src/cli/logging.rs b/src/common/logging.rs
similarity index 100%
rename from src/cli/logging.rs
rename to src/common/logging.rs
diff --git a/src/common/mod.rs b/src/common/mod.rs
new file mode 100644
index 0000000..e568b1f
--- /dev/null
+++ b/src/common/mod.rs
@@ -0,0 +1,3 @@
+//! Common code used across all protomask binaries
+
+pub mod logging;
diff --git a/src/lib.rs b/src/lib.rs
deleted file mode 100644
index 14f189f..0000000
--- a/src/lib.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-//! # Protomask library
-//!
-//! *Note: There is a fair chance you are looking for `src/cli/main.rs` instead of this file.*
-
-#![deny(clippy::pedantic)]
-#![allow(clippy::module_name_repetitions)]
-#![allow(clippy::missing_errors_doc)]
-#![allow(clippy::missing_panics_doc)]
-
-pub mod metrics;
-pub mod nat;
-mod packet;
diff --git a/src/metrics/http.rs b/src/metrics/http.rs
deleted file mode 100644
index 5f88e1c..0000000
--- a/src/metrics/http.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-use std::{convert::Infallible, net::SocketAddr};
-
-use hyper::{
-    service::{make_service_fn, service_fn},
-    Body, Method, Request, Response, Server,
-};
-use prometheus::{Encoder, TextEncoder};
-
-/// Handle an HTTP request
-#[allow(clippy::unused_async)]
-async fn handle_request(request: Request<Body>) -> Result<Response<Body>, Infallible> {
-    // If the request is targeting the metrics endpoint
-    if request.method() == Method::GET && request.uri().path() == "/metrics" {
-        // Gather metrics
-        let metric_families = prometheus::gather();
-        let body = {
-            let mut buffer = Vec::new();
-            let encoder = TextEncoder::new();
-            encoder.encode(&metric_families, &mut buffer).unwrap();
-            String::from_utf8(buffer).unwrap()
-        };
-
-        // Return the response
-        return Ok(Response::new(Body::from(body)));
-    }
-
-    // Otherwise, just return a 404
-    Ok(Response::builder()
-        .status(404)
-        .body(Body::from("Not found"))
-        .unwrap())
-}
-
-/// Bring up an HTTP server that listens for metrics requests
-pub async fn serve_metrics(bind_addr: SocketAddr) {
-    // Set up the server
-    let make_service =
-        make_service_fn(|_| async { Ok::<_, Infallible>(service_fn(handle_request)) });
-    let server = Server::bind(&bind_addr).serve(make_service);
-
-    // Run the server
-    if let Err(e) = server.await {
-        eprintln!("Metrics server error: {e}");
-    }
-}
diff --git a/src/metrics/metrics.rs b/src/metrics/metrics.rs
deleted file mode 100644
index ba32849..0000000
--- a/src/metrics/metrics.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use lazy_static::lazy_static;
-use prometheus::{
-    register_int_counter_vec, register_int_gauge, register_int_gauge_vec, IntCounterVec, IntGauge,
-    IntGaugeVec,
-};
-
-lazy_static! {
-    /// Counter for the number of packets processes
-    pub static ref PACKET_COUNTER: IntCounterVec = register_int_counter_vec!(
-        "packets",
-        "Number of packets processed",
-        &["protocol", "status"]
-    ).unwrap();
-
-    /// Counter for ICMP packet types
-    pub static ref ICMP_COUNTER: IntCounterVec = register_int_counter_vec!(
-        "icmp",
-        "Number of ICMP packets processed",
-        &["protocol", "type", "code"]
-    ).unwrap();
-
-    /// Gauge for the number of addresses in the IPv4 pool
-    pub static ref IPV4_POOL_SIZE: IntGauge = register_int_gauge!(
-        "ipv4_pool_size",
-        "Number of IPv4 addresses in the pool"
-    ).unwrap();
-
-    /// Gauge for the number of addresses currently reserved in the IPv4 pool
-    pub static ref IPV4_POOL_RESERVED: IntGaugeVec = register_int_gauge_vec!(
-        "ipv4_pool_reserved",
-        "Number of IPv4 addresses currently reserved",
-        &["static"]
-    ).unwrap();
-}
diff --git a/src/metrics/mod.rs b/src/metrics/mod.rs
deleted file mode 100644
index e79cd4c..0000000
--- a/src/metrics/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-mod http;
-#[allow(clippy::module_inception)]
-mod metrics;
-
-pub use http::serve_metrics;
-pub(crate) use metrics::*;
diff --git a/src/protomask-6over4.rs b/src/protomask-6over4.rs
new file mode 100644
index 0000000..9d38cbe
--- /dev/null
+++ b/src/protomask-6over4.rs
@@ -0,0 +1,5 @@
+
+#[tokio::main]
+pub async fn main(){
+    
+}
\ No newline at end of file
diff --git a/src/protomask-clat.rs b/src/protomask-clat.rs
new file mode 100644
index 0000000..150b650
--- /dev/null
+++ b/src/protomask-clat.rs
@@ -0,0 +1,51 @@
+//! Entrypoint for the `protomask-clat` binary.
+//!
+//! This binary is a Customer-side transLATor (CLAT) that translates all native
+//! IPv4 traffic to IPv6 traffic for transmission over an IPv6-only ISP network.
+
+use clap::Parser;
+use common::logging::enable_logger;
+use easy_tun::Tun;
+use ipnet::Ipv6Net;
+use nix::unistd::Uid;
+
+mod common;
+
+#[derive(Debug, Parser)]
+#[clap(author, version, about="IPv4 to IPv6 Customer-side transLATor (CLAT)", long_about = None)]
+struct Args {
+    /// IPv6 prefix to embed IPv4 addresses in
+    #[clap(long="via", default_value_t = ("64:ff9b::/96").parse().unwrap())]
+    embed_prefix: Ipv6Net,
+
+    /// Explicitly set the interface name to use
+    #[clap(short, long, default_value_t = ("clat%d").to_string())]
+    interface: String,
+
+    /// Enable verbose logging
+    #[clap(short, long)]
+    verbose: bool,
+}
+
+#[tokio::main]
+pub async fn main() {
+    // Parse CLI args
+    let args = Args::parse();
+
+    // Initialize logging
+    enable_logger(args.verbose);
+
+    // We must be root to continue program execution
+    if !Uid::effective().is_root() {
+        log::error!("This program must be run as root");
+        std::process::exit(1);
+    }
+
+    // Bring up a TUN interface
+    let mut tun = Tun::new(&args.interface).unwrap();
+
+    log::info!("Translating packets on {}", tun.name());
+    loop {
+
+    }
+}
diff --git a/src/protomask.rs b/src/protomask.rs
new file mode 100644
index 0000000..9d38cbe
--- /dev/null
+++ b/src/protomask.rs
@@ -0,0 +1,5 @@
+
+#[tokio::main]
+pub async fn main(){
+    
+}
\ No newline at end of file