118 lines
6.0 KiB
Markdown
118 lines
6.0 KiB
Markdown
# Host Agent Local Console Task Submission
|
|
|
|
The Host Agent local Console adds a **Submit task to current Host** form on
|
|
the authenticated Tasks page. The form lets an operator who is already
|
|
signed in to the loopback Console enqueue a goal task for the current Host
|
|
without going through the Cloud Console or the public SDK.
|
|
|
|
The submission reuses the existing Host self-submission contract (a single
|
|
authenticated `POST /internal/v1/hosts/{host_id}/tasks` against the control
|
|
plane). It does not introduce a new Cloud endpoint, a public task scope, or
|
|
a cross-Host target.
|
|
|
|
## Automatic vs explicit device
|
|
|
|
The form offers two submission modes for the device target:
|
|
|
|
- **Automatic** — the form omits the `device_id` field. The Cloud scheduler
|
|
picks any eligible device currently registered for the authenticated
|
|
Host. The Console labels this option *Automatic (let Host choose an
|
|
eligible device)*.
|
|
- **Explicit device** — the operator selects one of the device IDs that are
|
|
currently registered in the running `DeviceManager`. In
|
|
enrollment-managed deployments these are the Cloud device IDs assigned by
|
|
the Host enrollment flow (the same IDs the heartbeat sends to the
|
|
control plane), not the local configuration IDs.
|
|
|
|
The Console revalidates the selected device against a fresh
|
|
`DeviceManager.list_devices()` snapshot before issuing the outbound
|
|
request. If the device was removed between page render and POST, the
|
|
Console returns a validation error and never calls the Host client.
|
|
|
|
## Required state and policy
|
|
|
|
- The Console session must be valid (the same local account login used for
|
|
the rest of the Console). The submission form is rendered only for
|
|
authenticated sessions, and the POST handler requires both a valid
|
|
session and the matching CSRF token.
|
|
- The Host must have completed self-enrollment. The Host self-submission
|
|
client is wired in by `host_agent.app.create_application` from the same
|
|
asynchronous `HostAgentClient` instance that drives heartbeat, claim,
|
|
lease renewal, and result reporting, so the submission form disappears
|
|
with a clear *submission client is not available yet* message until
|
|
enrollment is complete.
|
|
- The Cloud control plane must have `self_submission_enabled = true` in
|
|
the Host governance policy. A disabled policy is returned to the
|
|
operator as a definitive 4xx rejection ("Host self-submission is
|
|
disabled") without recording a successful audit entry; the cached
|
|
policy is *not* used for local authorization, so a stale cache never
|
|
silently denies a newly enabled Host.
|
|
|
|
## Cloud queue semantics
|
|
|
|
- Task creation is **not** idempotent. After the POST reaches the control
|
|
plane, the Cloud scheduler assigns a task ID. The Console never retries
|
|
the creation call. Even when the response is lost, a 5xx is returned, or
|
|
the success payload is malformed, the Console only reports the outcome
|
|
as **unknown** and instructs the operator to check the Cloud console
|
|
before submitting again. This avoids the known failure mode where one
|
|
operator click duplicates a task in the Cloud queue.
|
|
- The local Console does not show queue progress for a freshly submitted
|
|
task. After a successful submission the form redirect carries the Cloud
|
|
task ID in the URL and the Tasks page shows *Task submitted. Cloud task
|
|
ID: \<id\>*; execution progress, lease state, and the terminal result
|
|
continue to appear on the local Tasks page only after the Cloud
|
|
scheduler assigns the task to this Host and the assignment is processed
|
|
locally.
|
|
|
|
## Outcome-unknown procedure
|
|
|
|
The Console intentionally distinguishes the **unknown outcome** case from
|
|
both a confirmed submission and a definitive rejection:
|
|
|
|
- A transport failure (DNS, connect, read timeout, dropped connection),
|
|
any 5xx response, or a malformed 2xx payload makes the local client
|
|
raise `HostTaskSubmissionUnknownError`. The Console catches it and
|
|
redirects the operator to `GET /tasks?outcome=unknown`, which renders a
|
|
*Submission outcome is unknown. The task may have been queued. Check
|
|
the Cloud console before submitting again.* notice. No history entry is
|
|
written and no success confirmation is shown.
|
|
- A definitive 4xx rejection (for example, a disabled self-submission
|
|
policy or a target device that the control plane does not recognize as
|
|
owned by this Host) raises `HostAgentAPIError`. The Console renders the
|
|
Cloud's `detail` message safely through Jinja autoescape without
|
|
echoing the goal text or any credentials. No history entry is written
|
|
and no task ID is shown.
|
|
|
|
## Auditing
|
|
|
|
- Confirmed submissions write a `task_submission` history row that records
|
|
the Cloud task ID and, when applicable, the target device ID. The row
|
|
deliberately does **not** persist the goal, the Host token, the
|
|
session cookie, the lease secret, or any other operator credential. The
|
|
row is bounded by the existing `console_history_limit` and survives
|
|
process restarts.
|
|
- A local audit write failure is best-effort: it does not turn a
|
|
Cloud-confirmed submission into a retryable failure. A `task_submission`
|
|
history row is only written after the Cloud returns a 2xx with a usable
|
|
task ID, so a rejection or unknown outcome can never produce a
|
|
misleading successful audit entry.
|
|
|
|
## Operator checklist
|
|
|
|
1. Sign in to the loopback Console with a valid local account.
|
|
2. Open **Tasks**.
|
|
3. Type a non-empty goal in the textarea.
|
|
4. Choose **Automatic** to let the Cloud pick a device, or pick a listed
|
|
device ID for an explicit target.
|
|
5. Click **Submit task**.
|
|
6. On confirmation, the page shows *Task submitted. Cloud task ID: \<id\>*.
|
|
Track the task from the Cloud console; the local Tasks list will fill
|
|
in once the scheduler assigns the task to this Host.
|
|
7. If the page shows the *outcome is unknown* notice, check the Cloud
|
|
console for a matching task before submitting again.
|
|
8. If the page shows a Cloud rejection, fix the underlying issue (policy,
|
|
device ownership, lease state) and submit again. The page does not
|
|
preserve the goal text in the URL, in the error message, or in the
|
|
history; retype the goal when retrying.
|