From 96b62f1018641b3dc28cc189d314a1bff475b751 Mon Sep 17 00:00:00 2001 From: Owen Jacobson Date: Sat, 30 Aug 2025 02:59:51 -0400 Subject: Allow administrators to rotate VAPID keys immediately if needed. In spite of my design preference away from CLI tools, this is a CLI tool: `pilcrow --database-url rotate-vapid-key`. This is something we can implement here and now, which does not require us to confront the long-avoided issue of how to handle the idea that some users are allowed to make server-operational changes and some aren't, by delegating the problem back to the OS. The implementation is a little half-baked to make it easier to rip out later. I would ordinarily prefer to push both `serve` (the default verb, not actually named in this change) and `rotate-vapid-key` into their own, separate CLI submodules, with their own argument structs, but that change is much more intrusive and would make this effectively permanent. This can be yanked out in a few minutes by deleting a few lines of `cli.rs` and inlining the `serve` function. Nonetheless, nothing is as permanent as a temporary solution, so I've written at least some bare-minimum operations documentation on how to use this and what it does. --- src/vapid/app.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'src/vapid/app.rs') diff --git a/src/vapid/app.rs b/src/vapid/app.rs index 8886c9f..ddb1f4d 100644 --- a/src/vapid/app.rs +++ b/src/vapid/app.rs @@ -1,4 +1,4 @@ -use chrono::TimeDelta; +use chrono::{TimeDelta, Utc}; use sqlx::SqlitePool; use super::{History, repo, repo::Provider as _}; @@ -18,6 +18,21 @@ impl<'a> Vapid<'a> { Self { db, events } } + pub async fn rotate_key(&self, rotate_at: &DateTime) -> Result<(), sqlx::Error> { + let mut tx = self.db.begin().await?; + // This is called from a separate CLI utility (see `cli.rs`), and we _can't_ deliver events + // to active clients from another process, so don't do anything that would require us to + // send events, like generating a new key. + // + // Instead, the server's next `refresh_key` call will generate a key and notify clients + // of the change. All we have to do is remove the existing key, so that the server can know + // to do so. + tx.vapid().clear().await?; + tx.commit().await?; + + Ok(()) + } + pub async fn refresh_key(&self, ensure_at: &DateTime) -> Result<(), Error> { let mut tx = self.db.begin().await?; let key = tx.vapid().current().await.optional()?; -- cgit v1.2.3