Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/build-and-deploy-rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ jobs:
- name: Build and Deploy VSS Server
run: |
cd rust
cargo build
cargo run server/vss-server-config.toml&
RUSTFLAGS="--cfg noop_authorizer" cargo build --no-default-features
./target/debug/vss-server server/vss-server-config.toml&

- name: Hit endpoint to verify service is up
run: |
sleep 5

# Put request with store='storeId' and key=k1
hex=0A0773746F726549641A150A026B3110FFFFFFFFFFFFFFFFFF011A046B317631
curl --data-binary "$(echo "$hex" | xxd -r -p)" http://localhost:8080/vss/putObjects
curl -f --data-binary "$(echo "$hex" | xxd -r -p)" http://localhost:8080/vss/putObjects

# Get request with store='storeId' and key=k1
hex=0A0773746F7265496412026B31
curl --data-binary "$(echo "$hex" | xxd -r -p)" http://localhost:8080/vss/getObject
curl -f --data-binary "$(echo "$hex" | xxd -r -p)" http://localhost:8080/vss/getObject
4 changes: 2 additions & 2 deletions .github/workflows/ldk-node-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ jobs:
- name: Build and Deploy VSS Server
run: |
cd vss-server/rust
cargo build
cargo run --no-default-features server/vss-server-config.toml&
RUSTFLAGS="--cfg noop_authorizer" cargo build --no-default-features
./target/debug/vss-server server/vss-server-config.toml&
- name: Run LDK Node Integration tests
run: |
cd ldk-node
Expand Down
4 changes: 4 additions & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ lto = true

[profile.dev]
panic = "abort"

[workspace.lints.rust.unexpected_cfgs]
level = "forbid"
check-cfg = ['cfg(noop_authorizer)']
4 changes: 3 additions & 1 deletion rust/api/src/auth.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::error::VssError;
use async_trait::async_trait;
use std::collections::HashMap;
use std::string::ToString;

/// Response returned for [`Authorizer`] request if user is authenticated and authorized.
#[derive(Debug, Clone)]
Expand All @@ -21,10 +20,13 @@ pub trait Authorizer: Send + Sync {
}

/// A no-operation authorizer, which lets any user-request go through.
#[cfg(feature = "_test_utils")]
pub struct NoopAuthorizer {}

#[cfg(feature = "_test_utils")]
const UNAUTHENTICATED_USER: &str = "unauth-user";

#[cfg(feature = "_test_utils")]
#[async_trait]
impl Authorizer for NoopAuthorizer {
async fn verify(
Expand Down
1 change: 1 addition & 0 deletions rust/impls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ bytes = "1.4.0"
tokio = { version = "1.38.0", default-features = false, features = ["rt", "macros"] }
native-tls = { version = "0.2.14", default-features = false }
postgres-native-tls = { version = "0.5.2", default-features = false, features = ["runtime"] }
log = { version = "0.4.29", default-features = false }

[dev-dependencies]
tokio = { version = "1.38.0", default-features = false, features = ["rt-multi-thread", "macros"] }
Expand Down
9 changes: 6 additions & 3 deletions rust/impls/src/postgres_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use tokio::sync::Mutex;
use tokio_postgres::tls::{MakeTlsConnect, TlsConnect};
use tokio_postgres::{error, Client, NoTls, Socket, Transaction};

use log::{debug, error, info, warn};

pub use native_tls::Certificate;

pub(crate) struct VssDbRecord {
Expand Down Expand Up @@ -104,6 +106,7 @@ where

async fn ensure_connected(&self, client: &mut Client) -> Result<(), Error> {
if client.is_closed() || client.check_connection().await.is_err() {
debug!("Rotating connection to the postgres database");
let new_client =
make_db_connection(&self.endpoint, &self.db_name, self.tls.clone()).await?;
*client = new_client;
Expand Down Expand Up @@ -145,7 +148,7 @@ where
// Connection must be driven on a separate task, and will resolve when the client is dropped
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("Connection error: {}", e);
warn!("Connection error: {}", e);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this recoverable? error then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can recover using fn ensure_connected if this happens on one of our 10 long-lived connections to the database.

Copy link
Contributor Author

@tankyleo tankyleo Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let me know if you would rather use error here it is true that if this happens during startup we do not recover.

In case we fail during startup we do have the error!("Failed to start postgres backend"); messages at the error level.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think error would be preferable in this case here, too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I'm not quite fully onboard yet can you explain further ? Currently I don't want to add an error log after launching the main service loop for something we can recover from.

}
});
Ok(client)
Expand Down Expand Up @@ -173,7 +176,7 @@ where
client.execute(&stmt, &[]).await.map_err(|e| {
Error::new(ErrorKind::Other, format!("Failed to create database {}: {}", db_name, e))
})?;
println!("Created database {}", db_name);
info!("Created database {}", db_name);
}

Ok(())
Expand Down Expand Up @@ -291,7 +294,7 @@ where
panic!("We do not allow downgrades");
}

println!("Applying migration(s) {} through {}", migration_start, migrations.len() - 1);
info!("Applying migration(s) {} through {}", migration_start, migrations.len() - 1);

for (idx, &stmt) in (&migrations[migration_start..]).iter().enumerate() {
let _num_rows = tx.execute(stmt, &[]).await.map_err(|e| {
Expand Down
9 changes: 9 additions & 0 deletions rust/server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,12 @@ prost = { version = "0.11.6", default-features = false, features = ["std"] }
bytes = "1.4.0"
serde = { version = "1.0.203", default-features = false, features = ["derive"] }
toml = { version = "0.8.9", default-features = false, features = ["parse"] }
log = { version = "0.4.29", default-features = false, features = ["std"] }
chrono = { version = "0.4", default-features = false, features = ["clock"] }
rand = { version = "0.9.2", default-features = false }

[target.'cfg(noop_authorizer)'.dependencies]
api = { path = "../api", features = ["_test_utils"] }

[lints]
workspace = true
69 changes: 52 additions & 17 deletions rust/server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,18 @@ use tokio::signal::unix::SignalKind;
use hyper::server::conn::http1;
use hyper_util::rt::TokioIo;

use api::auth::{Authorizer, NoopAuthorizer};
use log::{error, info, warn};

use api::auth::Authorizer;
#[cfg(noop_authorizer)]
use api::auth::NoopAuthorizer;
use api::kv_store::KvStore;
#[cfg(feature = "jwt")]
use auth_impls::jwt::JWTAuthorizer;
#[cfg(feature = "sigs")]
use auth_impls::signature::SignatureValidatingAuthorizer;
use impls::postgres_store::{PostgresPlaintextBackend, PostgresTlsBackend};
use util::logger::ServerLogger;
use vss_service::VssService;

mod util;
Expand All @@ -38,19 +43,36 @@ fn main() {
std::process::exit(-1);
});

let logger = match ServerLogger::init(config.log_level, &config.log_file) {
Ok(logger) => logger,
Err(e) => {
eprintln!("Failed to initialize logger: {e}");
std::process::exit(-1);
},
};

let runtime = match tokio::runtime::Builder::new_multi_thread().enable_all().build() {
Ok(runtime) => Arc::new(runtime),
Err(e) => {
eprintln!("Failed to setup tokio runtime: {}", e);
error!("Failed to setup tokio runtime: {}", e);
std::process::exit(-1);
},
};

runtime.block_on(async {
// Register SIGHUP handler for log rotation
let mut sighup_stream = match tokio::signal::unix::signal(SignalKind::hangup()) {
Ok(stream) => stream,
Err(e) => {
error!("Failed to register SIGHUP handler: {e}");
std::process::exit(-1);
}
};

let mut sigterm_stream = match tokio::signal::unix::signal(SignalKind::terminate()) {
Ok(stream) => stream,
Err(e) => {
println!("Failed to register for SIGTERM stream: {}", e);
error!("Failed to register for SIGTERM stream: {}", e);
std::process::exit(-1);
},
};
Expand All @@ -61,11 +83,11 @@ fn main() {
if let Some(rsa_pem) = config.rsa_pem {
authorizer = match JWTAuthorizer::new(&rsa_pem).await {
Ok(auth) => {
println!("Configured JWT authorizer with RSA public key");
info!("Configured JWT authorizer with RSA public key");
Some(Arc::new(auth))
},
Err(e) => {
println!("Failed to configure JWT authorizer: {}", e);
error!("Failed to configure JWT authorizer: {}", e);
std::process::exit(-1);
},
};
Expand All @@ -74,17 +96,25 @@ fn main() {
#[cfg(feature = "sigs")]
{
if authorizer.is_none() {
println!("Configured signature-validating authorizer");
info!("Configured signature-validating authorizer");
authorizer = Some(Arc::new(SignatureValidatingAuthorizer));
}
}

#[cfg(noop_authorizer)]
let authorizer = if let Some(auth) = authorizer {
auth
} else {
println!("No authentication method configured, all storage with the same store id will be commingled.");
warn!("No authentication method configured, all storage with the same store id will be commingled.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do start to wonder if we should only expose this behind a testing feature or cfg flag?

Copy link
Contributor Author

@tankyleo tankyleo Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now only available via cargo run --no-default-features --features testing see below

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you expand why a feature is preferable to a cfg flag here? Features are meant to be user-faced, while cfg flag is really more development-oriented, and I'm leaning that NoopAuthorizer is rather the latter, i.e., should only ever be used by us / our unit tests, maybe. Everybody else should even be testing against whatever auth impl they run in production?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the context switched to the cfg flag below

Arc::new(NoopAuthorizer {})
};

#[cfg(not(noop_authorizer))]
let authorizer = authorizer.unwrap_or_else(|| {
error!("No authentication method configured, please configure either `JWTAuthorizer` or `SignatureValidatingAuthorizer`");
std::process::exit(-1);
});

let store: Arc<dyn KvStore> = if let Some(crt_pem) = config.tls_config {
let postgres_tls_backend = PostgresTlsBackend::new(
&config.postgresql_prefix,
Expand All @@ -94,10 +124,10 @@ fn main() {
)
.await
.unwrap_or_else(|e| {
println!("Failed to start postgres TLS backend: {}", e);
error!("Failed to start postgres TLS backend: {}", e);
std::process::exit(-1);
});
println!(
info!(
"Connected to PostgreSQL TLS backend with DSN: {}/{}",
config.postgresql_prefix, config.vss_db
);
Expand All @@ -110,21 +140,21 @@ fn main() {
)
.await
.unwrap_or_else(|e| {
println!("Failed to start postgres plaintext backend: {}", e);
error!("Failed to start postgres plaintext backend: {}", e);
std::process::exit(-1);
});
println!(
info!(
"Connected to PostgreSQL plaintext backend with DSN: {}/{}",
config.postgresql_prefix, config.vss_db
);
Arc::new(postgres_plaintext_backend)
};

let rest_svc_listener = TcpListener::bind(&config.bind_address).await.unwrap_or_else(|e| {
println!("Failed to bind listening port: {}", e);
error!("Failed to bind listening port: {}", e);
std::process::exit(-1);
});
println!("Listening for incoming connections on {}{}", config.bind_address, crate::vss_service::BASE_PATH_PREFIX);
info!("Listening for incoming connections on {}{}", config.bind_address, crate::vss_service::BASE_PATH_PREFIX);

loop {
tokio::select! {
Expand All @@ -135,19 +165,24 @@ fn main() {
let vss_service = VssService::new(Arc::clone(&store), Arc::clone(&authorizer));
runtime.spawn(async move {
if let Err(err) = http1::Builder::new().serve_connection(io_stream, vss_service).await {
eprintln!("Failed to serve connection: {}", err);
warn!("Failed to serve connection: {}", err);
}
});
},
Err(e) => eprintln!("Failed to accept connection: {}", e),
Err(e) => warn!("Failed to accept connection: {}", e),
}
}
_ = tokio::signal::ctrl_c() => {
println!("Received CTRL-C, shutting down..");
info!("Received CTRL-C, shutting down..");
break;
}
_ = sighup_stream.recv() => {
if let Err(e) = logger.reopen() {
error!("Failed to reopen log file on SIGHUP: {e}");
}
}
_ = sigterm_stream.recv() => {
println!("Received SIGTERM, shutting down..");
info!("Received SIGTERM, shutting down..");
break;
}
}
Expand Down
Loading