Skip to content
Merged
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
1 change: 1 addition & 0 deletions crates/puzzled-server/src/bootstrap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub(crate) mod identity;
pub(crate) mod lifecycle;
mod observability_test;
mod router;
mod signals_induce;
mod state;

pub use lifecycle::{drain_tasks, http_port, request_shutdown, shutdown_signal};
Expand Down
9 changes: 9 additions & 0 deletions crates/puzzled-server/src/bootstrap/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ pub fn router(state: AppState) -> Router {
.route(DAILY_REMINDERS_PATH, post(daily_reminders_tick))
.route(TRYIT_CONVERSIONS_PATH, post(tryit_conversions_tick))
.route("/observability/test", post(observability_test))
.route(
"/signals:induce",
post(super::signals_induce::signals_induce),
)
.with_state(state)
.fallback_service(connect.into_axum_service())
.layer(axum::middleware::from_fn_with_state(
Expand All @@ -70,4 +74,9 @@ pub fn router(state: AppState) -> Router {
.layer(axum::middleware::from_fn(
crate::observability::capture_server_errors,
))
// Quality signals (owner standards/quality-signals.md): a 5xx is one
// `puzzled.issue.turn_failed.<route>` line, a user-mutating RPC one
// `puzzled.user.write.ok|failed` journey line. Outside the capturer so
// it sees every response, including a captured error's.
.layer(axum::middleware::from_fn(crate::shared::signals::observe))
}
60 changes: 60 additions & 0 deletions crates/puzzled-server/src/bootstrap/signals_induce.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//! `POST /signals:induce`: the production check for quality signals
//! (owner standards/quality-signals.md "How it is judged").
//!
//! With `Authorization: Bearer <SYLPHX_API_KEY>` it reports one induced issue
//! and answers 503, so the readback is deterministic: call it once and the
//! desk alert intake files one item; call it again within the hour and the
//! same fingerprint is a note on that item. It changes no data.
//!
//! Any other caller gets 404. The key is the same one `/observability/test`
//! uses (the environment's Access key, minted and injected by the platform).

use axum::body::Bytes;
use axum::http::{HeaderMap, StatusCode};
use axum::response::{IntoResponse, Response};

use super::observability_test::{authorized, nonce};
use crate::shared::signals::IssueSubject;

/// The fingerprint the induce route reports for one request body: the nonce
/// when it has one, else a fixed name. The nonce keeps calls distinguishable
/// (a repeated readback is a note on the same item) without free text.
pub(crate) fn subject(body: &[u8]) -> String {
match nonce(body) {
s if s.is_empty() => "induce".to_owned(),
s => s,
}
}

pub(crate) async fn signals_induce(headers: HeaderMap, body: Bytes) -> Response {
let key = std::env::var("SYLPHX_API_KEY").ok();
let header = headers
.get(axum::http::header::AUTHORIZATION)
.and_then(|v| v.to_str().ok());
if !authorized(header, key.as_deref()) {
return StatusCode::NOT_FOUND.into_response();
}
let mut response = StatusCode::SERVICE_UNAVAILABLE.into_response();
response
.extensions_mut()
.insert(IssueSubject(subject(&body)));
response
}

#[cfg(test)]
mod tests {
use super::*;
use crate::shared::signals::{self, Kind};

#[test]
fn the_subject_comes_from_the_nonce_or_a_fixed_name() {
assert_eq!(subject(br#"{"nonce":"qa-1"}"#), "qa-1");
assert_eq!(subject(b"{}"), "induce");
// The nonce already keeps word characters only, so the fingerprint
// stays a valid event name whatever the body held.
assert_eq!(
signals::fingerprint(Kind::TurnFailed, &subject(br#"{"nonce":"qa<pad>"}"#)),
"puzzled.issue.turn_failed.qapad"
);
}
}
1 change: 1 addition & 0 deletions crates/puzzled-server/src/shared/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pub mod db_config;

pub mod dest_http;
pub mod signals;
pub mod tick_receipt;

pub mod public_origin;
Loading
Loading