# Cade localhost API v1

The M9 service is a single-machine boundary around the existing deterministic
DimensionGraph pipeline. It binds only to `127.0.0.1`; the bind address is not a
command-line option. It does not call a cloud service or read an API key. M15
adds an optional, explicitly configured local reader-provider contact; no reader
is selected silently.

## Start

```powershell
cade-service --port 8765 --jobs-dir .\cade-jobs --web-dir .\web --workers 4
```

Port `0` requests an operating-system-selected loopback port and is intended for
tests. `GET /health` returns the actual host and port. When `--web-dir` points to
the committed `web` directory, the upload interface is available at `/`.
`--workers` accepts 1 through 32 and defaults to 4. HTTP request handling uses a
separate bounded pool, so uploads and status reads do not occupy model workers.

The reader is disabled by default. These provider selections are configuration,
not code changes:

```powershell
cade-service --reader-provider mock --reader-source .\mock_manifest.json --reader-source-root .
cade-service --reader-provider file --reader-source .\reader-responses
```

`mock` preloads a strict v2 manifest, `file` loads `<drawing_id>.json` from the
configured directory, and `embedded` is a reserved slot that currently fails at
startup because M-Reader is not authorized. `GET /health` reports the active
provider as `disabled`, `mock`, or `file`.

## Submit

`POST /api/jobs` requires `Content-Type: application/json` and a strict body:

```json
{
  "schema_version": "cade-local-job-v1",
  "drawing_id": "plate_01__view_layout_shift",
  "graph_filename": "plate_01.json",
  "graph": {},
  "source_image": {
    "filename": "plate_01__view_layout_shift.png",
    "media_type": "image/png",
    "data_base64": "..."
  }
}
```

With the default disabled provider, `graph` must contain the unchanged
DimensionGraph v0 object. With a configured provider, `graph` may be omitted and
`declared_dpi` must be supplied as a finite number from 72 through 1200. The
provider returns only strict reader-v2 readings; the service applies the existing
deterministic raster binding and rejects the request unless it produces a graph.
Because the service has no independent transcription oracle, every accepted
provider-derived dimension remains yellow even when model construction validates;
provider confidence cannot make it green.
The source image is required, must have a safe `.png` basename and PNG signature, and is preserved
byte-for-byte for the result view. Unknown fields, unsafe basenames, invalid
base64, non-PNG bytes, and bodies over 32 MiB fail explicitly. A valid envelope
returns HTTP 202 with a service-generated job ID, status URL, and result URL.

## State and result

| Method and path | Success | Purpose |
|---|---:|---|
| `GET /health` | 200 | Loopback liveness and bound port |
| `POST /api/jobs` | 202 | Validate the envelope and enqueue one job |
| `GET /api/jobs/{id}` | 200 | Read `queued`, `running`, `completed`, or `rejected` |
| `GET /api/jobs/{id}/result` | 200 | Read terminal traffic and artifact links |
| `GET /api/jobs/{id}/files/{name}` | 200 | Download a completed artifact |

The result file names are `source.png`, `model.step`, `front.svg`, `top.svg`,
`side.svg`, `report.json`, and `traffic.json`. A result request before a terminal
state returns `job_not_complete`; artifact access before completion returns
`artifact_not_ready`. Unknown jobs and artifacts return named 404 errors.

Graph or deterministic pipeline failures become a terminal `rejected` job. The
error object always carries a non-empty message and one of the named reasons
`invalid_dimension_graph`, `conflicting_constraints`, `underconstrained_sketch`,
`unsupported_geometry`, or `engine_failure`. Request-envelope and storage errors
remain HTTP failures and are never reported as successful jobs.

## Durability and local metrics

Every accepted job writes an atomically replaced `job_state.json` beside its
source and graph. A restart restores completed and rejected results, preserves
the next job number, and requeues a persisted `queued` or `running` job. If a
persisted input or completed artifact is missing, the recovered job is red with
the named `recovery_input_missing` or `recovery_artifact_missing` reason; the
service does not silently invent or discard a result.

Terminal jobs append one line to `<jobs-dir>/metrics.jsonl`. The
`cade-local-job-metric-v1` record contains only status, duration, green/yellow/red
counts, a nullable rejection reason, and the active-job count observed at start.
It deliberately excludes job and drawing IDs, filenames, paths, graph data,
source bytes, and user text. Metrics never leave the configured local directory.
State or metric write failures are exposed as `job_state_persist_failed` or
`local_metrics_write_failed` instead of a successful result.

## Evidence boundary

M9 proves service orchestration, deterministic model construction, and artifact
identity. On the client-graph path, a green row still does not independently
prove that the graph matches the pixels. On the provider path, strict reader-v2
output must survive deterministic raster binding before construction. M5-R is
the separate blind-reader quality evidence; M15 proves that provider substitution
and adversarial reader outputs cannot bypass fail-closed validation. Inputs
without a trustworthy graph are rejected rather than inferred silently.
