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    /// 401: a request reached a sidecar-guarded route (`/v1/*` or
36    /// `/__surface` on a sidecar that requires it) without a valid gateway
37    /// token from the trusted host (issue #131).
38    pub sidecar_unauthorized: ProblemDef,
39    /// 503: the deployment declares production but cannot satisfy the
40    /// abuse controls its own routes declare, so the guarded routes are
41    /// refused rather than served unprotected (issue #143).
42    pub not_production_ready: ProblemDef,
43    /// 413: the request body exceeded the 64 KiB `/v1/*` limit.
44    pub request_too_large: ProblemDef,
45    /// 429: the rate limit for this IP or address was exceeded.
46    pub rate_limited: ProblemDef,
47    /// 404: no route matched.
48    pub not_found: ProblemDef,
49    /// 500: unhandled error; body carries no internals.
50    pub internal: ProblemDef,
51    /// 503: the mailer is not configured (no API key / unverified sending
52    /// domain); forms should degrade to a direct address.
53    pub mail_not_configured: ProblemDef,
54    /// 503: readiness probe failed (`/__ready`): database missing, slow or
55    /// erroring.
56    pub not_ready: ProblemDef,
57    /// 404: the request's host resolves to no tenant in the registry. A
58    /// 404 and not a 403: which hosts are tenants is not a secret worth
59    /// a distinguishable answer, and "no such tenant" is the truth
60    /// (RECONCILIATION.md §6, TENANT-ROUTING.md §3).
61    pub unknown_tenant: ProblemDef,
62    /// 503: a sidecar-mounted module could not be reached (ADR 0009). Only
63    /// that prefix fails; every in-process module keeps serving.
64    pub sidecar_unavailable: ProblemDef,
65    /// 503: a sidecar answered with a different `HARNESS_API` than this
66    /// harness speaks, so its responses cannot be trusted.
67    pub sidecar_contract_mismatch: ProblemDef,
68    /// 503: the request's tenant is in the registry but `degraded` — its
69    /// schema is behind or its database is unreachable, so serving it
70    /// would read a shape the code no longer expects
71    /// (RECONCILIATION.md §6, TENANT-ROUTING.md §3).
72    pub tenant_degraded: ProblemDef,
73}
74
75pub const SLUGS: Slugs = Slugs {
76    validation_failed: ProblemDef {
77        slug: "validation-failed",
78        status: StatusCode::BAD_REQUEST,
79        title: "Request validation failed",
80        description: "The request body or query did not deserialize into a valid request.",
81    },
82    captcha_failed: ProblemDef {
83        slug: "captcha-failed",
84        status: StatusCode::BAD_REQUEST,
85        title: "Captcha verification failed",
86        description: "The captcha token was missing or rejected; retry the challenge.",
87    },
88    invalid_token: ProblemDef {
89        slug: "invalid-token",
90        status: StatusCode::BAD_REQUEST,
91        title: "Invalid or expired token",
92        description: "A signed link or token is malformed, tampered with, or expired.",
93    },
94    unknown_product: ProblemDef {
95        slug: "unknown-product",
96        status: StatusCode::BAD_REQUEST,
97        title: "Unknown product",
98        description: "The named product is not on this waitlist.",
99    },
100    admin_unauthorized: ProblemDef {
101        slug: "admin-unauthorized",
102        status: StatusCode::UNAUTHORIZED,
103        title: "Admin access unauthorized",
104        description: "Admin endpoints are disabled or the request has no bearer token.",
105    },
106    admin_forbidden: ProblemDef {
107        slug: "admin-forbidden",
108        status: StatusCode::FORBIDDEN,
109        title: "Admin token rejected",
110        description: "The presented admin token is wrong.",
111    },
112    sidecar_unauthorized: ProblemDef {
113        slug: "sidecar-unauthorized",
114        status: StatusCode::UNAUTHORIZED,
115        title: "Unauthorized sidecar caller",
116        description: "A request to a sidecar-guarded route could not be established as coming from the trusted gateway.",
117    },
118    not_production_ready: ProblemDef {
119        slug: "not-production-ready",
120        status: StatusCode::SERVICE_UNAVAILABLE,
121        title: "Not ready for production traffic",
122        description: "This deployment declares production but cannot satisfy the abuse controls its routes declare.",
123    },
124    request_too_large: ProblemDef {
125        slug: "request-too-large",
126        status: StatusCode::PAYLOAD_TOO_LARGE,
127        title: "Request body too large",
128        description: "The request body exceeded the 64 KiB limit for /v1 endpoints.",
129    },
130    rate_limited: ProblemDef {
131        slug: "rate-limited",
132        status: StatusCode::TOO_MANY_REQUESTS,
133        title: "Rate limit exceeded",
134        description: "Too many requests from this IP or address; retry after the pause.",
135    },
136    not_found: ProblemDef {
137        slug: "not-found",
138        status: StatusCode::NOT_FOUND,
139        title: "Not found",
140        description: "No route matched the request.",
141    },
142    unknown_tenant: ProblemDef {
143        slug: "unknown-tenant",
144        status: StatusCode::NOT_FOUND,
145        title: "Unknown tenant",
146        description: "The request's host resolves to no tenant in the registry.",
147    },
148    internal: ProblemDef {
149        slug: "internal",
150        status: StatusCode::INTERNAL_SERVER_ERROR,
151        title: "Internal error",
152        description: "Unhandled error; no internals are exposed in the body.",
153    },
154    mail_not_configured: ProblemDef {
155        slug: "mail-not-configured",
156        status: StatusCode::SERVICE_UNAVAILABLE,
157        title: "Mail is not configured",
158        description: "No sending domain is verified; use the direct address shown by the form.",
159    },
160    not_ready: ProblemDef {
161        slug: "not-ready",
162        status: StatusCode::SERVICE_UNAVAILABLE,
163        title: "Service not ready",
164        description: "Readiness probe failed: the database is missing, erroring or too slow.",
165    },
166    sidecar_unavailable: ProblemDef {
167        slug: "sidecar-unavailable",
168        status: StatusCode::SERVICE_UNAVAILABLE,
169        title: "Sidecar module unavailable",
170        description: "A sidecar-mounted module could not be reached; other modules are unaffected.",
171    },
172    sidecar_contract_mismatch: ProblemDef {
173        slug: "sidecar-contract-mismatch",
174        status: StatusCode::SERVICE_UNAVAILABLE,
175        title: "Sidecar contract mismatch",
176        description: "A sidecar answers a different HARNESS_API than this harness speaks.",
177    },
178    tenant_degraded: ProblemDef {
179        slug: "tenant-degraded",
180        status: StatusCode::SERVICE_UNAVAILABLE,
181        title: "Tenant is degraded",
182        description: "The tenant's schema is behind or its database is unreachable; its neighbours are unaffected.",
183    },
184};
185
186/// Every core slug definition, for tests and docs.
187pub fn registry() -> Vec<&'static ProblemDef> {
188    vec![
189        &SLUGS.validation_failed,
190        &SLUGS.captcha_failed,
191        &SLUGS.invalid_token,
192        &SLUGS.unknown_product,
193        &SLUGS.admin_unauthorized,
194        &SLUGS.admin_forbidden,
195        &SLUGS.sidecar_unauthorized,
196        &SLUGS.not_production_ready,
197        &SLUGS.request_too_large,
198        &SLUGS.rate_limited,
199        &SLUGS.not_found,
200        &SLUGS.unknown_tenant,
201        &SLUGS.internal,
202        &SLUGS.mail_not_configured,
203        &SLUGS.not_ready,
204        &SLUGS.sidecar_unavailable,
205        &SLUGS.sidecar_contract_mismatch,
206        &SLUGS.tenant_degraded,
207    ]
208}