rust/api/src/main.rs serves with axum::serve / axum_server::bind_rustls and has no graceful-shutdown handler, so systemctl restart gkapi kills in-flight requests immediately.
For /sign-certificate this can strand a paid donation. sign_certificate (rust/api/src/handle_sign_cert.rs) sets the PaymentIntent's certificate_signed metadata in Stripe, then signs, then responds. If the process is killed after the mark and before the response reaches the browser:
- the donor never receives the blind signature,
- the PaymentIntent stays marked, so a reload gets 409 "already signed",
- nothing clears the mark, because
release_certificate_mark only runs when signing returns an error.
The window is small (a Stripe round trip plus an RSA blind signature), but every deploy and every restart opens it. #192 adds a daily timer that doesn't restart gkapi, so it doesn't widen this.
Fix options: add graceful shutdown (with_graceful_shutdown on SIGTERM, plus a TimeoutStopSec that covers a Stripe round trip); and/or make an interrupted signing recoverable, for example by storing the blind signature on the PaymentIntent so a retry can return it instead of 409.
Found while reviewing #192.
[AI-assisted - Claude]
rust/api/src/main.rsserves withaxum::serve/axum_server::bind_rustlsand has no graceful-shutdown handler, sosystemctl restart gkapikills in-flight requests immediately.For
/sign-certificatethis can strand a paid donation.sign_certificate(rust/api/src/handle_sign_cert.rs) sets the PaymentIntent'scertificate_signedmetadata in Stripe, then signs, then responds. If the process is killed after the mark and before the response reaches the browser:release_certificate_markonly runs when signing returns an error.The window is small (a Stripe round trip plus an RSA blind signature), but every deploy and every restart opens it. #192 adds a daily timer that doesn't restart gkapi, so it doesn't widen this.
Fix options: add graceful shutdown (
with_graceful_shutdownon SIGTERM, plus aTimeoutStopSecthat covers a Stripe round trip); and/or make an interrupted signing recoverable, for example by storing the blind signature on the PaymentIntent so a retry can return it instead of 409.Found while reviewing #192.
[AI-assisted - Claude]