Skip to main content

Ash API Contract Endpoint Inventory

This inventory is the implementation starting point for Ash API contract unification. It maps externally accessible routes to their handlers, consumers, baseline tests, existing contract docs, classification, and migration decision. A route is not converted solely for uniformity: Ash-backed routes benefit from a generated contract, retained routes have an explicit protocol rationale, and deferred routes have no approved migration in this feature.

Classifications:

  • ash-backed: externally consumed resource/action contract using Ash and generated OpenAPI as the canonical contract. A compatibility wrapper may remain when it preserves an existing client wire format.
  • retained-controller: infrastructure or workflow protocol intentionally kept in a Phoenix controller with explicit contract documentation and rationale.
  • deferred: externally useful contract whose future need or migration boundary is not established; unchanged in this feature.
  • ui-only: browser/LiveView-only behavior with no durable external contract.

Generated OpenAPI And Ash JSON:API Surface​

RouteCurrent handlerConsumerBaseline testsContract docsClassificationMigration decisionRationale
GET /openapi.yamlPlug.Static via NixstasisWeb.Endpoint and NixstasisWeb.static_paths/0API readers, docs toolingAdd schema/static contract check if changedpackages/server/priv/static/openapi.yaml, website/docs/reference/contracts.mdash-backedKeep generated static artifactStatic generated OpenAPI artifact is the durable file referenced by docs.
GET /api/json/swaggeruiOpenApiSpex.Plug.SwaggerUIAPI readers, developersAdd route smoke test if changedwebsite/docs/reference/contracts.mdash-backedKeep generated UIDeveloper UI for the Ash generated API docs; not a product data endpoint.
GET /api/json/open_apiNixstasisWeb.AshJsonApiRouterAPI readers, developersAdd route smoke test if changedpackages/server/priv/static/openapi.yaml, website/docs/reference/contracts.mdash-backedKeep generated router endpointMachine-readable OpenAPI endpoint served through the Ash JSON:API router.
/api/json/devicesNixstasisWeb.AshJsonApiRouter / Nixstasis.Devices.DeviceOperator/developer API automation, internal docspackages/server/test/nixstasis_web/controllers/builder_contract_json_api_test.exspackages/server/priv/static/openapi.yamlash-backedKeep generated with operator role gateExisting Ash resource route group for device CRUD; separate from device runtime /api/v1/devices protocol and gated by Phoenix JSON:API permissions.
/api/json/pending_commandsNixstasisWeb.AshJsonApiRouter / Nixstasis.Devices.PendingCommandOperator/developer API automation, internal docsAdd Ash JSON:API route tests when expandedpackages/server/priv/static/openapi.yamlash-backedKeep generated with operator role gateExisting Ash resource route group for pending command CRUD; collection writes require unscoped device manage permission.
/api/json/alertsNixstasisWeb.AshJsonApiRouter / Nixstasis.Monitoring.AlertOperator/developer API automation, internal docsAdd Ash JSON:API route tests when expandedpackages/server/priv/static/openapi.yamlash-backedKeep generated with operator role gateExisting Ash resource route group for alert CRUD.
/api/json/alert_rulesNixstasisWeb.AshJsonApiRouter / Nixstasis.Monitoring.AlertRuleOperator/developer API automation, internal docspackages/server/test/nixstasis_web/controllers/builder_contract_json_api_test.exspackages/server/priv/static/openapi.yamlash-backedKeep generated with operator role gateExisting Ash resource route group for alert-rule CRUD called out by the feature plan.
/api/json/telemetry_eventsNixstasisWeb.AshJsonApiRouter / Nixstasis.Monitoring.TelemetryOperator/developer API automation, internal docsAdd Ash JSON:API route tests when expandedpackages/server/priv/static/openapi.yamlash-backedKeep generated with operator role gateExisting Ash resource route group for telemetry event create/read; collection writes require unscoped device manage permission.
/api/json/custom_reportsNixstasisWeb.AshJsonApiRouter / Nixstasis.Reporting.CustomReportOperator/developer API automation, internal docsAdd Ash JSON:API route tests when expandedpackages/server/priv/static/openapi.yaml, website/docs/modules/server-reporting.mdash-backedKeep generated with operator role gateExisting Ash resource route group for custom report CRUD.
/api/json/system_settingsNixstasisWeb.AshJsonApiRouter / Nixstasis.SystemSettingAdmin API automation, internal docspackages/server/test/nixstasis_web/controllers/builder_contract_json_api_test.exspackages/server/priv/static/openapi.yamlash-backedKeep generated with admin role gateExisting Ash resource route group for system settings.

Device Runtime Generated Target​

The five /api/v1 device rows below are the ash-backed migration group. Their canonical generated target is the additive route family below; the /api/v1 paths remain compatibility wrappers for the Go client and existing automation. The existing /api/json/devices operator CRUD family is not reused for these workflow actions.

Generated routeContract ownerSecurity and compatibility boundary
GET /api/json/device_runtime/devices:list_runtime_devices, adapting Devices.list_devices/1Operator bearer/device-view permission; preserves product, account_number, approval_status, connectivity_status, ipv4_address, and active-filter metadata.
POST /api/json/device_runtime/devices/register:register_runtime_device, calling Device.register through public normalization and token issuanceNo application device credential; generated route is additive while /api/v1/devices/register remains the application/json 201 wrapper.
POST /api/json/device_runtime/devices/{device_id}/heartbeat:heartbeat, delegating to Monitoring.heartbeat/2deviceBearer HTTP bearer scheme; generated route and wrapper preserve 30/60-second heartbeat limiting.
POST /api/json/device_runtime/devices/{device_id}/command_results:acknowledge_command_results plus script/policy ingestiondeviceBearer HTTP bearer scheme; /api/v1 retains 202, acknowledged-count, replay/duplicate, and malformed-list behavior.
GET /api/json/device_runtime/devices/{device_id}/command_payloads/{ref}:fetch_command_payload, backed by Devices.get_command_payload/2deviceBearer HTTP bearer scheme; /api/v1 retains the raw payload and 404 behavior.

Generated OpenAPI uses route-level security: deviceBearer is an HTTP bearer scheme using the Authorization header; it applies only to the last three operations, and query-string device credentials are rejected. Registration has security: [], and the generated list uses the operator bearer boundary. The raw token is assigned by the device-runtime permission plug and is not an Ash action argument. Unknown devices remain 404, missing or invalid bearer tokens 401, and unapproved devices 403. The device_runtime dispatch in the existing JsonApiPermissions pipeline sets the authenticated device actor and enables the list/registration routes. The generated heartbeat action preserves its 200 response, telemetry/inventory side effects, command delivery, probe, remote-access, and heartbeat-rate-limit behavior; the command-result acknowledgement and payload-fetch actions reuse the same branch. Generated action schemas and the compatibility device-api.yaml sections remain side by side because they document distinct generated and /api/v1 transport contracts. Runtime tests prove complete generated coverage in packages/server/priv/static/openapi.yaml.

Excluded UI-Only Ash Resources​

The script workbench persists its records through Ash resources, but those resources are not currently supported HTTP contracts. The LiveView uses Nixstasis.Domain directly, and exposing generic CRUD would bypass the workbench's validation, command-dispatch, and audit boundaries. Their AshJsonApi route declarations are therefore intentionally absent from Nixstasis.Domain and the generated OpenAPI artifact.

ResourceInternal consumerBaseline boundary testClassificationDecision
Nixstasis.Scripts.ScriptDraftScript workbench LiveView/domainpackages/server/test/nixstasis_web/controllers/script_json_api_test.exsui-onlyKeep Ash ownership; do not expose generic JSON:API CRUD.
Nixstasis.Scripts.ScriptVersionScript workbench LiveView/domainpackages/server/test/nixstasis_web/controllers/script_json_api_test.exsui-onlyKeep Ash ownership; do not expose generic JSON:API CRUD.
Nixstasis.Scripts.ScriptValidationRunScript workbench LiveView/domainpackages/server/test/nixstasis_web/controllers/script_json_api_test.exsui-onlyKeep Ash ownership; do not expose generic JSON:API CRUD.
Nixstasis.Scripts.ScriptTestRunScript workbench LiveView/domainpackages/server/test/nixstasis_web/controllers/script_json_api_test.exsui-onlyKeep Ash ownership; do not expose generic JSON:API CRUD.
Nixstasis.Scripts.ScriptDeploymentRunScript workbench LiveView/domainpackages/server/test/nixstasis_web/controllers/script_json_api_test.exsui-onlyKeep Ash ownership; do not expose generic JSON:API CRUD.
Nixstasis.Scripts.ScriptClientActionScript workbench LiveView/domainpackages/server/test/nixstasis_web/controllers/script_json_api_test.exsui-onlyKeep Ash ownership; do not expose generic JSON:API CRUD.

Bespoke /api/v1 Routes​

RouteCurrent handlerConsumerBaseline testsContract docsClassificationMigration decisionRationale
GET /api/v1/builder-schemasNixstasisWeb.BuilderSchemaController.index/2Builder UI, external automationpackages/server/test/nixstasis_web/controllers/builder_schema_controller_test.exswebsite/docs/reference/openapi/builder-api.yaml, website/docs/modules/server-web.md, packages/server/priv/static/openapi.yamlretained-controllerKeep wrapper, route canonical generated action separately/api/v1 remains a compatibility wrapper; canonical generated Ash action contract is GET /api/json/builder_contract/schema_references.
GET /api/v1/builder-schemas/:schema_id/versions/:schema_version/optionsNixstasisWeb.BuilderSchemaController.options/2Builder UI, external automationpackages/server/test/nixstasis_web/controllers/builder_schema_controller_test.exswebsite/docs/reference/openapi/builder-api.yaml, website/docs/modules/server-web.md, packages/server/priv/static/openapi.yamlretained-controllerKeep wrapper, route canonical generated action separately/api/v1 remains a compatibility wrapper; canonical generated Ash action contract is GET /api/json/builder_contract/schemas/:schema_id/versions/:schema_version/options. The generated route's bearer/report-view authorization and the wrapper's compatibility authorization are separate contracts; preserve not-found behavior.
POST /api/v1/builder-configurations/validateNixstasisWeb.BuilderConfigValidationController.create/2Builder UI, external automationpackages/server/test/nixstasis_web/controllers/builder_config_validation_controller_test.exswebsite/docs/reference/openapi/builder-api.yaml, website/docs/modules/server-web.md, packages/server/priv/static/openapi.yamlretained-controllerKeep wrapper, route canonical generated action separately/api/v1 remains a compatibility wrapper; canonical generated Ash action contract is POST /api/json/builder_contract/builder_configurations/validate.
GET /api/v1/devicesNixstasisWeb.DeviceController.index/2Device list API consumers, possible UI helperspackages/server/test/nixstasis_web/controllers/device_controller_test.exswebsite/docs/reference/openapi/device-api.yaml, website/docs/modules/server-web.mdash-backedRetain compatibility wrapper; generated list action is canonicalResource-oriented device list overlaps Ash Device routes; preserve runtime filters and status semantics.
POST /api/v1/devices/registerNixstasisWeb.DeviceController.register/2Go clientpackages/server/test/nixstasis_web/controllers/device_controller_test.exswebsite/docs/reference/openapi/device-api.yaml, website/docs/client-server-interface.md, website/docs/modules/server-devices.mdash-backedRetain compatibility wrapper; generated registration action is canonicalDurable credential issuance contract; preserve pending/approved token behavior exactly.
POST /api/v1/devices/:device_id/heartbeatNixstasisWeb.HeartbeatController.create/2Go clientpackages/server/test/nixstasis_web/controllers/heartbeat_controller_test.exswebsite/docs/reference/openapi/device-api.yaml, website/docs/client-server-interface.md, website/docs/modules/server-devices.mdash-backedRetain compatibility wrapper; generated heartbeat action is canonicalDurable protocol endpoint with telemetry, command polling, rate limiting, and remote-access token behavior.
POST /api/v1/devices/:device_id/command_resultsNixstasisWeb.DeviceCommandController.command_results/2Go clientpackages/server/test/nixstasis_web/controllers/device_command_controller_test.exswebsite/docs/reference/openapi/device-api.yaml, website/docs/client-server-interface.md, website/docs/modules/server-devices.mdash-backedRetain compatibility wrapper; generated command-result action is canonicalPreserve replay/duplicate acknowledgement behavior, malformed-list 400, and command-policy side effects.
GET /api/v1/devices/:device_id/command_payloads/:refNixstasisWeb.DeviceCommandController.command_payload/2Go clientpackages/server/test/nixstasis_web/controllers/device_command_controller_test.exswebsite/docs/reference/openapi/device-api.yaml, website/docs/client-server-interface.md, website/docs/modules/server-devices.mdash-backedRetain compatibility wrapper; generated payload-fetch action is canonicalPreserve payload shape, device authentication, and 404 behavior.
GET /api/v1/reports/:id/resultsNixstasisWeb.ReportResultController.show/2Current report preview path; no confirmed external export consumerpackages/server/test/nixstasis_web/controllers/report_result_controller_test.exswebsite/docs/reference/openapi/report-api.yaml, website/docs/modules/server-reporting.mddeferredLeave unchanged; revisit only when an external report/export contract is approvedReport result shaping is dynamic and separate from Ash CustomReport CRUD. Controller coverage now verifies trusted operator admission and SQL-level authorized-device scoping for telemetry results.
GET /api/v1/check_domainNixstasisWeb.TLSController.check_domain/2Caddy on-demand TLS ask endpointpackages/server/test/nixstasis_web/controllers/tls_controller_test.exswebsite/docs/reference/openapi/device-api.yaml, website/docs/client-server-interface.md, website/docs/runtime-boundaries.md, website/docs/modules/deployment-compose.mdretained-controllerRetainCaddy is the only consumer. This is an ingress protocol with simple allow/deny behavior, so Ash/OpenAPI adds no current product-contract benefit.

Development Diagnostics​

RouteCurrent handlerConsumerBaseline testsContract docsClassificationMigration decisionRationale
GET /_nixstasis/laptop/tls_observationsNixstasisWeb.TLSController.observations/2Compose development harness diagnosticspackages/server/test/nixstasis_web/controllers/tls_controller_test.exswebsite/docs/runtime-boundaries.md, website/docs/features/compose-dev-harness/design.mdretained-controllerRetainDevelopment-only diagnostic route, not a production product API contract.
DELETE /_nixstasis/laptop/tls_observationsNixstasisWeb.TLSController.clear_observations/2Compose development harness diagnosticspackages/server/test/nixstasis_web/controllers/tls_controller_test.exswebsite/docs/runtime-boundaries.md, website/docs/features/compose-dev-harness/design.mdretained-controllerRetainDevelopment-only diagnostic route, not a production product API contract.

/e2e Routes​

These routes remain retained-controller for this feature. They were added to exercise end-to-end testing and reporting; their future OpenAPI value and test model are separate decisions, and this feature does not expand into that review.

RouteCurrent handlerConsumerBaseline testsContract docsClassificationMigration decisionRationale
GET /e2e/suitesNixstasisWeb.E2ERunController.suites/2E2E harnesspackages/server/test/nixstasis_web/controllers/e2e_run_controller_test.exswebsite/docs/reference/openapi/e2e-api.yaml, website/docs/client-server-interface.md, website/docs/modules/server-e2e.mdretained-controllerRetain by defaultHarness workflow route gated by NixstasisWeb.Plugs.E2EEnabled; may become Ash-backed only if suite listing maps cleanly without weakening gate behavior.
GET /e2e/runsNixstasisWeb.E2ERunController.index/2E2E harness, diagnosticspackages/server/test/nixstasis_web/controllers/e2e_run_controller_test.exswebsite/docs/reference/openapi/e2e-api.yaml, website/docs/client-server-interface.md, website/docs/modules/server-e2e.mdretained-controllerRetain by defaultRun listing is resource-like but belongs to a gated harness API with protocol lifecycle semantics.
POST /e2e/runsNixstasisWeb.E2ERunController.create/2E2E harnesspackages/server/test/nixstasis_web/controllers/e2e_run_controller_test.exs, packages/server/test/nixstasis/e2e/e2e_test.exswebsite/docs/reference/openapi/e2e-api.yaml, website/docs/client-server-interface.md, website/docs/modules/server-e2e.mdretained-controllerRetain by defaultWorkflow endpoint with protocol-version header, idempotent reuse, environment locks, seed execution, and typed errors.
GET /e2e/runs/:idNixstasisWeb.E2ERunController.show/2E2E harness, diagnosticspackages/server/test/nixstasis_web/controllers/e2e_run_controller_test.exswebsite/docs/reference/openapi/e2e-api.yaml, website/docs/client-server-interface.md, website/docs/modules/server-e2e.mdretained-controllerRetain by defaultResource-like read under gated E2E protocol surface; can be revisited after workflow endpoints are stable.
POST /e2e/runs/:id/cancelNixstasisWeb.E2ERunController.cancel/2E2E harnesspackages/server/test/nixstasis_web/controllers/e2e_run_controller_test.exswebsite/docs/reference/openapi/e2e-api.yaml, website/docs/client-server-interface.md, website/docs/modules/server-e2e.mdretained-controllerRetain by defaultWorkflow transition endpoint with typed persistence errors and harness gating.
GET /e2e/runs/:id/resultsNixstasisWeb.E2ERunResultController.index/2E2E harness, diagnosticspackages/server/test/nixstasis_web/controllers/e2e_run_result_controller_test.exswebsite/docs/reference/openapi/e2e-api.yaml, website/docs/client-server-interface.md, website/docs/modules/server-e2e.mdretained-controllerRetain by defaultGated harness result listing; resource-like but tied to E2E lifecycle semantics.
POST /e2e/runs/:id/resultsNixstasisWeb.E2ERunResultController.create/2E2E harnesspackages/server/test/nixstasis_web/controllers/e2e_run_result_controller_test.exs, packages/server/test/nixstasis/e2e/e2e_test.exswebsite/docs/reference/openapi/e2e-api.yaml, website/docs/client-server-interface.md, website/docs/modules/server-e2e.mdretained-controllerRetain by defaultWorkflow ingestion endpoint with result submission and run status side effects.
GET /e2e/runs/:id/results/:journey_id/logNixstasisWeb.E2ERunResultController.log/2E2E harness, diagnosticspackages/server/test/nixstasis_web/controllers/e2e_run_result_controller_test.exswebsite/docs/reference/openapi/e2e-api.yaml, website/docs/client-server-interface.md, website/docs/modules/server-e2e.mdretained-controllerRetain by defaultLog retrieval has retention/pruned-log semantics that should stay explicit unless a future Ash action cleanly models them.

Reconciliation Notes​

  • website/docs/reference/openapi/device-api.yaml currently mixes device runtime APIs with Caddy TLS approval. Device runtime routes are an Ash-backed migration group; check_domain remains a Caddy-only retained controller.
  • GET /api/v1/devices accepts ipv4_address; preserve that filter in the Ash-backed contract or compatibility wrapper and generated/reference docs.
  • Heartbeat command_inventory is untrusted evidence persisted separately from sanitized telemetry; the server-owned command_inventory_probe is returned in the heartbeat response and does not get a new endpoint. Both fields belong in the generated action schema and the compatibility reference.
  • website/docs/reference/openapi/builder-api.yaml documents compatibility wrappers; generated /api/json/builder_contract/* is the canonical Ash contract. Its generated bearer/report-view authorization, 403 responses, raw success payloads, and 201 validation status are documented separately from the /api/v1 wrapper pipeline and its legacy 200/422 behavior.
  • The six script persistence resources remain Ash-owned for the workbench but are intentionally excluded from /api/json and the generated OpenAPI artifact. The current workbench calls the domain directly, and generic CRUD would bypass validation, command dispatch, and audit boundaries. A boundary test covers that no generic route is exposed; a future external contract requires audited domain-specific actions and a new design decision.
  • website/docs/reference/openapi/report-api.yaml remains the current preview contract, but report export is deferred until an external consumer is approved.
  • website/docs/reference/openapi/e2e-api.yaml remains the current retained contract; E2E conversion and test-model review are outside this feature.