Server: no built-in public host, no trusted proxy by default; OpenAPI server variable host (D-123)

KVITT_PUBLIC_URL is required: every command refuses to start without it (compose
always sets it). KVITT_TRUSTED_PROXY defaults to none, which the rate limiter never
resolves. contracts/openapi.yaml and the utoipa document: servers[].url
https://{host}:8443 with variable host (default kvitt.example.invalid). E-7 and E-12
examples on kvitt.example.invalid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
2026-10-19 20:11:33 +02:00
parent 4b1ae0c473
commit c9abd24c77
13 changed files with 168 additions and 39 deletions
+21 -4
View File
@@ -23,7 +23,7 @@ use utoipa::openapi::path::{Operation, ParameterBuilder, ParameterIn};
use utoipa::openapi::response::{Response, ResponseBuilder};
use utoipa::openapi::schema::{ObjectBuilder, Schema, SchemaType, Type};
use utoipa::openapi::security::{Http, HttpAuthScheme, SecurityRequirement, SecurityScheme};
use utoipa::openapi::server::Server;
use utoipa::openapi::server::{ServerBuilder, ServerVariableBuilder};
use utoipa::openapi::{Content, Ref, RefOr, Required};
use utoipa::{Modify, OpenApi};
@@ -35,8 +35,15 @@ const INFO_DESCRIPTION: &str = "Shared-expense server for a two-person household
timestamps are RFC 3339 UTC with millisecond precision (`2026-10-17T06:00:00.000Z`), ids are lowercase UUIDv7 \
strings. Common rules: SPEC §14.3.1.";
/// The production base URL (SPEC §7).
pub const PUBLIC_SERVER_URL: &str = "https://icehack.asuscomm.com:8443";
/// The production base URL (SPEC §7, §14.3): the host is the server variable [`HOST_VARIABLE`], because the Kvitt
/// host name is owner-chosen (SPEC §10.5).
pub const PUBLIC_SERVER_URL: &str = "https://{host}:8443";
/// The server variable in [`PUBLIC_SERVER_URL`].
pub const HOST_VARIABLE: &str = "host";
/// The default of [`HOST_VARIABLE`]: a reserved name that never resolves (RFC 6761), not a real host.
pub const HOST_VARIABLE_DEFAULT: &str = "kvitt.example.invalid";
/// The description of [`HOST_VARIABLE`].
const HOST_VARIABLE_DESCRIPTION: &str = "the owner-chosen Kvitt host name, SPEC §10.5";
const JSON: &str = api::JSON_CONTENT_TYPE;
const SERVER_VERSION_HEADER: &str = "X-Kvitt-Server-Version";
@@ -191,7 +198,17 @@ impl Modify for CommonRules {
openapi.info.description = Some(INFO_DESCRIPTION.to_owned());
openapi.info.license = None;
openapi.info.contact = None;
openapi.servers = Some(vec![Server::new(PUBLIC_SERVER_URL)]);
openapi.servers = Some(vec![
ServerBuilder::new()
.url(PUBLIC_SERVER_URL)
.parameter(
HOST_VARIABLE,
ServerVariableBuilder::new()
.default_value(HOST_VARIABLE_DEFAULT)
.description(Some(HOST_VARIABLE_DESCRIPTION)),
)
.build(),
]);
openapi.security = Some(vec![SecurityRequirement::new(
"device",
Vec::<String>::new(),