{% extends "base.html" %} {% block title %}Standalone host — {{ plugin.package }}{% endblock %} {# trust-1: the mutation forms' header bag. The operator action token rides ONLY an armed page view (one whose query carried it); the token values are per-launch urlsafe secrets, so nothing here can break out of the attribute. #} {% macro action_headers(csrf_token, operator_token) -%} {"X-CSRF-Token": "{{ csrf_token }}"{% if operator_token %}, "X-Operator-Action-Token": "{{ operator_token }}"{% endif %}} {%- endmacro %} {% block content %}

Standalone host

One adapter session over one device. No gateway stands behind this host: no {{ absent | join(", ") }}.

{% for device in devices %}

{{ device.manufacturer }} {{ device.model }}

device
{{ device.id }}
transport
{{ device.transport }}
connection key
{{ device.connection_key }}
{%- if scan_available and device.port_path %} {# The endpoint fields (#385 §1.2): the physical candidate this row names. #}
port
{{ device.port_path }}
USB serial
{{ device.usb_serial or "—" }}
{%- endif %}
state
{%- if connected %}connected {%- else %}disconnected{% endif -%}
{%- if scan_available and device.port_path %} {# The pick flow (#385 §1.5): each confirmed candidate carries its Bind action; the bound candidate carries the marker instead. #} {%- if binding and binding.port_path == device.port_path %}

bound endpoint

{%- else %}
{%- endif %} {%- endif %}

Open device page

{% if has_presentation %}

Presentation pages

{% else %}

This plugin declares no presentation documents; the device page serves its readings.

{% endif %}
{% endfor %} {% if scan_available %}

Discovery

{# NFR-O3: serial hosts scan explicitly; loading this page never transmits. #} {% if scan_error %} {% endif %} {% if binding_error %} {% endif %} {# trust-1: the per-launch operator action token. Only a page view whose query carried the token renders it into the forms' headers — a page GET without the credential never yields it. The unarmed view says so and names where the token lives, never a silent refusal. #} {% if not operator_token %}

This page view carries no operator action token. Bind and unbind are refused without it. Open the URL that the serve command printed. That URL carries the token. Or append ?operator_action=<token> to this page. The token is printed at serve start. It is also written to the operator-action-token.json file under the capture root.

{% endif %} {% if binding %}

Bound endpoint — {{ binding.endpoint_kind }}: {{ binding.port_path }}{% if binding.usb_serial %} (USB serial {{ binding.usb_serial }}){% endif %}, bound {{ binding.bound_at }}.

{% else %}

No endpoint is bound yet. A scan opens candidate ports and asks each for its identity; nothing else is transmitted, and loading this page transmits nothing. Pick a listed port to bind it to this host.

{% endif %}
{% endif %} {% endblock %}