Wheelhouse docs

Hint placeholders#

A hint is an offer an installed app makes on another page of the web UI: "AdGuard Home is running at 10.99.0.10 — point the router's resolver at it", together with the exact operations that would do it. The operator still stages and commits them. Nothing is applied by a hint.

The operations in a hint are templates, because the catalogue ships to every router and cannot know any one router's addresses. A path element may carry a placeholder, which is resolved from the router the hint is offered on, never from the catalogue.

bash
curl -sk "$R/api/apps/hints/dns" -H "Authorization: Bearer $T"
json
[
  {
    "app_id": "adguard",
    "app_name": "AdGuard Home",
    "address": "10.99.0.10",
    "title": "Point the resolver at AdGuard Home",
    "body": "…",
    "ops": [
      {"op": "set", "path": ["service","dns","forwarding","name-server","10.99.0.10"]}
    ]
  }
]

The four placeholders#

PlaceholderResolved from
{{address}}The app's own container address, as the router's configuration holds it. Per app, added to the variable set for that app's hints only.
{{dhcp.network}}The first shared-network name under service dhcp-server that has at least one subnet, taking names in sorted order.
{{dhcp.subnet}}That scope's first subnet, again in sorted order.
{{dns.domain}}system domain-name. If that is unset, the first authoritative zone under service dns forwarding.

The pattern is {{[a-z.]+}} and it is matched inside a path element, so a template may embed one in a larger string. Every occurrence of the same placeholder in one hint resolves to the same value.

When a placeholder cannot be filled#

A hint whose router cannot fill a placeholder keeps its advice and loses its operations. The body gains a sentence naming what was missing:

(No commands offered: the router has no DHCP scope yet.)
MissingThe sentence
addressthe container has no address yet
dhcp.networkthe router has no DHCP scope yet
dhcp.subnetthe router has no DHCP subnet yet
dns.domainthe router has no domain (set system domain-name, or an authoritative zone)
anything else<name> is unknown

That behaviour is not politeness. Staging a command with an empty path element fails at commit with an error the operator did not cause, and an operation that named one laboratory's LAN 10.0.0.0/16 was once staged, verbatim, on every router the catalogue shipped to. Nothing site-specific may be written into the catalogue itself.

An app running on the host network has no container address, so its {{address}} hints become advice.

Which apps offer hints#

An app contributes hints only when all of these hold:

  • it matched a catalogue entry by image;
  • it is not disabled in the configuration (container name <n> disable);
  • the hint's page equals the page being asked about.

The pages hints target#

GET /api/apps/hints/{page} takes the page name. The catalogue currently places 20 hints on twelve pages:

PageHints
dns5
firewall3
nat2
dhcp2
apps, certificates, daemons, qos, routes, sessions, system, wireguard1 each

Re-derive that for the version you are running:

bash
python3 -c '
import json, collections
c = json.load(open("agent/catalog.json"))
n = collections.Counter(h["page"]
                        for a in c["apps"]
                        for h in a.get("integration", {}).get("hints", []))
print(dict(n))
'

Anatomy of a hint#

FieldMeaning
pageThe UI page it appears on.
titleOne line, shown as the offer.
bodyThe explanation. A missing placeholder appends its note here.
opsZero or more {op, path} templates. op is set or delete.

A hint with no ops at all is pure advice, which is legitimate — some of them exist to tell you a thing is possible, not to write it for you.

Cost#

Resolving the variables reads three configuration subtrees — service dhcp-server, system and service dns — from the agent's read cache, and listing installed apps reads the container configuration. Every one of those is a cached read; the endpoint does not health-probe, so asking for hints does not cost a probe round trip per app.

See also#

Checked against#

agent/catalog.go (Hint, HintOp, expandOps, placeholder), agent/apps.go (hintsForPage, missingHintNote, resolveHintVars, handleAppHints, collectInstalled), agent/catalog.json, agent/catalog_test.go, docs/apps.md "Integration".

Updated 2026-09-02 catalogue hints apps integration