{% set page_title = 'System' %} {% include '_header.html' %}

System · little-sister {{ version }}

{# Server-seeded, same contract as the dashboard's stamp (ADR-0005): this page doesn't navigate on its own, so it always says from when the information below actually is. Under the kiosk layout the slim line already carries it — there must be exactly one #status-updated on the page, because that is what the poller looks for. #} {% if layout != 'kiosk' %}
updated {{ rendered_at }}
{% endif %}
{% include '_system_info.html' %}
{# The registered check types and who answers for each. A type may come from a separately released package (ADR-0051), so this is the only place an operator can read which code — and which version — is behind a `type:` name in their YAML. Always shown: there are always the built-ins, and the point is the comparison between them and everything else. Split, though. The types this configuration runs come first; the rest fold into a disclosure, because on a small deployment they are most of the card and none of the question being asked. They stay reachable because a registered-but-unused type is the proof a freshly installed plugin took — see `_type_roster`, which owns the reasoning. #} {%- macro type_table(rows) %} {% for origin, count in rows %} {#- "used by", because a bare count under a type name reads as something the *package* brought — two checks shipped — when it is the opposite: how many checks in this deployment's configuration name this type. A package contributing one type to two checks is the ordinary case, so the ambiguous phrasing was wrong most of the time. -#} {#- The loaded path under the module name, because the version beside it is the installed metadata's claim: a source copy earlier on the path shadows an installed distribution of the same name, and then the version is answering for code that is not running. Exactly the case somebody reads this table to diagnose, so the table shows both. -#} {% endfor %}
TypeModuleVersionFrom
{{ origin.type_name }} {%- if count %}
used by {{ count }} check{{ 's' if count != 1 else '' }}{% endif %}
{{ origin.module }} {% if origin.path %}
{{ origin.path }}{% endif %}
{% if origin.version %}{{ origin.version }}{% else %}—{% endif %} {% if origin.builtin %}built-in{% elif origin.distribution %}{{ origin.distribution }}{% else %}source{% endif %}
{%- endmacro %}

Check types

{% if types_in_use %} {{ type_table(types_in_use) }} {% if types_folded %} {#- The count belongs in the summary: a closed disclosure has to say what it is hiding, or an operator looking for a type they cannot find has no reason to open it. #}
{{ types_folded | length }} more registered type{{ 's' if types_folded | length != 1 else '' }}, unused by this configuration
{{ type_table(types_folded) }}
{% endif %} {% else %} {#- Nothing to split against (no engine, or no checks) — and that is the moment the roster matters most, so it is shown whole. #} {{ type_table(types_folded) }} {% endif %}
{# What is installed beside the library. A package reaches this instance through four seams and the tables below are each about one of them, so until this card the *version* of a package that registers no check type was unreadable from the running instance — see `_registering_packages`. One row per installation, not per contribution: the loaded path and the release belong to the package, and printing them on every slot row would say the same thing twice for a package that fills two. Absent when nothing but the library registered anything. #} {% if packages %}

Packages

{#- Two columns, not three. The loaded path is the longest thing on this page — an absolute site-packages path, most of it the same prefix on every row — and a column of its own left it a narrow ribbon breaking at the hyphen in "site-packages" while squeezing everything either side of it. Under the name it gets the table's whole width, which is what a path that long needs to stay readable. -#} {% for row in packages %} {#- The import name is what every other table on this page prints, so it leads; the distribution and version sit beside it because that pair is what an operator compares against what they installed. A package with no installed metadata is a copy on the path — the shadowing case, worth saying out loud. -#} {#- Each registration is one fact and must not break across lines: "slot reason.actions" split after "slot" reads as two entries, and the column is narrow enough for that to happen. The list still wraps *between* items — that is what the breaks are for. -#} {% endfor %}
PackageRegisters
{{ row.origin.package }} {% if row.origin.distribution %}· {{ row.origin.distribution }} {{ row.origin.version }}{% else %}· source{% endif %} {%- if row.origin.path %}
{{ row.origin.path }}{% endif %}
{%- for item in row.registers %} {{ item }}{% if not loop.last %}
{% endif %} {%- endfor %}
{% endif %} {# The extension seams (ADR-0034 / ADR-0035), outside #system-info because they are decided at import time and never move while the process runs — re-fetching them on the ~10s poll would say nothing new. Shown only when something registered, so a deployment with no extensions has no section rather than an empty one. Registration is import-order sensitive and each of its failures is a log warning, so the running instance has to be able to answer "did my extension take?" without anyone opening a log file. #} {% if slot_fillers or extra_aspects %}

Extensions

{% if slot_fillers %} {% for filler in slot_fillers %} {#- Which kind: a dependent package's registration, or the library's own declarative action list, which fills the same slots through the same seam (ADR-0038). Worth naming, because an operator looking here after adding a config action needs to see the difference between "mine" and "theirs". -#} {% endfor %}
SlotPackageTemplateContextFrom
{{ filler.slot }} {{ filler.package }} {{ filler.template }} {% if filler.context %}provider{% else %}—{% endif %}{% if filler.builtin %}built-in{% else %}extension{% endif %}
{% endif %} {% if extra_aspects %} {#- Each name with the package that registered it: the name alone says the file is *declared*, which is not the question when the file is misbehaving. An owner is absent only where the caller could not be inferred, and then nothing is claimed. #}

Registered configuration aspects: {% for name, owner in extra_aspects %}config/{{ name }}{% if owner %} ({{ owner }}){% endif %}{% if not loop.last %}, {% endif %}{% endfor %}

{% endif %}
{% endif %} {# The effective Content-Security-Policy (ADR-0048 §7). Always shown, unlike the extensions card above: there is always a policy, and the reason to print it is that a source may come from a *package* — a request blocked inside somebody else's code is undebuggable from here unless the page says who asked for each origin. The header name is part of the answer: a Report-Only policy reports and blocks nothing. #}

Content-Security-Policy

{{ csp_header }}

{% for directive, sources in csp_directives %} {% endfor %}
DirectiveSources
{{ directive }} {%- for source in sources %} {{ source.value }} ({{ source.by }}) {%- if not loop.last %}
{% endif %} {%- endfor %}
{% include '_page_end.html' %}