Skip to content

Commit 3ea01fa

Browse files
avi-starkwareclaude
andcommitted
starknet_transaction_prover: fold the startup banner into the resolved-config log
The banner and `config_resolved` were adjacent startup `info!`s sharing six fields. Move version and git SHA into `log_startup_summary` and delete the banner, leaving one startup event. Add the test this PR was missing: its whole promise is that the resolved config is logged with secrets redacted, and nothing exercised that. The test feeds credential-bearing URLs and asserts the hosts are logged while the password and API-key path segment are not. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent bdfe72b commit 3ea01fa

4 files changed

Lines changed: 53 additions & 24 deletions

File tree

crates/starknet_transaction_prover/README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,8 @@ docker run -e LOG_FORMAT=json ... <IMAGE>
256256
and `span` keys. The `text` format may include ANSI colour codes. `json` never does, so use
257257
`--log-format json` in containers and in production. A URL can carry credentials in its userinfo
258258
component, so the service redacts every logged URL down to `scheme://host[:port]`. That covers
259-
`rpc_node_url` in the startup logs and in its CLI-override message, and `blocking_check_url` in its
260-
CLI-override message. The startup logs say only whether the blocking check is enabled, never its
261-
URL.
259+
`rpc_node_url` and `blocking_check_url` in the startup `config_resolved` log, and each of them
260+
again in its own CLI-override message.
262261

263262
## Compression
264263

@@ -371,6 +370,21 @@ there was never a status.
371370
The logging layer never reads request bodies. Transaction calldata is private user data and stays
372371
out of the logs.
373372

373+
### Startup logs
374+
375+
Startup emits two `info` logs, plus an `OHTTP envelope encryption enabled` line between them when
376+
OHTTP is enabled. Before binding, an `event="config_resolved"` log records the build identity
377+
(version, git SHA) together with the resolved service and prover settings, which are the merge of
378+
the config file, environment variables, and CLI flags. Read that line to see the values the process
379+
runs with, instead of working the precedence rules out by hand. The `contract_class_manager_config`
380+
and `runner_config` sub-configs are not included. After the listener is bound, a second log records
381+
what the server came up on: local address, scheme (`http` or `https`), `max_concurrent_requests`,
382+
`max_connections`, `ohttp_enabled`, and the CORS mode and allowed origins.
383+
384+
Both lines are redacted. The service logs the RPC and blocking-check URLs host-only (see
385+
[Logging](#logging)), never logs TLS certificate and key paths, and puts no transaction-scoped data
386+
in either line.
387+
374388
### Shutdown
375389

376390
`SIGTERM` and `SIGINT` start a graceful shutdown. The server stops accepting new requests and lets

crates/starknet_transaction_prover/src/main.rs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ async fn main() -> anyhow::Result<()> {
2323
};
2424
use starknet_transaction_prover::server::cors::{build_cors_layer, cors_mode};
2525
use starknet_transaction_prover::server::health::HealthLayer;
26-
use starknet_transaction_prover::server::log_redact::redact_url_host;
2726
use starknet_transaction_prover::server::metrics::{install_exporter, spawn_upkeep};
2827
use starknet_transaction_prover::server::panic::install_panic_hook;
2928
use starknet_transaction_prover::server::rpc_api::ProvingRpcServer;
@@ -70,20 +69,6 @@ async fn main() -> anyhow::Result<()> {
7069
let metrics_layer = MetricsLayer::new(prometheus_handle.clone());
7170
spawn_upkeep(prometheus_handle);
7271

73-
// Startup banner — version + chain id + redacted RPC host only. No URLs
74-
// with userinfo, no fee token address, no TLS paths, no tx data.
75-
info!(
76-
version = env!("CARGO_PKG_VERSION"),
77-
git_sha = option_env!("GIT_SHA").unwrap_or("unknown"),
78-
chain_id = %config.prover_config.chain_id,
79-
rpc_node_host = %redact_url_host(&config.prover_config.rpc_node_url),
80-
validate_zero_fee_fields = config.prover_config.validate_zero_fee_fields,
81-
blocking_check_enabled = config.prover_config.blocking_check_url.is_some(),
82-
blocking_check_fail_open = config.prover_config.blocking_check_fail_open,
83-
ohttp_enabled = config.ohttp_enabled,
84-
"Starting Starknet transaction prover."
85-
);
86-
8772
// Build and start the JSON-RPC server. The request path and the health probe share one
8873
// saturation monitor. The request path records rejects and worker-slot progress, and the
8974
// probe reads it.

crates/starknet_transaction_prover/src/server/config.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -503,22 +503,27 @@ impl ServiceConfig {
503503
})
504504
}
505505

506-
/// Logs the fully resolved configuration (file + env + CLI merge) once at startup.
506+
/// Logs the resolved service and prover settings (file + env + CLI merge) once at startup.
507+
/// The `contract_class_manager_config` and `runner_config` sub-configs are not included.
507508
///
508-
/// Secrets are redacted: RPC and blocking-check URLs are logged host-only; TLS key/cert
509-
/// paths are never logged.
509+
/// It logs the RPC and blocking-check URLs host-only, and never logs the TLS key and
510+
/// certificate paths.
510511
pub fn log_startup_summary(&self) {
511-
let transport = match self.transport {
512+
let version = env!("CARGO_PKG_VERSION");
513+
let git_sha = option_env!("GIT_SHA").unwrap_or("unknown");
514+
let transport_label = match self.transport {
512515
TransportMode::Http => "http",
513516
TransportMode::Https { .. } => "https",
514517
};
515518
let blocking_check_host =
516519
self.prover_config.blocking_check_url.as_deref().map(redact_url_host);
517520
info!(
518521
event = "config_resolved",
522+
version,
523+
git_sha,
519524
ip = %self.ip,
520525
port = self.port,
521-
transport,
526+
transport = transport_label,
522527
max_concurrent_requests = self.max_concurrent_requests,
523528
max_queued_requests = self.max_queued_requests,
524529
queue_wait_timeout_millis = self.queue_wait_timeout_millis,
@@ -536,7 +541,7 @@ impl ServiceConfig {
536541
blocking_check_host = ?blocking_check_host,
537542
blocking_check_timeout_millis = self.prover_config.blocking_check_timeout_millis,
538543
blocking_check_fail_open = self.prover_config.blocking_check_fail_open,
539-
"Resolved service configuration."
544+
"Starting Starknet transaction prover."
540545
);
541546
}
542547
}

crates/starknet_transaction_prover/src/server/config_test.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::sync::{Mutex, MutexGuard};
55
use clap::Parser;
66
use rstest::rstest;
77
use tempfile::NamedTempFile;
8+
use tracing_test::traced_test;
89

910
use crate::errors::ConfigError;
1011
use crate::server::config::{CliArgs, LogFormat, ServiceConfig, TransportMode};
@@ -344,3 +345,27 @@ fn env_var_sets_tls_key_file() {
344345

345346
assert_eq!(args.tls_key_file, Some(PathBuf::from("/etc/ssl/key.pem")));
346347
}
348+
349+
/// The startup summary is the only place that writes the resolved service and
350+
/// prover settings to the log stream, so its redaction is what keeps credentials
351+
/// out of the operator's log aggregator.
352+
#[test]
353+
#[traced_test]
354+
fn startup_summary_logs_hosts_and_redacts_url_credentials() {
355+
let mut args = base_args();
356+
args.rpc_url = Some("https://user:sekret@rpc.example.com:8545/v2/api-key".to_string());
357+
args.blocking_check_url = Some("https://ops:hunter2@screen.example.com/check".to_string());
358+
let config = ServiceConfig::from_args(args).unwrap();
359+
360+
config.log_startup_summary();
361+
362+
assert!(logs_contain("event=\"config_resolved\""), "must tag the event for log-based checks");
363+
assert!(logs_contain("rpc.example.com"), "the host is operational context, so it is logged");
364+
assert!(
365+
logs_contain("screen.example.com"),
366+
"the blocking-check host is operational context, safe to log"
367+
);
368+
for secret in ["sekret", "hunter2", "api-key"] {
369+
assert!(!logs_contain(secret), "`{secret}` from a URL must never reach the log stream");
370+
}
371+
}

0 commit comments

Comments
 (0)