Skip to main content

Credential Management

Every integration credential ServiceRadar holds lives in the database, encrypted, and reaches a plugin only as a short-lived scoped grant. There is one place to put it: Settings -> Networks -> Credential Rules (/settings/networks/credentials), under the Settings Discovery group. Managing anything on that page requires the settings.credentials.manage permission.

No integration credential is read from an environment variable, a Kubernetes Secret, or a config file. The one remaining exception is the agent's SNMP file path, documented under SNMP.

Not everything on this page has shipped yet

The current release is 1.4.46. Five mechanisms described below are merged work that has not appeared in a release yet, and each one is marked in place with a :::caution Not in 1.4.46 box:

  • the TLS Policy control rendering for UniFi Protect and Axis (TLS policy)
  • allowed_networks on the UniFi Protect, Axis, OpenText and AWX plugin manifests (0 cameras, 0 streams)
  • action-only:v1 on the AWX manifest (AWX api_token is required)
  • ca_bundle_pem and server_cert_fingerprint on a credential rule (CA trust material)
  • the netbox credential profile and its inventory_sync purpose (NetBox)

Replace <first-release> in those boxes with the version that ships them once it is cut. If you are reading this on a deployment running 1.4.46 or earlier, the behaviour described in a marked box is not present and the workaround in that box is the current answer.

Two objects: the credential and the rule

The page holds two different things, and most confusion comes from treating them as one.

What it isWhat it decides
Credential (network_credential_secrets)The material: an API token, a password, an SSH private key. Encrypted at rest; the secret half is never rendered back.Nothing. It is inert until a rule or a form binds it.
Rule (network_credential_rules)A binding: this credential, for this provider and purpose, against these targets, delivered to this edge scope.Which agent may use it, against which devices, over what transport.

A credential can be internal_encrypted (ServiceRadar holds the ciphertext, the default) or an external_reference (ServiceRadar holds a pointer into an external secret provider and resolves it at use time). Both look the same to a rule.

Not every provider takes a rule. A provider's descriptor declares supports_rules, and three of the providers on this page are credential-only:

ProviderRule?Where the credential is bound instead
Proxmox, UniFi Protect, Axis, OpenTextYesThe rule itself
NetBoxYes, from <first-release>The rule itself. Through 1.4.46 there is no netbox provider on this page and the token is a plugin assignment parameter -- see NetBox
AWX / AAPNoSettings -> Ansible -> Controllers, per controller and purpose
VulnCheckNoSettings -> Security -> Vulnerability Feeds, per feed
SNMPNoSettings -> SNMP Profiles, per profile or target

Providers come from packages, not from the UI

The provider list, the auth methods, the credential fields, the purposes, and the form's defaults are all read from approved, signed plugin package manifests (integrations.credential_profiles), merged with two descriptors the platform owns because they have no package: SNMP and VulnCheck.

Nothing is hardcoded in the web UI. If a provider is missing from the New Rule dropdown, either its package is not imported and approved yet -- see Wasm Plugins -- or it declares supports_rules: false and is credential-only. An empty New Rule menu reads "No approved integration descriptors"; an empty New Credential menu reads "Import and approve an integration package first".

Create a credential

  1. New Credential -> pick <provider> - <auth method>, for example Proxmox VE - API token. The choice fixes the provider and the auth method; there is no free-text provider field.
  2. Fill in the fields the descriptor declared. They differ per method: an API token is one password box, a Proxmox token is user/realm/token ID/token secret, an SSH key is username/private key/passphrase.
  3. Name it something you will recognise in a select box six months from now. pve-iad-readonly beats proxmox.
  4. Save.

Public fields (a username, a Proxmox token user) are stored in the clear and shown back to you. Secret fields are encrypted and never re-rendered; to change one, save a new value over it.

Create a rule

New Rule -> pick the provider. The form is prefilled from the manifest's rule_defaults, and only the controls the manifest declares in rule_controls are shown -- a provider that does not let you set allowed ports has no allowed ports box.

A worked Proxmox example, which exercises most of the fields:

FieldValueWhy
Namepve-iad-inventorySite plus purpose. It appears in the Consumers panel and in audit records.
Providerproxmox
Priority100The default. Lower wins; see Priority.
Secretpve-iad-readonlyOnly credentials matching this provider and auth method are offered.
Auth methodproxmox_api_token
Purposeinventory_enrichmentNot console_access; see Purposes.
Scope type / valueagent / agent-iad-01The agent that can reach the PVE management network.
Target queryin:devices metadata.proxmox_candidate:trueThe manifest default.
TLS policyverifyProxmox inventory enrichment refuses anything else.
Allowed ports8006

Save. The rule is enabled by default and materialises within a reconciliation cycle.

Nothing on this form makes verify succeed against a node whose certificate the agent does not trust. That is agent-side configuration, and for Proxmox it also depends on the node certificate's SAN list -- see CA trust material and Proxmox: TLS Verification and Node Certificates.

Purposes

A purpose is what the credential is allowed to be used for. Purposes are declared per provider by the package, not chosen from a global list:

ProviderPurposes
proxmoxinventory_enrichment, console_access
unifi-protectcamera_inventory, camera_stream
axiscamera_inventory, camera_stream
opentext-network-automationdevice_inventory
netboxinventory_sync (from <first-release>; see NetBox)
awxautomation_execution
vulncheckvulnerability_feed_download
snmpsnmp_monitoring

A rule can carry more than one purpose, and each purpose materialises its own plugin assignment with its own grant. Ticking camera_inventory and camera_stream on one UniFi rule produces two assignments, one per plugin.

Keep purposes separate when the access they imply is different. A read-only Proxmox API token belongs on an inventory_enrichment rule; SSH shell access belongs on a separate console_access rule with its own credential. Selecting console_access reveals a Console credential users fieldset -- at least one role, user/IdP subject, or IdP group must be named, and ServiceRadar rechecks those selectors when the console stream attaches.

Scope type and scope value

Scope answers "which edge path may hold this grant". It is the blast radius.

Scope typeScope valueUse when
agentan agent ID (offered as a dropdown when agents are known)The normal case. One agent reaches the appliance.
gatewaya gateway IDSeveral agents behind one gateway may need it.
partitiona partition nameA whole partition legitimately shares the credential.

Which scope types are offered comes from the provider descriptor. SNMP, for example, declares agent only.

Scope is enforced independently of the target query. A device that matches the query but is assigned to an agent outside the scope gets no grant -- the Preview panel's Matched and In Scope counts are exactly this difference.

Target query and the Runtime column

The target query is SRQL. It selects the devices the rule applies to, and the selection is re-evaluated -- a device that starts matching tomorrow is covered tomorrow.

The Runtime column on the rules table reports how the rule finds its targets. It shows one of five values:

BadgeMeaning
SRQLThe rule's own target query selects the targets. This is the normal case.
AutoThe rule additionally has Allow auto-discovery credential trials ticked (metadata.auto_discovery_enabled).
ScheduledA producer_schedule provider whose recurring refresh is enabled.
On demandA producer_schedule provider whose schedule exists but is not enabled. Use Run Now.
PendingA producer_schedule provider whose schedule has not been provisioned yet. Wait for credential reconciliation.

OpenText Network Automation is the only producer_schedule provider shipped today, so the last three badges only appear on an opentext-network-automation rule.

Auto is Proxmox-specific today and worth understanding before ticking it. An enabled Proxmox inventory_enrichment rule with auto-discovery on makes the mapper compile proxmox_candidate_probe_enabled into its job options. The mapper then runs an unauthenticated HTTPS fingerprint against port 8006 on reachable hosts and stamps anything that answers like a PVE web UI with metadata.proxmox_candidate=true (plus proxmox_candidate_source, _evidence, _observed_at, _port, _service, _title). Because the Proxmox manifest's default target query is in:devices metadata.proxmox_candidate:true, those devices then become rule targets and receive credentialed collection.

So Auto widens the target set from "devices you named" to "devices that answered a probe". The fingerprint itself sends no credential, but the credentialed collection that follows does. Leave it off unless the rule's scope can only reach the intended PVE network and the token is read-only.

Priority

Rules are ordered per provider by priority, lowest value wins, with insertion order breaking a tie. Priority only matters where two enabled rules of the same provider match the same device in the same scope; the Preview panel lists those overlaps under Credential Conflicts with the count of shared devices.

Use it deliberately: a narrow site-specific rule at 50 overriding a broad fallback at 100 is the pattern. Two rules at the same priority matching the same device is not a configuration, it is a coin flip.

TLS policy

verify (the default) or skip_verify.

Not in 1.4.46

The TLS Policy control renders whenever the provider declares transport rule controls. Through 1.4.46 the control was rendered only when the selected auth method also declared a non-empty tls_policies list, while the save path required a valid tls_policy unconditionally. UniFi Protect and Axis declare rule_controls.transport: true but no per-method tls_policies, so on those two providers the input was never drawn, the browser submitted nothing, and every save failed with Invalid TLS policy. There is no way to save such a rule on 1.4.46 or earlier -- see Invalid TLS policy.

First release containing the fix: <first-release>.

When the selected auth method narrows the permitted policies, only those are offered. An auth method that declares no tls_policies narrows nothing, and every policy stays on offer -- which is how core has always read an absent list.

Some methods narrow it to nothing else. Proxmox's proxmox_api_token declares tls_policies: [verify], so the select offers only verify, and inventory_enrichment is additionally checked at resolution time -- a skip_verify rule fails closed with proxmox_tls_verification_required and no grant is ever minted.

skip_verify is appropriate for an appliance whose certificate you cannot replace and whose network path you already trust -- a camera on an isolated VLAN reached by an agent on that VLAN. It is not appropriate anywhere the credential being sent is worth more than the appliance: Proxmox inventory enrichment writes into device identity, so a machine-in-the-middle there is an identity-forgery primitive, which is why the check refuses rather than warns.

Where you want verification and the appliance has a private or self-signed certificate, do not reach for skip_verify. Give the agent the trust anchor instead.

CA trust material

Today, trust for plugin HTTP is configured on the agent, not on the rule. The agent builds one host-owned HTTP client for every Wasm plugin call. Its trust roots are the operating system pool plus every PEM bundle listed in plugin_http_trusted_ca_files in agent.json (Helm: agent.pluginHTTPTrustedCAFiles, which already includes the ServiceRadar runtime CA). Those roots augment the system pool -- they widen what the agent will accept, they do not restrict it to your CA.

Three properties of that client decide every question below:

  • Wasm modules never see the bundle contents and cannot select or replace the roots.
  • If a configured path is unreadable, oversized, or holds no certificate, the agent disables outbound plugin HTTP entirely rather than falling back.
  • Hostname verification is normal Go TLS verification against the URL the agent actually dials. When that URL has an IP literal for a host, the certificate must carry that address as an IP Address SAN; a DNS SAN never matches an IP literal.

Restart the agent after changing the bundle.

Not in 1.4.46

ca_bundle_pem and server_cert_fingerprint on a credential rule. Merged work adds two optional, mutually exclusive columns to a rule:

  • ca_bundle_pem -- a PEM chain, rejected at save time if it does not parse as unencrypted CERTIFICATE blocks or if any certificate in it has expired.
  • server_cert_fingerprint -- a leaf pin, sha256: followed by 64 lowercase hex characters.

Both are validated when the rule is saved rather than at first use, and supplying both is rejected by an Ash validation and by a database check constraint. They are stored in the clear: a CA certificate and a fingerprint are trust anchors, not authenticators, so an operator can read back what a rule trusts and the values never go through the credential broker.

Scope of what has landed, stated precisely so nobody plans against more than exists: the change adds the rule columns, their validation, the migration, and permission for a manifest params template to reference them as $source: rule, field: ca_bundle_pem / field: server_cert_fingerprint. It does not add a form control on the Credential Rules page, no shipped plugin manifest references either field yet, and the agent's plugin HTTP client does not read them. Setting one on a rule therefore records an intent; it does not by itself change what the agent trusts. Until a manifest and the agent transport consume them, use plugin_http_trusted_ca_files above.

First release containing the columns: <first-release>.

Allowed ports

A comma- or space-separated list of ports (1-65535) the grant may be used against. The manifest supplies a default: 8006 for Proxmox, 443, 7447 for UniFi Protect, 443, 554 for Axis, and (from <first-release>) 443, 8443 for NetBox. Narrow it, do not widen it -- a rule that allows 443 and nothing else cannot be redirected at an SSH daemon.

SSH host key policy

Shown only for auth methods that declare host key policies (today, Proxmox's ssh_private_key). known_hosts when the agent has a managed known-hosts file, trust_on_first_use for first-connection pinning, skip_verify only for temporary testing.

How a rule reaches a plugin

Nothing about this path hands raw secret material to the sandboxed plugin.

  1. Materialisation. A reconciliation worker resolves the rule's target query and scope, and writes one plugin_assignments row per purpose per eligible agent. Each row is stamped policy_id: network-credential-rule:<rule id>[:<purpose>], which is how the Consumers panel answers "what is this rule doing" from the assignments table alone.
  2. The params template. The assignment's params are rendered from the manifest's provisioning.consumers[].params template. It carries a credential_broker grant envelope -- grant type, credential rule ID, secret reference, consumer, target, resolution location, TTL, the header to inject, and an allow-list of methods and paths -- plus a <field>_secret_ref placeholder such as api_token_secret_ref. No secret value is in the row.
  3. Delivery. At agent config generation, core checks the embedded grant is still fresh, re-mints it through the broker if not, resolves <field>_secret_ref to <field> for host-brokered paths, and writes an audit row for the resolution.
  4. Use. The Wasm guest calls out through the host, which applies the grant's injection and allow-list and the manifest's egress permissions. For host-brokered paths the guest never sees the material.

Grants are short-lived (300 seconds for the providers above) and pinned to one host, port, method set, and path set. There is no catch-all grant.

Row actions

Each rule row on the table offers:

ActionWhat it does
PreviewOpens the target preview. Shown for every provider except producer_schedule ones, which get Run Now instead.
Run NowDispatches an immediate refresh. Shown for producer_schedule providers only, and disabled until the schedule is provisioned.
ConsumersExpands an inline panel listing the assignments this rule currently materialises: agent, plugin, purpose, enabled, last materialised.
EditOpens the rule form.
Enable / DisableFlips enabled. A disabled rule matches nothing and mints no grants; existing grants expire on their own TTL.

Preview is the one to use before enabling anything. It shows:

  • Matched -- devices the target query returns.
  • In Scope -- of those, the ones whose agent falls inside the rule scope. This is the number that matters. Matched high and In Scope zero means the query is fine and the scope is wrong.
  • Agents -- per-agent device distribution, so you can see which edge agent would receive the grant.
  • Conflicts -- other rules of the same provider that also match these devices, with priority and overlap count.
  • Effective Inputs (dry run) -- the rendered params template per purpose, including the plugin ID, the policy ID, the interval, and the timeout. Secret references are shown as references; secret material is never resolved or displayed here.

Provider setup

Proxmox VE

Proxmox inventory enrichment mandates verify, and ServiceRadar always dials a PVE node at https://<ip>:8006, so the certificate work is not optional and it is not satisfied by "signed by a CA we trust" alone. The full procedure, with the commands and the reasoning, is Proxmox: TLS Verification and Node Certificates. The short form:

  1. Create a least-privilege PVE token. See Proxmox: Least-Privilege Role. Record the user, realm, token ID, and token secret.
  2. Make the node certificate verifiable from the agent, for the IP the agent uses. Two independent conditions, both required: the chain must reach a root the agent trusts (add the PVE cluster CA to plugin_http_trusted_ca_files), and the node certificate's SAN list must contain IP Address:<that address>. Inspect the SAN list before assuming; proxmox.md gives the command and how to read its output.
  3. Create the credential. New Credential -> Proxmox VE - API token. Stored public identity is <user>@<realm>!<token_id>; the token secret is encrypted.
  4. Create the rule. Provider proxmox, auth method proxmox_api_token, purpose inventory_enrichment, TLS verify, allowed ports 8006, scope the agent that reaches the PVE management network. Target query: start from in:devices metadata.proxmox_candidate:true, or name the hosts explicitly if you are not using auto-discovery.
  5. Preview, then enable. Confirm In Scope is non-zero and the agent shown is the one you expect.

Console access is a separate rule with a separate credential -- see Proxmox: Console Access.

UniFi Protect

The plugin talks to the UniFi OS controller (Dream Machine, Cloud Gateway, or a UniFi OS console), not to each camera. Cameras are enumerated from Protect after login. Full detail in UniFi Protect.

  1. Create an API key in UniFi OS under Settings -> Control Plane -> Integrations. Prefer this over a local admin account.
  2. Create the credential. New Credential -> UniFi Protect - API key. (UniFi Protect - Username and password also exists if you cannot use a key.)
  3. Create the rule. Provider unifi-protect, purposes camera_inventory and camera_stream, allowed ports 443, 7447, target query in:devices vendor:"Ubiquiti".
  4. Set the controller field. The UniFi OS / Protect controller box takes the controller address -- hostname, IP, or a full URL, from which ServiceRadar keeps the host. The controller is almost always reached by a private IP such as 192.168.1.1. Leave it blank only when the target query already resolves the controller device itself; the plugin calls that host, not a seed row's IP.
  5. TLS. verify if the controller presents a certificate the agent can chain to and that is valid for the address the agent dials -- add the issuing CA to plugin_http_trusted_ca_files (see CA trust material). skip_verify is defensible here when the controller still has its factory certificate and the agent is on the same trusted segment; unlike Proxmox, the UniFi grant is resolved at the control plane, so a skip_verify rule does relax the plugin's own transport.

Because the controller is on private address space, the plugin manifest must declare allowed_networks covering it. A wildcard in allowed_domains does not cover an IP literal by design. See the egress troubleshooting entry for which releases carry that declaration.

Axis

Axis cameras are addressed directly over VAPIX; there is no controller.

  1. Create a dedicated camera account on the cameras -- viewer or operator level, not root.
  2. Create the credential. New Credential -> Axis (VAPIX) - Username and password.
  3. Create the rule. Provider axis, auth method username_password, purposes camera_inventory and camera_stream, allowed ports 443, 554, target query in:devices vendor:"Axis", scope the agent on the camera VLAN.
  4. TLS. verify if the fleet has a managed CA you can add to plugin_http_trusted_ca_files. Axis cameras normally present a self-signed certificate issued per camera, which no shared anchor covers -- for a fleet, either install CA-signed certificates on the cameras or use skip_verify on an isolated segment.

Axis, like UniFi Protect, is reached by private IP and needs the manifest's allowed_networks declaration -- see 0 cameras, 0 streams for which releases carry it.

AWX / AAP

AWX is credential-only: its descriptor sets supports_rules: false, so an AWX token is a credential, bound to an Ansible Controller record. There is no AWX credential rule, and the AWX bridge is a command bridge -- each dispatch carries its own grant. Full detail in Ansible Integration.

  1. Create three purpose-scoped AWX tokens in AWX, per Ansible: store purpose-scoped tokens: a sync token, an execution token, and (for callback-enabled playbooks) a callback token.
  2. Create one credential per token. New Credential -> AWX / AAP - API token. Name them for the purpose: awx-prod-sync, awx-prod-exec, awx-prod-callback.
  3. Bind them on the controller. Settings -> Ansible -> Controllers -> add or edit a controller and select each credential in the Sync, Execution, and Callback fields.

Do not create an AWX credential rule. It will not appear in the New Rule dropdown, and the AWX bridge does not read one.

NetBox

NetBox has two independent paths, and they take their credentials differently:

  • Device inventory sync (netbox-inventory Wasm plugin) reads its token from the plugin assignment's sources[].api_token, under Admin -> Plugins (/settings/agents/plugins). The plugin fails a source outright when that value is blank.
  • IPAM prefix tag import (a core Oban worker) uses the NetBox source under Settings -> Integrations. It is not a credential-rule path and is not changed by the box below.

Setup for both is in NetBox Integration.

Not in 1.4.46

The netbox credential profile. Merged work adds an integrations.credential_profiles block to go/cmd/wasm-plugins/netbox/plugin.yaml, which makes NetBox a credential-rule provider on this page. What the manifest declares:

FieldDeclared value
Provider / labelnetbox / NetBox, supports_rules: true
Auth methodone: api_token ("API token"), credential kind api_token, a single required secret api_token password field, TLS policies verify and skip_verify
Purposeinventory_sync
Scope typesagent, gateway, partition
Rule controlsallowed ports, controller host, target query, transport
Rule defaultstarget query in:devices metadata.netbox_candidate:true, scope type agent, allowed ports 443, 8443, TLS policy verify
Provisioningtarget_policy, consumer netbox-inventory, grant type netbox_api_token resolved at the control plane with a 300 second TTL

Delivery follows the ordinary path in How a rule reaches a plugin: the stored assignment row carries api_token_secret_ref, credential_rule_id and the grant envelope rather than the token, and because the grant's resolution location is the control plane, config delivery resolves that reference into api_token in the config the agent hands the plugin. base_url is injected from the rule's controller host -- the template reads rule metadata base_url, then host, then controller_host, so an operator-set base_url wins and a BASE_PATH deployment (https://tools.example.com/netbox) can be expressed. insecure_skip_verify is derived from the rule's TLS policy being skip_verify, and page_size / timeout_ms default to 100 / 30000.

Two things to know before planning against it. The default target query keys on metadata.netbox_candidate, and nothing in the tree writes that key today -- unlike proxmox_candidate, which the mapper stamps -- so the default matches nothing until you replace it with a query that selects the agent's NetBox host. And a rule renders the plugin's flat single-source fields, not a sources[] entry; the plugin reads sources[] first and falls back to the flat fields only when it is absent, so an assignment that still has a non-empty sources[] ignores everything the rule delivers. Clear sources[] when you move a single-source assignment onto a rule, and keep multi-source assignments on hand-entered parameters.

On 1.4.46 there is no NetBox entry under New Credential or New Rule, no netbox provider, and no inventory_sync purpose -- the token is a plugin assignment parameter and there is no rule to move it to.

First release containing the profile: <first-release>.

VulnCheck

VulnCheck is credential-only. Core downloads the KEV and nist-nvd2 feeds itself; there is no plugin package and no rule.

  1. Create the credential. New Credential -> VulnCheck - API token.
  2. Select it on the feed. Settings -> Security -> Vulnerability Feeds (/settings/security/vulnerability-feeds) -> pick the credential in the feed's credential select.

There is no environment-variable fallback. A feed with no credential reference fails with a message pointing here rather than silently reading a token from the process environment.

SNMP

SNMP is a protocol ServiceRadar speaks itself, so its descriptor is platform-owned rather than package-published. It is credential-only, scoped to agents, with one purpose (snmp_monitoring) and two auth methods:

  • community -- a community string, for v1/v2c.
  • v3 -- username, auth protocol and password, optional privacy protocol and password.
  1. Create the credential. New Credential -> SNMP - Community string (v1 / v2c) or SNMP - SNMPv3 user.
  2. Bind it on a profile. Settings -> SNMP Profiles (/settings/snmp) -> pick the credential instead of "Store on this profile (encrypted here)". A profile form can also tick "save as reusable" to promote the values typed there into a shared credential and bind to it.

Only credentials of kind snmp are offered on an SNMP profile; the resolver cannot read any other payload shape.

The one remaining file-based credential path. The Go agent still reads SNMP community strings and v3 passwords from a local file when one is present:

/etc/serviceradar/snmp.json # Linux
/usr/local/etc/serviceradar/snmp.json # macOS

This predates the credential store and is the only supported non-database credential source left. Migrate it by creating the equivalent SNMP credential above, binding it to the profile that covers those targets, and then removing the credentials from the file. Do not add new deployments to this path.

Troubleshooting

UniFi Protect reports "0 cameras, 0 streams"

Symptom. A CRITICAL plugin result whose summary is a bare count, with details.collection_error reading host error -2 (http_request).

Cause. Egress denial, not a credential problem. -2 is the host's permission-denied code. The sandbox refuses to let an allowed_domains: ["*"] wildcard expand to an IP literal -- a wildcard is a claim about DNS names, and private address space is a much more sensitive reachability claim -- so a literal-IP destination falls through to allowed_networks. If the manifest does not declare the network the controller is on, the request is denied before a socket is opened.

Fix. The plugin manifest must declare allowed_networks covering private address space.

Not in 1.4.46

allowed_networks on the UniFi Protect, Axis, OpenText and AWX manifests. Merged work adds RFC1918 plus CGNAT 100.64.0.0/10 to all four (link-local 169.254.0.0/16 is deliberately excluded: no controller is deliberately addressed there). On 1.4.46 those four manifests declare no allowed_networks at all, so every literal-IP destination is denied and there is no rule setting that changes it.

The netbox-inventory manifest is not affected -- it has declared RFC1918 allowed_networks since 1.4.46, without CGNAT.

Until the fix ships, the only in-product workaround is to give the controller a hostname that resolves for the agent, so the request matches allowed_domains: ["*"] instead of falling through to allowed_networks.

First release containing the declarations: <first-release>.

Publishing a manifest change is not enough on its own: assignments point at a specific package version, so publish, register, and re-materialise the updated package. Confirm with a fresh service_status row timestamped after the rollout, not with a successful build.

"Invalid TLS policy" when saving a rule

Symptom. Saving a UniFi Protect or Axis rule fails every time with Invalid TLS policy, and there is no TLS policy control on the form.

Cause. The control was rendered only when the selected auth method declared a non-empty tls_policies list, while validation required a valid tls_policy unconditionally. Neither manifest declares the key, so the input was never rendered, the browser submitted nothing, and the save could not succeed. Core never agreed with the form here: an undeclared list already means "any policy permitted" everywhere else.

Not in 1.4.46

The fix makes the control render whenever the provider declares transport rule controls, defaulting to the full policy set when the auth method does not narrow it, and makes an absent tls_policy on submit fall back to the resource default instead of failing the save. It is merged but unreleased.

First release containing it: <first-release>.

Until then. There is no way to save a UniFi Protect or Axis rule from the Credential Rules form on 1.4.46 or earlier, and no supported workaround: the Credential Rules page is the only route to the resource (there is no REST or GraphQL endpoint for network_credential_rules), nothing about the rule you are entering is wrong, and no combination of fields makes the save succeed, because the form cannot submit a value the validator demands. Wait for the release.

"AWX configuration invalid: api_token is required (resolved from credential broker grant)"

Symptom. An AWX assignment fails on a 60-second cadence, forever, with that message -- while awx.ping reports the controller reachable.

Cause. The AWX package is a command bridge: its token arrives per dispatch, inside the grant that dispatch carries. Its assignment params hold no token by design. An assignment without the action-only:v1 capability also gets a periodic runner, and that scheduled run invokes run_check with the assignment's own params, which can never satisfy the check.

Fix. The failure is not a missing credential -- do not go looking for one.

Not in 1.4.46

action-only:v1 on the AWX manifest. Merged work adds that capability to go/cmd/wasm-plugins/awx/plugin.yaml, so the assignment is addressable through AgentCommandBus without a periodic runner and the recurring failure stops. After it ships, republish, register, and re-materialise the package -- an existing assignment points at the old package version and keeps its runner.

On 1.4.46 the message repeats on the assignment's cadence and is cosmetic: dispatched AWX commands still carry their own grant and still work. Judge AWX health from awx.ping and from an actual dispatch, not from this result.

First release containing the capability: <first-release>.

"NetBox inventory_sync has no sources configured"

Symptom. The netbox-inventory assignment reports UNKNOWN with that message. On builds carrying the credential profile the wording is longer: NetBox inventory_sync has no source configured: set base_url and api_token, or attach a NetBox credential rule. Both mean the same thing.

Cause. The assignment named no source at all. The plugin reads sources[] from the assignment params and falls back to the flat base_url / api_token fields only when sources is absent, so this message means neither shape was populated.

Fix. Fill in sources[] on the plugin assignment under Admin -> Plugins, with source_id, base_url, and api_token per source. See NetBox: Configuration.

Related messages, all UNKNOWN, all naming the source that is short a field:

MessageMeaning
NetBox source <id> has no api_token configuredThe source entry exists, its token is blank.
NetBox source <id> has an invalid base_urlThe base URL is blank or does not parse. From <first-release> a blank one is reported separately as has no base_url configured, and a malformed one appends the reason -- has an invalid base_url: base url must be http or https.

On 1.4.46 there is no NetBox credential rule to configure any of this from, and an API token in assignment params is the placement the credential model exists to remove. Merged work adds the netbox credential profile -- see NetBox above for what it declares and what it does not fix.

"proxmox_tls_verification_required"

Symptom. Proxmox inventory enrichment resolves no scope and mints no grant; the error is proxmox_tls_verification_required.

Cause. The rule's TLS policy is skip_verify, and Proxmox inventory_enrichment requires exactly proxmox_api_token with verify. The check is fail-closed on purpose: Proxmox enrichment writes into device identity.

Fix. Set the rule's TLS policy to verify, then make verify succeed: add the PVE cluster CA to the agent's plugin_http_trusted_ca_files, and confirm the node certificate carries the agent's IP as an IP Address SAN. See Proxmox: TLS Verification and Node Certificates for the commands. Do not weaken the rule to skip_verify; that path is rejected at resolution regardless of what the form accepted, and it would not relax the plugin's transport anyway -- the agent refuses to inject a Proxmox grant into a request that skips verification.

The same requirement applies to console_access when the auth method is proxmox_api_token. SSH-backed console rules are checked against the SSH host key policy instead, and fail with proxmox_ssh_host_key_verification_required.

A rule saves but nothing happens

Work through Preview first:

Preview saysMeaning
Matched 0The target query returns nothing. Test it on the Devices page.
Matched > 0, In Scope 0The scope is wrong -- the matched devices belong to a different agent, gateway, or partition.
In Scope > 0, no consumersMaterialisation has not run yet, or no approved package provides that purpose. The dry run flags no approved package.
Conflicts listedAnother rule at the same or lower priority is winning.

If Consumers lists assignments but the plugin still fails, the problem is past the rule: check the plugin result's details and the agent log for the paired HTTP host-call entries.