@@ -104,7 +104,7 @@ pub async fn run_server(credentials: Credentials) -> Result<()> {
104104 db : std:: sync:: Mutex :: new ( db_conn) ,
105105 tls_active,
106106 behind_proxy,
107- rate_limiter : std:: sync:: Mutex :: new ( HashMap :: new ( ) ) ,
107+ rate_limiter : std:: sync:: Mutex :: new ( HashMap :: with_capacity ( 256 ) ) ,
108108 } ) ;
109109
110110 // Background task: clean expired magic links, old deposit nonces, and stale rate limiter entries
@@ -116,9 +116,13 @@ pub async fn run_server(credentials: Credentials) -> Result<()> {
116116 let _ = tokio:: task:: spawn_blocking ( move || {
117117 if let Ok ( conn) = db_ref. db . lock ( ) {
118118 let now = chrono:: Utc :: now ( ) . timestamp ( ) ;
119- let _ = conn. execute ( "DELETE FROM magic_links WHERE expires_at <= ?1" , [ now] ) ;
119+ if let Err ( e) = conn. execute ( "DELETE FROM magic_links WHERE expires_at <= ?1" , [ now] ) {
120+ tracing:: warn!( "Failed to clean expired magic links: {e}" ) ;
121+ }
120122 let cutoff = now - 7 * 86400 ;
121- let _ = conn. execute ( "DELETE FROM used_deposits WHERE used_at < ?1" , [ cutoff] ) ;
123+ if let Err ( e) = conn. execute ( "DELETE FROM used_deposits WHERE used_at < ?1" , [ cutoff] ) {
124+ tracing:: warn!( "Failed to clean old deposit nonces: {e}" ) ;
125+ }
122126 }
123127 // Evict stale rate limiter entries
124128 if let Ok ( mut map) = db_ref. rate_limiter . lock ( ) {
@@ -129,14 +133,20 @@ pub async fn run_server(credentials: Credentials) -> Result<()> {
129133 }
130134 } ) ;
131135
136+ // CORS only on the public agent.json endpoint; deposit and magic link
137+ // endpoints are called by servers, not browsers, and don't need CORS.
132138 let cors = CorsLayer :: new ( )
133139 . allow_origin ( Any )
134140 . allow_methods ( Any )
135141 . allow_headers ( Any ) ;
136142
143+ let public_routes = Router :: new ( )
144+ . route ( "/.well-known/agent.json" , get ( serve_agent_json) )
145+ . layer ( cors) ;
146+
137147 let app = Router :: new ( )
138148 . route ( "/" , get ( root_redirect) )
139- . route ( "/.well-known/agent.json" , get ( serve_agent_json ) )
149+ . merge ( public_routes )
140150 . route ( "/d/{token}" , post ( handle_deposit) )
141151 . route ( "/m/{code}" , get ( handle_magic_link) )
142152 . fallback ( handle_404)
@@ -145,7 +155,6 @@ pub async fn run_server(credentials: Credentials) -> Result<()> {
145155 state. clone ( ) ,
146156 security_headers,
147157 ) )
148- . layer ( cors)
149158 . with_state ( state) ;
150159
151160 let pid_path = config:: pid_path ( ) ?;
@@ -245,40 +254,39 @@ async fn handle_deposit(
245254 // DB operations in spawn_blocking.
246255 // Access vault_key via the Arc<AppState> reference inside the closure
247256 // to avoid copying the key out of its Zeroizing wrapper.
248- let label = payload. label . clone ( ) ;
249257 let state_clone = state. clone ( ) ;
250258 let body_clone = body;
251259 let deposit_result = tokio:: task:: spawn_blocking ( move || {
252260 let conn = state_clone. db . lock ( )
253261 . map_err ( |e| anyhow:: anyhow!( "DB mutex poisoned: {e}" ) ) ?;
254262 crate :: deposit:: claim_nonce_with_conn ( & payload, & conn) ?;
255263 crate :: vault:: vault_set_with_conn ( & conn, & payload. label , & body_clone, state_clone. vault_key ( ) ) ?;
256- crate :: deposit:: log_deposit ( & conn, & payload. label , & source_ip, & user_agent)
264+ crate :: deposit:: log_deposit ( & conn, & payload. label , & source_ip, & user_agent) ?;
265+ Ok :: < _ , anyhow:: Error > ( payload. label )
257266 } ) . await ;
258267
259268 match deposit_result {
260- Ok ( Ok ( ( ) ) ) => { } ,
269+ Ok ( Ok ( label) ) => {
270+ info ! ( "Deposit received: '{label}'" ) ;
271+ let resp = DepositResponse { status : "deposited" , label } ;
272+ match serde_json:: to_string ( & resp) {
273+ Ok ( json) => ( StatusCode :: OK , [ ( header:: CONTENT_TYPE , "application/json" ) ] , json) . into_response ( ) ,
274+ Err ( _) => StatusCode :: INTERNAL_SERVER_ERROR . into_response ( ) ,
275+ }
276+ }
261277 Ok ( Err ( e) ) => {
262278 let err_msg = format ! ( "{e}" ) ;
263279 if err_msg. contains ( "replay" ) || err_msg. contains ( "Nonce already used" ) {
264280 return StatusCode :: NOT_FOUND . into_response ( ) ;
265281 }
266282 tracing:: error!( "Deposit failed: {}" , e) ;
267- return StatusCode :: INTERNAL_SERVER_ERROR . into_response ( ) ;
283+ StatusCode :: INTERNAL_SERVER_ERROR . into_response ( )
268284 }
269285 Err ( e) => {
270286 tracing:: error!( "Deposit task panicked: {}" , e) ;
271- return StatusCode :: INTERNAL_SERVER_ERROR . into_response ( ) ;
287+ StatusCode :: INTERNAL_SERVER_ERROR . into_response ( )
272288 }
273289 }
274-
275- info ! ( "Deposit received: '{label}'" ) ;
276-
277- let resp = DepositResponse { status : "deposited" , label } ;
278- match serde_json:: to_string ( & resp) {
279- Ok ( json) => ( StatusCode :: OK , [ ( header:: CONTENT_TYPE , "application/json" ) ] , json) . into_response ( ) ,
280- Err ( _) => StatusCode :: INTERNAL_SERVER_ERROR . into_response ( ) ,
281- }
282290}
283291
284292async fn handle_magic_link (
@@ -342,6 +350,10 @@ async fn security_headers(
342350 header:: REFERRER_POLICY ,
343351 HeaderValue :: from_static ( "no-referrer" ) ,
344352 ) ;
353+ headers. insert (
354+ header:: CONTENT_SECURITY_POLICY ,
355+ HeaderValue :: from_static ( "default-src 'none'" ) ,
356+ ) ;
345357 if state. tls_active {
346358 headers. insert (
347359 header:: STRICT_TRANSPORT_SECURITY ,
0 commit comments