ADRs
ADR-0013: Proxy input validation and stable error envelope
Architecture decision record 0013.
ADR-0013: Proxy input validation and stable error envelope
- Status: Accepted
- Date: 2026-06-02
- Authors: IBEX Harness team
Context
Milestone 1.2.2 parses OpenAI-shaped chat JSON after auth with an unbounded body read and a minimal error envelope. Goal 1.2 requires security boundaries (body size, Content-Type), semantic validation with field_errors, and consistent response headers before rate limiting (1.2.4) and provider HTTP (Phase 2).
Decision
1) Limits (security controls; ADR amendment to change)
| Constant | Value |
|---|---|
MaxRequestBodyBytes | 1 MiB |
MaxMessagesPerRequest | 1000 |
MaxMessageContentBytes | 100 KiB |
MaxModelNameLength | 256 |
MaxChatMaxTokens | 1_048_576 |
| Temperature (when set) | 0.0–2.0 inclusive |
2) HTTP mapping
| Condition | HTTP | code |
|---|---|---|
| Body exceeds limit | 413 | PAYLOAD_TOO_LARGE |
Wrong Content-Type on chat POST | 415 | UNSUPPORTED_MEDIA_TYPE |
| Semantic / header validation | 400 | VALIDATION_ERROR + field_errors |
| Malformed JSON (parse) | 400 | INVALID_JSON (unchanged) |
| Auth failures | 401/403/503 | MISSING_TOKEN, INVALID_TOKEN, INSUFFICIENT_PERMISSIONS, SERVICE_DEGRADED (ADR-0011; no renames) |
| Missing agent header | 400 | MISSING_AGENT_ID (ADR-0016) |
| Agent not authorized | 403 | AGENT_NOT_AUTHORIZED, AGENT_SUSPENDED (ADR-0016) |
| Agent verify unavailable | 503 | AUTH_UNAVAILABLE (ADR-0016; distinct from token SERVICE_DEGRADED) |
| Valid request, no provider | 501 | PROVIDER_NOT_CONFIGURED |
| Rate limit exceeded | 429 | RATE_LIMITED |
| Wrong HTTP method (JSON routes) | 405 | METHOD_NOT_ALLOWED |
3) Field error codes (canonical)
REQUIRED, TOO_LONG, TOO_MANY, INVALID_ENUM, INVALID_FORMAT
4) Content-Type
POST /v1/chat/completions requires Content-Type: application/json (allows application/json; charset=utf-8).
5) IBEX headers (chat)
X-IBEX-Agent-IDrequired; value must be a UUID (v4/v7) viagithub.com/google/uuid- Optional session header format checks deferred unless specified in API doc
6) Envelope
Single package services/proxy/internal/errors:
Detailincludes optionaldocs_url,field_errors- All proxy JSON errors use
errors.Write/WriteFromRequest - Optional docs URL pattern:
{IBEX_ERROR_DOCS_BASE}/errors/{CODE}(empty base omitsdocs_url)
7) Request / trace IDs
RequestContextMiddleware(outer chain): accept incoming request ID header when valid UUID (v4 or v7), else generate UUID v7 viapackages/reqid(ADR-0017); generate synthetictrace_idper request until OTel (1.3.1)- Context key owned by
packages/reqid; proxyhttplayer delegates toreqid.FromContext AuthMiddlewarereuses request ID from context (fallback generate for isolated tests)ResponseHeadersMiddleware: sets request ID, trace ID, andX-Response-Time(ms) on every response- Header names configurable:
IBEX_REQUEST_ID_HEADER(defaultX-Request-ID),IBEX_TRACE_ID_HEADER(defaultX-Trace-ID) - Proxy → auth gRPC:
x-request-idmetadata via client interceptor (ADR-0017)
8) Middleware order
metrics → requestContext → responseHeaders → logging → mux
POST /v1/chat/completions:
bodyLimit → contentType → auth → agentVerify → rateLimit → handler
GET /v1/internal/auth-probe:
auth → agentVerify → rateLimit → handler
GET /v1/orgs/{org_id}/auth-probe:
pathOrgUUID → auth → agentVerify → rateLimit → handlerRate limit added in 1.2.4 (ADR-0015). Agent verify added in 1.2.5 (ADR-0016).
Amends ADR-0012 §6 footnote accordingly.
9) Packages
internal/validation/— limits, chat semantic validation, header validationinternal/http/middleware.go— body limit, content-type, request context, response headers- Extend
internal/errors/— do not addinternal/http/errors.go
10) Deferred / out of scope
- OTel exporter (1.3.1)
- Multimodal
contentarrays (string-only until Phase 2+) - Bloom/LRU auth cache (2.2.1)
- Provider HTTP (
upstream/)
Consequences
Positive
- Completes Goal 1.2 validation and stable envelope criteria on the proxy
- DoS risk from unbounded bodies closed before JSON decode
- Single error writer; auth codes stable for clients
Negative
- Platform
/healthand/readysuccess payloads remain minimal{status}; errors use stable envelope - Trace IDs are synthetic until 1.3.1
References
Was this page helpful?
Edit on GitHub
Last updated on