diff --git a/Cargo.lock b/Cargo.lock
index 04102f1..86ec307 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -481,7 +481,6 @@ dependencies = [
  "digest",
  "fiat-crypto",
  "rustc_version",
- "serde",
  "subtle",
  "zeroize",
 ]
@@ -1948,17 +1947,13 @@ dependencies = [
  "clap",
  "dropshot",
  "ed25519-dalek",
- "futures",
  "hex",
- "hkdf",
  "http",
  "instrumentation",
  "rand",
  "schemars",
  "serde",
- "serde_json",
  "serde_with",
- "sha2",
  "slog",
  "slog-async",
  "sqlx",
@@ -1967,7 +1962,6 @@ dependencies = [
  "tracing",
  "tracing-slog",
  "uuid",
- "x25519-dalek",
 ]
 
 [[package]]
@@ -2644,9 +2638,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.138"
+version = "1.0.135"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949"
+checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
 dependencies = [
  "itoa",
  "memchr",
@@ -4173,18 +4167,6 @@ version = "0.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
 
-[[package]]
-name = "x25519-dalek"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
-dependencies = [
- "curve25519-dalek",
- "rand_core",
- "serde",
- "zeroize",
-]
-
 [[package]]
 name = "xtask"
 version = "0.2.0"
@@ -4266,20 +4248,6 @@ name = "zeroize"
 version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
-dependencies = [
- "zeroize_derive",
-]
-
-[[package]]
-name = "zeroize_derive"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
 
 [[package]]
 name = "zerovec"
diff --git a/Cargo.toml b/Cargo.toml
index ea7bb55..fdf3949 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -36,7 +36,6 @@ dropshot = "0.15.1"
 ed25519-dalek = { version = "2.1.1", features = ["pem", "rand_core"] }
 futures = "0.3"
 hex = "0.4.3"
-hkdf = "0.12.4"
 http = "1.2.0"
 once_cell = "1.20.2"
 progenitor = "0.9"
@@ -45,10 +44,8 @@ reqwest = { version = "0.12.12", features = ["json", "stream", "rustls-tls"] }
 schemars = { version = "0.8.21", features = ["bytes", "chrono", "derive"] }
 semver = "1.0.24"
 serde = { version = "1.0.217", features = ["derive"] }
-serde_json = "1.0.138"
 serde_with = { version = "3.12.0", features = ["base64", "hex", "macros", "std"] }
 # serde_with_macros = { version = "3.12.0", features = ["schemars_0_8"] }
-sha2 = "0.10.8"
 slog = "2.7.0"
 slog-async = "2.8.0"
 tokio = { version = "1.43.0", features = ["full"] }
@@ -57,4 +54,3 @@ tracing-core = "0.1.33"
 tracing-chrome = "0.7.2"
 tracing-slog = { git = "https://github.com/oxidecomputer/tracing-slog", default-features = false }
 uuid = { version = "1", features = [ "serde", "v4" ] }
-x25519-dalek = { version = "2.0.1", features = ["serde", "static_secrets"] }
diff --git a/api.json b/api.json
index d7c3f25..ae26182 100644
--- a/api.json
+++ b/api.json
@@ -12,16 +12,6 @@
         ],
         "summary": "Get registration info",
         "operationId": "get_registration_info",
-        "parameters": [
-          {
-            "in": "query",
-            "name": "key",
-            "required": true,
-            "schema": {
-              "type": "string"
-            }
-          }
-        ],
         "responses": {
           "200": {
             "description": "successful operation",
@@ -181,26 +171,26 @@
       "RegistrationInfo": {
         "type": "object",
         "properties": {
+          "challenge": {
+            "type": "string"
+          },
+          "expiration": {
+            "type": "string",
+            "format": "date-time"
+          },
           "node_id": {
             "type": "string",
             "format": "uuid"
           },
-          "nonce": {
+          "public_key": {
             "type": "string"
-          },
-          "server_dh_pub": {
-            "type": "string"
-          },
-          "timestamp": {
-            "type": "string",
-            "format": "date-time"
           }
         },
         "required": [
+          "challenge",
+          "expiration",
           "node_id",
-          "nonce",
-          "server_dh_pub",
-          "timestamp"
+          "public_key"
         ]
       },
       "User": {
diff --git a/controller/Cargo.toml b/controller/Cargo.toml
index f51b59a..6b2f178 100644
--- a/controller/Cargo.toml
+++ b/controller/Cargo.toml
@@ -11,17 +11,13 @@ chrono.workspace = true
 clap.workspace = true
 dropshot.workspace = true
 ed25519-dalek.workspace = true
-futures.workspace = true
 hex.workspace = true
-hkdf.workspace = true
 http.workspace = true
 instrumentation = { path = "../instrumentation" }
 rand.workspace = true
 schemars.workspace = true
 serde.workspace = true
-serde_json.workspace = true
 serde_with.workspace = true
-sha2.workspace = true
 slog-async.workspace = true
 slog.workspace = true
 sqlx = { version = "0.8.3", default-features = false, features = [
@@ -32,7 +28,6 @@ trace-request = { path = "../trace-request" }
 tracing-slog.workspace = true
 tracing.workspace = true
 uuid.workspace = true
-x25519-dalek.workspace = true
 
 [package.metadata.cargo-machete]
 ignored = ["http"]
diff --git a/controller/src/context.rs b/controller/src/context.rs
index 3d4d4e1..b99d559 100644
--- a/controller/src/context.rs
+++ b/controller/src/context.rs
@@ -1,20 +1,11 @@
 use sqlx::postgres::PgPool;
 
-use crate::node::NodeController;
-
 pub struct ControllerContext {
     pub pg_pool: PgPool,
-    // pub node_controller: Mutex<NodeController>,
-    pub node_controller: NodeController,
 }
 
 impl ControllerContext {
     pub fn new(pg_pool: PgPool) -> ControllerContext {
-        // let node_controller = Mutex::new(NodeController::new(pg_pool.to_owned()));
-        let node_controller = NodeController::new(pg_pool.to_owned());
-        ControllerContext {
-            pg_pool,
-            node_controller,
-        }
+        ControllerContext { pg_pool }
     }
 }
diff --git a/controller/src/node/api.rs b/controller/src/node/api.rs
index 63517a0..cf51771 100644
--- a/controller/src/node/api.rs
+++ b/controller/src/node/api.rs
@@ -1,28 +1,45 @@
-use dropshot::{endpoint, HttpError, HttpResponseOk, Query, RequestContext};
+use chrono::DateTime;
+use dropshot::{endpoint, HttpError, HttpResponseOk, RequestContext};
 use dropshot::{ApiDescription, ApiDescriptionRegisterError};
+use rand::RngCore;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
+use serde_with::base64::Base64;
+use serde_with::base64::Crypt;
 use serde_with::serde_as;
 use trace_request::trace_request;
-use x25519_dalek::PublicKey;
+use uuid::Uuid;
 
 use std::sync::Arc;
 
 use crate::context::ControllerContext;
-use crate::node::controller::RegistrationInfo;
-
-#[serde_as]
-#[derive(Clone, Debug, Default, Deserialize, JsonSchema, Serialize)]
-pub struct RegistrationParams {
-    #[serde_as(as = "serde_with::base64::Base64<serde_with::base64::UrlSafe>")]
-    #[schemars(with = "String")]
-    key: [u8; 32],
-}
 
 pub fn register_api(
     api: &mut ApiDescription<Arc<ControllerContext>>,
 ) -> Result<(), ApiDescriptionRegisterError> {
     api.register(get_registration_info)
+    // api.register(register_node)
+}
+
+#[serde_as]
+#[derive(Deserialize, JsonSchema, Serialize)]
+struct RegistrationInfo {
+    node_id: Uuid,
+    expiration: DateTime<chrono::Utc>,
+
+    // #[serde_as(as = "Base64<serde_with::base64::BinHex, serde_with::formats::Unpadded>")]
+    // #[serde_as(as = "Base64<serde_with::base64::UrlSafe, serde_with::formats::Unpadded>")]
+    #[serde_as(as = "serde_with::base64::Base64")]
+    // #[serde_as(as = "serde_with::hex::Hex<serde_with::formats::Uppercase>")]
+    #[schemars(with = "String")]
+    challenge: [u8; 60],
+
+    // #[serde_as(as = "serde_with::base64::Base64")]
+    #[serde_as(as = "serde_with::hex::Hex")]
+    // #[serde_as(as = "Base64<serde_with::base64::UrlSafe, serde_with::formats::Unpadded>")]
+    // #[serde_as(as = "serde_with::hex::Hex<serde_with::formats::Uppercase>")]
+    #[schemars(with = "String")]
+    public_key: [u8; 32],
 }
 
 /// Get registration info
@@ -34,10 +51,20 @@ pub fn register_api(
 #[trace_request]
 async fn get_registration_info(
     rqctx: RequestContext<Arc<ControllerContext>>,
-    params: Query<RegistrationParams>,
 ) -> Result<HttpResponseOk<RegistrationInfo>, HttpError> {
-    let key = PublicKey::from(params.into_inner().key);
-    tracing::debug!("Registration info for public key: {:?}", key);
-    let info = rqctx.context().node_controller.new_registration(&key);
+    tracing::debug!("Registration info");
+
+    let rng = &mut rand::rngs::OsRng;
+    let mut challenge = [0u8; 60];
+    rng.fill_bytes(&mut challenge);
+    let key = ed25519_dalek::SigningKey::generate(rng);
+
+    let info = RegistrationInfo {
+        node_id: Uuid::new_v4(),
+        expiration: chrono::Utc::now(),
+        challenge,
+        public_key: key.verifying_key().to_bytes(),
+    };
+
     Ok(HttpResponseOk(info))
 }
diff --git a/controller/src/node/controller.rs b/controller/src/node/controller.rs
deleted file mode 100644
index 1698dc7..0000000
--- a/controller/src/node/controller.rs
+++ /dev/null
@@ -1,179 +0,0 @@
-use chrono::DateTime;
-use ed25519_dalek::ed25519::signature::SignerMut;
-use hkdf::Hkdf;
-use rand::Rng;
-use schemars::JsonSchema;
-use serde::{Deserialize, Serialize};
-use serde_with::serde_as;
-use sha2::Sha256;
-use sqlx::postgres::PgPool;
-use uuid::Uuid;
-use x25519_dalek::{PublicKey, StaticSecret};
-
-use std::sync::{Arc, RwLock};
-
-#[serde_as]
-#[derive(Debug, Deserialize, JsonSchema, Serialize)]
-pub struct RegistrationInfo {
-    node_id: Uuid,
-    challenge: Challenge,
-
-    #[serde_as(as = "serde_with::base64::Base64")]
-    #[schemars(with = "String")]
-    challenge_signature: [u8; 64],
-}
-
-#[serde_as]
-#[derive(Debug, Deserialize, JsonSchema, Serialize)]
-pub struct Challenge {
-    timestamp: DateTime<chrono::Utc>,
-
-    #[serde_as(as = "serde_with::base64::Base64")]
-    #[schemars(with = "String")]
-    nonce: [u8; 32],
-
-    #[serde_as(as = "serde_with::base64::Base64")]
-    #[schemars(with = "String")]
-    server_dh_pub: [u8; 32],
-
-    params: ArgonParams,
-}
-
-#[serde_as]
-#[derive(Debug, Deserialize, JsonSchema, Serialize)]
-struct ArgonParams {
-    iterations: u32,
-    memory_kb: u32,
-    threads: u32,
-}
-
-pub struct NodeController {
-    _pg_pool: PgPool,
-    signing_key: RwLock<Option<Arc<SecretKey>>>,
-}
-
-type SecretKey = [u8; 32];
-type Nonce256 = [u8; 32];
-
-trait SecretKeyExt {
-    fn derive_dh_secret(&self, nonce: &Nonce256) -> StaticSecret;
-}
-
-impl SecretKeyExt for SecretKey {
-    fn derive_dh_secret(&self, nonce: &Nonce256) -> StaticSecret {
-        let hk = Hkdf::<Sha256>::new(Some(nonce), self);
-        let mut out = [0u8; 32];
-        hk.expand(b"x25519-secret", &mut out).unwrap();
-        StaticSecret::from(out)
-    }
-}
-
-impl RegistrationInfo {
-    pub fn from(server_secret: &SecretKey, client_dh_public_key: &PublicKey) -> RegistrationInfo {
-        let mut rng = rand::thread_rng();
-        let nonce = rng.gen();
-
-        let server_dh_secret = server_secret.derive_dh_secret(&nonce);
-        let _shared_secret = server_dh_secret.diffie_hellman(client_dh_public_key);
-        let server_dh_public_key = PublicKey::from(&server_dh_secret);
-
-        let timestamp = chrono::Utc::now();
-
-        let params = ArgonParams {
-            iterations: 10,
-            memory_kb: 220 * 1024, // 220 MiB
-            threads: 4,
-        };
-
-        let challenge = Challenge {
-            timestamp,
-            nonce,
-            server_dh_pub: server_dh_public_key.to_bytes(),
-            params,
-        };
-
-        let challenge_json = serde_json::to_vec(&challenge).unwrap();
-
-        let mut sk = ed25519_dalek::SigningKey::from_bytes(server_secret);
-        let sig = sk.sign(challenge_json.as_slice());
-
-        let info = RegistrationInfo {
-            node_id: Uuid::new_v4(),
-            challenge,
-            challenge_signature: sig.to_bytes(),
-        };
-
-        tracing::debug!("Info: {:?}", info);
-        info
-    }
-}
-
-impl NodeController {
-    pub fn new(pg_pool: PgPool) -> NodeController {
-        NodeController {
-            _pg_pool: pg_pool,
-            signing_key: RwLock::new(None),
-        }
-    }
-
-    pub fn new_registration(&self, client_dh_public_key: &PublicKey) -> RegistrationInfo {
-        let server_secret = self.master_key();
-        RegistrationInfo::from(&server_secret, client_dh_public_key)
-    }
-
-    fn master_key(&self) -> Arc<SecretKey> {
-        self.signing_key
-            .write()
-            .unwrap()
-            .get_or_insert_with(|| {
-                tracing::debug!("Generating key");
-                Arc::new(rand::rngs::OsRng.gen())
-            })
-            .clone()
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[tokio::test]
-    async fn test_registration_info() {
-        let client_key: SecretKey = rand::rngs::OsRng.gen();
-        let server_key = rand::rngs::OsRng.gen();
-        let client_public_key = PublicKey::from(client_key);
-
-        let info = RegistrationInfo::from(&server_key, &client_public_key);
-        let info2 = RegistrationInfo::from(&server_key, &client_public_key);
-
-        // Randomness
-        assert_ne!(info.node_id, info2.node_id, "Node ID should be unique");
-        assert_ne!(
-            info.challenge.nonce, info2.challenge.nonce,
-            "Nonce should be unique"
-        );
-        assert_ne!(
-            info.challenge.server_dh_pub, info2.challenge.server_dh_pub,
-            "Server public key derived from nonce should be unique"
-        );
-
-        // Timestamp
-        let now = chrono::Utc::now();
-        assert!(
-            info.challenge.timestamp <= now,
-            "Timestamp is in the future"
-        );
-        assert!(
-            info.challenge.timestamp >= now - chrono::Duration::milliseconds(5),
-            "Timestamp is too old"
-        );
-
-        // Public key
-        let server_dh_secret = server_key.derive_dh_secret(&info.challenge.nonce);
-        let server_dh_public_key = PublicKey::from(&server_dh_secret).to_bytes();
-        assert_eq!(
-            info.challenge.server_dh_pub, server_dh_public_key,
-            "Server public key should be derived from nonce"
-        );
-    }
-}
diff --git a/controller/src/node/mod.rs b/controller/src/node/mod.rs
index c67ce0c..f35db4c 100644
--- a/controller/src/node/mod.rs
+++ b/controller/src/node/mod.rs
@@ -1,5 +1,3 @@
 mod api;
-mod controller;
 
 pub use self::api::register_api;
-pub(crate) use self::controller::NodeController;