18 — Forgeyard API / Axum System Architecture
Document type: Core Public API & Edge Interface System Architecture
Project: Forgeyard CI/CD
Subsystem: Axum HTTP API, REST/JSON DTOs, authentication/authorization middleware, idempotency, pagination, filtering, WebSocket/SSE, uploads/downloads, public/admin API separation, webhooks, OpenAPI, rate limiting, CORS/CSRF, API versioning, error envelopes, backpressure, and API observability
Architecture style: Thin transport boundary over domain services, versioned public contracts, explicit authorization, stateless request handling where practical, event-stream subscriptions for live updates, and strict separation from native QUIC agent protocol
Status: Target production architecture for the new Forgeyard repository
Relationship to prior work: Builds on Identity/Authz/Policy, Events/Reconciliation, Observability/Health, Release, Deployment, Run/Job, CAS, Storage, Transport/Protocol, and all domain service APIs. It exposes Forgeyard to humans, CLIs, Dioxus UI, automation clients, and external integrations.
1. Purpose
Forgeyard needs a production-grade public API for:
CLI
Dioxus UI
automation
SCM integrations
administration
release/deployment control
artifact access
live status
The API must not become a second implementation of domain logic.
The central rule is:
Axum is the transport and composition boundary. Domain services own business rules.
A second rule is:
Public HTTP/JSON APIs and native agent QUIC/Postcard APIs are separate protocol families with separate versioning and security models.
A third rule is:
Every mutating public API is authenticated, authorized, validated, idempotent where retried, observable, and bounded.
2. Architectural Position
Browser / CLI / Automation / Provider
│
HTTPS / JSON
│
▼
Axum API
│
┌────────┼─────────┐
▼ ▼ ▼
Authn Authz Validation
│ │ │
└────────┼─────────┘
▼
Domain Service
│
▼
Store / CAS / Events
Live updates:
Domain Events
↓
Event Stream
↓
WS / SSE
↓
UI / CLI
Internal agent path remains:
Daemon ↔ Agent
QUIC + Postcard
not REST.
3. Goals
The API subsystem MUST:
- use Axum;
- expose versioned REST/JSON;
- support CLI and UI;
- support browser sessions;
- support API tokens/service accounts;
- support OIDC-backed sessions;
- enforce authz per operation;
- support idempotency keys;
- support cursor pagination;
- support filtering/sorting;
- support WebSocket/SSE;
- support artifact upload/download;
- support presigned/direct CAS flows;
- support webhook ingress;
- support admin APIs;
- support health/readiness endpoints;
- support OpenAPI generation;
- support stable error envelopes;
- support request size limits;
- support rate limiting;
- support CORS;
- support CSRF protection;
- support request deadlines;
- support cancellation on client disconnect where appropriate;
- support backpressure;
- support trace propagation;
- support structured audit for sensitive actions;
- remain domain-thin;
- avoid agent-protocol duplication;
- support rolling API evolution.
4. Non-Goals
The HTTP API does not:
execute arbitrary shell
schedule jobs directly
hold business policy logic
replace QUIC worker protocol
serve as CAS blob store for all large traffic if direct object storage is better
5. Workspace Structure
crates/api/
├── forgeyard-api/
├── forgeyard-api-model/
├── forgeyard-api-error/
├── forgeyard-api-auth/
├── forgeyard-api-authz/
├── forgeyard-api-pagination/
├── forgeyard-api-idempotency/
├── forgeyard-api-rate-limit/
├── forgeyard-api-websocket/
├── forgeyard-api-sse/
├── forgeyard-api-upload/
├── forgeyard-api-download/
├── forgeyard-api-webhook/
├── forgeyard-api-openapi/
├── forgeyard-api-admin/
├── forgeyard-api-health/
└── forgeyard-api-testkit/
Axum composition:
apps/forgeyard-daemon/src/
├── api/
│ ├── mod.rs
│ ├── router.rs
│ ├── middleware.rs
│ ├── state.rs
│ └── shutdown.rs
6. Public API Version
Use path-based major version:
/v1/...
7. Why Major Path Version
Clear external compatibility boundary.
8. Minor Evolution
Within /v1:
add optional fields
add endpoints
add enum variants only with compatibility discipline
9. Breaking Change
Create:
/v2
10. API DTOs
Public DTOs live in:
forgeyard-api-model
11. DTO / Domain Separation
Never expose domain structs directly just because Serde can serialize them.
12. Why Separate DTOs
Allows:
domain refactor
field hiding
redaction
public compatibility
13. JSON
Public REST uses JSON.
This is appropriate interoperability usage.
14. RON/Postcard
Not used as default public web API body format.
15. Content Type
application/json
16. Request Envelope
Most REST requests need no generic outer envelope.
Use normal resource-oriented JSON.
17. Error Envelope
#![allow(unused)]
fn main() {
pub struct ApiErrorResponse {
pub error: ApiErrorBody,
pub request_id: RequestId,
}
}
18. ApiErrorBody
#![allow(unused)]
fn main() {
pub struct ApiErrorBody {
pub code: ApiErrorCode,
pub message: String,
pub retryable: bool,
pub details: Option<serde_json::Value>,
}
}
19. Safe Details
Never expose:
SQL
stack trace
secret
private filesystem path
20. Error Mapping
Domain error -> API error.
21. HTTP Status Mapping
Examples:
400 validation
401 unauthenticated
403 forbidden
404 not found
409 conflict
412 precondition
422 semantic invalid
429 rate limited
503 unavailable
22. RequestId
Generated per HTTP request.
23. TraceId
Separate from RequestId.
24. Authentication Middleware
Extracts:
browser session
Bearer token
service token
25. Principal Context
Request extensions contain validated:
#![allow(unused)]
fn main() {
pub struct RequestPrincipal {
pub principal: PrincipalId,
pub authn: AuthenticationContext,
}
}
26. Middleware Does Not Fully Authorize
Route/domain action still checks required permission/resource.
27. Authorization Layer
Pattern:
extract principal
↓
resolve target resource
↓
authorize permission
↓
call service
28. Avoid Hidden Route-Only Authz
Critical services can also require authorization/capability argument.
29. Browser Session
Cookie-based.
30. Cookie Requirements
Secure
HttpOnly
SameSite
31. CSRF
Required for cookie-authenticated state-changing requests.
32. Bearer Token
CSRF not applicable in same way.
33. CORS
Deny broad wildcard by default.
34. UI Same-Origin
Preferred deployment.
35. Cross-Origin Clients
Explicit allowed origins.
36. Preflight
Handled centrally.
37. API Token Scope
Authz still enforced.
38. Token Prefix
May use recognizable token prefix for operational identification.
39. Never Log Token
Redacted.
40. Idempotency
Mutating operations that clients may retry should support:
Idempotency-Key
41. Idempotency Record
#![allow(unused)]
fn main() {
pub struct ApiIdempotencyRecord {
pub principal: PrincipalId,
pub key: IdempotencyKey,
pub route: ApiOperationId,
pub request_digest: Digest,
pub response_ref: IdempotentResponseRef,
}
}
42. Same Key, Same Request
Return prior result.
43. Same Key, Different Request
409 Conflict
44. Idempotency Scope
Principal + operation + key.
45. Idempotency TTL
Operation-specific.
46. Strong Candidates
create run
create release
promote release
create deployment
rollback deployment
create token
47. GET
Naturally idempotent.
48. PUT/PATCH
Use optimistic concurrency/precondition where appropriate.
49. Entity Version
Expose ETag/version.
50. ETag
Can represent:
EntityVersion
51. If-Match
For update/delete.
52. Lost Update Prevention
Mutating stale version -> 412/409.
53. Pagination
Use cursor/keyset pagination.
54. Avoid Offset at Scale
Offset may still exist for tiny admin lists, but not default.
55. Cursor Request
?cursor=...&limit=50
56. Cursor Response
#![allow(unused)]
fn main() {
pub struct Page<T> {
pub items: Vec<T>,
pub next_cursor: Option<String>,
}
}
57. Cursor Opacity
Clients must not parse.
58. Cursor Authorization
Cursor does not bypass tenant/authz filtering.
59. Limit
Bound maximum.
60. Filtering
Typed query parameters.
Examples:
state=running
project_id=...
created_after=...
61. Sorting
Allow bounded predefined sort fields.
62. No Arbitrary SQL-Like Filters
Avoid injection/complexity.
63. Search
Separate endpoint/typed search service if needed.
64. Resource Endpoints
Examples:
/v1/projects
/v1/runs
/v1/jobs
/v1/runners
/v1/artifacts
/v1/releases
/v1/deployments
/v1/policies
65. Run API
POST /v1/runs
GET /v1/runs/{id}
POST /v1/runs/{id}/cancel
GET /v1/runs/{id}/jobs
GET /v1/runs/{id}/events
66. Job API
GET /v1/jobs/{id}
GET /v1/jobs/{id}/attempts
GET /v1/jobs/{id}/logs
67. Scheduler API
Mostly admin/read:
GET /v1/admin/scheduler/status
GET /v1/admin/scheduler/explain/{job}
68. Runner API
GET /v1/runners
GET /v1/runners/{id}
POST /v1/runners/{id}/drain
POST /v1/runners/{id}/resume
69. Artifact API
GET /v1/artifacts/{id}
GET /v1/artifacts/{id}/download
GET /v1/artifacts/{id}/evidence
70. Package API
GET /v1/packages
GET /v1/packages/{id}
POST /v1/packages/{id}/validate
71. Release API
As defined in release architecture.
72. Deployment API
As defined in deployment architecture.
73. Policy API
GET /v1/policies/effective
POST /v1/policies/validate
POST /v1/policies/activate
74. Secret API
Metadata only by default:
GET /v1/secrets
POST /v1/secrets
POST /v1/secrets/{id}/rotate
75. No Plaintext Secret GET by Default
Explicit privileged reveal endpoint only if product supports it.
76. Audit API
GET /v1/audit/events
permission-gated.
77. Health API
GET /health/live
GET /health/ready
GET /v1/admin/health
78. Metrics API
/metrics
internal/admin.
79. WebSocket
Use for interactive live updates where bidirectional behavior useful.
80. SSE
Use for simple server→client event streams.
81. Recommendation
Prefer SSE for:
run/release/deployment live events
when only server push needed.
82. WebSocket Use
Potential for:
interactive log control
future terminal-like UI
but no arbitrary shell.
83. Event Stream Endpoint
GET /v1/events/stream
with filters.
84. Run Stream
GET /v1/runs/{id}/events/stream
85. Cursor Resume
Header/query:
Last-Event-ID
cursor
86. Backfill
Event stream service reads durable event store.
87. Slow Client
Bound buffer.
Disconnect when client falls too far behind.
88. Reconnect
Client resumes from cursor.
89. WebSocket/SSE Auth
Same principal/session auth.
90. Event Authorization
Filter by tenant/resource permission.
91. No Global Internal Event Leakage
Public event DTO is redacted projection.
92. Job Logs API
Live:
SSE/WebSocket
Historical:
paged/chunked download
93. Log Cursor
Sequence-based.
94. Binary Logs
Can use downloadable blob/chunk endpoint.
95. Uploads
Small metadata JSON through normal API.
Large artifacts/files use streaming/direct CAS.
96. Artifact Upload Flow
request upload intent
↓
authz
↓
create upload session
↓
direct/presigned upload
↓
finalize
↓
digest verify
97. Upload Session
#![allow(unused)]
fn main() {
pub struct UploadSession {
pub id: UploadSessionId,
pub expected_digest: Option<Digest>,
pub max_size: ByteSize,
pub expires_at: Timestamp,
}
}
98. Direct Daemon Upload
Supported for standalone/small objects.
99. Object Store Presigned Upload
Preferred at scale.
100. Upload Auth
Presigned capability scoped:
object
size
expiry
101. Finalize Upload
Daemon verifies object exists/digest.
102. No Trust in Client-Claimed Digest Alone
Server/CAS verifies.
103. Downloads
Small metadata via API.
Large artifacts:
presigned/direct object store
streaming endpoint
104. Download Authorization
Check artifact metadata authz before issuing URL.
105. Presigned URL TTL
Short.
106. Range Requests
Support for large artifact download where backend allows.
107. Content-Disposition
Safe sanitized filename.
108. Content-Type
From artifact/package metadata.
109. Checksums
Expose exact digest headers/metadata.
110. Webhook Ingress
Separate route family:
/webhooks/{provider}/...
111. Webhook Flow
receive
↓
size limit
↓
verify signature
↓
dedupe delivery
↓
persist
↓
normalize
↓
domain event/command
112. Verify Before Parse Deeply
Use raw body signature where provider requires.
113. Webhook Delivery ID
Dedup.
114. Provider-Specific Parser
SCM adapter layer.
115. Webhook Response
Fast.
Do not block on full CI workflow.
116. Webhook Queue
Persist accepted delivery then process asynchronously.
117. Webhook Retry
Provider may redeliver.
Safe.
118. Webhook Secrets
SecretRef.
119. Admin API
Separate path:
/v1/admin/...
120. Admin Authz
Explicit permissions.
121. Admin Endpoints
Examples:
health
migrations
scheduler
reconciliation
events/deadletters
runner enrollment
trust roots
system config
122. No Hidden Superuser Route
Same authz framework.
123. Break-Glass API
Explicit action endpoint if needed.
124. Break-Glass Requirements
Strong auth/reason/audit.
125. API State
#![allow(unused)]
fn main() {
pub struct ApiState {
pub services: Arc<ServiceRegistry>,
pub authn: Arc<AuthenticationService>,
pub authz: Arc<AuthorizationService>,
pub telemetry: Arc<Telemetry>,
}
}
126. ServiceRegistry
Explicit typed app composition.
Avoid service locator abuse if possible.
127. Better Pattern
State contains concrete service handles/grouped modules.
128. Router Composition
#![allow(unused)]
fn main() {
pub fn build_router(state: ApiState) -> Router
}
129. Route Modules
api/routes/
├── runs.rs
├── jobs.rs
├── runners.rs
├── artifacts.rs
├── releases.rs
├── deployments.rs
├── policies.rs
├── secrets.rs
├── admin.rs
└── webhooks.rs
130. Handler Rule
Handler should:
extract
validate
authorize
call service
map response
131. Handler Must Not
write SQL
implement scheduler
open CAS internals directly unless API service abstraction
decide release policy
132. Validation
Use typed request DTO validation.
133. Validation Error
Return field-level diagnostics.
134. Request Size Limits
Per route.
135. JSON Size
Bound globally + route-specific.
136. Webhook Size
Provider-specific limit.
137. Multipart
Use only where appropriate.
Streaming.
138. Timeouts
Global request timeout.
Long operations return operation resource rather than holding HTTP open.
139. Async Operation Pattern
Example release promotion:
POST
↓
202 Accepted
↓
operation/release state
140. Operation Resource
Optional generic:
#![allow(unused)]
fn main() {
pub struct OperationId(Ulid);
}
141. Prefer Domain Resource State
If Release/Deployment already has async state, no generic operation needed.
142. Client Disconnect
Do not automatically cancel durable domain action after accepted commit.
143. Before Commit Disconnect
Request cancellation can propagate if safe.
144. Streaming Disconnect
Stop stream only.
145. Rate Limiting
Multi-layer:
global
principal
route class
IP supplemental
146. IP Limit
Not sole identity because NAT.
147. Rate Limit Classes
read
write
login
webhook
upload
admin
148. RateLimitKey
Principal when authenticated.
149. 429 Response
Include retry guidance.
150. Distributed Rate Limit
Can use DB/Redis-like external later.
Initial per-daemon + security-sensitive central counters where needed.
151. Correctness
Rate limiter is protection, not domain authority.
152. Load Shedding
If server overloaded:
reject low-priority requests
preserve health/control
153. Backpressure
Bound:
request body
inflight handlers
DB concurrency
upload streams
event streams
154. Axum Concurrency Layer
Use Tower layers.
155. Tower
Appropriate for:
timeout
load shed
rate limit
trace
CORS
compression
156. Compression
JSON responses can compress.
Avoid already compressed artifacts.
157. Request Decompression
If supported, bound decompressed size.
158. TLS
Terminate in daemon or trusted reverse proxy.
159. Reverse Proxy
Support:
Caddy
nginx
cloud LB
160. Trusted Proxy Headers
Only honor forwarded headers from configured trusted proxy.
161. Client IP
Diagnostic/rate supplemental.
162. HTTP/2
Supported.
163. HTTP/3
Optional future.
Do not conflate with internal QUIC protocol.
164. OpenAPI
Generate/document public API.
165. OpenAPI Source
DTO/routes annotations or code generation approach.
166. Schema Stability
Published OpenAPI becomes compatibility artifact.
167. API Documentation
Host:
/docs/api
optional.
168. SDK Generation
Possible future from OpenAPI.
169. Rust CLI Client
Can use typed internal client crate generated/handwritten from DTOs.
170. API Client Crate
crates/api/forgeyard-api-client/
optional.
171. Client Responsibilities
auth
pagination
idempotency
retry safe operations
SSE reconnect
172. Retry Policy
Only retry safe/idempotent operations.
173. Retryable Codes
429
502
503
504
with idempotency considerations.
174. No Automatic Retry on Arbitrary POST
Unless Idempotency-Key present/operation known safe.
175. API Compatibility Tests
Golden JSON fixtures.
176. Unknown Fields
Clients should ignore unknown response fields where possible.
177. Required Fields
Do not remove/change semantics within major version.
178. Enum Evolution
Use string enums carefully.
Unknown enum values can break generated clients.
179. Extensible Enum Pattern
Potential wrapper/string newtype for provider/extensible values.
180. Timestamps
RFC 3339 UTC.
181. IDs
Canonical string representation.
182. Byte Sizes
Numeric bytes + optional human rendering.
183. Durations
Use explicit units/string schema.
184. Digests
Expose:
algorithm
hex
or standardized digest string.
185. Pagination Stability
Cursor tied to sort/filter parameters.
186. Invalid Cursor
187. Expired Cursor
410/400 depending API convention.
188. Field Selection
Not needed initially.
189. Expand Relations
Avoid arbitrary GraphQL-like expansion initially.
190. GraphQL
Not required.
REST + SSE sufficient.
191. gRPC Public API
Not needed.
Keep only RBE/interoperability.
192. API Security Headers
Set:
X-Content-Type-Options
Referrer-Policy
Content-Security-Policy for UI
HSTS where HTTPS direct
193. CSP
UI-specific but server can provide.
194. Frame Protection
Use CSP frame-ancestors.
195. Request Logging
Log:
method
route template
status
duration
request_id
principal_id optional internal
196. Do Not Log Full URL Query Blindly
May contain sensitive data.
197. Route Template
Better metric dimension than raw path.
198. HTTP Metrics
forgeyard_http_requests_total
forgeyard_http_request_duration_seconds
forgeyard_http_inflight_requests
forgeyard_http_rate_limited_total
199. Labels
method
route
status_class
bounded.
200. Trace Middleware
Create span per request.
201. W3C Trace
Extract/inject.
202. Request Context
#![allow(unused)]
fn main() {
pub struct RequestContext {
pub request_id: RequestId,
pub principal: Option<PrincipalId>,
pub trace: TraceContext,
}
}
203. Audit Trigger
Sensitive mutation route/domain service emits audit.
204. No Audit from Access Log Alone
Audit semantics separate.
205. Health Endpoint Metrics
Exclude/special-case from noisy request metrics if needed.
206. SSE Metrics
Connections, lag, disconnect reason.
207. WebSocket Metrics
Same.
208. Upload Metrics
Bytes, duration, failures.
209. Webhook Metrics
Verification failure, duplicate, processing lag.
210. API Health
Checks:
router ready
authn provider
store
event stream
211. API Readiness
Depends on required store/services.
212. API Degraded Mode
Read-only if storage writes unavailable and app policy allows.
213. Read-Only Middleware
Can reject mutating methods with 503/423 depending convention.
214. Maintenance Mode
Explicit admin state.
215. Maintenance Response
Retry guidance.
216. Migration Mode
During incompatible DB migration:
readiness false
217. Webhook During Degraded DB
Reject so provider retries.
218. API Config
Example:
(
http: (
listen: "0.0.0.0:8080",
public_base_url: "https://forgeyard.example.com",
request_timeout: "30s",
max_json_body: "2MiB",
cors: (
allowed_origins: [
"https://forgeyard.example.com",
],
),
),
)
219. Session Config
Separate auth config.
220. API Base URL
Used for callbacks/webhooks.
221. Host Validation
Protect against Host header abuse where relevant.
222. OIDC Callback
Dedicated endpoint.
223. Login Flow
/login
/auth/oidc/callback
/logout
224. API vs UI Routes
UI static/app routes separate from /v1.
225. Dioxus UI
Calls same API as CLI where possible.
226. Internal Server-Side UI Shortcuts
Avoid bypassing API/service boundaries unless same service layer and authz.
227. CLI
Uses public API.
228. Local Standalone CLI
Can optionally use local socket/native internal client for zero-config, but semantics same.
229. Unix Socket API
Optional local admin/standalone transport.
230. Named Pipe
Windows equivalent.
231. HTTP Over Local Socket
Possible.
232. Local Auth
OS peer credentials.
233. Same DTO/Service
Do not fork logic.
234. API Testkit
forgeyard-api-testkit/src/
├── lib.rs
├── app.rs
├── auth.rs
├── request.rs
├── response.rs
├── idempotency.rs
├── pagination.rs
├── sse.rs
├── webhook.rs
└── assertions.rs
235. Unit Tests
Test DTO validation/error mapping.
236. Router Integration Tests
Real Axum router with fake services.
237. Auth Tests
no auth -> 401
permission missing -> 403
scope mismatch -> 403
238. Tenant Isolation Test
Guess other tenant resource ID -> denied/not-found per anti-enumeration policy.
239. Idempotency Test
Duplicate create run same key -> same result.
240. Idempotency Conflict Test
Same key different body -> 409.
241. ETag Test
Stale If-Match rejected.
242. Pagination Test
No duplicate/missing items under stable snapshot assumptions.
243. Cursor Tamper Test
Invalid signed/opaque cursor rejected.
244. SSE Resume Test
Disconnect/reconnect from cursor receives missed events.
245. Slow SSE Client Test
Bounded buffer/disconnect.
246. Upload Test
Oversized upload rejected.
247. Digest Test
Client-declared wrong digest rejected at finalize.
248. Presigned URL Auth Test
Unauthorized user cannot obtain URL.
249. Webhook Signature Test
Invalid signature rejected.
250. Webhook Duplicate Test
Same delivery processed once semantically.
251. Webhook Fast Ack Test
Does not wait for full CI completion.
252. Rate Limit Test
429 + retry guidance.
253. CSRF Test
Cookie mutation without CSRF token rejected.
254. CORS Test
Unknown origin denied.
255. Security Header Test
Expected headers set.
256. Body Bomb Test
Compressed/decompressed size bounded.
257. JSON Fuzzing
Fuzz request decoders/validation.
258. SSE/WS Fuzzing
Malformed client messages bounded.
259. OpenAPI Test
Generated schema matches route contract.
260. Compatibility Test
Old v1 JSON fixtures still decode/serve.
261. Failure Injection
DB timeout
CAS unavailable
authz unavailable
event stream lag
rate limiter failure
262. Fail-Closed Authz
If protected authz cannot determine:
deny/503
not allow.
263. Load Test
High concurrent reads/writes/SSE.
264. Long Polling
Not preferred.
SSE better.
265. API Performance
Avoid N+1 store calls.
266. Batch Endpoints
Add when real workflows need.
267. Batch Authorization
Authorize each resource/scope safely.
268. Bulk Operations
Return per-item result.
269. Partial Failure
Explicit.
270. Transaction Scope
Only where semantically atomic.
271. No Huge DB Tx Around HTTP Stream
Critical.
272. API Documentation Examples
Use synthetic data.
273. OpenAPI Security Schemes
Document:
cookie session
Bearer token
274. Webhook Docs
Provider-specific.
275. API Deprecation
Return:
Deprecation
Sunset
headers where useful.
276. Deprecation Policy
Document before removal.
277. Rolling Upgrade
New daemon must serve current public API throughout deployment.
278. API Feature Discovery
Optional:
GET /v1/capabilities
279. Capability Endpoint
Returns supported public features.
280. No Internal Capability Leak
Public safe subset only.
281. API Error Codes
Stable registry.
282. Example Codes
RUN_NOT_FOUND
PERMISSION_DENIED
VERSION_CONFLICT
IDEMPOTENCY_CONFLICT
RATE_LIMITED
SERVICE_UNAVAILABLE
283. Client Behavior
Clients should key logic on code, not message.
284. Localization
API messages may remain English stable; UI localizes separately.
285. Error Details
Machine-structured field errors.
286. Validation Error Example
{
"error": {
"code": "VALIDATION_FAILED",
"message": "request validation failed",
"retryable": false,
"details": {
"fields": {
"version": "invalid format"
}
}
}
}
287. Sensitive Resource Not Found
May return 404 instead of 403 to reduce enumeration, per policy.
288. API Authz Explain
Admin endpoint can expose decision details if permitted.
289. API Request Replay
Idempotency cache is not a general replay log.
290. API Operation IDs
Stable OpenAPI operation names.
291. Handler Naming
Match operation IDs.
292. Code Generation
Optional.
293. Axum State Cloning
Use Arc handles.
294. Blocking Work
Never run blocking CPU/FS-heavy work on async executor directly.
295. CPU Work
Use Rayon/spawn_blocking/service workers where appropriate.
296. Streaming
Use Body streaming.
297. Backpressure-Aware Body
Yes.
298. Database Pool
Shared through store adapter.
299. Per-Request Transaction
Only when domain operation needs.
300. Middleware Ordering
Recommended:
request ID
trace
security headers
body limit
CORS
rate limit
authn
CSRF
handler/authz
timeout
error mapping
Exact Tower order tested.
301. Compression Placement
After response generation.
302. Panic Handling
Convert panic to 500 safely.
303. Panic Logging
Trace/request ID, no sensitive body dump.
304. Graceful Shutdown
Stop accepting new connections.
305. Drain
Allow in-flight bounded requests.
306. SSE Shutdown
Send close/reconnect hint if possible.
307. WebSocket Shutdown
Close frames.
308. Upload Shutdown
May abort resumable upload safely.
309. API Server Health on Shutdown
Readiness false first.
310. Implementation Phase 1 — Core Router/DTO/Error
Implement:
Axum app
/v1
DTO separation
error envelope
request IDs
311. Phase 2 — Authn/Authz
Sessions/tokens/permission enforcement.
312. Phase 3 — Run/Job/Artifact API
Core CI workflow.
313. Phase 4 — Pagination/Idempotency/ETag
Production mutation/read semantics.
314. Phase 5 — SSE
Run/job/release/deployment live updates.
315. Phase 6 — Upload/Download
CAS/presigned flow.
316. Phase 7 — Webhooks
SCM/provider ingress.
317. Phase 8 — Release/Deployment APIs
Full orchestration.
318. Phase 9 — Admin/Health/OpenAPI
Operations.
319. Phase 10 — Rate Limit/Load Shed
Hardening.
320. Phase 11 — Local Socket
Standalone convenience.
321. Phase 12 — Compatibility/Fuzz/Scale
Production hardening.
322. Acceptance Tests
- Axum handlers contain no core business logic.
- Public DTOs are separate from domain models.
- Public API version is explicit.
- Breaking changes require new major version.
- Unauthenticated protected request returns 401.
- Unauthorized resource action returns safe denial.
- Tenant boundary is enforced server-side.
- UI-hidden controls are not relied on for security.
- Idempotency-Key safely deduplicates supported mutations.
- Same idempotency key with different body conflicts.
- Entity version/ETag prevents lost updates.
- Cursor pagination is opaque and bounded.
- Invalid cursor cannot bypass authz.
- SSE reconnect resumes from durable event cursor.
- Slow live clients cannot consume unbounded server memory.
- Artifact upload is size-bounded.
- Uploaded artifact digest is verified.
- Large download can use direct/presigned flow.
- Download URL is issued only after authz.
- Webhook signature is verified before semantic processing.
- Duplicate webhook delivery is idempotent.
- Webhook route responds quickly after durable acceptance.
- Cookie mutations require CSRF protection.
- CORS is deny-by-default.
- API tokens are never logged.
- Request bodies are bounded.
- Request deadlines are bounded.
- Telemetry traces propagate through API.
- Metrics use route templates, not raw IDs.
- Authz uncertainty fails closed for protected writes.
- Public REST remains separate from agent QUIC protocol.
- OpenAPI matches implemented routes.
- Standalone/local socket uses same service semantics.
- Graceful shutdown drains safely.
- Forgeyard UI and CLI dogfood the same public API.
323. Production Readiness Gates
Do not call API production-ready until:
DTO/domain separation enforced
authn/authz complete
tenant isolation tested
idempotency supported for critical writes
pagination stable
SSE backfill/resume works
upload/download bounded
webhook verification/dedup works
CSRF/CORS hardened
rate limiting/load shedding configured
OpenAPI published
compatibility tests pass
324. Architectural Invariants
- Axum is transport boundary, not business layer;
- public DTOs are not domain models;
- public HTTP and internal QUIC protocols are separate;
- JSON is used for public interoperability;
- every protected mutation requires authn/authz;
- UI is never security authority;
- mutating retries use idempotency where needed;
- stale writes use version/precondition checks;
- pagination is cursor/keyset-based by default;
- live streams are bounded and resumable;
- public events are redacted projections;
- large blobs are streamed/direct, not giant JSON;
- uploaded content is digest-verified;
- webhook signatures are verified;
- webhook deliveries are deduplicated;
- cookie-authenticated writes are CSRF-protected;
- CORS is explicit;
- request/body sizes are bounded;
- request deadlines are bounded;
- external client disconnect does not undo committed durable work;
- rate limiting is defense, not domain authority;
- authz fails closed for protected actions;
- internal stack traces/secrets never enter API errors;
- metric labels use route templates, not raw IDs;
- OpenAPI is compatibility artifact;
- breaking changes create new API major version;
- provider-specific webhooks stay adapter-local;
- standalone/distributed share API semantics;
- local socket convenience does not fork business logic;
- Forgeyard UI/CLI dogfood the same API.
325. Final Target Architecture
Browser / CLI / Automation
│
HTTPS / JSON
│
▼
Axum
│
┌─────────────────┼──────────────────┐
▼ ▼ ▼
Authn Validation Limits
│ │ │
└─────────────────┼──────────────────┘
▼
Authz
│
▼
Domain Services
│
┌──────────────┼──────────────┐
▼ ▼ ▼
Store CAS Events
│
▼
SSE/WS
326. Final Architectural Position
Request path:
HTTP request
↓
request ID + trace
↓
body/size validation
↓
authentication
↓
resource-scoped authorization
↓
domain service
↓
stable API DTO/error
Mutation safety:
Idempotency-Key
+
EntityVersion / If-Match
+
authz
↓
safe retry / conflict handling
Live state:
durable events
↓
public projection
↓
SSE/WS
↓
cursor resume
The key guarantee is:
Forgeyard’s Axum API is a stable, secure, observable edge over the same domain services that power the rest of the platform. It never becomes a parallel source of business truth, and it never substitutes REST for the purpose-built internal runner protocol.
327. New-Repository Sequence
01 Core Domain & Foundation
02 Storage & Metadata
03 CAS & Artifact Data Plane
04 Pipeline IR / Parsing / Planning
05 Run / Job State Machine
06 Scheduler
07 Runner / Agent
08 Sandbox & Executor
09 Transport / QUIC / Protocol
10 Events / Reconciliation
11 Policy / Authorization / Identity
12 Secrets & Trust
13 Supply Chain / SBOM / Provenance / Signing
14 Packaging
15 Release
16 Deployment
17 Observability / Health / Doctor
18 API / Axum
19 Dioxus UI
20 Device Lab
21 SCM Provider Integrations
22 HA / Coordination / Raft
23 RBE Interop
24 Plugin / Extension Architecture
25 Operations / Backup / Upgrade / DR
26 Self-Hosting / Bootstrap / Release of Forgeyard