Skip to main content

cratefield_core/
problems.rs

1//! The registry of every problem slug the harness can emit (architecture
2//! section 6): stable `type` URIs, statuses and descriptions. Modules add
3//! their own slugs as `const ProblemDef`s in the same shape.
4
5use axum::http::StatusCode;
6
7/// Definition of one problem slug.
8#[derive(Debug, Clone, Copy)]
9pub struct ProblemDef {
10    /// Last path segment of the `type` URI.
11    pub slug: &'static str,
12    pub status: StatusCode,
13    /// Short, stable human title (RFC 9457 `title`).
14    pub title: &'static str,
15    /// One-line description for reviewers; not part of the response.
16    pub description: &'static str,
17}
18
19/// Every slug core emits. Keep sorted by status.
20pub struct Slugs {
21    /// 400: a request body/query did not deserialize or failed validation.
22    pub validation_failed: ProblemDef,
23    /// 400: a captcha token was missing or rejected.
24    pub captcha_failed: ProblemDef,
25    /// 400: a signed link/token is malformed, tampered or expired.
26    pub invalid_token: ProblemDef,
27    /// 400: the request named a resource outside the configured set
28    /// (e.g. an unknown waitlist product).
29    pub unknown_product: ProblemDef,
30    /// 401: admin endpoints are disabled (`ADMIN_TOKEN` unset) or the
31    /// request carried no bearer token.
32    pub admin_unauthorized: ProblemDef,
33    /// 403: the presented admin token is wrong.
34    pub admin_forbidden: ProblemDef,
35    /// 413: the request body exceeded the 64 KiB `/v1/*` limit.
36    pub request_too_large: ProblemDef,
37    /// 429: the rate limit for this IP or address was exceeded.
38    pub rate_limited: ProblemDef,
39    /// 404: no route matched.
40    pub not_found: ProblemDef,
41    /// 500: unhandled error; body carries no internals.
42    pub internal: ProblemDef,
43    /// 503: the mailer is not configured (no API key / unverified sending
44    /// domain); forms should degrade to a direct address.
45    pub mail_not_configured: ProblemDef,
46    /// 503: readiness probe failed (`/__ready`): database missing, slow or
47    /// erroring.
48    pub not_ready: ProblemDef,
49    /// 503: a sidecar-mounted module could not be reached (ADR 0009). Only
50    /// that prefix fails; every in-process module keeps serving.
51    pub sidecar_unavailable: ProblemDef,
52    /// 503: a sidecar answered with a different `HARNESS_API` than this
53    /// harness speaks, so its responses cannot be trusted.
54    pub sidecar_contract_mismatch: ProblemDef,
55}
56
57pub const SLUGS: Slugs = Slugs {
58    validation_failed: ProblemDef {
59        slug: "validation-failed",
60        status: StatusCode::BAD_REQUEST,
61        title: "Request validation failed",
62        description: "The request body or query did not deserialize into a valid request.",
63    },
64    captcha_failed: ProblemDef {
65        slug: "captcha-failed",
66        status: StatusCode::BAD_REQUEST,
67        title: "Captcha verification failed",
68        description: "The captcha token was missing or rejected; retry the challenge.",
69    },
70    invalid_token: ProblemDef {
71        slug: "invalid-token",
72        status: StatusCode::BAD_REQUEST,
73        title: "Invalid or expired token",
74        description: "A signed link or token is malformed, tampered with, or expired.",
75    },
76    unknown_product: ProblemDef {
77        slug: "unknown-product",
78        status: StatusCode::BAD_REQUEST,
79        title: "Unknown product",
80        description: "The named product is not on this waitlist.",
81    },
82    admin_unauthorized: ProblemDef {
83        slug: "admin-unauthorized",
84        status: StatusCode::UNAUTHORIZED,
85        title: "Admin access unauthorized",
86        description: "Admin endpoints are disabled or the request has no bearer token.",
87    },
88    admin_forbidden: ProblemDef {
89        slug: "admin-forbidden",
90        status: StatusCode::FORBIDDEN,
91        title: "Admin token rejected",
92        description: "The presented admin token is wrong.",
93    },
94    request_too_large: ProblemDef {
95        slug: "request-too-large",
96        status: StatusCode::PAYLOAD_TOO_LARGE,
97        title: "Request body too large",
98        description: "The request body exceeded the 64 KiB limit for /v1 endpoints.",
99    },
100    rate_limited: ProblemDef {
101        slug: "rate-limited",
102        status: StatusCode::TOO_MANY_REQUESTS,
103        title: "Rate limit exceeded",
104        description: "Too many requests from this IP or address; retry after the pause.",
105    },
106    not_found: ProblemDef {
107        slug: "not-found",
108        status: StatusCode::NOT_FOUND,
109        title: "Not found",
110        description: "No route matched the request.",
111    },
112    internal: ProblemDef {
113        slug: "internal",
114        status: StatusCode::INTERNAL_SERVER_ERROR,
115        title: "Internal error",
116        description: "Unhandled error; no internals are exposed in the body.",
117    },
118    mail_not_configured: ProblemDef {
119        slug: "mail-not-configured",
120        status: StatusCode::SERVICE_UNAVAILABLE,
121        title: "Mail is not configured",
122        description: "No sending domain is verified; use the direct address shown by the form.",
123    },
124    not_ready: ProblemDef {
125        slug: "not-ready",
126        status: StatusCode::SERVICE_UNAVAILABLE,
127        title: "Service not ready",
128        description: "Readiness probe failed: the database is missing, erroring or too slow.",
129    },
130    sidecar_unavailable: ProblemDef {
131        slug: "sidecar-unavailable",
132        status: StatusCode::SERVICE_UNAVAILABLE,
133        title: "Sidecar module unavailable",
134        description: "A sidecar-mounted module could not be reached; other modules are unaffected.",
135    },
136    sidecar_contract_mismatch: ProblemDef {
137        slug: "sidecar-contract-mismatch",
138        status: StatusCode::SERVICE_UNAVAILABLE,
139        title: "Sidecar contract mismatch",
140        description: "A sidecar answers a different HARNESS_API than this harness speaks.",
141    },
142};
143
144/// Every core slug definition, for tests and docs.
145pub fn registry() -> Vec<&'static ProblemDef> {
146    vec![
147        &SLUGS.validation_failed,
148        &SLUGS.captcha_failed,
149        &SLUGS.invalid_token,
150        &SLUGS.unknown_product,
151        &SLUGS.admin_unauthorized,
152        &SLUGS.admin_forbidden,
153        &SLUGS.request_too_large,
154        &SLUGS.rate_limited,
155        &SLUGS.not_found,
156        &SLUGS.internal,
157        &SLUGS.mail_not_configured,
158        &SLUGS.not_ready,
159        &SLUGS.sidecar_unavailable,
160        &SLUGS.sidecar_contract_mismatch,
161    ]
162}