api: Metal HTTP server
For Go code, follow the repository Go anti-pattern rules.
Metal specification · Behavior: controller contract and metald
Purpose
This package owns request validation, the public response shape, and the error-to-status mapping. It owns no VM, image, or host state. A mutation stores desired state, wakes the reconciler, and returns without waiting for the host.
Types
New(Config, Dependencies)returns the Atlas API router.NewCoordinationreturns the node coordination router.Serverholds the injected services and handlers.Dependenciesholds the injected services. Each is an interface declared in this package.
Request flow
Each request goes through these steps in order:
- Add the correlation IDs.
- Write the request log.
- Authenticate the route group.
- Decode strict JSON in the handler.
- Validate the request.
- Call the domain service.
- Wake the reconciler when needed.
- Write the response.
- Each request gets an
X-Request-IDand anX-Operation-ID. A caller value is kept only when it is safe to echo. - JSON decoding ignores unknown fields and rejects trailing values.
- Request bodies are size capped.
Routes
The generated Metal API reference lists each route. Rules:
- Use PUT when a request replaces desired state, so a repeat is safe.
- Use POST only for an action that runs on each call, such as a restart, or to create a resource.
- The network PUT requires the complete network object, including
firewall. - Every coordination route carries the VM ID as the
virtual_machine_idquery value. - The coordination stop route is idempotent. It returns the final snapshot.
- The coordination memory snapshot route streams one memory snapshot file for the final sequence with a SHA-256 trailer. The migration manager checks the source lock and file name.
- The coordination kernel route streams the kernel of the locked source VM with zstd. A source without the kernel returns 404.
| Listener | Caller | Routes |
|---|---|---|
| Control | Atlas client certificate | /v1/sync, /v1/vms, /v1/snapshots, destination /v1/migrations/:id |
| Coordination | Regional node certificate | Source routes under /v1/migrations/:id |
| Snapshot stream | One-shot mutual TLS | Owned by vm/migration |
Capacity
Compute and disk handlers check only the increase against host memory and storage. CPU is never checked. See the controller contract.
Authentication
- Each route group names its middleware, so a route cannot inherit the wrong rule.
- The TLS listeners verify the client certificate. This package holds no credential.
- An unknown path or a wrong method under
/v1returns404.
See security for the certificate that each listener accepts.
Errors
A domain error maps to one status and one safe message. An unknown error becomes 500 with no detail, and the log keeps its cause under the request ID.
| Domain error | Status | Code |
|---|---|---|
vm.ErrNotFound, storage.ErrNotFound | 404 | not_found |
vm.ErrConflict, storage.ErrInUse | 409 | conflict |
| Compute or disk increase without host capacity | 409 | insufficient_capacity |
storage.ErrImageConflict | 409 | image_content_conflict |
storage.ErrImageIntegrity | 422 | image_integrity_failed |
storage.ErrShuttingDown | 503 | unavailable |
network.ErrInvalidPeers, storage.ErrInvalidUpload, vm.ErrMetadataServiceTooLarge | 400 | invalid_request |
- Every error body carries a
retryableflag.501is never retryable. POST /v1/syncmaps a host-state not-found to503, not404. A404would stop the controller retry.- Responses never carry command output, signed URL values, or local error detail.
http.Server.Shutdowndoes not track WebSockets. Close each console with1012on shutdown so the Atlas bridge reconnects.
Related
- internal/vm/SPEC.md defines the VM contracts and the generation model.
- internal/console/SPEC.md owns the serial console.
- cmd/metald/SPEC.md injects server dependencies.