trust_api.services.task_poller ============================== .. py:module:: trust_api.services.task_poller .. autoapi-nested-parse:: Background polling service: heartbeat → pull pending tasks → dispatch → report. All communication is outbound from the trust to the hub. The hub identifies the trust by API key alone — there is no ``{trust_name}`` segment in any URL. On the first successful heartbeat the hub returns ``{trust_id, trust_name, …}``; we log the resolved identity (so the operator can see which trust this host came up as) and, when ``EXPECTED_TRUST_ID`` is set in the kit file, verify the hub's answer against it. A mismatch means the wrong kit was deployed to the wrong host — we exit the process so a process-supervisor restart loop surfaces the misconfiguration loudly rather than the host silently acting as the wrong trust. Attributes ---------- .. autoapisummary:: trust_api.services.task_poller.CENTRAL_HUB_API_URL trust_api.services.task_poller.TRUST_API_KEY trust_api.services.task_poller.TRUST_API_KEY_HEADER trust_api.services.task_poller.EXPECTED_TRUST_ID trust_api.services.task_poller.POLL_INTERVAL_SECONDS trust_api.services.task_poller._identity_logged trust_api.services.task_poller._REPORT_MAX_RETRIES trust_api.services.task_poller._REPORT_RETRY_DELAY_SECONDS Functions --------- .. autoapisummary:: trust_api.services.task_poller._auth_headers trust_api.services.task_poller._maybe_announce_identity trust_api.services.task_poller._poll_for_tasks trust_api.services.task_poller._send_heartbeat trust_api.services.task_poller._report_task_result trust_api.services.task_poller._process_task trust_api.services.task_poller.run_poller Module Contents --------------- .. py:data:: CENTRAL_HUB_API_URL .. py:data:: TRUST_API_KEY .. py:data:: TRUST_API_KEY_HEADER .. py:data:: EXPECTED_TRUST_ID :value: '' .. py:data:: POLL_INTERVAL_SECONDS :value: 5 .. py:data:: _identity_logged :value: False .. py:function:: _auth_headers() -> dict[str, str] Return authentication headers for hub API calls. :returns: Single-entry mapping of the trust API key header to the configured key. :rtype: dict[str, str] .. py:function:: _maybe_announce_identity(body: dict) -> None Log resolved identity from the hub once; abort on EXPECTED_TRUST_ID mismatch. Called from every successful trust-facing response. The hub embeds ``{trust_id, trust_name}`` in every response body so the trust can identify itself; we only act on the first one (idempotent thanks to ``_identity_logged``). :param body: Parsed JSON response from the hub. :type body: dict :raises SystemExit: If ``EXPECTED_TRUST_ID`` is set and disagrees with ``trust_id``. .. py:function:: _poll_for_tasks(client: httpx.AsyncClient) -> list[dict] :async: Poll the central hub for pending tasks. :param client: HTTP client for making requests. :type client: httpx.AsyncClient :returns: Pending task dicts from the hub (encrypted payloads). :rtype: list[dict] .. py:function:: _send_heartbeat(client: httpx.AsyncClient) -> None :async: Send a heartbeat to the central hub and run the identity self-check. :param client: HTTP client for making requests. :type client: httpx.AsyncClient .. py:data:: _REPORT_MAX_RETRIES :value: 3 .. py:data:: _REPORT_RETRY_DELAY_SECONDS :value: 2 .. py:function:: _report_task_result(client: httpx.AsyncClient, task_id: str, result: dict) -> None :async: Report the result of a completed task back to the central hub. Retries up to ``_REPORT_MAX_RETRIES`` times with exponential backoff on failure, since a lost result can leave a task permanently stuck in IN_PROGRESS on the hub. :param client: HTTP client for making requests. :type client: httpx.AsyncClient :param task_id: The ID of the completed task. :type task_id: str :param result: Result dict with ``success`` and optional ``result`` / ``error`` fields. :type result: dict .. py:function:: _process_task(task: dict) -> dict :async: Process a single task by dispatching to the appropriate handler. :param task: Task dict with ``id``, ``task_type``, and ``payload`` fields. :type task: dict :returns: Result dict with ``success`` status (and optional ``result`` / ``error``). :rtype: dict .. py:function:: run_poller() -> None :async: Main polling loop. Runs indefinitely, polling the hub for tasks and processing them. This function is started as a background task during the FastAPI lifespan.